mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-12 15:26:28 +01:00
Compare commits
1 Commits
feature/ru
...
shawn/fixI
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f04a59d0c8 |
18
.github/actions/spell-check/expect.txt
vendored
18
.github/actions/spell-check/expect.txt
vendored
@@ -215,7 +215,6 @@ cim
|
||||
CImage
|
||||
cla
|
||||
CLASSDC
|
||||
classmethod
|
||||
CLASSNOTAVAILABLE
|
||||
CLEARTYPE
|
||||
clickable
|
||||
@@ -365,9 +364,7 @@ DEFAULTFLAGS
|
||||
DEFAULTICON
|
||||
defaultlib
|
||||
DEFAULTONLY
|
||||
DEFAULTSIZE
|
||||
DEFAULTTONEAREST
|
||||
Defaulttonearest
|
||||
DEFAULTTONULL
|
||||
DEFAULTTOPRIMARY
|
||||
DEFERERASE
|
||||
@@ -832,11 +829,9 @@ ITHUMBNAIL
|
||||
IUI
|
||||
IUWP
|
||||
IWIC
|
||||
jeli
|
||||
jfif
|
||||
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
|
||||
jjw
|
||||
JOBOBJECT
|
||||
jobject
|
||||
jpe
|
||||
jpnime
|
||||
@@ -870,7 +865,6 @@ lastcodeanalysissucceeded
|
||||
LASTEXITCODE
|
||||
LAYOUTRTL
|
||||
lbl
|
||||
Lbuttondown
|
||||
LCh
|
||||
lcid
|
||||
LCIDTo
|
||||
@@ -993,7 +987,6 @@ maxversiontested
|
||||
mber
|
||||
MBM
|
||||
MBR
|
||||
Mbuttondown
|
||||
MDICHILD
|
||||
MDL
|
||||
mdtext
|
||||
@@ -1452,7 +1445,6 @@ RAWINPUTHEADER
|
||||
RAWMODE
|
||||
RAWPATH
|
||||
rbhid
|
||||
Rbuttondown
|
||||
rclsid
|
||||
RCZOOMIT
|
||||
remotedesktop
|
||||
@@ -1488,7 +1480,6 @@ remoteip
|
||||
Removelnk
|
||||
renamable
|
||||
RENAMEONCOLLISION
|
||||
RENDERFULLCONTENT
|
||||
reparented
|
||||
reparenting
|
||||
reportfileaccesses
|
||||
@@ -1603,7 +1594,7 @@ sharpfuzz
|
||||
SHCNE
|
||||
SHCNF
|
||||
SHCONTF
|
||||
shcore
|
||||
Shcore
|
||||
shellapi
|
||||
SHELLDETAILS
|
||||
SHELLDLL
|
||||
@@ -1702,7 +1693,6 @@ srw
|
||||
srwlock
|
||||
sse
|
||||
ssf
|
||||
sszzz
|
||||
STACKFRAME
|
||||
stackoverflow
|
||||
STARTF
|
||||
@@ -1713,7 +1703,6 @@ STARTUPINFOW
|
||||
startupscreen
|
||||
STATFLAG
|
||||
STATICEDGE
|
||||
staticmethod
|
||||
STATSTG
|
||||
stdafx
|
||||
STDAPI
|
||||
@@ -1757,7 +1746,6 @@ svgz
|
||||
SVSI
|
||||
SWFO
|
||||
SWP
|
||||
Swp
|
||||
SWPNOSIZE
|
||||
SWPNOZORDER
|
||||
SWRESTORE
|
||||
@@ -1777,7 +1765,6 @@ syskeydown
|
||||
SYSKEYUP
|
||||
SYSLIB
|
||||
SYSMENU
|
||||
Sysmenu
|
||||
systemai
|
||||
SYSTEMAPPS
|
||||
SYSTEMMODAL
|
||||
@@ -1823,6 +1810,7 @@ TILEDWINDOW
|
||||
TILLSON
|
||||
timedate
|
||||
timediff
|
||||
timeunion
|
||||
timeutil
|
||||
TITLEBARINFO
|
||||
Titlecase
|
||||
@@ -1880,7 +1868,6 @@ uild
|
||||
uitests
|
||||
UITo
|
||||
ULONGLONG
|
||||
Ultrawide
|
||||
ums
|
||||
UMax
|
||||
UMin
|
||||
@@ -2103,7 +2090,6 @@ Wwanpp
|
||||
xap
|
||||
XAxis
|
||||
XButton
|
||||
Xbuttondown
|
||||
xclip
|
||||
xcopy
|
||||
XDeployment
|
||||
|
||||
3
.github/actions/spell-check/patterns.txt
vendored
3
.github/actions/spell-check/patterns.txt
vendored
@@ -273,6 +273,3 @@ St&yle
|
||||
# Usernames with numbers
|
||||
# 0x6f677548 is user name but user folder causes a flag
|
||||
\bx6f677548\b
|
||||
|
||||
# Microsoft Store URLs and product IDs
|
||||
ms-windows-store://\S+
|
||||
|
||||
80
.github/copilot-instructions.md
vendored
80
.github/copilot-instructions.md
vendored
@@ -1,45 +1,59 @@
|
||||
---
|
||||
description: 'PowerToys AI contributor guidance'
|
||||
description: PowerToys AI contributor guidance.
|
||||
applyTo: pullRequests
|
||||
---
|
||||
|
||||
# PowerToys – Copilot Instructions
|
||||
# PowerToys - Copilot guide (concise)
|
||||
|
||||
Concise guidance for AI contributions. For complete details, see [AGENTS.md](../AGENTS.md).
|
||||
This is the top-level guide for AI changes. Keep edits small, follow existing patterns, and cite exact paths in PRs.
|
||||
|
||||
## Quick Reference
|
||||
# Repo map (1-line per area)
|
||||
- Core apps: `src/runner/**` (tray/loader), `src/settings-ui/**` (Settings app)
|
||||
- Shared libs: `src/common/**`
|
||||
- Modules: `src/modules/*` (one per utility; Command Palette in `src/modules/cmdpal/**`)
|
||||
- Build tools/docs: `tools/**`, `doc/devdocs/**`
|
||||
|
||||
- **Build**: `tools\build\build-essentials.cmd` (first time), then `tools\build\build.cmd`
|
||||
- **Tests**: Find `<Product>*UnitTests` project, build it, run via VS Test Explorer
|
||||
- **Exit code 0 = success** – do not proceed if build fails
|
||||
# Build and test (defaults)
|
||||
- Prerequisites: Visual Studio 2022 17.4+, minimal Windows 10 1803+.
|
||||
- Build discipline:
|
||||
- One terminal per operation (build -> test). Do not switch or open new ones mid-flow.
|
||||
- After making changes, `cd` to the project folder that changed (`.csproj`/`.vcxproj`).
|
||||
- Use scripts to build, synchronously block and wait in foreground for completion: `tools/build/build.ps1|.cmd` (current folder), `build-essentials.*` (once per brand new build for missing nuget packages).
|
||||
- Treat build exit code 0 as success; any non-zero exit code is a failure. Read the errors log in the build folder (such as `build.*.*.errors.log`) and surface problems.
|
||||
- Do not start tests or launch Runner until the previous step succeeded.
|
||||
- Tests (fast and targeted):
|
||||
- Find the test project by product code prefix (for example FancyZones, AdvancedPaste). Look for a sibling folder or one to two levels up named like `<Product>*UnitTests` or `<Product>*UITests`.
|
||||
- Build the test project, wait for exit, then run only those tests via VS Test Explorer or `vstest.console.exe` with filters. Avoid `dotnet test` in this repo.
|
||||
- Add or adjust tests when changing behavior; if skipped, state why (for example comment-only or string rename).
|
||||
|
||||
## Key Rules
|
||||
# Pull requests (expectations)
|
||||
- Atomic: one logical change; no drive-by refactors.
|
||||
- Describe: problem, approach, risk, test evidence.
|
||||
- List: touched paths if not obvious.
|
||||
|
||||
- One terminal per operation (build → test)
|
||||
- Atomic PRs: one logical change, no drive-by refactors
|
||||
- Add tests when changing behavior
|
||||
- Keep hot paths quiet (no logging in hooks/tight loops)
|
||||
# When to ask for clarification
|
||||
- Ambiguous spec after scanning relevant docs (see below).
|
||||
- Cross-module impact (shared enum or struct) not clear.
|
||||
- Security, elevation, or installer changes.
|
||||
|
||||
## Style Enforcement
|
||||
# Logging (use existing stacks)
|
||||
- C++ logging lives in `src/common/logger/**` (`Logger::info`, `Logger::warn`, `Logger::error`, `Logger::debug`). Keep hot paths quiet (hooks, tight loops).
|
||||
- C# logging goes through `ManagedCommon.Logger` (`LogInfo`, `LogWarning`, `LogError`, `LogDebug`, `LogTrace`). Some UIs use injected `ILogger` via `LoggerInstance.Logger`.
|
||||
|
||||
- C#: `src/.editorconfig`, StyleCop.Analyzers
|
||||
- C++: `src/.clang-format`
|
||||
- XAML: XamlStyler
|
||||
# Docs to consult
|
||||
- `tools/build/BUILD-GUIDELINES.md`
|
||||
- `doc/devdocs/core/architecture.md`
|
||||
- `doc/devdocs/core/runner.md`
|
||||
- `doc/devdocs/core/settings/readme.md`
|
||||
- `doc/devdocs/modules/readme.md`
|
||||
|
||||
## When to Ask for Clarification
|
||||
# Language style rules
|
||||
- Always enforce repo analyzers: root `.editorconfig` plus any `stylecop.json`.
|
||||
- C# code follows StyleCop.Analyzers and Microsoft.CodeAnalysis.NetAnalyzers.
|
||||
- C++ code honors `.clang-format` plus `.clang-tidy` (modernize/cppcoreguidelines/readability).
|
||||
- Markdown files wrap at 80 characters and use ATX headers with fenced code blocks that include language tags.
|
||||
- YAML files indent two spaces and add comments for complex settings while keeping keys clear.
|
||||
- PowerShell scripts use Verb-Noun names and prefer single-quoted literals while documenting parameters and satisfying PSScriptAnalyzer.
|
||||
|
||||
- Ambiguous spec after scanning docs
|
||||
- Cross-module impact unclear
|
||||
- Security, elevation, or installer changes
|
||||
|
||||
## Component-Specific Instructions
|
||||
|
||||
These are auto-applied based on file location:
|
||||
- [Runner & Settings UI](.github/instructions/runner-settings-ui.instructions.md)
|
||||
- [Common Libraries](.github/instructions/common-libraries.instructions.md)
|
||||
|
||||
## Detailed Documentation
|
||||
|
||||
- [AGENTS.md](../AGENTS.md) – Full contributor guide
|
||||
- [Build Guidelines](../tools/build/BUILD-GUIDELINES.md)
|
||||
- [Architecture](../doc/devdocs/core/architecture.md)
|
||||
- [Coding Style](../doc/devdocs/development/style.md)
|
||||
# Done checklist (self review before finishing)
|
||||
- Build clean? Tests updated or passed? No unintended formatting? Any new dependency? Documented skips?
|
||||
|
||||
791
.github/instructions/agents.instructions.md
vendored
791
.github/instructions/agents.instructions.md
vendored
@@ -1,791 +0,0 @@
|
||||
---
|
||||
description: 'Guidelines for creating custom agent files for GitHub Copilot'
|
||||
applyTo: '**/*.agent.md'
|
||||
---
|
||||
|
||||
# Custom Agent File Guidelines
|
||||
|
||||
Instructions for creating effective and maintainable custom agent files that provide specialized expertise for specific development tasks in GitHub Copilot.
|
||||
|
||||
## Project Context
|
||||
|
||||
- Target audience: Developers creating custom agents for GitHub Copilot
|
||||
- File format: Markdown with YAML frontmatter
|
||||
- File naming convention: lowercase with hyphens (e.g., `test-specialist.agent.md`)
|
||||
- Location: `.github/agents/` directory (repository-level) or `agents/` directory (organization/enterprise-level)
|
||||
- Purpose: Define specialized agents with tailored expertise, tools, and instructions for specific tasks
|
||||
- Official documentation: https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/create-custom-agents
|
||||
|
||||
## Required Frontmatter
|
||||
|
||||
Every agent file must include YAML frontmatter with the following fields:
|
||||
|
||||
```yaml
|
||||
---
|
||||
description: 'Brief description of the agent purpose and capabilities'
|
||||
name: 'Agent Display Name'
|
||||
tools: ['read', 'edit', 'search']
|
||||
model: 'Claude Sonnet 4.5'
|
||||
target: 'vscode'
|
||||
infer: true
|
||||
---
|
||||
```
|
||||
|
||||
### Core Frontmatter Properties
|
||||
|
||||
#### **description** (REQUIRED)
|
||||
- Single-quoted string, clearly stating the agent's purpose and domain expertise
|
||||
- Should be concise (50-150 characters) and actionable
|
||||
- Example: `'Focuses on test coverage, quality, and testing best practices'`
|
||||
|
||||
#### **name** (OPTIONAL)
|
||||
- Display name for the agent in the UI
|
||||
- If omitted, defaults to filename (without `.md` or `.agent.md`)
|
||||
- Use title case and be descriptive
|
||||
- Example: `'Testing Specialist'`
|
||||
|
||||
#### **tools** (OPTIONAL)
|
||||
- List of tool names or aliases the agent can use
|
||||
- Supports comma-separated string or YAML array format
|
||||
- If omitted, agent has access to all available tools
|
||||
- See "Tool Configuration" section below for details
|
||||
|
||||
#### **model** (STRONGLY RECOMMENDED)
|
||||
- Specifies which AI model the agent should use
|
||||
- Supported in VS Code, JetBrains IDEs, Eclipse, and Xcode
|
||||
- Example: `'Claude Sonnet 4.5'`, `'gpt-4'`, `'gpt-4o'`
|
||||
- Choose based on agent complexity and required capabilities
|
||||
|
||||
#### **target** (OPTIONAL)
|
||||
- Specifies target environment: `'vscode'` or `'github-copilot'`
|
||||
- If omitted, agent is available in both environments
|
||||
- Use when agent has environment-specific features
|
||||
|
||||
#### **infer** (OPTIONAL)
|
||||
- Boolean controlling whether Copilot can automatically use this agent based on context
|
||||
- Default: `true` if omitted
|
||||
- Set to `false` to require manual agent selection
|
||||
|
||||
#### **metadata** (OPTIONAL, GitHub.com only)
|
||||
- Object with name-value pairs for agent annotation
|
||||
- Example: `metadata: { category: 'testing', version: '1.0' }`
|
||||
- Not supported in VS Code
|
||||
|
||||
#### **mcp-servers** (OPTIONAL, Organization/Enterprise only)
|
||||
- Configure MCP servers available only to this agent
|
||||
- Only supported for organization/enterprise level agents
|
||||
- See "MCP Server Configuration" section below
|
||||
|
||||
## Tool Configuration
|
||||
|
||||
### Tool Specification Strategies
|
||||
|
||||
**Enable all tools** (default):
|
||||
```yaml
|
||||
# Omit tools property entirely, or use:
|
||||
tools: ['*']
|
||||
```
|
||||
|
||||
**Enable specific tools**:
|
||||
```yaml
|
||||
tools: ['read', 'edit', 'search', 'execute']
|
||||
```
|
||||
|
||||
**Enable MCP server tools**:
|
||||
```yaml
|
||||
tools: ['read', 'edit', 'github/*', 'playwright/navigate']
|
||||
```
|
||||
|
||||
**Disable all tools**:
|
||||
```yaml
|
||||
tools: []
|
||||
```
|
||||
|
||||
### Standard Tool Aliases
|
||||
|
||||
All aliases are case-insensitive:
|
||||
|
||||
| Alias | Alternative Names | Category | Description |
|
||||
|-------|------------------|----------|-------------|
|
||||
| `execute` | shell, Bash, powershell | Shell execution | Execute commands in appropriate shell |
|
||||
| `read` | Read, NotebookRead, view | File reading | Read file contents |
|
||||
| `edit` | Edit, MultiEdit, Write, NotebookEdit | File editing | Edit and modify files |
|
||||
| `search` | Grep, Glob, search | Code search | Search for files or text in files |
|
||||
| `agent` | custom-agent, Task | Agent invocation | Invoke other custom agents |
|
||||
| `web` | WebSearch, WebFetch | Web access | Fetch web content and search |
|
||||
| `todo` | TodoWrite | Task management | Create and manage task lists (VS Code only) |
|
||||
|
||||
### Built-in MCP Server Tools
|
||||
|
||||
**GitHub MCP Server**:
|
||||
```yaml
|
||||
tools: ['github/*'] # All GitHub tools
|
||||
tools: ['github/get_file_contents', 'github/search_repositories'] # Specific tools
|
||||
```
|
||||
- All read-only tools available by default
|
||||
- Token scoped to source repository
|
||||
|
||||
**Playwright MCP Server**:
|
||||
```yaml
|
||||
tools: ['playwright/*'] # All Playwright tools
|
||||
tools: ['playwright/navigate', 'playwright/screenshot'] # Specific tools
|
||||
```
|
||||
- Configured to access localhost only
|
||||
- Useful for browser automation and testing
|
||||
|
||||
### Tool Selection Best Practices
|
||||
|
||||
- **Principle of Least Privilege**: Only enable tools necessary for the agent's purpose
|
||||
- **Security**: Limit `execute` access unless explicitly required
|
||||
- **Focus**: Fewer tools = clearer agent purpose and better performance
|
||||
- **Documentation**: Comment why specific tools are required for complex configurations
|
||||
|
||||
## Sub-Agent Invocation (Agent Orchestration)
|
||||
|
||||
Agents can invoke other agents using `runSubagent` to orchestrate multi-step workflows.
|
||||
|
||||
### How It Works
|
||||
|
||||
Include `agent` in tools list to enable sub-agent invocation:
|
||||
|
||||
```yaml
|
||||
tools: ['read', 'edit', 'search', 'agent']
|
||||
```
|
||||
|
||||
Then invoke other agents with `runSubagent`:
|
||||
|
||||
```javascript
|
||||
const result = await runSubagent({
|
||||
description: 'What this step does',
|
||||
prompt: `You are the [Specialist] specialist.
|
||||
|
||||
Context:
|
||||
- Parameter: ${parameterValue}
|
||||
- Input: ${inputPath}
|
||||
- Output: ${outputPath}
|
||||
|
||||
Task:
|
||||
1. Do the specific work
|
||||
2. Write results to output location
|
||||
3. Return summary of completion`
|
||||
});
|
||||
```
|
||||
|
||||
### Basic Pattern
|
||||
|
||||
Structure each sub-agent call with:
|
||||
|
||||
1. **description**: Clear one-line purpose of the sub-agent invocation
|
||||
2. **prompt**: Detailed instructions with substituted variables
|
||||
|
||||
The prompt should include:
|
||||
- Who the sub-agent is (specialist role)
|
||||
- What context it needs (parameters, paths)
|
||||
- What to do (concrete tasks)
|
||||
- Where to write output
|
||||
- What to return (summary)
|
||||
|
||||
### Example: Multi-Step Processing
|
||||
|
||||
```javascript
|
||||
// Step 1: Process data
|
||||
const processing = await runSubagent({
|
||||
description: 'Transform raw input data',
|
||||
prompt: `You are the Data Processor specialist.
|
||||
|
||||
Project: ${projectName}
|
||||
Input: ${basePath}/raw/
|
||||
Output: ${basePath}/processed/
|
||||
|
||||
Task:
|
||||
1. Read all files from input directory
|
||||
2. Apply transformations
|
||||
3. Write processed files to output
|
||||
4. Create summary: ${basePath}/processed/summary.md
|
||||
|
||||
Return: Number of files processed and any issues found`
|
||||
});
|
||||
|
||||
// Step 2: Analyze (depends on Step 1)
|
||||
const analysis = await runSubagent({
|
||||
description: 'Analyze processed data',
|
||||
prompt: `You are the Data Analyst specialist.
|
||||
|
||||
Project: ${projectName}
|
||||
Input: ${basePath}/processed/
|
||||
Output: ${basePath}/analysis/
|
||||
|
||||
Task:
|
||||
1. Read processed files from input
|
||||
2. Generate analysis report
|
||||
3. Write to: ${basePath}/analysis/report.md
|
||||
|
||||
Return: Key findings and identified patterns`
|
||||
});
|
||||
```
|
||||
|
||||
### Key Points
|
||||
|
||||
- **Pass variables in prompts**: Use `${variableName}` for all dynamic values
|
||||
- **Keep prompts focused**: Clear, specific tasks for each sub-agent
|
||||
- **Return summaries**: Each sub-agent should report what it accomplished
|
||||
- **Sequential execution**: Use `await` to maintain order when steps depend on each other
|
||||
- **Error handling**: Check results before proceeding to dependent steps
|
||||
|
||||
### ⚠️ Tool Availability Requirement
|
||||
|
||||
**Critical**: If a sub-agent requires specific tools (e.g., `edit`, `execute`, `search`), the orchestrator must include those tools in its own `tools` list. Sub-agents cannot access tools that aren't available to their parent orchestrator.
|
||||
|
||||
**Example**:
|
||||
```yaml
|
||||
# If your sub-agents need to edit files, execute commands, or search code
|
||||
tools: ['read', 'edit', 'search', 'execute', 'agent']
|
||||
```
|
||||
|
||||
The orchestrator's tool permissions act as a ceiling for all invoked sub-agents. Plan your tool list carefully to ensure all sub-agents have the tools they need.
|
||||
|
||||
### ⚠️ Important Limitation
|
||||
|
||||
**Sub-agent orchestration is NOT suitable for large-scale data processing.** Avoid using `runSubagent` when:
|
||||
- Processing hundreds or thousands of files
|
||||
- Handling large datasets
|
||||
- Performing bulk transformations on big codebases
|
||||
- Orchestrating more than 5-10 sequential steps
|
||||
|
||||
Each sub-agent call adds latency and context overhead. For high-volume processing, implement logic directly in a single agent instead. Use orchestration only for coordinating specialized tasks on focused, manageable datasets.
|
||||
|
||||
## Agent Prompt Structure
|
||||
|
||||
The markdown content below the frontmatter defines the agent's behavior, expertise, and instructions. Well-structured prompts typically include:
|
||||
|
||||
1. **Agent Identity and Role**: Who the agent is and its primary role
|
||||
2. **Core Responsibilities**: What specific tasks the agent performs
|
||||
3. **Approach and Methodology**: How the agent works to accomplish tasks
|
||||
4. **Guidelines and Constraints**: What to do/avoid and quality standards
|
||||
5. **Output Expectations**: Expected output format and quality
|
||||
|
||||
### Prompt Writing Best Practices
|
||||
|
||||
- **Be Specific and Direct**: Use imperative mood ("Analyze", "Generate"); avoid vague terms
|
||||
- **Define Boundaries**: Clearly state scope limits and constraints
|
||||
- **Include Context**: Explain domain expertise and reference relevant frameworks
|
||||
- **Focus on Behavior**: Describe how the agent should think and work
|
||||
- **Use Structured Format**: Headers, bullets, and lists make prompts scannable
|
||||
|
||||
## Variable Definition and Extraction
|
||||
|
||||
Agents can define dynamic parameters to extract values from user input and use them throughout the agent's behavior and sub-agent communications. This enables flexible, context-aware agents that adapt to user-provided data.
|
||||
|
||||
### When to Use Variables
|
||||
|
||||
**Use variables when**:
|
||||
- Agent behavior depends on user input
|
||||
- Need to pass dynamic values to sub-agents
|
||||
- Want to make agents reusable across different contexts
|
||||
- Require parameterized workflows
|
||||
- Need to track or reference user-provided context
|
||||
|
||||
**Examples**:
|
||||
- Extract project name from user prompt
|
||||
- Capture certification name for pipeline processing
|
||||
- Identify file paths or directories
|
||||
- Extract configuration options
|
||||
- Parse feature names or module identifiers
|
||||
|
||||
### Variable Declaration Pattern
|
||||
|
||||
Define variables section early in the agent prompt to document expected parameters:
|
||||
|
||||
```markdown
|
||||
# Agent Name
|
||||
|
||||
## Dynamic Parameters
|
||||
|
||||
- **Parameter Name**: Description and usage
|
||||
- **Another Parameter**: How it's extracted and used
|
||||
|
||||
## Your Mission
|
||||
|
||||
Process [PARAMETER_NAME] to accomplish [task].
|
||||
```
|
||||
|
||||
### Variable Extraction Methods
|
||||
|
||||
#### 1. **Explicit User Input**
|
||||
Ask the user to provide the variable if not detected in the prompt:
|
||||
|
||||
```markdown
|
||||
## Your Mission
|
||||
|
||||
Process the project by analyzing your codebase.
|
||||
|
||||
### Step 1: Identify Project
|
||||
If no project name is provided, **ASK THE USER** for:
|
||||
- Project name or identifier
|
||||
- Base path or directory location
|
||||
- Configuration type (if applicable)
|
||||
|
||||
Use this information to contextualize all subsequent tasks.
|
||||
```
|
||||
|
||||
#### 2. **Implicit Extraction from Prompt**
|
||||
Automatically extract variables from the user's natural language input:
|
||||
|
||||
```javascript
|
||||
// Example: Extract certification name from user input
|
||||
const userInput = "Process My Certification";
|
||||
|
||||
// Extract key information
|
||||
const certificationName = extractCertificationName(userInput);
|
||||
// Result: "My Certification"
|
||||
|
||||
const basePath = `certifications/${certificationName}`;
|
||||
// Result: "certifications/My Certification"
|
||||
```
|
||||
|
||||
#### 3. **Contextual Variable Resolution**
|
||||
Use file context or workspace information to derive variables:
|
||||
|
||||
```markdown
|
||||
## Variable Resolution Strategy
|
||||
|
||||
1. **From User Prompt**: First, look for explicit mentions in user input
|
||||
2. **From File Context**: Check current file name or path
|
||||
3. **From Workspace**: Use workspace folder or active project
|
||||
4. **From Settings**: Reference configuration files
|
||||
5. **Ask User**: If all else fails, request missing information
|
||||
```
|
||||
|
||||
### Using Variables in Agent Prompts
|
||||
|
||||
#### Variable Substitution in Instructions
|
||||
|
||||
Use template variables in agent prompts to make them dynamic:
|
||||
|
||||
```markdown
|
||||
# Agent Name
|
||||
|
||||
## Dynamic Parameters
|
||||
- **Project Name**: ${projectName}
|
||||
- **Base Path**: ${basePath}
|
||||
- **Output Directory**: ${outputDir}
|
||||
|
||||
## Your Mission
|
||||
|
||||
Process the **${projectName}** project located at `${basePath}`.
|
||||
|
||||
## Process Steps
|
||||
|
||||
1. Read input from: `${basePath}/input/`
|
||||
2. Process files according to project configuration
|
||||
3. Write results to: `${outputDir}/`
|
||||
4. Generate summary report
|
||||
|
||||
## Quality Standards
|
||||
|
||||
- Maintain project-specific coding standards for **${projectName}**
|
||||
- Follow directory structure: `${basePath}/[structure]`
|
||||
```
|
||||
|
||||
#### Passing Variables to Sub-Agents
|
||||
|
||||
When invoking a sub-agent, pass all context through template variables in the prompt:
|
||||
|
||||
```javascript
|
||||
// Extract and prepare variables
|
||||
const basePath = `projects/${projectName}`;
|
||||
const inputPath = `${basePath}/src/`;
|
||||
const outputPath = `${basePath}/docs/`;
|
||||
|
||||
// Pass to sub-agent with all variables substituted
|
||||
const result = await runSubagent({
|
||||
description: 'Generate project documentation',
|
||||
prompt: `You are the Documentation specialist.
|
||||
|
||||
Project: ${projectName}
|
||||
Input: ${inputPath}
|
||||
Output: ${outputPath}
|
||||
|
||||
Task:
|
||||
1. Read source files from ${inputPath}
|
||||
2. Generate comprehensive documentation
|
||||
3. Write to ${outputPath}/index.md
|
||||
4. Include code examples and usage guides
|
||||
|
||||
Return: Summary of documentation generated (file count, word count)`
|
||||
});
|
||||
```
|
||||
|
||||
The sub-agent receives all necessary context embedded in the prompt. Variables are resolved before sending the prompt, so the sub-agent works with concrete paths and values, not variable placeholders.
|
||||
|
||||
### Real-World Example: Code Review Orchestrator
|
||||
|
||||
Example of a simple orchestrator that validates code through multiple specialized agents:
|
||||
|
||||
```javascript
|
||||
async function reviewCodePipeline(repositoryName, prNumber) {
|
||||
const basePath = `projects/${repositoryName}/pr-${prNumber}`;
|
||||
|
||||
// Step 1: Security Review
|
||||
const security = await runSubagent({
|
||||
description: 'Scan for security vulnerabilities',
|
||||
prompt: `You are the Security Reviewer specialist.
|
||||
|
||||
Repository: ${repositoryName}
|
||||
PR: ${prNumber}
|
||||
Code: ${basePath}/changes/
|
||||
|
||||
Task:
|
||||
1. Scan code for OWASP Top 10 vulnerabilities
|
||||
2. Check for injection attacks, auth flaws
|
||||
3. Write findings to ${basePath}/security-review.md
|
||||
|
||||
Return: List of critical, high, and medium issues found`
|
||||
});
|
||||
|
||||
// Step 2: Test Coverage Check
|
||||
const coverage = await runSubagent({
|
||||
description: 'Verify test coverage for changes',
|
||||
prompt: `You are the Test Coverage specialist.
|
||||
|
||||
Repository: ${repositoryName}
|
||||
PR: ${prNumber}
|
||||
Changes: ${basePath}/changes/
|
||||
|
||||
Task:
|
||||
1. Analyze code coverage for modified files
|
||||
2. Identify untested critical paths
|
||||
3. Write report to ${basePath}/coverage-report.md
|
||||
|
||||
Return: Current coverage percentage and gaps`
|
||||
});
|
||||
|
||||
// Step 3: Aggregate Results
|
||||
const finalReport = await runSubagent({
|
||||
description: 'Compile all review findings',
|
||||
prompt: `You are the Review Aggregator specialist.
|
||||
|
||||
Repository: ${repositoryName}
|
||||
Reports: ${basePath}/*.md
|
||||
|
||||
Task:
|
||||
1. Read all review reports from ${basePath}/
|
||||
2. Synthesize findings into single report
|
||||
3. Determine overall verdict (APPROVE/NEEDS_FIXES/BLOCK)
|
||||
4. Write to ${basePath}/final-review.md
|
||||
|
||||
Return: Final verdict and executive summary`
|
||||
});
|
||||
|
||||
return finalReport;
|
||||
}
|
||||
```
|
||||
|
||||
This pattern applies to any orchestration scenario: extract variables, call sub-agents with clear context, await results.
|
||||
|
||||
|
||||
### Variable Best Practices
|
||||
|
||||
#### 1. **Clear Documentation**
|
||||
Always document what variables are expected:
|
||||
|
||||
```markdown
|
||||
## Required Variables
|
||||
- **projectName**: The name of the project (string, required)
|
||||
- **basePath**: Root directory for project files (path, required)
|
||||
|
||||
## Optional Variables
|
||||
- **mode**: Processing mode - quick/standard/detailed (enum, default: standard)
|
||||
- **outputFormat**: Output format - markdown/json/html (enum, default: markdown)
|
||||
|
||||
## Derived Variables
|
||||
- **outputDir**: Automatically set to ${basePath}/output
|
||||
- **logFile**: Automatically set to ${basePath}/.log.md
|
||||
```
|
||||
|
||||
#### 2. **Consistent Naming**
|
||||
Use consistent variable naming conventions:
|
||||
|
||||
```javascript
|
||||
// Good: Clear, descriptive naming
|
||||
const variables = {
|
||||
projectName, // What project to work on
|
||||
basePath, // Where project files are located
|
||||
outputDirectory, // Where to save results
|
||||
processingMode, // How to process (detail level)
|
||||
configurationPath // Where config files are
|
||||
};
|
||||
|
||||
// Avoid: Ambiguous or inconsistent
|
||||
const bad_variables = {
|
||||
name, // Too generic
|
||||
path, // Unclear which path
|
||||
mode, // Too short
|
||||
config // Too vague
|
||||
};
|
||||
```
|
||||
|
||||
#### 3. **Validation and Constraints**
|
||||
Document valid values and constraints:
|
||||
|
||||
```markdown
|
||||
## Variable Constraints
|
||||
|
||||
**projectName**:
|
||||
- Type: string (alphanumeric, hyphens, underscores allowed)
|
||||
- Length: 1-100 characters
|
||||
- Required: yes
|
||||
- Pattern: `/^[a-zA-Z0-9_-]+$/`
|
||||
|
||||
**processingMode**:
|
||||
- Type: enum
|
||||
- Valid values: "quick" (< 5min), "standard" (5-15min), "detailed" (15+ min)
|
||||
- Default: "standard"
|
||||
- Required: no
|
||||
```
|
||||
|
||||
## MCP Server Configuration (Organization/Enterprise Only)
|
||||
|
||||
MCP servers extend agent capabilities with additional tools. Only supported for organization and enterprise-level agents.
|
||||
|
||||
### Configuration Format
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: my-custom-agent
|
||||
description: 'Agent with MCP integration'
|
||||
tools: ['read', 'edit', 'custom-mcp/tool-1']
|
||||
mcp-servers:
|
||||
custom-mcp:
|
||||
type: 'local'
|
||||
command: 'some-command'
|
||||
args: ['--arg1', '--arg2']
|
||||
tools: ["*"]
|
||||
env:
|
||||
ENV_VAR_NAME: ${{ secrets.API_KEY }}
|
||||
---
|
||||
```
|
||||
|
||||
### MCP Server Properties
|
||||
|
||||
- **type**: Server type (`'local'` or `'stdio'`)
|
||||
- **command**: Command to start the MCP server
|
||||
- **args**: Array of command arguments
|
||||
- **tools**: Tools to enable from this server (`["*"]` for all)
|
||||
- **env**: Environment variables (supports secrets)
|
||||
|
||||
### Environment Variables and Secrets
|
||||
|
||||
Secrets must be configured in repository settings under "copilot" environment.
|
||||
|
||||
**Supported syntax**:
|
||||
```yaml
|
||||
env:
|
||||
# Environment variable only
|
||||
VAR_NAME: COPILOT_MCP_ENV_VAR_VALUE
|
||||
|
||||
# Variable with header
|
||||
VAR_NAME: $COPILOT_MCP_ENV_VAR_VALUE
|
||||
VAR_NAME: ${COPILOT_MCP_ENV_VAR_VALUE}
|
||||
|
||||
# GitHub Actions-style (YAML only)
|
||||
VAR_NAME: ${{ secrets.COPILOT_MCP_ENV_VAR_VALUE }}
|
||||
VAR_NAME: ${{ var.COPILOT_MCP_ENV_VAR_VALUE }}
|
||||
```
|
||||
|
||||
## File Organization and Naming
|
||||
|
||||
### Repository-Level Agents
|
||||
- Location: `.github/agents/`
|
||||
- Scope: Available only in the specific repository
|
||||
- Access: Uses repository-configured MCP servers
|
||||
|
||||
### Organization/Enterprise-Level Agents
|
||||
- Location: `.github-private/agents/` (then move to `agents/` root)
|
||||
- Scope: Available across all repositories in org/enterprise
|
||||
- Access: Can configure dedicated MCP servers
|
||||
|
||||
### Naming Conventions
|
||||
- Use lowercase with hyphens: `test-specialist.agent.md`
|
||||
- Name should reflect agent purpose
|
||||
- Filename becomes default agent name (if `name` not specified)
|
||||
- Allowed characters: `.`, `-`, `_`, `a-z`, `A-Z`, `0-9`
|
||||
|
||||
## Agent Processing and Behavior
|
||||
|
||||
### Versioning
|
||||
- Based on Git commit SHAs for the agent file
|
||||
- Create branches/tags for different agent versions
|
||||
- Instantiated using latest version for repository/branch
|
||||
- PR interactions use same agent version for consistency
|
||||
|
||||
### Name Conflicts
|
||||
Priority (highest to lowest):
|
||||
1. Repository-level agent
|
||||
2. Organization-level agent
|
||||
3. Enterprise-level agent
|
||||
|
||||
Lower-level configurations override higher-level ones with the same name.
|
||||
|
||||
### Tool Processing
|
||||
- `tools` list filters available tools (built-in and MCP)
|
||||
- No tools specified = all tools enabled
|
||||
- Empty list (`[]`) = all tools disabled
|
||||
- Specific list = only those tools enabled
|
||||
- Unrecognized tool names are ignored (allows environment-specific tools)
|
||||
|
||||
### MCP Server Processing Order
|
||||
1. Out-of-the-box MCP servers (e.g., GitHub MCP)
|
||||
2. Custom agent MCP configuration (org/enterprise only)
|
||||
3. Repository-level MCP configurations
|
||||
|
||||
Each level can override settings from previous levels.
|
||||
|
||||
## Agent Creation Checklist
|
||||
|
||||
### Frontmatter
|
||||
- [ ] `description` field present and descriptive (50-150 chars)
|
||||
- [ ] `description` wrapped in single quotes
|
||||
- [ ] `name` specified (optional but recommended)
|
||||
- [ ] `tools` configured appropriately (or intentionally omitted)
|
||||
- [ ] `model` specified for optimal performance
|
||||
- [ ] `target` set if environment-specific
|
||||
- [ ] `infer` set to `false` if manual selection required
|
||||
|
||||
### Prompt Content
|
||||
- [ ] Clear agent identity and role defined
|
||||
- [ ] Core responsibilities listed explicitly
|
||||
- [ ] Approach and methodology explained
|
||||
- [ ] Guidelines and constraints specified
|
||||
- [ ] Output expectations documented
|
||||
- [ ] Examples provided where helpful
|
||||
- [ ] Instructions are specific and actionable
|
||||
- [ ] Scope and boundaries clearly defined
|
||||
- [ ] Total content under 30,000 characters
|
||||
|
||||
### File Structure
|
||||
- [ ] Filename follows lowercase-with-hyphens convention
|
||||
- [ ] File placed in correct directory (`.github/agents/` or `agents/`)
|
||||
- [ ] Filename uses only allowed characters
|
||||
- [ ] File extension is `.agent.md`
|
||||
|
||||
### Quality Assurance
|
||||
- [ ] Agent purpose is unique and not duplicative
|
||||
- [ ] Tools are minimal and necessary
|
||||
- [ ] Instructions are clear and unambiguous
|
||||
- [ ] Agent has been tested with representative tasks
|
||||
- [ ] Documentation references are current
|
||||
- [ ] Security considerations addressed (if applicable)
|
||||
|
||||
## Common Agent Patterns
|
||||
|
||||
### Testing Specialist
|
||||
**Purpose**: Focus on test coverage and quality
|
||||
**Tools**: All tools (for comprehensive test creation)
|
||||
**Approach**: Analyze, identify gaps, write tests, avoid production code changes
|
||||
|
||||
### Implementation Planner
|
||||
**Purpose**: Create detailed technical plans and specifications
|
||||
**Tools**: Limited to `['read', 'search', 'edit']`
|
||||
**Approach**: Analyze requirements, create documentation, avoid implementation
|
||||
|
||||
### Code Reviewer
|
||||
**Purpose**: Review code quality and provide feedback
|
||||
**Tools**: `['read', 'search']` only
|
||||
**Approach**: Analyze, suggest improvements, no direct modifications
|
||||
|
||||
### Refactoring Specialist
|
||||
**Purpose**: Improve code structure and maintainability
|
||||
**Tools**: `['read', 'search', 'edit']`
|
||||
**Approach**: Analyze patterns, propose refactorings, implement safely
|
||||
|
||||
### Security Auditor
|
||||
**Purpose**: Identify security issues and vulnerabilities
|
||||
**Tools**: `['read', 'search', 'web']`
|
||||
**Approach**: Scan code, check against OWASP, report findings
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
### Frontmatter Errors
|
||||
- ❌ Missing `description` field
|
||||
- ❌ Description not wrapped in quotes
|
||||
- ❌ Invalid tool names without checking documentation
|
||||
- ❌ Incorrect YAML syntax (indentation, quotes)
|
||||
|
||||
### Tool Configuration Issues
|
||||
- ❌ Granting excessive tool access unnecessarily
|
||||
- ❌ Missing required tools for agent's purpose
|
||||
- ❌ Not using tool aliases consistently
|
||||
- ❌ Forgetting MCP server namespace (`server-name/tool`)
|
||||
|
||||
### Prompt Content Problems
|
||||
- ❌ Vague, ambiguous instructions
|
||||
- ❌ Conflicting or contradictory guidelines
|
||||
- ❌ Lack of clear scope definition
|
||||
- ❌ Missing output expectations
|
||||
- ❌ Overly verbose instructions (exceeding character limits)
|
||||
- ❌ No examples or context for complex tasks
|
||||
|
||||
### Organizational Issues
|
||||
- ❌ Filename doesn't reflect agent purpose
|
||||
- ❌ Wrong directory (confusing repo vs org level)
|
||||
- ❌ Using spaces or special characters in filename
|
||||
- ❌ Duplicate agent names causing conflicts
|
||||
|
||||
## Testing and Validation
|
||||
|
||||
### Manual Testing
|
||||
1. Create the agent file with proper frontmatter
|
||||
2. Reload VS Code or refresh GitHub.com
|
||||
3. Select the agent from the dropdown in Copilot Chat
|
||||
4. Test with representative user queries
|
||||
5. Verify tool access works as expected
|
||||
6. Confirm output meets expectations
|
||||
|
||||
### Integration Testing
|
||||
- Test agent with different file types in scope
|
||||
- Verify MCP server connectivity (if configured)
|
||||
- Check agent behavior with missing context
|
||||
- Test error handling and edge cases
|
||||
- Validate agent switching and handoffs
|
||||
|
||||
### Quality Checks
|
||||
- Run through agent creation checklist
|
||||
- Review against common mistakes list
|
||||
- Compare with example agents in repository
|
||||
- Get peer review for complex agents
|
||||
- Document any special configuration needs
|
||||
|
||||
## Additional Resources
|
||||
|
||||
### Official Documentation
|
||||
- [Creating Custom Agents](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/create-custom-agents)
|
||||
- [Custom Agents Configuration](https://docs.github.com/en/copilot/reference/custom-agents-configuration)
|
||||
- [Custom Agents in VS Code](https://code.visualstudio.com/docs/copilot/customization/custom-agents)
|
||||
- [MCP Integration](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/extend-coding-agent-with-mcp)
|
||||
|
||||
### Community Resources
|
||||
- [Awesome Copilot Agents Collection](https://github.com/github/awesome-copilot/tree/main/agents)
|
||||
- [Customization Library Examples](https://docs.github.com/en/copilot/tutorials/customization-library/custom-agents)
|
||||
- [Your First Custom Agent Tutorial](https://docs.github.com/en/copilot/tutorials/customization-library/custom-agents/your-first-custom-agent)
|
||||
|
||||
### Related Files
|
||||
- [Prompt Files Guidelines](./prompt.instructions.md) - For creating prompt files
|
||||
- [Instructions Guidelines](./instructions.instructions.md) - For creating instruction files
|
||||
|
||||
## Version Compatibility Notes
|
||||
|
||||
### GitHub.com (Coding Agent)
|
||||
- ✅ Fully supports all standard frontmatter properties
|
||||
- ✅ Repository and org/enterprise level agents
|
||||
- ✅ MCP server configuration (org/enterprise)
|
||||
- ❌ Does not support `model`, `argument-hint`, `handoffs` properties
|
||||
|
||||
### VS Code / JetBrains / Eclipse / Xcode
|
||||
- ✅ Supports `model` property for AI model selection
|
||||
- ✅ Supports `argument-hint` and `handoffs` properties
|
||||
- ✅ User profile and workspace-level agents
|
||||
- ❌ Cannot configure MCP servers at repository level
|
||||
- ⚠️ Some properties may behave differently
|
||||
|
||||
When creating agents for multiple environments, focus on common properties and test in all target environments. Use `target` property to create environment-specific agents when necessary.
|
||||
@@ -1,187 +0,0 @@
|
||||
---
|
||||
description: 'Best practices for Azure DevOps Pipeline YAML files'
|
||||
applyTo: '**/azure-pipelines.yml, **/azure-pipelines*.yml, **/*.pipeline.yml'
|
||||
---
|
||||
|
||||
# Azure DevOps Pipeline YAML Best Practices
|
||||
|
||||
Guidelines for creating maintainable, secure, and efficient Azure DevOps pipelines in PowerToys.
|
||||
|
||||
## General Guidelines
|
||||
|
||||
- Use YAML syntax consistently with proper indentation (2 spaces)
|
||||
- Always include meaningful names and display names for pipelines, stages, jobs, and steps
|
||||
- Implement proper error handling and conditional execution
|
||||
- Use variables and parameters to make pipelines reusable and maintainable
|
||||
- Follow the principle of least privilege for service connections and permissions
|
||||
- Include comprehensive logging and diagnostics for troubleshooting
|
||||
|
||||
## Pipeline Structure
|
||||
|
||||
- Organize complex pipelines using stages for better visualization and control
|
||||
- Use jobs to group related steps and enable parallel execution when possible
|
||||
- Implement proper dependencies between stages and jobs
|
||||
- Use templates for reusable pipeline components
|
||||
- Keep pipeline files focused and modular - split large pipelines into multiple files
|
||||
|
||||
## Build Best Practices
|
||||
|
||||
- Use specific agent pool versions and VM images for consistency
|
||||
- Cache dependencies (npm, NuGet, Maven, etc.) to improve build performance
|
||||
- Implement proper artifact management with meaningful names and retention policies
|
||||
- Use build variables for version numbers and build metadata
|
||||
- Include code quality gates (lint checks, testing, security scans)
|
||||
- Ensure builds are reproducible and environment-independent
|
||||
|
||||
## Testing Integration
|
||||
|
||||
- Run unit tests as part of the build process
|
||||
- Publish test results in standard formats (JUnit, VSTest, etc.)
|
||||
- Include code coverage reporting and quality gates
|
||||
- Implement integration and end-to-end tests in appropriate stages
|
||||
- Use test impact analysis when available to optimize test execution
|
||||
- Fail fast on test failures to provide quick feedback
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Use Azure Key Vault for sensitive configuration and secrets
|
||||
- Implement proper secret management with variable groups
|
||||
- Use service connections with minimal required permissions
|
||||
- Enable security scans (dependency vulnerabilities, static analysis)
|
||||
- Implement approval gates for production deployments
|
||||
- Use managed identities when possible instead of service principals
|
||||
|
||||
## Deployment Strategies
|
||||
|
||||
- Implement proper environment promotion (dev → staging → production)
|
||||
- Use deployment jobs with proper environment targeting
|
||||
- Implement blue-green or canary deployment strategies when appropriate
|
||||
- Include rollback mechanisms and health checks
|
||||
- Use infrastructure as code (ARM, Bicep, Terraform) for consistent deployments
|
||||
- Implement proper configuration management per environment
|
||||
|
||||
## Variable and Parameter Management
|
||||
|
||||
- Use variable groups for shared configuration across pipelines
|
||||
- Implement runtime parameters for flexible pipeline execution
|
||||
- Use conditional variables based on branches or environments
|
||||
- Secure sensitive variables and mark them as secrets
|
||||
- Document variable purposes and expected values
|
||||
- Use variable templates for complex variable logic
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
- Use parallel jobs and matrix strategies when appropriate
|
||||
- Implement proper caching strategies for dependencies and build outputs
|
||||
- Use shallow clone for Git operations when full history isn't needed
|
||||
- Optimize Docker image builds with multi-stage builds and layer caching
|
||||
- Monitor pipeline performance and optimize bottlenecks
|
||||
- Use pipeline resource triggers efficiently
|
||||
|
||||
## Monitoring and Observability
|
||||
|
||||
- Include comprehensive logging throughout the pipeline
|
||||
- Use Azure Monitor and Application Insights for deployment tracking
|
||||
- Implement proper notification strategies for failures and successes
|
||||
- Include deployment health checks and automated rollback triggers
|
||||
- Use pipeline analytics to identify improvement opportunities
|
||||
- Document pipeline behavior and troubleshooting steps
|
||||
|
||||
## Template and Reusability
|
||||
|
||||
- Create pipeline templates for common patterns
|
||||
- Use extends templates for complete pipeline inheritance
|
||||
- Implement step templates for reusable task sequences
|
||||
- Use variable templates for complex variable logic
|
||||
- Version templates appropriately for stability
|
||||
- Document template parameters and usage examples
|
||||
|
||||
## Branch and Trigger Strategy
|
||||
|
||||
- Implement appropriate triggers for different branch types
|
||||
- Use path filters to trigger builds only when relevant files change
|
||||
- Configure proper CI/CD triggers for main/master branches
|
||||
- Use pull request triggers for code validation
|
||||
- Implement scheduled triggers for maintenance tasks
|
||||
- Consider resource triggers for multi-repository scenarios
|
||||
|
||||
## Example Structure
|
||||
|
||||
```yaml
|
||||
# azure-pipelines.yml
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
- develop
|
||||
paths:
|
||||
exclude:
|
||||
- docs/*
|
||||
- README.md
|
||||
|
||||
variables:
|
||||
- group: shared-variables
|
||||
- name: buildConfiguration
|
||||
value: 'Release'
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
displayName: 'Build and Test'
|
||||
jobs:
|
||||
- job: Build
|
||||
displayName: 'Build Application'
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Use .NET SDK'
|
||||
inputs:
|
||||
version: '8.x'
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Restore dependencies'
|
||||
inputs:
|
||||
command: 'restore'
|
||||
projects: '**/*.csproj'
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Build application'
|
||||
inputs:
|
||||
command: 'build'
|
||||
projects: '**/*.csproj'
|
||||
arguments: '--configuration $(buildConfiguration) --no-restore'
|
||||
|
||||
- stage: Deploy
|
||||
displayName: 'Deploy to Staging'
|
||||
dependsOn: Build
|
||||
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
|
||||
jobs:
|
||||
- deployment: DeployToStaging
|
||||
displayName: 'Deploy to Staging Environment'
|
||||
environment: 'staging'
|
||||
strategy:
|
||||
runOnce:
|
||||
deploy:
|
||||
steps:
|
||||
- download: current
|
||||
displayName: 'Download drop artifact'
|
||||
artifact: drop
|
||||
- task: AzureWebApp@1
|
||||
displayName: 'Deploy to Azure Web App'
|
||||
inputs:
|
||||
azureSubscription: 'staging-service-connection'
|
||||
appType: 'webApp'
|
||||
appName: 'myapp-staging'
|
||||
package: '$(Pipeline.Workspace)/drop/**/*.zip'
|
||||
```
|
||||
|
||||
## Common Anti-Patterns to Avoid
|
||||
|
||||
- Hardcoding sensitive values directly in YAML files
|
||||
- Using overly broad triggers that cause unnecessary builds
|
||||
- Mixing build and deployment logic in a single stage
|
||||
- Not implementing proper error handling and cleanup
|
||||
- Using deprecated task versions without upgrade plans
|
||||
- Creating monolithic pipelines that are difficult to maintain
|
||||
- Not using proper naming conventions for clarity
|
||||
- Ignoring pipeline security best practices
|
||||
@@ -1,61 +0,0 @@
|
||||
---
|
||||
description: 'Guidelines for shared libraries including logging, IPC, settings, DPI, telemetry, and utilities consumed by multiple modules'
|
||||
applyTo: 'src/common/**'
|
||||
---
|
||||
|
||||
# Common Libraries – Shared Code Guidance
|
||||
|
||||
Guidelines for modifying shared code in `src/common/`. Changes here can have wide-reaching impact across the entire PowerToys codebase.
|
||||
|
||||
## Scope
|
||||
|
||||
- Logging infrastructure (`src/common/logger/`)
|
||||
- IPC primitives and named pipe utilities
|
||||
- Settings serialization and management
|
||||
- DPI awareness and scaling utilities
|
||||
- Telemetry helpers
|
||||
- General utilities (JSON parsing, string helpers, etc.)
|
||||
|
||||
## Guidelines
|
||||
|
||||
### API Stability
|
||||
|
||||
- Avoid breaking public headers/APIs; if changed, search & update all callers
|
||||
- Coordinate ABI-impacting struct/class layout changes; keep binary compatibility
|
||||
- When modifying public interfaces, grep the entire codebase for usages
|
||||
|
||||
### Performance
|
||||
|
||||
- Watch perf in hot paths (hooks, timers, serialization)
|
||||
- Avoid avoidable allocations in frequently called code
|
||||
- Profile changes that touch performance-sensitive areas
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Ask before adding third-party deps or changing serialization formats
|
||||
- New dependencies must be MIT-licensed or approved by PM team
|
||||
- Add any new external packages to `NOTICE.md`
|
||||
|
||||
### Logging
|
||||
|
||||
- C++ logging uses spdlog (`Logger::info`, `Logger::warn`, `Logger::error`, `Logger::debug`)
|
||||
- Initialize with `init_logger()` early in startup
|
||||
- Keep hot paths quiet – no logging in tight loops or hooks
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- No unintended ABI breaks
|
||||
- No noisy logs in hot paths
|
||||
- New non-obvious symbols briefly commented
|
||||
- All callers updated when interfaces change
|
||||
|
||||
## Code Style
|
||||
|
||||
- **C++**: Follow `.clang-format` in `src/`; use Modern C++ patterns per C++ Core Guidelines
|
||||
- **C#**: Follow `src/.editorconfig`; enforce StyleCop.Analyzers
|
||||
|
||||
## Validation
|
||||
|
||||
- Build: `tools\build\build.cmd` from `src/common/` folder
|
||||
- Verify no ABI breaks: grep for changed function/struct names across codebase
|
||||
- Check logs: ensure no new logging in performance-critical paths
|
||||
256
.github/instructions/instructions.instructions.md
vendored
256
.github/instructions/instructions.instructions.md
vendored
@@ -1,256 +0,0 @@
|
||||
---
|
||||
description: 'Guidelines for creating high-quality custom instruction files for GitHub Copilot'
|
||||
applyTo: '**/*.instructions.md'
|
||||
---
|
||||
|
||||
# Custom Instructions File Guidelines
|
||||
|
||||
Instructions for creating effective and maintainable custom instruction files that guide GitHub Copilot in generating domain-specific code and following project conventions.
|
||||
|
||||
## Project Context
|
||||
|
||||
- Target audience: Developers and GitHub Copilot working with domain-specific code
|
||||
- File format: Markdown with YAML frontmatter
|
||||
- File naming convention: lowercase with hyphens (e.g., `react-best-practices.instructions.md`)
|
||||
- Location: `.github/instructions/` directory
|
||||
- Purpose: Provide context-aware guidance for code generation, review, and documentation
|
||||
|
||||
## Required Frontmatter
|
||||
|
||||
Every instruction file must include YAML frontmatter with the following fields:
|
||||
|
||||
```yaml
|
||||
---
|
||||
description: 'Brief description of the instruction purpose and scope'
|
||||
applyTo: 'glob pattern for target files (e.g., **/*.ts, **/*.py)'
|
||||
---
|
||||
```
|
||||
|
||||
### Frontmatter Guidelines
|
||||
|
||||
- **description**: Single-quoted string, 1-500 characters, clearly stating the purpose
|
||||
- **applyTo**: Glob pattern(s) specifying which files these instructions apply to
|
||||
- Single pattern: `'**/*.ts'`
|
||||
- Multiple patterns: `'**/*.ts, **/*.tsx, **/*.js'`
|
||||
- Specific files: `'src/**/*.py'`
|
||||
- All files: `'**'`
|
||||
|
||||
## File Structure
|
||||
|
||||
A well-structured instruction file should include the following sections:
|
||||
|
||||
### 1. Title and Overview
|
||||
|
||||
- Clear, descriptive title using `#` heading
|
||||
- Brief introduction explaining the purpose and scope
|
||||
- Optional: Project context section with key technologies and versions
|
||||
|
||||
### 2. Core Sections
|
||||
|
||||
Organize content into logical sections based on the domain:
|
||||
|
||||
- **General Instructions**: High-level guidelines and principles
|
||||
- **Best Practices**: Recommended patterns and approaches
|
||||
- **Code Standards**: Naming conventions, formatting, style rules
|
||||
- **Architecture/Structure**: Project organization and design patterns
|
||||
- **Common Patterns**: Frequently used implementations
|
||||
- **Security**: Security considerations (if applicable)
|
||||
- **Performance**: Optimization guidelines (if applicable)
|
||||
- **Testing**: Testing standards and approaches (if applicable)
|
||||
|
||||
### 3. Examples and Code Snippets
|
||||
|
||||
Provide concrete examples with clear labels:
|
||||
|
||||
```markdown
|
||||
### Good Example
|
||||
\`\`\`language
|
||||
// Recommended approach
|
||||
code example here
|
||||
\`\`\`
|
||||
|
||||
### Bad Example
|
||||
\`\`\`language
|
||||
// Avoid this pattern
|
||||
code example here
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
### 4. Validation and Verification (Optional but Recommended)
|
||||
|
||||
- Build commands to verify code
|
||||
- Lint checks and formatting tools
|
||||
- Testing requirements
|
||||
- Verification steps
|
||||
|
||||
## Content Guidelines
|
||||
|
||||
### Writing Style
|
||||
|
||||
- Use clear, concise language
|
||||
- Write in imperative mood ("Use", "Implement", "Avoid")
|
||||
- Be specific and actionable
|
||||
- Avoid ambiguous terms like "should", "might", "possibly"
|
||||
- Use bullet points and lists for readability
|
||||
- Keep sections focused and scannable
|
||||
|
||||
### Best Practices
|
||||
|
||||
- **Be Specific**: Provide concrete examples rather than abstract concepts
|
||||
- **Show Why**: Explain the reasoning behind recommendations when it adds value
|
||||
- **Use Tables**: For comparing options, listing rules, or showing patterns
|
||||
- **Include Examples**: Real code snippets are more effective than descriptions
|
||||
- **Stay Current**: Reference current versions and best practices
|
||||
- **Link Resources**: Include official documentation and authoritative sources
|
||||
|
||||
### Common Patterns to Include
|
||||
|
||||
1. **Naming Conventions**: How to name variables, functions, classes, files
|
||||
2. **Code Organization**: File structure, module organization, import order
|
||||
3. **Error Handling**: Preferred error handling patterns
|
||||
4. **Dependencies**: How to manage and document dependencies
|
||||
5. **Comments and Documentation**: When and how to document code
|
||||
6. **Version Information**: Target language/framework versions
|
||||
|
||||
## Patterns to Follow
|
||||
|
||||
### Bullet Points and Lists
|
||||
|
||||
```markdown
|
||||
## Security Best Practices
|
||||
|
||||
- Always validate user input before processing
|
||||
- Use parameterized queries to prevent SQL injection
|
||||
- Store secrets in environment variables, never in code
|
||||
- Implement proper authentication and authorization
|
||||
- Enable HTTPS for all production endpoints
|
||||
```
|
||||
|
||||
### Tables for Structured Information
|
||||
|
||||
```markdown
|
||||
## Common Issues
|
||||
|
||||
| Issue | Solution | Example |
|
||||
| ---------------- | ------------------- | ----------------------------- |
|
||||
| Magic numbers | Use named constants | `const MAX_RETRIES = 3` |
|
||||
| Deep nesting | Extract functions | Refactor nested if statements |
|
||||
| Hardcoded values | Use configuration | Store API URLs in config |
|
||||
```
|
||||
|
||||
### Code Comparison
|
||||
|
||||
```markdown
|
||||
### Good Example - Using TypeScript interfaces
|
||||
\`\`\`typescript
|
||||
interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
function getUser(id: string): User {
|
||||
// Implementation
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Bad Example - Using any type
|
||||
\`\`\`typescript
|
||||
function getUser(id: any): any {
|
||||
// Loses type safety
|
||||
}
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
### Conditional Guidance
|
||||
|
||||
```markdown
|
||||
## Framework Selection
|
||||
|
||||
- **For small projects**: Use Minimal API approach
|
||||
- **For large projects**: Use controller-based architecture with clear separation
|
||||
- **For microservices**: Consider domain-driven design patterns
|
||||
```
|
||||
|
||||
## Patterns to Avoid
|
||||
|
||||
- **Overly verbose explanations**: Keep it concise and scannable
|
||||
- **Outdated information**: Always reference current versions and practices
|
||||
- **Ambiguous guidelines**: Be specific about what to do or avoid
|
||||
- **Missing examples**: Abstract rules without concrete code examples
|
||||
- **Contradictory advice**: Ensure consistency throughout the file
|
||||
- **Copy-paste from documentation**: Add value by distilling and providing context
|
||||
|
||||
## Testing Your Instructions
|
||||
|
||||
Before finalizing instruction files:
|
||||
|
||||
1. **Test with Copilot**: Try the instructions with actual prompts in VS Code
|
||||
2. **Verify Examples**: Ensure code examples are correct and run without errors
|
||||
3. **Check Glob Patterns**: Confirm `applyTo` patterns match intended files
|
||||
|
||||
## Example Structure
|
||||
|
||||
Here's a minimal example structure for a new instruction file:
|
||||
|
||||
```markdown
|
||||
---
|
||||
description: 'Brief description of purpose'
|
||||
applyTo: '**/*.ext'
|
||||
---
|
||||
|
||||
# Technology Name Development
|
||||
|
||||
Brief introduction and context.
|
||||
|
||||
## General Instructions
|
||||
|
||||
- High-level guideline 1
|
||||
- High-level guideline 2
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Specific practice 1
|
||||
- Specific practice 2
|
||||
|
||||
## Code Standards
|
||||
|
||||
### Naming Conventions
|
||||
- Rule 1
|
||||
- Rule 2
|
||||
|
||||
### File Organization
|
||||
- Structure 1
|
||||
- Structure 2
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Pattern 1
|
||||
Description and example
|
||||
|
||||
\`\`\`language
|
||||
code example
|
||||
\`\`\`
|
||||
|
||||
### Pattern 2
|
||||
Description and example
|
||||
|
||||
## Validation
|
||||
|
||||
- Build command: `command to verify`
|
||||
- Lint checks: `command to lint`
|
||||
- Testing: `command to test`
|
||||
```
|
||||
|
||||
## Maintenance
|
||||
|
||||
- Review instructions when dependencies or frameworks are updated
|
||||
- Update examples to reflect current best practices
|
||||
- Remove outdated patterns or deprecated features
|
||||
- Add new patterns as they emerge in the community
|
||||
- Keep glob patterns accurate as project structure evolves
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Custom Instructions Documentation](https://code.visualstudio.com/docs/copilot/customization/custom-instructions)
|
||||
- [Awesome Copilot Instructions](https://github.com/github/awesome-copilot/tree/main/instructions)
|
||||
88
.github/instructions/prompt.instructions.md
vendored
88
.github/instructions/prompt.instructions.md
vendored
@@ -1,88 +0,0 @@
|
||||
---
|
||||
description: 'Guidelines for creating high-quality prompt files for GitHub Copilot'
|
||||
applyTo: '**/*.prompt.md'
|
||||
---
|
||||
|
||||
# Copilot Prompt Files Guidelines
|
||||
|
||||
Instructions for creating effective and maintainable prompt files that guide GitHub Copilot in delivering consistent, high-quality outcomes across any repository.
|
||||
|
||||
## Scope and Principles
|
||||
- Target audience: maintainers and contributors authoring reusable prompts for Copilot Chat.
|
||||
- Goals: predictable behaviour, clear expectations, minimal permissions, and portability across repositories.
|
||||
- Primary references: VS Code documentation on prompt files and organization-specific conventions.
|
||||
|
||||
## Frontmatter Requirements
|
||||
|
||||
Every prompt file should include YAML frontmatter with the following fields:
|
||||
|
||||
### Required/Recommended Fields
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `description` | Recommended | A short description of the prompt (single sentence, actionable outcome) |
|
||||
| `name` | Optional | The name shown after typing `/` in chat. Defaults to filename if not specified |
|
||||
| `agent` | Recommended | The agent to use: `ask`, `edit`, `agent`, or a custom agent name. Defaults to current agent |
|
||||
| `model` | Optional | The language model to use. Defaults to currently selected model |
|
||||
| `tools` | Optional | List of tool/tool set names available for this prompt |
|
||||
| `argument-hint` | Optional | Hint text shown in chat input to guide user interaction |
|
||||
|
||||
### Guidelines
|
||||
|
||||
- Use consistent quoting (single quotes recommended) and keep one field per line for readability and version control clarity
|
||||
- If `tools` are specified and current agent is `ask` or `edit`, the default agent becomes `agent`
|
||||
- Preserve any additional metadata (`language`, `tags`, `visibility`, etc.) required by your organization
|
||||
|
||||
## File Naming and Placement
|
||||
- Use kebab-case filenames ending with `.prompt.md` and store them under `.github/prompts/` unless your workspace standard specifies another directory.
|
||||
- Provide a short filename that communicates the action (for example, `generate-readme.prompt.md` rather than `prompt1.prompt.md`).
|
||||
|
||||
## Body Structure
|
||||
- Start with an `#` level heading that matches the prompt intent so it surfaces well in Quick Pick search.
|
||||
- Organize content with predictable sections. Recommended baseline: `Mission` or `Primary Directive`, `Scope & Preconditions`, `Inputs`, `Workflow` (step-by-step), `Output Expectations`, and `Quality Assurance`.
|
||||
- Adjust section names to fit the domain, but retain the logical flow: why → context → inputs → actions → outputs → validation.
|
||||
- Reference related prompts or instruction files using relative links to aid discoverability.
|
||||
|
||||
## Input and Context Handling
|
||||
- Use `${input:variableName[:placeholder]}` for required values and explain when the user must supply them. Provide defaults or alternatives where possible.
|
||||
- Call out contextual variables such as `${selection}`, `${file}`, `${workspaceFolder}` only when they are essential, and describe how Copilot should interpret them.
|
||||
- Document how to proceed when mandatory context is missing (for example, “Request the file path and stop if it remains undefined”).
|
||||
|
||||
## Tool and Permission Guidance
|
||||
- Limit `tools` to the smallest set that enables the task. List them in the preferred execution order when the sequence matters.
|
||||
- If the prompt inherits tools from a chat mode, mention that relationship and state any critical tool behaviours or side effects.
|
||||
- Warn about destructive operations (file creation, edits, terminal commands) and include guard rails or confirmation steps in the workflow.
|
||||
|
||||
## Instruction Tone and Style
|
||||
- Write in direct, imperative sentences targeted at Copilot (for example, “Analyze”, “Generate”, “Summarize”).
|
||||
- Keep sentences short and unambiguous, following Google Developer Documentation translation best practices to support localization.
|
||||
- Avoid idioms, humor, or culturally specific references; favor neutral, inclusive language.
|
||||
|
||||
## Output Definition
|
||||
- Specify the format, structure, and location of expected results (for example, “Create an architecture decision record file using the template below, such as `docs/architecture-decisions/record-XXXX.md`).
|
||||
- Include success criteria and failure triggers so Copilot knows when to halt or retry.
|
||||
- Provide validation steps—manual checks, automated commands, or acceptance criteria lists—that reviewers can execute after running the prompt.
|
||||
|
||||
## Examples and Reusable Assets
|
||||
- Embed Good/Bad examples or scaffolds (Markdown templates, JSON stubs) that the prompt should produce or follow.
|
||||
- Maintain reference tables (capabilities, status codes, role descriptions) inline to keep the prompt self-contained. Update these tables when upstream resources change.
|
||||
- Link to authoritative documentation instead of duplicating lengthy guidance.
|
||||
|
||||
## Quality Assurance Checklist
|
||||
- [ ] Frontmatter fields are complete, accurate, and least-privilege.
|
||||
- [ ] Inputs include placeholders, default behaviours, and fallbacks.
|
||||
- [ ] Workflow covers preparation, execution, and post-processing without gaps.
|
||||
- [ ] Output expectations include formatting and storage details.
|
||||
- [ ] Validation steps are actionable (commands, diff checks, review prompts).
|
||||
- [ ] Security, compliance, and privacy policies referenced by the prompt are current.
|
||||
- [ ] Prompt executes successfully in VS Code (`Chat: Run Prompt`) using representative scenarios.
|
||||
|
||||
## Maintenance Guidance
|
||||
- Version-control prompts alongside the code they affect; update them when dependencies, tooling, or review processes change.
|
||||
- Review prompts periodically to ensure tool lists, model requirements, and linked documents remain valid.
|
||||
- Coordinate with other repositories: when a prompt proves broadly useful, extract common guidance into instruction files or shared prompt packs.
|
||||
|
||||
## Additional Resources
|
||||
- [Prompt Files Documentation](https://code.visualstudio.com/docs/copilot/customization/prompt-files#_prompt-file-format)
|
||||
- [Awesome Copilot Prompt Files](https://github.com/github/awesome-copilot/tree/main/prompts)
|
||||
- [Tool Configuration](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode#_agent-mode-tools)
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
description: 'Guidelines for Runner and Settings UI components that communicate via named pipes and manage module lifecycle'
|
||||
applyTo: 'src/runner/**,src/settings-ui/**'
|
||||
---
|
||||
|
||||
# Runner & Settings UI – Core Components Guidance
|
||||
|
||||
Guidelines for modifying the Runner (tray/module loader) and Settings UI (configuration app). These components communicate via Windows Named Pipes using JSON messages.
|
||||
|
||||
## Runner (`src/runner/`)
|
||||
|
||||
### Scope
|
||||
|
||||
- Module bootstrap, hotkey management, settings bridge, update/elevation handling
|
||||
|
||||
### Guidelines
|
||||
|
||||
- If IPC/JSON contracts change, mirror updates in `src/settings-ui/**`
|
||||
- Keep module discovery in `src/runner/main.cpp` in sync when adding/removing modules
|
||||
- Keep startup lean: avoid blocking/network calls in early init path
|
||||
- Preserve GPO & elevation behaviors; confirm no regression in policy handling
|
||||
- Ask before modifying update workflow or elevation logic
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- Stable startup, consistent contracts, no unnecessary logging noise
|
||||
|
||||
## Settings UI (`src/settings-ui/`)
|
||||
|
||||
### Scope
|
||||
|
||||
- WinUI/WPF UI, communicates with Runner over named pipes; manages persisted settings schema
|
||||
|
||||
### Guidelines
|
||||
|
||||
- Don't break settings schema silently; add migration when shape changes
|
||||
- If IPC/JSON contracts change, align with `src/runner/**` implementation
|
||||
- Keep UI responsive: marshal to UI thread for UI-bound operations
|
||||
- Reuse existing styles/resources; avoid duplicate theme keys
|
||||
- Add/adjust migration or serialization tests when changing persisted settings
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- Schema integrity preserved, responsive UI, consistent contracts, no style duplication
|
||||
|
||||
## Shared Concerns
|
||||
|
||||
### IPC Contract Changes
|
||||
|
||||
When modifying the JSON message format between Runner and Settings UI:
|
||||
|
||||
1. Update both `src/runner/` and `src/settings-ui/` in the same PR
|
||||
2. Preserve backward compatibility where possible
|
||||
3. Add migration logic for settings schema changes
|
||||
4. Test both directions of communication
|
||||
|
||||
### Code Style
|
||||
|
||||
- **C++ (Runner)**: Follow `.clang-format` in `src/`
|
||||
- **C# (Settings UI)**: Follow `src/.editorconfig`, use StyleCop.Analyzers
|
||||
- **XAML**: Use XamlStyler or run `.\.pipelines\applyXamlStyling.ps1 -Main`
|
||||
|
||||
## Validation
|
||||
|
||||
- Build Runner: `tools\build\build.cmd` from `src/runner/`
|
||||
- Build Settings UI: `tools\build\build.cmd` from `src/settings-ui/`
|
||||
- Test IPC: Launch both Runner and Settings UI, verify communication works
|
||||
- Schema changes: Run serialization tests if settings shape changed
|
||||
58
.github/prompts/create-commit-title.prompt.md
vendored
58
.github/prompts/create-commit-title.prompt.md
vendored
@@ -1,50 +1,16 @@
|
||||
---
|
||||
agent: 'agent'
|
||||
model: 'GPT-5.1-Codex-Max'
|
||||
description: 'Generate an 80-character git commit title for the local diff'
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
description: 'Generate an 80-character git commit title for the local diff.'
|
||||
---
|
||||
|
||||
# Generate Commit Title
|
||||
**Goal:** Provide a ready-to-paste git commit title (<= 80 characters) that captures the most important local changes since `HEAD`.
|
||||
|
||||
## Purpose
|
||||
Provide a single-line, ready-to-paste git commit title (<= 80 characters) that reflects the most important local changes since `HEAD`.
|
||||
|
||||
## Input to collect
|
||||
- Run exactly one command to view the local diff:
|
||||
```@terminal
|
||||
git diff HEAD
|
||||
```
|
||||
|
||||
## How to decide the title
|
||||
1. From the diff, find the dominant area (e.g., `src/modules/*`, `doc/devdocs/**`) and the change type (bug fix, docs update, config tweak).
|
||||
2. Draft an imperative, plain-ASCII title that:
|
||||
- Mentions the primary component when obvious (e.g., `FancyZones:` or `Docs:`)
|
||||
- Stays within 80 characters and has no trailing punctuation
|
||||
|
||||
## Final output
|
||||
- Reply with only the commit title on a single line—no extra text.
|
||||
|
||||
## PR title convention (when asked)
|
||||
Use Conventional Commits style:
|
||||
|
||||
`<type>(<scope>): <summary>`
|
||||
|
||||
**Allowed types**
|
||||
- feat, fix, docs, refactor, perf, test, build, ci, chore
|
||||
|
||||
**Scope rules**
|
||||
- Use a short, PowerToys-focused scope (one word preferred). Common scopes:
|
||||
- Core: `runner`, `settings-ui`, `common`, `docs`, `build`, `ci`, `installer`, `gpo`, `dsc`
|
||||
- Modules: `fancyzones`, `powerrename`, `awake`, `colorpicker`, `imageresizer`, `keyboardmanager`, `mouseutils`, `peek`, `hosts`, `file-locksmith`, `screen-ruler`, `text-extractor`, `cropandlock`, `paste`, `powerlauncher`
|
||||
- If unclear, pick the closest module or subsystem; omit only if unavoidable
|
||||
|
||||
**Summary rules**
|
||||
- Imperative, present tense (“add”, “update”, “remove”, “fix”)
|
||||
- Keep it <= 72 characters when possible; be specific, avoid “misc changes”
|
||||
|
||||
**Examples**
|
||||
- `feat(fancyzones): add canvas template duplication`
|
||||
- `fix(mouseutils): guard crosshair toggle when dpi info missing`
|
||||
- `docs(runner): document tray icon states`
|
||||
- `build(installer): align wix v5 suffix flag`
|
||||
- `ci(ci): cache pipeline artifacts for x64`
|
||||
**Workflow:**
|
||||
1. Run a single command to view the local diff since the last commit:
|
||||
```@terminal
|
||||
git diff HEAD
|
||||
```
|
||||
2. From that diff, identify the dominant area (reference key paths like `src/modules/*`, `doc/devdocs/**`, etc.), the type of change (bug fix, docs update, config tweak), and any notable impact.
|
||||
3. Draft a concise, imperative commit title summarizing the dominant change. Keep it plain ASCII, <= 80 characters, and avoid trailing punctuation. Mention the primary component when obvious (for example `FancyZones:` or `Docs:`).
|
||||
4. Respond with only the final commit title on a single line so it can be pasted directly into `git commit`.
|
||||
|
||||
9
.github/prompts/create-pr-summary.prompt.md
vendored
9
.github/prompts/create-pr-summary.prompt.md
vendored
@@ -1,11 +1,9 @@
|
||||
---
|
||||
agent: 'agent'
|
||||
model: 'GPT-5.1-Codex-Max'
|
||||
description: 'Generate a PowerToys-ready pull request description from the local diff'
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
description: 'Generate a PowerToys-ready pull request description from the local diff.'
|
||||
---
|
||||
|
||||
# Generate PR Summary
|
||||
|
||||
**Goal:** Produce a ready-to-paste PR title and description that follows PowerToys conventions by comparing the current branch against a user-selected target branch.
|
||||
|
||||
**Repo guardrails:**
|
||||
@@ -22,4 +20,3 @@ description: 'Generate a PowerToys-ready pull request description from the local
|
||||
5. Confirm validation: list tests executed with results or state why tests were skipped in line with repo guidance.
|
||||
6. Load `.github/pull_request_template.md`, mirror its section order, and populate it with the gathered facts. Include only relevant checklist entries, marking them `[x]/[ ]` and noting any intentional omissions as "N/A".
|
||||
7. Present the filled template inside a fenced ```markdown code block with no extra commentary so it is ready to paste into a PR, clearly flagging any placeholders that still need user input.
|
||||
8. Prepend the PR title above the filled template, applying the Conventional Commit type/scope rules from `.github/prompts/create-commit-title.prompt.md`; pick the dominant component from the diff and keep the title concise and imperative.
|
||||
|
||||
10
.github/prompts/fix-issue.prompt.md
vendored
10
.github/prompts/fix-issue.prompt.md
vendored
@@ -1,12 +1,10 @@
|
||||
---
|
||||
agent: 'agent'
|
||||
model: 'GPT-5.1-Codex-Max'
|
||||
description: 'Execute the fix for a GitHub issue using the previously generated implementation plan'
|
||||
mode: 'agent'
|
||||
model: GPT-5-Codex (Preview)
|
||||
description: " Execute the fix for a GitHub issue using the previously generated implementation plan. Apply code & tests directly in the repo. Output only a PR description (and optional manual steps)."
|
||||
---
|
||||
|
||||
# Fix GitHub Issue
|
||||
|
||||
## Dependencies
|
||||
# DEPENDENCY
|
||||
Source review prompt (for generating the implementation plan if missing):
|
||||
- .github/prompts/review-issue.prompt.md
|
||||
|
||||
|
||||
17
.github/prompts/fix-spelling.prompt.md
vendored
17
.github/prompts/fix-spelling.prompt.md
vendored
@@ -1,17 +1,15 @@
|
||||
---
|
||||
agent: 'agent'
|
||||
model: 'GPT-5.1-Codex-Max'
|
||||
description: 'Resolve Code scanning / check-spelling comments on the active PR'
|
||||
mode: 'agent'
|
||||
model: GPT-5-Codex (Preview)
|
||||
description: 'Resolve Code scanning / check-spelling comments on the active PR.'
|
||||
---
|
||||
|
||||
# Fix Spelling Comments
|
||||
|
||||
**Goal:** Clear every outstanding GitHub pull request comment created by the `Code scanning / check-spelling` workflow by explicitly allowing intentional terms.
|
||||
|
||||
**Guardrails:**
|
||||
- Update only discussion threads authored by `github-actions` or `github-actions[bot]` that mention `Code scanning results / check-spelling`.
|
||||
- Prefer improving the wording in the originally flagged file when it clarifies intent without changing meaning; if the wording is already clear/standard for the context, handle it via `.github/actions/spell-check/expect.txt` and reuse existing entries.
|
||||
- Limit edits to the flagged text and `.github/actions/spell-check/expect.txt`; leave all other files and topics untouched.
|
||||
- Resolve findings solely by editing `.github/actions/spell-check/expect.txt`; reuse existing entries.
|
||||
- Leave all other files and topics untouched.
|
||||
|
||||
**Prerequisites:**
|
||||
- Install GitHub CLI if it is not present: `winget install GitHub.cli`.
|
||||
@@ -20,6 +18,5 @@ description: 'Resolve Code scanning / check-spelling comments on the active PR'
|
||||
**Workflow:**
|
||||
1. Determine the active pull request with a single `gh pr view --json number` call (default to the current branch).
|
||||
2. Fetch all PR discussion data once via `gh pr view --json comments,reviews` and filter to check-spelling comments authored by `github-actions` or `github-actions[bot]` that are not minimized; when several remain, process only the most recent comment body.
|
||||
3. For each flagged token, first consider tightening or rephrasing the original text to avoid the false positive while keeping the meaning intact; if the existing wording is already normal and professional for the context, proceed to allowlisting instead of changing it.
|
||||
4. When allowlisting, review `.github/actions/spell-check/expect.txt` for an equivalent term (for example an existing lowercase variant); when found, reuse that normalized term rather than adding a new entry, even if the flagged token differs only by casing. Only add a new entry after confirming no equivalent already exists.
|
||||
5. Add any remaining missing token to `.github/actions/spell-check/expect.txt`, keeping surrounding formatting intact.
|
||||
3. For each flagged token, review `.github/actions/spell-check/expect.txt` for an equivalent term (for example an existing lowercase variant); when found, reuse that normalized term rather than adding a new entry, even if the flagged token differs only by casing. Only add a new entry after confirming no equivalent already exists.
|
||||
4. Add any remaining missing token to `.github/actions/spell-check/expect.txt`, keeping surrounding formatting intact.
|
||||
16
.github/prompts/review-issue.prompt.md
vendored
16
.github/prompts/review-issue.prompt.md
vendored
@@ -1,22 +1,20 @@
|
||||
---
|
||||
agent: 'agent'
|
||||
model: 'GPT-5.1-Codex-Max'
|
||||
description: 'Review a GitHub issue, score it (0-100), and generate an implementation plan'
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
description: "You are github issue review and planning expertise, Score (0–100) and write one Implementation Plan. Outputs: overview.md, implementation-plan.md."
|
||||
---
|
||||
|
||||
# Review GitHub Issue
|
||||
|
||||
## Goal
|
||||
# GOAL
|
||||
For **#{{issue_number}}** produce:
|
||||
1) `Generated Files/issueReview/{{issue_number}}/overview.md`
|
||||
2) `Generated Files/issueReview/{{issue_number}}/implementation-plan.md`
|
||||
|
||||
## Inputs
|
||||
Figure out required inputs {{issue_number}} from the invocation context; if anything is missing, ask for the value or note it as a gap.
|
||||
figure out from the prompt on the
|
||||
|
||||
# CONTEXT (brief)
|
||||
Ground evidence using `gh issue view {{issue_number}} --json number,title,body,author,createdAt,updatedAt,state,labels,milestone,reactions,comments,linkedPullRequests`, and download images to better understand the issue context.
|
||||
Locate source code in the current workspace; feel free to use `rg`/`git grep`. Link related issues and PRs.
|
||||
Ground evidence using `gh issue view {{issue_number}} --json number,title,body,author,createdAt,updatedAt,state,labels,milestone,reactions,comments,linkedPullRequests`, and download the image for understand the context of the issue more.
|
||||
Locate source code in current workspace, but also free feel to use via `rg`/`git grep`. Link related issues/PRs.
|
||||
|
||||
# OVERVIEW.MD
|
||||
## Summary
|
||||
|
||||
8
.github/prompts/review-pr.prompt.md
vendored
8
.github/prompts/review-pr.prompt.md
vendored
@@ -1,10 +1,10 @@
|
||||
---
|
||||
agent: 'agent'
|
||||
model: 'GPT-5.1-Codex-Max'
|
||||
description: 'Perform a comprehensive PR review with per-step Markdown and machine-readable outputs'
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
description: "gh-driven PR review; per-step Markdown + machine-readable outputs"
|
||||
---
|
||||
|
||||
# Review Pull Request
|
||||
# PR Review — gh + stepwise
|
||||
|
||||
**Goal**: Given `{{pr_number}}`, run a *one-topic-per-step* review. Write files to `Generated Files/prReview/{{pr_number}}/` (replace `{{pr_number}}` with the integer). Emit machine‑readable blocks for a GitHub MCP to post review comments.
|
||||
|
||||
|
||||
@@ -117,7 +117,6 @@
|
||||
"WinUI3Apps\\PowerToys.FileLocksmithUI.dll",
|
||||
"WinUI3Apps\\PowerToys.FileLocksmithContextMenu.dll",
|
||||
"FileLocksmithContextMenuPackage.msix",
|
||||
"FileLocksmithCLI.exe",
|
||||
|
||||
"WinUI3Apps\\Peek.Common.dll",
|
||||
"WinUI3Apps\\Peek.FilePreviewer.dll",
|
||||
@@ -125,10 +124,6 @@
|
||||
"WinUI3Apps\\Powertoys.Peek.UI.exe",
|
||||
"WinUI3Apps\\Powertoys.Peek.dll",
|
||||
|
||||
"WinUI3Apps\\PowerToys.QuickAccess.dll",
|
||||
"WinUI3Apps\\PowerToys.QuickAccess.exe",
|
||||
"WinUI3Apps\\PowerToys.Settings.UI.Controls.dll",
|
||||
|
||||
"WinUI3Apps\\PowerToys.EnvironmentVariablesModuleInterface.dll",
|
||||
"WinUI3Apps\\PowerToys.EnvironmentVariablesUILib.dll",
|
||||
"WinUI3Apps\\PowerToys.EnvironmentVariables.dll",
|
||||
|
||||
@@ -68,13 +68,14 @@ jobs:
|
||||
|
||||
- template: .\steps-restore-nuget.yml
|
||||
|
||||
- task: MSBuild@1
|
||||
- task: NuGetCommand@2
|
||||
displayName: Restore solution-level NuGet packages
|
||||
inputs:
|
||||
solution: PowerToys.slnx
|
||||
msbuildArguments: '/t:restore /p:RestorePackagesConfig=true'
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
command: restore
|
||||
feedsToUse: config
|
||||
configPath: nuget.config
|
||||
restoreSolution: PowerToys.slnx
|
||||
restoreDirectory: '$(Build.SourcesDirectory)\packages'
|
||||
|
||||
# Build all UI test projects if no specific modules are specified
|
||||
- ${{ if eq(length(parameters.uiTestModules), 0) }}:
|
||||
|
||||
17
.vscode/settings.json
vendored
17
.vscode/settings.json
vendored
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"github.copilot.chat.reviewSelection.instructions": [
|
||||
{
|
||||
"file": ".github/prompts/review-pr.prompt.md"
|
||||
}
|
||||
],
|
||||
"github.copilot.chat.commitMessageGeneration.instructions": [
|
||||
{
|
||||
"file": ".github/prompts/create-commit-title.prompt.md"
|
||||
}
|
||||
],
|
||||
"github.copilot.chat.pullRequestDescriptionGeneration.instructions": [
|
||||
{
|
||||
"file": ".github/prompts/create-pr-summary.prompt.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
106
.vscode/tasks.json
vendored
106
.vscode/tasks.json
vendored
@@ -1,106 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"windows": {
|
||||
"options": {
|
||||
"shell": {
|
||||
"executable": "cmd.exe",
|
||||
"args": ["/d", "/c"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"inputs": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "pickString",
|
||||
"description": "Configuration",
|
||||
"options": ["Debug", "Release"],
|
||||
"default": "Debug"
|
||||
},
|
||||
{
|
||||
"id": "platform",
|
||||
"type": "pickString",
|
||||
"description": "Platform (leave empty to auto-detect host platform)",
|
||||
"options": ["", "X64", "ARM64"],
|
||||
"default": "X64"
|
||||
},
|
||||
{
|
||||
"id": "msbuildExtra",
|
||||
"type": "promptString",
|
||||
"description": "Extra MSBuild args (optional). Example: /p:CIBuild=true /m",
|
||||
"default": ""
|
||||
}
|
||||
],
|
||||
|
||||
"tasks": [
|
||||
{
|
||||
"label": "PT: Build (quick)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build.cmd\"",
|
||||
"args": [
|
||||
"-Path",
|
||||
"${fileDirname}"
|
||||
],
|
||||
"group": { "kind": "build", "isDefault": true },
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
{
|
||||
"label": "PT: Build (with options)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build.cmd\"",
|
||||
"args": [
|
||||
"-Path",
|
||||
"${fileDirname}",
|
||||
"-Platform",
|
||||
"${input:platform}",
|
||||
"-Configuration",
|
||||
"${input:config}",
|
||||
"${input:msbuildExtra}"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
{
|
||||
"label": "PT: Build Essentials (quick)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build-essentials.cmd\"",
|
||||
"args": [],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
{
|
||||
"label": "PT: Build Essentials (with options)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build-essentials.cmd\"",
|
||||
"args": [
|
||||
"-Platform",
|
||||
"${input:platform}",
|
||||
"-Configuration",
|
||||
"${input:config}",
|
||||
"${input:msbuildExtra}"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
165
AGENTS.md
165
AGENTS.md
@@ -1,165 +0,0 @@
|
||||
---
|
||||
description: 'Top-level AI contributor guidance for developing PowerToys - a collection of Windows productivity utilities'
|
||||
applyTo: '**'
|
||||
---
|
||||
|
||||
# PowerToys – AI Contributor Guide
|
||||
|
||||
This is the top-level guidance for AI contributions to PowerToys. Keep changes atomic, follow existing patterns, and cite exact paths in PRs.
|
||||
|
||||
## Overview
|
||||
|
||||
PowerToys is a set of utilities for power users to tune and streamline their Windows experience.
|
||||
|
||||
| Area | Location | Description |
|
||||
|------|----------|-------------|
|
||||
| Runner | `src/runner/` | Main executable, tray icon, module loader, hotkey management |
|
||||
| Settings UI | `src/settings-ui/` | WinUI/WPF configuration app communicating via named pipes |
|
||||
| Modules | `src/modules/` | Individual PowerToys utilities (each in its own subfolder) |
|
||||
| Common Libraries | `src/common/` | Shared code: logging, IPC, settings, DPI, telemetry, utilities |
|
||||
| Build Tools | `tools/build/` | Build scripts and automation |
|
||||
| Documentation | `doc/devdocs/` | Developer documentation |
|
||||
| Installer | `installer/` | WiX-based installer projects |
|
||||
|
||||
For architecture details and module types, see [Architecture Overview](doc/devdocs/core/architecture.md).
|
||||
|
||||
## Conventions
|
||||
|
||||
For detailed coding conventions, see:
|
||||
- [Coding Guidelines](doc/devdocs/development/guidelines.md) – Dependencies, testing, PR management
|
||||
- [Coding Style](doc/devdocs/development/style.md) – Formatting, C++/C#/XAML style rules
|
||||
- [Logging](doc/devdocs/development/logging.md) – C++ spdlog and C# Logger usage
|
||||
|
||||
### Component-Specific Instructions
|
||||
|
||||
These instruction files are automatically applied when working in their respective areas:
|
||||
- [Runner & Settings UI](.github/instructions/runner-settings-ui.instructions.md) – IPC contracts, schema migrations
|
||||
- [Common Libraries](.github/instructions/common-libraries.instructions.md) – ABI stability, shared code guidelines
|
||||
|
||||
## Build
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Visual Studio 2022 17.4+
|
||||
- Windows 10 1803+ (April 2018 Update or newer)
|
||||
- Initialize submodules once: `git submodule update --init --recursive`
|
||||
|
||||
### Build Commands
|
||||
|
||||
| Task | Command |
|
||||
|------|---------|
|
||||
| First build / NuGet restore | `tools\build\build-essentials.cmd` |
|
||||
| Build current folder | `tools\build\build.cmd` |
|
||||
| Build with options | `build.ps1 -Platform x64 -Configuration Release` |
|
||||
|
||||
### Build Discipline
|
||||
|
||||
1. One terminal per operation (build → test). Do not switch or open new ones mid-flow
|
||||
2. After making changes, `cd` to the project folder that changed (`.csproj`/`.vcxproj`)
|
||||
3. Use scripts to build: `tools/build/build.ps1` or `tools/build/build.cmd`
|
||||
4. For first build or missing NuGet packages, run `build-essentials.cmd` first
|
||||
5. **Exit code 0 = success; non-zero = failure** – treat this as absolute
|
||||
6. On failure, read the errors log: `build.<config>.<platform>.errors.log`
|
||||
7. Do not start tests or launch Runner until the build succeeds
|
||||
|
||||
### Build Logs
|
||||
|
||||
Located next to the solution/project being built:
|
||||
- `build.<configuration>.<platform>.errors.log` – errors only (check this first)
|
||||
- `build.<configuration>.<platform>.all.log` – full log
|
||||
- `build.<configuration>.<platform>.trace.binlog` – for MSBuild Structured Log Viewer
|
||||
|
||||
For complete details, see [Build Guidelines](tools/build/BUILD-GUIDELINES.md).
|
||||
|
||||
## Tests
|
||||
|
||||
### Test Discovery
|
||||
|
||||
- Find test projects by product code prefix (e.g., `FancyZones`, `AdvancedPaste`)
|
||||
- Look for sibling folders or 1-2 levels up named `<Product>*UnitTests` or `<Product>*UITests`
|
||||
|
||||
### Running Tests
|
||||
|
||||
1. **Build the test project first**, wait for exit code 0
|
||||
2. Run via VS Test Explorer (`Ctrl+E, T`) or `vstest.console.exe` with filters
|
||||
3. **Avoid `dotnet test`** in this repo – use VS Test Explorer or vstest.console.exe
|
||||
|
||||
### Test Types
|
||||
|
||||
| Type | Requirements | Setup |
|
||||
|------|--------------|-------|
|
||||
| Unit Tests | Standard dev environment | None |
|
||||
| UI Tests | WinAppDriver v1.2.1, Developer Mode | Install from [WinAppDriver releases](https://github.com/microsoft/WinAppDriver/releases/tag/v1.2.1) |
|
||||
| Fuzz Tests | OneFuzz, .NET 8 | See [Fuzzing Tests](doc/devdocs/tools/fuzzingtesting.md) |
|
||||
|
||||
### Test Discipline
|
||||
|
||||
1. Add or adjust tests when changing behavior
|
||||
2. If tests skipped, state why (e.g., comment-only change, string rename)
|
||||
3. New modules handling file I/O or user input **must** implement fuzzing tests
|
||||
|
||||
### Special Requirements
|
||||
|
||||
- **Mouse Without Borders**: Requires 2+ physical computers (not VMs)
|
||||
- **Multi-monitor utilities**: Test with 2+ monitors, different DPI settings
|
||||
|
||||
For UI test setup details, see [UI Tests](doc/devdocs/development/ui-tests.md).
|
||||
|
||||
## Boundaries
|
||||
|
||||
### Ask for Clarification When
|
||||
|
||||
- Ambiguous spec after scanning relevant docs
|
||||
- Cross-module impact (shared enum/struct) is unclear
|
||||
- Security, elevation, or installer changes involved
|
||||
- GPO or policy handling modifications needed
|
||||
|
||||
### Areas Requiring Extra Care
|
||||
|
||||
| Area | Concern | Reference |
|
||||
|------|---------|-----------|
|
||||
| `src/common/` | ABI breaks | [Common Libraries Instructions](.github/instructions/common-libraries.instructions.md) |
|
||||
| `src/runner/`, `src/settings-ui/` | IPC contracts, schema | [Runner & Settings UI Instructions](.github/instructions/runner-settings-ui.instructions.md) |
|
||||
| Installer files | Release impact | Careful review required |
|
||||
| Elevation/GPO logic | Security | Confirm no regression in policy handling |
|
||||
|
||||
### What NOT to Do
|
||||
|
||||
- Don't merge incomplete features into main (use feature branches)
|
||||
- Don't break IPC/JSON contracts without updating both runner and settings-ui
|
||||
- Don't add noisy logs in hot paths
|
||||
- Don't introduce third-party deps without PM approval and `NOTICE.md` update
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
Before finishing, verify:
|
||||
|
||||
- [ ] Build clean with exit code 0
|
||||
- [ ] Tests updated and passing locally
|
||||
- [ ] No unintended ABI breaks or schema changes
|
||||
- [ ] IPC contracts consistent between runner and settings-ui
|
||||
- [ ] New dependencies added to `NOTICE.md`
|
||||
- [ ] PR is atomic (one logical change), with issue linked
|
||||
|
||||
## Documentation Index
|
||||
|
||||
### Core Architecture
|
||||
- [Architecture Overview](doc/devdocs/core/architecture.md)
|
||||
- [Runner](doc/devdocs/core/runner.md)
|
||||
- [Settings System](doc/devdocs/core/settings/readme.md)
|
||||
- [Module Interface](doc/devdocs/modules/interface.md)
|
||||
|
||||
### Development
|
||||
- [Coding Guidelines](doc/devdocs/development/guidelines.md)
|
||||
- [Coding Style](doc/devdocs/development/style.md)
|
||||
- [Logging](doc/devdocs/development/logging.md)
|
||||
- [UI Tests](doc/devdocs/development/ui-tests.md)
|
||||
- [Fuzzing Tests](doc/devdocs/tools/fuzzingtesting.md)
|
||||
|
||||
### Build & Tools
|
||||
- [Build Guidelines](tools/build/BUILD-GUIDELINES.md)
|
||||
- [Tools Overview](doc/devdocs/tools/readme.md)
|
||||
|
||||
### Instructions (Auto-Applied)
|
||||
- [Runner & Settings UI](.github/instructions/runner-settings-ui.instructions.md)
|
||||
- [Common Libraries](.github/instructions/common-libraries.instructions.md)
|
||||
36
COMMUNITY.md
36
COMMUNITY.md
@@ -6,6 +6,9 @@ Names are in alphabetical order based on first name.
|
||||
|
||||
## High impact community members
|
||||
|
||||
### [@Noraa-Junker](https://github.com/Noraa-Junker) - [Noraa Junker](https://noraajunker.ch)
|
||||
Noraa has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Noraa was the primary person for helping build the File Explorer preview pane handler for developer files.
|
||||
|
||||
### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com)
|
||||
Christian contributed New+ utility
|
||||
|
||||
@@ -39,12 +42,6 @@ Jay has helped triaging, discussing, creating a substantial number of issues and
|
||||
### [@jefflord](https://github.com/Jjefflord) - Jeff Lord
|
||||
Jeff added in multiple new features into Keyboard manager, such as key chord support and launching apps. He also contributed multiple features/fixes to PowerToys.
|
||||
|
||||
### [@snickler](https://github.com/snickler) - [Jeremy Sinclair](http://sinclairinat0r.com)
|
||||
Jeremy has helped drive large sums of the ARM64 support inside PowerToys
|
||||
|
||||
### [@jiripolasek](https://github.com/jiripolasek) - [Jiří Polášek](https://github.com/jiripolasek)
|
||||
Jiří has contributed a massive number of features and improvements to Command Palette, including drag & drop support, custom themes, Web Search enhancements, Remote Desktop extension fixes, and many UX improvements.
|
||||
|
||||
### [@TheJoeFin](https://github.com/TheJoeFin) - [Joe Finney](https://joefinapps.com)
|
||||
Joe has helped triaging, discussing, issues as well as fixing bugs and building features for Text Extractor.
|
||||
|
||||
@@ -60,9 +57,6 @@ Color Picker is from Martin.
|
||||
### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com)
|
||||
Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility.
|
||||
|
||||
### [@Noraa-Junker](https://github.com/Noraa-Junker) - [Noraa Junker](https://noraajunker.ch)
|
||||
Noraa has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Noraa was the primary person for helping build the File Explorer preview pane handler for developer files.
|
||||
|
||||
### [@pedrolamas](https://github.com/pedrolamas/) - Pedro Lamas
|
||||
Pedro helped create the thumbnail and File Explorer previewers for 3D files like STL and GCode. If you like 3D printing, these are very helpful.
|
||||
|
||||
@@ -75,12 +69,15 @@ Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/m
|
||||
### [@royvou](https://github.com/royvou)
|
||||
Roy has helped out contributing multiple features to PowerToys Run
|
||||
|
||||
### [@ThiefZero](https://github.com/ThiefZero)
|
||||
ThiefZero has helped out contributing a features to PowerToys Run such as the unit converter plugin
|
||||
### [@snickler](https://github.com/snickler) - [Jeremy Sinclair](http://sinclairinat0r.com)
|
||||
Jeremy has helped drive large sums of the ARM64 support inside PowerToys
|
||||
|
||||
### [@TobiasSekan](https://github.com/TobiasSekan) - Tobias Sekan
|
||||
Tobias Sekan has helped out contributing features to PowerToys Run such as Settings plugin, Registry plugin
|
||||
|
||||
### [@ThiefZero](https://github.com/ThiefZero)
|
||||
ThiefZero has helped out contributing a features to PowerToys Run such as the unit converter plugin
|
||||
|
||||
## Open source projects
|
||||
|
||||
As PowerToys creates new utilities, some will be based off existing technology. We'll continue to do our best to contribute back to these projects but their efforts were the base of some of our projects. We want to be sure their work is directly recognized.
|
||||
@@ -190,10 +187,18 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@niels9001](https://github.com/niels9001/) - Niels Laute - Product Manager
|
||||
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev Lead
|
||||
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev Lead
|
||||
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev Lead
|
||||
- [@lei9444](https://github.com/lei9444) - Leilei Zhang - Dev
|
||||
- [@shuaiyuanxx](https://github.com/shuaiyuanxx) - Shawn Yuan - Dev
|
||||
- [@moooyo](https://github.com/moooyo) - Yu Leng - Dev
|
||||
- [@haoliuu](https://github.com/haoliuu) - Hao Liu - Dev
|
||||
- [@chenmy77](https://github.com/chenmy77) - Mengyuan Chen - Dev
|
||||
- [@chemwolf6922](https://github.com/chemwolf6922) - Feng Wang - Dev
|
||||
- [@yaqingmi](https://github.com/yaqingmi) - Yaqing Mi - Dev
|
||||
- [@zhaoqpcn](https://github.com/zhaoqpcn) - Qingpeng Zhao - Dev
|
||||
- [@urnotdfs](https://github.com/urnotdfs) - Xiaofeng Wang - Dev
|
||||
- [@zhaopy536](https://github.com/zhaopy536) - Peiyao Zhao - Dev
|
||||
- [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev
|
||||
- [@vanzue](https://github.com/vanzue) - Kai Tao - Dev
|
||||
- [@zadjii-msft](https://github.com/zadjii-msft) - Mike Griese - Dev
|
||||
- [@khmyznikov](https://github.com/khmyznikov) - Gleb Khmyznikov - Dev
|
||||
@@ -224,12 +229,3 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
|
||||
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
|
||||
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev Lead
|
||||
- [@haoliuu](https://github.com/haoliuu) - Hao Liu - Dev
|
||||
- [@chenmy77](https://github.com/chenmy77) - Mengyuan Chen - Dev
|
||||
- [@chemwolf6922](https://github.com/chemwolf6922) - Feng Wang - Dev
|
||||
- [@yaqingmi](https://github.com/yaqingmi) - Yaqing Mi - Dev
|
||||
- [@zhaoqpcn](https://github.com/zhaoqpcn) - Qingpeng Zhao - Dev
|
||||
- [@urnotdfs](https://github.com/urnotdfs) - Xiaofeng Wang - Dev
|
||||
- [@zhaopy536](https://github.com/zhaopy536) - Peiyao Zhao - Dev
|
||||
- [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev
|
||||
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev Lead
|
||||
|
||||
@@ -694,30 +694,6 @@ _If you want to find diagnostic data events in the source code, these two links
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Light Switch
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
<th>Event Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.LightSwitch_EnableLightSwitch</td>
|
||||
<td>Triggered when Light Switch is enabled or disabled.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.LightSwitch_ShortcutInvoked</td>
|
||||
<td>Occurs when the shortcut for Light Switch is invoked.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.LightSwitch_ScheduleModeToggled</td>
|
||||
<td>Occurs when a new schedule mode is selected for Light Switch.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.LightSwitch_ThemeTargetChanged</td>
|
||||
<td>Occurs when the options for targeting the system or apps is updated.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Mouse Highlighter
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
|
||||
304
PowerToys.slnx
304
PowerToys.slnx
@@ -143,6 +143,7 @@
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
<Deploy />
|
||||
</Project>
|
||||
<Project Path="src/modules/AdvancedPaste/AdvancedPasteModuleInterface/AdvancedPasteModuleInterface.vcxproj" Id="fc373b24-3293-453c-aaf5-cf2909dcee6a" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/AdvancedPaste/Tests/">
|
||||
<Project Path="src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/AdvancedPaste.FuzzTests.csproj">
|
||||
@@ -156,6 +157,7 @@
|
||||
</Folder>
|
||||
<Folder Name="/modules/AlwaysOnTop/">
|
||||
<Project Path="src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj" Id="1dc3be92-ce89-43fb-8110-9c043a2fe7a2" />
|
||||
<Project Path="src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj" Id="48a0a19e-a0be-4256-acf8-cc3b80291af9" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/awake/">
|
||||
<Project Path="src/modules/awake/Awake.ModuleServices/Awake.ModuleServices.csproj">
|
||||
@@ -166,12 +168,19 @@
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj" Id="5e7360a8-d048-4ed3-8f09-0bfd64c5529a" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/cmdNotFound/">
|
||||
<Project Path="src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj" Id="0014d652-901f-4456-8d65-06fc5f997fb0" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/colorpicker/">
|
||||
<Project Path="src/modules/colorPicker/ColorPicker.ModuleServices/ColorPicker.ModuleServices.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/colorPicker/ColorPicker/ColorPicker.vcxproj" Id="655c9af2-18d3-4da6-80e4-85504a7722ba">
|
||||
<BuildDependency Project="src/common/logger/logger.vcxproj" />
|
||||
</Project>
|
||||
<Project Path="src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
@@ -185,6 +194,7 @@
|
||||
</Folder>
|
||||
<Folder Name="/modules/CommandPalette/">
|
||||
<Project Path="src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj" Id="5f63c743-f6ce-4dba-a200-2b3f8a14e8c2" />
|
||||
<Project Path="src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj" Id="0adeb797-c8c7-4ffa-acd5-2af6cad7ecd8" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/CommandPalette/Built-in Extensions/">
|
||||
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj">
|
||||
@@ -361,12 +371,14 @@
|
||||
</Folder>
|
||||
<Folder Name="/modules/CropAndLock/">
|
||||
<Project Path="src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj" Id="f5e1146e-b7b3-4e11-85fd-270a500bd78c" />
|
||||
<Project Path="src/modules/CropAndLock/CropAndLockModuleInterface/CropAndLockModuleInterface.vcxproj" Id="3157fa75-86cf-4ee2-8f62-c43f776493c6" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/EnvironmentVariables/">
|
||||
<Project Path="src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/EnvironmentVariablesModuleInterface.vcxproj" Id="b9420661-b0e4-4241-abd4-4a27a1f64250" />
|
||||
<Project Path="src/modules/EnvironmentVariables/EnvironmentVariablesUILib/EnvironmentVariablesUILib.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/fancyzones/">
|
||||
@@ -384,6 +396,7 @@
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj" Id="f9c68edf-ac74-4b77-9af1-005d9c9f6a99" />
|
||||
<Project Path="src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj" Id="48804216-2a0e-4168-a6d8-9cd068d14227" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/fancyzones/Tests/">
|
||||
<Project Path="src/modules/fancyzones/FancyZones.FuzzTests/FancyZones.FuzzTests.csproj">
|
||||
@@ -406,121 +419,7 @@
|
||||
<BuildDependency Project="src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/File Explorer/">
|
||||
<Project Path="src/modules/previewpane/BgcodePreviewHandler/BgcodePreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/BgcodeThumbnailProvider/BgcodeThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/Common/PreviewHandlerCommon.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/FileExplorerDllExporter/FileExplorerDllExporter.vcxproj" Id="f6088a11-1c9e-4420-aa90-cf7e78dd7f1c" />
|
||||
<Project Path="src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/QoiPreviewHandler/QoiPreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/QoiThumbnailProvider/QoiThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/File Explorer/Tests/">
|
||||
<Project Path="src/modules/previewpane/UnitTests-BgcodePreviewHandler/Preview.BgcodePreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-BgcodeThumbnailProvider/Preview.BgcodeThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-GcodePreviewHandler/Preview.GcodePreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-GcodeThumbnailProvider/Preview.GcodeThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-MarkdownPreviewHandler/Preview.MarkdownPreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-PdfPreviewHandler/Preview.PdfPreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-PdfThumbnailProvider/Preview.PdfThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-PreviewHandlerCommon/Preview.PreviewHandlerCommon.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-QoiPreviewHandler/Preview.QoiPreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-QoiThumbnailProvider/Preview.QoiThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-StlThumbnailProvider/Preview.StlThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-SvgPreviewHandler/Preview.SvgPreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-SvgThumbnailProvider/Preview.SvgThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/FileLocksmith/">
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/FileLocksmithCLI.vcxproj" Id="49D456D3-F485-45AF-8875-45B44F193DDC" />
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj" Id="799a50d8-de89-4ed1-8ff8-ad5a9ed8c0ca" />
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj" Id="57175ec7-92a5-4c1e-8244-e3fbca2a81de" />
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj" Id="9d52fd25-ef90-4f9a-a015-91efc5daf54f" />
|
||||
@@ -530,14 +429,12 @@
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/FileLocksmith/Tests/">
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/tests/FileLocksmithCLIUnitTests.vcxproj" Id="A1B2C3D4-E5F6-7890-1234-567890ABCDEF" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/Hosts/">
|
||||
<Project Path="src/modules/Hosts/Hosts/Hosts.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/Hosts/HostsModuleInterface/HostsModuleInterface.vcxproj" Id="b41b888c-7db8-4747-b262-4062e05a230d" />
|
||||
<Project Path="src/modules/Hosts/HostsUILib/HostsUILib.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/Hosts/Tests/">
|
||||
@@ -578,6 +475,7 @@
|
||||
</Folder>
|
||||
<Folder Name="/modules/keyboardmanager/">
|
||||
<Project Path="src/modules/keyboardmanager/common/KeyboardManagerCommon.vcxproj" Id="8affa899-0b73-49ec-8c50-0fadda57b2fc" />
|
||||
<Project Path="src/modules/keyboardmanager/dll/KeyboardManager.vcxproj" Id="89f34af7-1c34-4a72-aa6e-534bcf972bd9" />
|
||||
<Project Path="src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj" Id="8df78b53-200e-451f-9328-01eb907193ae" />
|
||||
<Project Path="src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyboardManagerEditorLibrary.vcxproj" Id="23d2070d-e4ad-4add-85a7-083d9c76ad49" />
|
||||
<Project Path="src/modules/keyboardmanager/KeyboardManagerEditorLibraryWrapper/KeyboardManagerEditorLibraryWrapper.vcxproj" Id="4382a954-179a-4078-92af-715187dfff50" />
|
||||
@@ -593,6 +491,9 @@
|
||||
<Project Path="src/modules/keyboardmanager/KeyboardManagerEngineTest/KeyboardManagerEngineTest.vcxproj" Id="7f4b3a60-bc27-45a7-8000-68b0b6ea7466" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/launcher/">
|
||||
<Project Path="src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj" Id="e364f67b-bb12-4e91-b639-355866ebcd8b">
|
||||
<BuildDependency Project="src/modules/launcher/PowerLauncher/PowerLauncher.csproj" />
|
||||
</Project>
|
||||
<Project Path="src/modules/launcher/PowerLauncher.Telemetry/PowerLauncher.Telemetry.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
@@ -760,6 +661,7 @@
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/LightSwitch/">
|
||||
<Project Path="src/modules/LightSwitch/LightSwitchModuleInterface/LightSwitchModuleInterface.vcxproj" Id="38177d56-6ad1-4adf-88c9-2843a7932166" />
|
||||
<Project Path="src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj" Id="08e71c67-6a7e-4ca1-b04e-2fb336410bac" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/LightSwitch/Tests/">
|
||||
@@ -775,6 +677,7 @@
|
||||
<BuildDependency Project="src/common/SettingsAPI/SettingsAPI.vcxproj" />
|
||||
<BuildDependency Project="src/common/version/version.vcxproj" />
|
||||
</Project>
|
||||
<Project Path="src/modules/MeasureTool/MeasureToolModuleInterface/MeasureToolModuleInterface.vcxproj" Id="92c39820-9f84-4529-bc7d-22aae514d63b" />
|
||||
<Project Path="src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
@@ -794,6 +697,7 @@
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/MouseUtils/MouseJump/MouseJump.vcxproj" Id="8a08d663-4995-40e3-b42c-3f910625f284" />
|
||||
<Project Path="src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
@@ -865,12 +769,14 @@
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/poweraccent/PowerAccentKeyboardService/PowerAccentKeyboardService.vcxproj" Id="c97d9a5d-206c-454e-997e-009e227d7f02" />
|
||||
<Project Path="src/modules/poweraccent/PowerAccentModuleInterface/PowerAccentModuleInterface.vcxproj" Id="34a354c5-23c7-4343-916c-c52daf4fc39d" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/PowerOCR/">
|
||||
<Project Path="src/modules/PowerOCR/PowerOCR/PowerOCR.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/PowerOCR/PowerOCRModuleInterface/PowerOCRModuleInterface.vcxproj" Id="6ab6a2d6-f859-4a82-9184-0bd29c9f07d1" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/PowerOCR/Tests/">
|
||||
<Project Path="src/modules/PowerOCR/PowerOCR-UITests/PowerOCR.UITests.csproj">
|
||||
@@ -900,11 +806,140 @@
|
||||
<BuildDependency Project="src/modules/powerrename/lib/PowerRenameLib.vcxproj" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/previewpane/">
|
||||
<Project Path="src/modules/previewpane/BgcodePreviewHandler/BgcodePreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/BgcodePreviewHandlerCpp/BgcodePreviewHandlerCpp.vcxproj" Id="f6088a11-1c9e-4420-aa90-cf7e78dd7f1c" />
|
||||
<Project Path="src/modules/previewpane/BgcodeThumbnailProvider/BgcodeThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/BgcodeThumbnailProviderCpp/BgcodeThumbnailProviderCpp.vcxproj" Id="47b0678c-806b-4fe1-9f50-46ba88989532" />
|
||||
<Project Path="src/modules/previewpane/Common/PreviewHandlerCommon.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/GcodePreviewHandlerCpp/GcodePreviewHandlerCpp.vcxproj" Id="5a5dd09d-723a-44d3-8f2b-293584c3d731" />
|
||||
<Project Path="src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProviderCpp.vcxproj" Id="56cc2f10-6e41-453d-be16-c593a5e58482" />
|
||||
<Project Path="src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/MarkdownPreviewHandlerCpp/MarkdownPreviewHandlerCpp.vcxproj" Id="ed9a1ac6-aeb0-4569-a6e9-e1696182b545" />
|
||||
<Project Path="src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/MonacoPreviewHandlerCpp/MonacoPreviewHandlerCpp.vcxproj" Id="b3e869c4-8210-4ebd-a621-ff4c4afcbfa9" />
|
||||
<Project Path="src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/PdfPreviewHandlerCpp/PdfPreviewHandlerCpp.vcxproj" Id="54f7c616-fd41-4e62-bff9-015686914f4d" />
|
||||
<Project Path="src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProviderCpp.vcxproj" Id="ca5518ed-0458-4b09-8f53-4122b9888655" />
|
||||
<Project Path="src/modules/previewpane/powerpreview/powerpreview.vcxproj" Id="217df501-135c-4e38-bfc8-99d4821032ea">
|
||||
<BuildDependency Project="src/common/version/version.vcxproj" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/QoiPreviewHandler/QoiPreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/QoiPreviewHandlerCpp/QoiPreviewHandlerCpp.vcxproj" Id="3baf9c81-a194-4925-a035-5e24a5d1e542" />
|
||||
<Project Path="src/modules/previewpane/QoiThumbnailProvider/QoiThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/QoiThumbnailProviderCpp/QoiThumbnailProviderCpp.vcxproj" Id="ccb5e44f-84d9-4203-83c6-1c9ec9302bc7" />
|
||||
<Project Path="src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProviderCpp.vcxproj" Id="d6dcc3ae-18c0-488a-b978-baa9e3cff09d" />
|
||||
<Project Path="src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/SvgPreviewHandlerCpp/SvgPreviewHandlerCpp.vcxproj" Id="143f13e3-d2e3-4d83-b035-356612d99956" />
|
||||
<Project Path="src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProviderCpp.vcxproj" Id="2bbc9e33-21ec-401c-84da-bb6590a9b2aa" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/previewpane/Tests/">
|
||||
<Project Path="src/modules/previewpane/UnitTests-BgcodePreviewHandler/Preview.BgcodePreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-BgcodeThumbnailProvider/Preview.BgcodeThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-GcodePreviewHandler/Preview.GcodePreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-GcodeThumbnailProvider/Preview.GcodeThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-MarkdownPreviewHandler/Preview.MarkdownPreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-PdfPreviewHandler/Preview.PdfPreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-PdfThumbnailProvider/Preview.PdfThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-PreviewHandlerCommon/Preview.PreviewHandlerCommon.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-QoiPreviewHandler/Preview.QoiPreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-QoiThumbnailProvider/Preview.QoiThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-StlThumbnailProvider/Preview.StlThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-SvgPreviewHandler/Preview.SvgPreviewHandler.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/previewpane/UnitTests-SvgThumbnailProvider/Preview.SvgThumbnailProvider.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/RegistryPreview/">
|
||||
<Project Path="src/modules/registrypreview/RegistryPreview/RegistryPreview.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj" Id="697c6af9-0a48-49a9-866c-67da12384015" />
|
||||
<Project Path="src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewUILib.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/RegistryPreview/Test/">
|
||||
@@ -936,6 +971,7 @@
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj" Id="b31fcc55-b5a4-4ea7-b414-2dceae6af332" />
|
||||
<Project Path="src/modules/Workspaces/WorkspacesModuleInterface/WorkspacesModuleInterface.vcxproj" Id="45285df2-9742-4eca-9ac9-58951fc26489" />
|
||||
<Project Path="src/modules/Workspaces/WorkspacesSnapshotTool/WorkspacesSnapshotTool.vcxproj" Id="3d63307b-9d27-44fd-b033-b26f39245b85" />
|
||||
<Project Path="src/modules/Workspaces/WorkspacesWindowArranger/WorkspacesWindowArranger.vcxproj" Id="37d07516-4185-43a4-924f-3c7a5d95ecf6" />
|
||||
</Folder>
|
||||
@@ -961,17 +997,10 @@
|
||||
</Folder>
|
||||
<Folder Name="/modules/ZoomIt/">
|
||||
<Project Path="src/modules/ZoomIt/ZoomIt/ZoomIt.vcxproj" Id="0a84f764-3a88-44cd-aa96-41bdbd48627b" />
|
||||
<Project Path="src/modules/ZoomIt/ZoomItModuleInterface/ZoomItModuleInterface.vcxproj" Id="e4585179-2ac1-4d5f-a3ff-cfc5392f694c" />
|
||||
<Project Path="src/modules/ZoomIt/ZoomItSettingsInterop/ZoomItSettingsInterop.vcxproj" Id="ca7d8106-30b9-4aec-9d05-b69b31b8c461" />
|
||||
</Folder>
|
||||
<Folder Name="/settings-ui/">
|
||||
<Project Path="src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/settings-ui/Settings.UI.Controls/Settings.UI.Controls.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
@@ -1005,36 +1034,37 @@
|
||||
<File Path="src/Solution.props" />
|
||||
<File Path="src/Version.props" />
|
||||
</Folder>
|
||||
<Project Path="src/PackageIdentity/PackageIdentity.vcxproj" Id="e2a5a82e-1e5b-4c8d-9a4f-2b1a8f9e5c3d" />
|
||||
<Project Path="src/PowerToys.ActionRunner/PowerToys.ActionRunner.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
<Project Path="src/ActionRunner/ActionRunner.vcxproj" Id="d29ddd63-e2cf-4657-9fd5-2aede4257e5d">
|
||||
<BuildDependency Project="src/common/updating/updating.vcxproj" />
|
||||
</Project>
|
||||
<Project Path="src/PackageIdentity/PackageIdentity.vcxproj" Id="e2a5a82e-1e5b-4c8d-9a4f-2b1a8f9e5c3d" />
|
||||
<Project Path="src/runner/runner.vcxproj" Id="9412d5c6-2cf2-4fc2-a601-b55508ea9b27">
|
||||
<BuildDependency Project="src/ActionRunner/ActionRunner.vcxproj" />
|
||||
<BuildDependency Project="src/common/notifications/BackgroundActivator/BackgroundActivator.vcxproj" />
|
||||
<BuildDependency Project="src/common/notifications/BackgroundActivatorDLL/BackgroundActivatorDLL.vcxproj" />
|
||||
<BuildDependency Project="src/common/updating/updating.vcxproj" />
|
||||
<BuildDependency Project="src/modules/awake/Awake/Awake.csproj" />
|
||||
<BuildDependency Project="src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj" />
|
||||
<BuildDependency Project="src/modules/colorPicker/ColorPicker/ColorPicker.vcxproj" />
|
||||
<BuildDependency Project="src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj" />
|
||||
<BuildDependency Project="src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj" />
|
||||
<BuildDependency Project="src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj" />
|
||||
<BuildDependency Project="src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj" />
|
||||
<BuildDependency Project="src/modules/imageresizer/dll/ImageResizerExt.vcxproj" />
|
||||
<BuildDependency Project="src/modules/imageresizer/ui/ImageResizerUI.csproj" />
|
||||
<BuildDependency Project="src/modules/keyboardmanager/dll/KeyboardManager.vcxproj" />
|
||||
<BuildDependency Project="src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj" />
|
||||
<BuildDependency Project="src/modules/LightSwitch/LightSwitchModuleInterface/LightSwitchModuleInterface.vcxproj" />
|
||||
<BuildDependency Project="src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj" />
|
||||
<BuildDependency Project="src/modules/powerrename/dll/PowerRenameExt.vcxproj" />
|
||||
<BuildDependency Project="src/modules/powerrename/lib/PowerRenameLib.vcxproj" />
|
||||
<BuildDependency Project="src/modules/previewpane/Common/PreviewHandlerCommon.csproj" />
|
||||
<BuildDependency Project="src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj" />
|
||||
<BuildDependency Project="src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj" />
|
||||
<BuildDependency Project="src/modules/previewpane/powerpreview/powerpreview.vcxproj" />
|
||||
<BuildDependency Project="src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj" />
|
||||
<BuildDependency Project="src/PackageIdentity/PackageIdentity.vcxproj" />
|
||||
</Project>
|
||||
<Project Path="src/RunnerV2/RunnerV2/RunnerV2.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/Update/Update.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/Update/PowerToys.Update.vcxproj" Id="44ce9ae1-4390-42c5-bacc-0fd6b40aa203" />
|
||||
<Project Path="tools/project_template/ModuleTemplate/ModuleTemplateCompileTest.vcxproj" Id="64a80062-4d8b-4229-8a38-dfa1d7497749" />
|
||||
</Solution>
|
||||
|
||||
@@ -58,8 +58,8 @@ string validUIDisplayString = Resources.ValidUIDisplayString;
|
||||
## More On Coding Guidance
|
||||
Please review these brief docs below relating to our coding standards, etc.
|
||||
|
||||
* [Coding Style](development/style.md)
|
||||
* [Code Organization](readme.md)
|
||||
* [Coding Style](./style.md)
|
||||
* [Code Organization](./readme.md)
|
||||
|
||||
|
||||
[VS Resource Editor]: https://learn.microsoft.com/cpp/windows/resource-editors?view=vs-2019
|
||||
|
||||
@@ -20,9 +20,6 @@ Creates a window showing the selected area of the original window. Changes in th
|
||||
### Reparent Mode
|
||||
Creates a window that replaces the original window, showing only the selected area. The application is controlled through the cropped window.
|
||||
|
||||
### Screenshot Mode
|
||||
Creates a window showing a freezed snapshot of the original window.
|
||||
|
||||
## Code Structure
|
||||
|
||||
### Project Layout
|
||||
@@ -33,7 +30,6 @@ The Crop and Lock module is part of the PowerToys solution. All the logic-relate
|
||||
- **OverlayWindow.cpp**: Thumbnail module type's window concrete implementation.
|
||||
- **ReparentCropAndLockWindow.cpp**: Defines the UI for the reparent mode.
|
||||
- **ChildWindow.cpp**: Reparent module type's window concrete implementation.
|
||||
- **ScreenshotCropAndLockWindow.cpp**: Defines the UI for the screenshot mode.
|
||||
|
||||
## Known Issues
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and an
|
||||
## Rules
|
||||
|
||||
- **Follow the pattern of what you already see in the code.**
|
||||
- [Coding style](development/style.md).
|
||||
- [Coding style](style.md).
|
||||
- Try to package new functionality/components into libraries that have nicely defined interfaces.
|
||||
- Package new functionality into classes or refactor existing functionality into a class as you extend the code.
|
||||
- When adding new classes/methods/changing existing code, add new unit tests or update the existing tests.
|
||||
|
||||
@@ -50,8 +50,6 @@ Contact the developers of a plugin directly for assistance with a specific plugi
|
||||
| [Hotkeys](https://github.com/ruslanlap/PowerToysRun-Hotkeys) | [ruslanlap](https://github.com/ruslanlap) | Create, manage, and trigger custom keyboard shortcuts directly from PowerToys Run. |
|
||||
| [RandomGen](https://github.com/ruslanlap/PowerToysRun-RandomGen) | [ruslanlap](https://github.com/ruslanlap) | 🎲 Generate random data instantly with a single keystroke. Perfect for developers, testers, designers, and anyone who needs quick access to random data. Features include secure passwords, PINs, names, business data, dates, numbers, GUIDs, color codes, and more. Especially useful for designers who need random color codes and placeholder content. |
|
||||
| [Open With Cursor](https://github.com/VictorNoxx/PowerToys-Run-Cursor/) | [VictorNoxx](https://github.com/VictorNoxx) | Open Visual Studio, VS Code recents with Cursor AI |
|
||||
| [Open With Antigravity](https://github.com/artickc/PowerToys-Run-Antygravity) | [artickc](https://github.com/artickc) | Open Visual Studio, VS Code recents with Antigravity AI |
|
||||
| [Project Launcher Plugin](https://github.com/artickc/ProjectLauncherPowerToysPlugin) | [artickc](https://github.com/artickc) | Access your projects using Project Launcher and PowerToys Run |
|
||||
| [CheatSheets](https://github.com/ruslanlap/PowerToysRun-CheatSheets) | [ruslanlap](https://github.com/ruslanlap) | 📚 Find cheat sheets and command examples instantly from tldr pages, cheat.sh, and devhints.io. Features include favorites system, categories, offline mode, and smart caching. |
|
||||
| [QuickAI](https://github.com/ruslanlap/PowerToysRun-QuickAi) | [ruslanlap](https://github.com/ruslanlap) | AI-powered assistance with instant, smart responses from multiple providers (Groq, Together, Fireworks, OpenRouter, Cohere) |
|
||||
|
||||
|
||||
70
src/ActionRunner/Resources.resx
Normal file
70
src/ActionRunner/Resources.resx
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="DOTNET_CORE_DOWNLOAD_FAILURE" xml:space="preserve">
|
||||
<value>Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.</value>
|
||||
</data>
|
||||
<data name="DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" xml:space="preserve">
|
||||
<value>PowerToys installation error</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT" xml:space="preserve">
|
||||
<value>An update to PowerToys is available. Visit our GitHub page to update.</value>
|
||||
</data>
|
||||
</root>
|
||||
36
src/ActionRunner/actionRunner.base.rc
Normal file
36
src/ActionRunner/actionRunner.base.rc
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <windows.h>
|
||||
#include "resource.h"
|
||||
#include "../common/version/version.h"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", FILE_DESCRIPTION
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", INTERNAL_NAME
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", ORIGINAL_FILENAME
|
||||
VALUE "ProductName", PRODUCT_NAME
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
|
||||
END
|
||||
END
|
||||
109
src/ActionRunner/actionRunner.cpp
Normal file
109
src/ActionRunner/actionRunner.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include "Generated Files/resource.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
|
||||
#include <common/utils/elevation.h>
|
||||
#include <common/utils/process_path.h>
|
||||
#include <common/utils/resources.h>
|
||||
#include <common/utils/timeutil.h>
|
||||
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
|
||||
#include "../runner/tray_icon.h"
|
||||
#include "../runner/ActionRunnerUtils.h"
|
||||
|
||||
using namespace cmdArg;
|
||||
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
{
|
||||
int nArgs = 0;
|
||||
LPWSTR* args = CommandLineToArgvW(GetCommandLineW(), &nArgs);
|
||||
if (!args || nArgs < 2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::wstring_view action{ args[1] };
|
||||
|
||||
std::filesystem::path logFilePath(PTSettingsHelper::get_root_save_folder_location());
|
||||
logFilePath.append(LogSettings::actionRunnerLogPath);
|
||||
Logger::init(LogSettings::actionRunnerLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
|
||||
|
||||
if (action == RUN_NONELEVATED)
|
||||
{
|
||||
int nextArg = 2;
|
||||
|
||||
std::wstring_view target;
|
||||
std::wstring_view pidFile;
|
||||
std::wstring params;
|
||||
|
||||
while (nextArg < nArgs)
|
||||
{
|
||||
if (std::wstring_view(args[nextArg]) == L"-target" && nextArg + 1 < nArgs)
|
||||
{
|
||||
target = args[nextArg + 1];
|
||||
nextArg += 2;
|
||||
}
|
||||
else if (std::wstring_view(args[nextArg]) == L"-pidFile" && nextArg + 1 < nArgs)
|
||||
{
|
||||
pidFile = args[nextArg + 1];
|
||||
nextArg += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
params += args[nextArg];
|
||||
params += L' ';
|
||||
nextArg++;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE hMapFile = NULL;
|
||||
PDWORD pidBuffer = NULL;
|
||||
|
||||
if (!pidFile.empty())
|
||||
{
|
||||
hMapFile = OpenFileMappingW(FILE_MAP_WRITE, FALSE, pidFile.data());
|
||||
if (hMapFile)
|
||||
{
|
||||
pidBuffer = static_cast<PDWORD>(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(DWORD)));
|
||||
if (pidBuffer)
|
||||
{
|
||||
*pidBuffer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run_same_elevation(target.data(), params, pidBuffer);
|
||||
|
||||
if (!pidFile.empty())
|
||||
{
|
||||
if (pidBuffer)
|
||||
{
|
||||
FlushViewOfFile(pidBuffer, sizeof(DWORD));
|
||||
UnmapViewOfFile(pidBuffer);
|
||||
}
|
||||
|
||||
if (hMapFile)
|
||||
{
|
||||
FlushFileBuffers(hMapFile);
|
||||
CloseHandle(hMapFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
75
src/ActionRunner/actionRunner.vcxproj
Normal file
75
src/ActionRunner/actionRunner.vcxproj
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
|
||||
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h actionRunner.base.rc actionRunner.rc" />
|
||||
</Target>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}</ProjectGuid>
|
||||
<RootNamespace>actionRunner</RootNamespace>
|
||||
<ProjectName>PowerToys.ActionRunner</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\deps\expected.props" />
|
||||
<PropertyGroup>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup>
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>WindowsApp.lib;Msi.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ActionRunner.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\common\logger\logger.vcxproj">
|
||||
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ActionRunner.base.rc" />
|
||||
<ResourceCompile Include="Generated Files\ActionRunner.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
11
src/ActionRunner/resource.base.h
Normal file
11
src/ActionRunner/resource.base.h
Normal file
@@ -0,0 +1,11 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by PowerToys.ActionRunner.rc
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "PowerToys ActionRunner"
|
||||
#define INTERNAL_NAME "PowerToys.ActionRunner"
|
||||
#define ORIGINAL_FILENAME "PowerToys.ActionRunner.exe"
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,119 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace PowerToys.ActionRunner;
|
||||
|
||||
internal sealed partial class Program
|
||||
{
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args[0])
|
||||
{
|
||||
case "-run-non-elevated":
|
||||
ExecuteRunNonElevated(args[1..]);
|
||||
break;
|
||||
default:
|
||||
Environment.Exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ExecuteRunNonElevated(string[] args)
|
||||
{
|
||||
string? target = null;
|
||||
string? pidFile = null;
|
||||
string? arguments = null;
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
string arg = args[i];
|
||||
if (arg == "-target" && i + 1 < args.Length)
|
||||
{
|
||||
target = args[i + 1];
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
else if (arg == "-pidFile" && i + 1 < args.Length)
|
||||
{
|
||||
pidFile = args[i + 1];
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
arguments = args[i + 1] + " ";
|
||||
i++;
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(pidFile))
|
||||
{
|
||||
IntPtr pidBuffer = IntPtr.Zero;
|
||||
IntPtr mapFile = OpenFileMapping(0x0002 /* FILE_MAP_WRITE */, false, pidFile);
|
||||
if (mapFile != IntPtr.Zero)
|
||||
{
|
||||
pidBuffer = MapViewOfFile(mapFile, 0x001F /* FILE_MAP_ALL_ACCESS */, 0, 0, sizeof(uint));
|
||||
if (pidBuffer != IntPtr.Zero)
|
||||
{
|
||||
Marshal.WriteInt32(pidBuffer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Process? p = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = target,
|
||||
Arguments = arguments.Trim(),
|
||||
UseShellExecute = true,
|
||||
});
|
||||
|
||||
if (pidBuffer != IntPtr.Zero)
|
||||
{
|
||||
Marshal.WriteInt32(pidBuffer, p?.Id ?? 0);
|
||||
FlushViewOfFile(pidBuffer, sizeof(uint));
|
||||
UnmapViewOfFile(pidBuffer);
|
||||
}
|
||||
|
||||
if (mapFile != IntPtr.Zero)
|
||||
{
|
||||
FlushFileBuffers(mapFile);
|
||||
CloseHandle(mapFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[LibraryImport("Kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
|
||||
private static partial IntPtr OpenFileMapping(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName);
|
||||
|
||||
[LibraryImport("Kernel32.dll", SetLastError = true)]
|
||||
private static partial IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, UIntPtr dwNumberOfBytesToMap);
|
||||
|
||||
[LibraryImport("Kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool UnmapViewOfFile(IntPtr lpBaseAddress);
|
||||
|
||||
[LibraryImport("Kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool FlushViewOfFile(IntPtr lpBaseAddress, uint dwNumberOfBytesToFlush);
|
||||
|
||||
[LibraryImport("Kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool FlushFileBuffers(IntPtr hFile);
|
||||
|
||||
[LibraryImport("Kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool CloseHandle(IntPtr hObject);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.ApplicationModel;
|
||||
|
||||
namespace RunnerV2.Extensions
|
||||
{
|
||||
internal static class PackageVersionExtensions
|
||||
{
|
||||
public static Version ToVersion(this PackageVersion packageVersion)
|
||||
{
|
||||
return new Version(packageVersion.Major, packageVersion.Minor, packageVersion.Build, packageVersion.Revision);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace RunnerV2.Helpers
|
||||
{
|
||||
internal static class COMUtils
|
||||
{
|
||||
public static void InitializeCOMSecurity(string securityDescriptor)
|
||||
{
|
||||
if (!NativeMethods.ConvertStringSecurityDescriptorToSecurityDescriptorW(
|
||||
securityDescriptor,
|
||||
1,
|
||||
out IntPtr pSD,
|
||||
out _))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint absoluteSDSize = 0;
|
||||
uint daclSize = 0;
|
||||
uint groupSize = 0;
|
||||
uint ownerSize = 0;
|
||||
uint saclSize = 0;
|
||||
|
||||
if (!NativeMethods.MakeAbsoluteSD(pSD, IntPtr.Zero, ref absoluteSDSize, IntPtr.Zero, ref daclSize, IntPtr.Zero, ref saclSize, IntPtr.Zero, ref ownerSize, IntPtr.Zero, ref groupSize))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IntPtr absoluteSD = Marshal.AllocHGlobal((int)absoluteSDSize);
|
||||
IntPtr dacl = Marshal.AllocHGlobal((int)daclSize);
|
||||
IntPtr sacl = Marshal.AllocHGlobal((int)saclSize);
|
||||
IntPtr owner = Marshal.AllocHGlobal((int)ownerSize);
|
||||
IntPtr group = Marshal.AllocHGlobal((int)groupSize);
|
||||
|
||||
if (!NativeMethods.MakeAbsoluteSD(pSD, absoluteSD, ref absoluteSDSize, dacl, ref daclSize, sacl, ref saclSize, owner, ref ownerSize, group, ref groupSize))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_ = NativeMethods.CoInitializeSecurity(
|
||||
absoluteSD,
|
||||
-1,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
6, // RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
||||
2, // RPC_C_IMP_LEVEL_IDENTIFY
|
||||
IntPtr.Zero,
|
||||
64 | 4096, // EOAC_DYNAMIC_CLOAKING | EOAC_DISABLE_AAA
|
||||
IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Windows.System;
|
||||
|
||||
namespace RunnerV2.Helpers
|
||||
{
|
||||
internal static class CentralizedKeyboardHookManager
|
||||
{
|
||||
private static readonly UIntPtr _ignoreKeyEventFlag = 0x5555;
|
||||
|
||||
private static readonly Dictionary<string, List<(HotkeySettings HotkeySettings, Action Action)>> _keyboardHooks = [];
|
||||
|
||||
private static HotkeySettingsControlHook _hotkeySettingsControlHook = new(OnKeyDown, OnKeyUp, IsActive, (_, specialFlags) => specialFlags != _ignoreKeyEventFlag);
|
||||
|
||||
private static void OnKeyDown(int key)
|
||||
{
|
||||
if ((VirtualKey)key == VirtualKey.RightMenu && _ctrlState)
|
||||
{
|
||||
_ctrlAltState = true;
|
||||
}
|
||||
|
||||
switch ((VirtualKey)key)
|
||||
{
|
||||
case VirtualKey.Control:
|
||||
case VirtualKey.LeftControl:
|
||||
case VirtualKey.RightControl:
|
||||
_ctrlState = true;
|
||||
break;
|
||||
case VirtualKey.Menu:
|
||||
case VirtualKey.LeftMenu:
|
||||
case VirtualKey.RightMenu:
|
||||
_altState = true;
|
||||
break;
|
||||
case VirtualKey.Shift:
|
||||
case VirtualKey.LeftShift:
|
||||
case VirtualKey.RightShift:
|
||||
_shiftState = true;
|
||||
break;
|
||||
case VirtualKey.LeftWindows:
|
||||
case VirtualKey.RightWindows:
|
||||
_winState = true;
|
||||
break;
|
||||
default:
|
||||
if (OnKeyboardEvent(new HotkeySettings
|
||||
{
|
||||
Code = key,
|
||||
Ctrl = _ctrlState,
|
||||
Alt = _altState,
|
||||
Shift = _shiftState,
|
||||
Win = _winState,
|
||||
}))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
SendSingleKeyboardInput((short)key, (uint)NativeKeyboardHelper.KeyEventF.KeyDown);
|
||||
}
|
||||
|
||||
private static void OnKeyUp(int key)
|
||||
{
|
||||
switch ((VirtualKey)key)
|
||||
{
|
||||
case VirtualKey.Control:
|
||||
case VirtualKey.LeftControl:
|
||||
case VirtualKey.RightControl:
|
||||
_ctrlState = false;
|
||||
break;
|
||||
case VirtualKey.Menu:
|
||||
case VirtualKey.LeftMenu:
|
||||
case VirtualKey.RightMenu:
|
||||
_altState = false;
|
||||
break;
|
||||
case VirtualKey.Shift:
|
||||
case VirtualKey.LeftShift:
|
||||
case VirtualKey.RightShift:
|
||||
_shiftState = false;
|
||||
break;
|
||||
case VirtualKey.LeftWindows:
|
||||
case VirtualKey.RightWindows:
|
||||
_winState = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Correctly release Ctrl key if Ctrl+Alt (AltGr) was used.
|
||||
if (_ctrlAltState && (VirtualKey)key == VirtualKey.RightMenu)
|
||||
{
|
||||
_ctrlAltState = false;
|
||||
_ctrlState = false;
|
||||
|
||||
SendSingleKeyboardInput((short)VirtualKey.LeftControl, (uint)NativeKeyboardHelper.KeyEventF.KeyUp);
|
||||
}
|
||||
|
||||
SendSingleKeyboardInput((short)key, (uint)NativeKeyboardHelper.KeyEventF.KeyUp);
|
||||
}
|
||||
|
||||
private static bool _ctrlState;
|
||||
private static bool _altState;
|
||||
private static bool _shiftState;
|
||||
private static bool _winState;
|
||||
private static bool _ctrlAltState;
|
||||
|
||||
private static bool _isActive;
|
||||
|
||||
private static bool IsActive()
|
||||
{
|
||||
return _isActive;
|
||||
}
|
||||
|
||||
public static void AddKeyboardHook(string moduleName, HotkeySettings hotkeySettings, Action action)
|
||||
{
|
||||
#pragma warning disable CA1854 // Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method
|
||||
if (!_keyboardHooks.ContainsKey(moduleName))
|
||||
{
|
||||
_keyboardHooks[moduleName] = [];
|
||||
}
|
||||
#pragma warning restore CA1854 // Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method
|
||||
|
||||
_keyboardHooks[moduleName].Add((hotkeySettings, action));
|
||||
}
|
||||
|
||||
public static void RemoveAllHooksFromModule(string moduleName)
|
||||
{
|
||||
_keyboardHooks.Remove(moduleName);
|
||||
}
|
||||
|
||||
private static bool OnKeyboardEvent(HotkeySettings pressedHotkey)
|
||||
{
|
||||
bool shortcutHandled = false;
|
||||
|
||||
foreach (var moduleHooks in _keyboardHooks.Values)
|
||||
{
|
||||
foreach (var (hotkeySettings, action) in moduleHooks)
|
||||
{
|
||||
if (hotkeySettings == pressedHotkey)
|
||||
{
|
||||
action();
|
||||
shortcutHandled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shortcutHandled;
|
||||
}
|
||||
|
||||
public static void Start()
|
||||
{
|
||||
if (_hotkeySettingsControlHook.GetDisposedState())
|
||||
{
|
||||
_hotkeySettingsControlHook = new(OnKeyDown, OnKeyUp, IsActive, (_, specialFlags) => specialFlags != _ignoreKeyEventFlag);
|
||||
}
|
||||
|
||||
_isActive = true;
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
_isActive = false;
|
||||
_hotkeySettingsControlHook.Dispose();
|
||||
}
|
||||
|
||||
// Function to send a single key event to the system which would be ignored by the hotkey control.
|
||||
private static void SendSingleKeyboardInput(short keyCode, uint keyStatus)
|
||||
{
|
||||
if (IsExtendedVirtualKey(keyCode))
|
||||
{
|
||||
keyStatus |= (uint)NativeKeyboardHelper.KeyEventF.ExtendedKey;
|
||||
}
|
||||
|
||||
NativeKeyboardHelper.INPUT input = new()
|
||||
{
|
||||
type = NativeKeyboardHelper.INPUTTYPE.INPUT_KEYBOARD,
|
||||
data = new NativeKeyboardHelper.InputUnion
|
||||
{
|
||||
ki = new NativeKeyboardHelper.KEYBDINPUT
|
||||
{
|
||||
wVk = keyCode,
|
||||
dwFlags = keyStatus,
|
||||
dwExtraInfo = _ignoreKeyEventFlag,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
NativeKeyboardHelper.INPUT[] inputs = [input];
|
||||
|
||||
_ = NativeMethods.SendInput(1, inputs, NativeKeyboardHelper.INPUT.Size);
|
||||
}
|
||||
|
||||
private static bool IsExtendedVirtualKey(short vk)
|
||||
{
|
||||
return vk switch
|
||||
{
|
||||
0xA5 => true, // VK_RMENU (Right Alt - AltGr)
|
||||
0xA3 => true, // VK_RCONTROL
|
||||
0x2D => true, // VK_INSERT
|
||||
0x2E => true, // VK_DELETE
|
||||
0x23 => true, // VK_END
|
||||
0x24 => true, // VK_HOME
|
||||
0x21 => true, // VK_PRIOR (Page Up)
|
||||
0x22 => true, // VK_NEXT (Page Down)
|
||||
0x25 => true, // VK_LEFT
|
||||
0x26 => true, // VK_UP
|
||||
0x27 => true, // VK_RIGHT
|
||||
0x28 => true, // VK_DOWN
|
||||
0x90 => true, // VK_NUMLOCK
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using static RunnerV2.NativeMethods;
|
||||
|
||||
namespace RunnerV2.Helpers
|
||||
{
|
||||
internal static partial class ElevationHelper
|
||||
{
|
||||
internal static RestartScheduledMode RestartScheduled { get; set; } = RestartScheduledMode.None;
|
||||
|
||||
internal enum RestartScheduledMode
|
||||
{
|
||||
None,
|
||||
RestartElevated,
|
||||
RestartElevatedWithOpenSettings,
|
||||
RestartNonElevated,
|
||||
}
|
||||
|
||||
private static bool? _cachedValue;
|
||||
|
||||
internal static void RestartIfScheudled()
|
||||
{
|
||||
switch (RestartScheduled)
|
||||
{
|
||||
case RestartScheduledMode.None:
|
||||
return;
|
||||
case RestartScheduledMode.RestartElevated:
|
||||
RestartAsAdministrator("--restartedElevated");
|
||||
break;
|
||||
case RestartScheduledMode.RestartElevatedWithOpenSettings:
|
||||
RestartAsAdministrator("--restartedElevated --open-settings");
|
||||
break;
|
||||
case RestartScheduledMode.RestartNonElevated:
|
||||
// Todo: restart unelevated
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RestartAsAdministrator(string arguments)
|
||||
{
|
||||
ProcessStartInfo processStartInfo = new()
|
||||
{
|
||||
Arguments = arguments,
|
||||
Verb = "runas",
|
||||
UseShellExecute = true,
|
||||
FileName = Environment.ProcessPath,
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Process.Start(processStartInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Failed to restart as administrator: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsProcessElevated(bool useCachedValue = true)
|
||||
{
|
||||
if (_cachedValue is not null && useCachedValue)
|
||||
{
|
||||
return _cachedValue.Value;
|
||||
}
|
||||
|
||||
bool elevated = false;
|
||||
if (OpenProcessToken(Process.GetCurrentProcess().Handle, TOKENQUERY, out nint token))
|
||||
{
|
||||
TokenElevation elevation = default;
|
||||
if (GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TOKEN_ELEVATION, ref elevation, (uint)Marshal.SizeOf(elevation), out uint _))
|
||||
{
|
||||
elevated = elevation.TokenIsElevated != 0;
|
||||
}
|
||||
|
||||
if (token != IntPtr.Zero)
|
||||
{
|
||||
CloseHandle(token);
|
||||
}
|
||||
}
|
||||
|
||||
_cachedValue = elevated;
|
||||
return elevated;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using ManagedCommon;
|
||||
using RunnerV2.Extensions;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Foundation;
|
||||
using Windows.Management.Deployment;
|
||||
|
||||
namespace RunnerV2.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides helper methods for working with UWP packages.
|
||||
/// </summary>
|
||||
internal static partial class PackageHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the registered UWP package based on the display name and version check.
|
||||
/// </summary>
|
||||
/// <param name="packageDisplayName">The display name of the package.</param>
|
||||
/// <param name="checkVersion">If true, the package version will be checked against the executing assembly version.</param>
|
||||
/// <returns>If a package is found the corresponding <see cref="Package"/> object. If none is found <c>null</c>.</returns>
|
||||
internal static Package? GetRegisteredPackage(string packageDisplayName, bool checkVersion)
|
||||
{
|
||||
PackageManager packageManager = new();
|
||||
foreach (var package in packageManager.FindPackagesForUser(null))
|
||||
{
|
||||
if (package.Id.FullName.Contains(packageDisplayName) && (!checkVersion || package.Id.Version.ToVersion() == Assembly.GetExecutingAssembly().GetName().Version))
|
||||
{
|
||||
return package;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static string[] FindMsixFiles(string directoryPath, bool recursive)
|
||||
{
|
||||
if (!Directory.Exists(directoryPath))
|
||||
{
|
||||
Logger.LogError("Tried to search msix files in " + directoryPath + ", but it does not exist.");
|
||||
return [];
|
||||
}
|
||||
|
||||
List<string> matchedFiles = [];
|
||||
|
||||
try
|
||||
{
|
||||
foreach (string file in Directory.GetFiles(directoryPath, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
if (File.Exists(file) && msixPackagePattern().IsMatch(Path.GetFileName(file)))
|
||||
{
|
||||
matchedFiles.Add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError("An error occured while searching for MSIX files.", e);
|
||||
}
|
||||
|
||||
return [.. matchedFiles];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs the specified appx package along with its dependencies.
|
||||
/// </summary>
|
||||
/// <param name="packagePath">Path to the package</param>
|
||||
/// <param name="dependencies">Array of dependency package paths</param>
|
||||
/// <returns>True if the installation was successful, false otherwise</returns>
|
||||
internal static bool InstallPackage(string packagePath, string[] dependencies)
|
||||
{
|
||||
Logger.LogInfo("Starting package install of package \"" + packagePath + "\"");
|
||||
PackageManager packageManager = new();
|
||||
List<Uri> uris = [];
|
||||
|
||||
foreach (string dependency in dependencies)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsPackageSatisfied(dependency))
|
||||
{
|
||||
Logger.LogInfo("Dependency \"" + dependency + "\" is already satisfied.");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
uris.Add(new Uri(packagePath));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Could not process dependency package at path \"" + dependency + "\"", ex);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress> deploymentOperation = packageManager.AddPackageAsync(new Uri(packagePath), uris, DeploymentOptions.ForceApplicationShutdown);
|
||||
deploymentOperation.Get();
|
||||
|
||||
switch (deploymentOperation.Status)
|
||||
{
|
||||
case AsyncStatus.Error:
|
||||
Logger.LogError($"Registering {packagePath} failed. ErrorCode: {deploymentOperation.ErrorCode}, ErrorText: {deploymentOperation.GetResults().ErrorText}");
|
||||
break;
|
||||
case AsyncStatus.Canceled:
|
||||
Logger.LogError($"Registering {packagePath} was canceled.");
|
||||
break;
|
||||
case AsyncStatus.Completed:
|
||||
Logger.LogInfo($"Registering {packagePath} succeded.");
|
||||
break;
|
||||
default:
|
||||
Logger.LogDebug($"Registering {packagePath} package started.");
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Exception thrown while trying to register package: {packagePath}", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the package specified by the given path is already installed and satisfies the required version.
|
||||
/// </summary>
|
||||
/// <param name="packagePath">Path to the package.</param>
|
||||
/// <returns>True if the package is already installed and satisfies the required version, false otherwise.</returns>
|
||||
private static bool IsPackageSatisfied(string packagePath)
|
||||
{
|
||||
if (!GetPackageNameAndVersionFromAppx(packagePath, out string name, out PackageVersion version))
|
||||
{
|
||||
Logger.LogError("Could not get package name and version from dependency package at path \"" + packagePath + "\"");
|
||||
return false;
|
||||
}
|
||||
|
||||
PackageManager packageManager = new();
|
||||
|
||||
foreach (var package in packageManager.FindPackagesForUser(null))
|
||||
{
|
||||
if (package.Id.Name.Equals(name, StringComparison.OrdinalIgnoreCase) &&
|
||||
package.Id.Version.ToVersion() > version.ToVersion())
|
||||
{
|
||||
Logger.LogInfo($@"Package ""{name}"" is already statisfied with version: {package.Id.Version}. Target version: {version}. PackagePath: {packagePath}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogInfo($@"Package ""{name}"" with version {version} is not satisfied. PackagePath: {packagePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the package name and version from the specified appx package file.
|
||||
/// </summary>
|
||||
/// <param name="packagePath">Path to the package file.</param>
|
||||
/// <param name="name">Output parameter for the package name.</param>
|
||||
/// <param name="packageVersion">Output parameter for the package version.</param>
|
||||
/// <returns>True if the package name and version were successfully retrieved, false otherwise.</returns>
|
||||
private static bool GetPackageNameAndVersionFromAppx(string packagePath, out string name, out PackageVersion packageVersion)
|
||||
{
|
||||
// Todo: Implement this without interop if possible
|
||||
return NativeMethods.GetPackageNameAndVersionFromAppx(packagePath, out name, out packageVersion);
|
||||
}
|
||||
|
||||
[GeneratedRegex("(^.+\\.(appx|msix|msixbundle)$)", RegexOptions.IgnoreCase)]
|
||||
private static partial Regex msixPackagePattern();
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RunnerV2.Helpers
|
||||
{
|
||||
internal static class ProcessHelper
|
||||
{
|
||||
internal static void ScheudleProcessKill(string processName, int msDelay = 500)
|
||||
{
|
||||
new Thread(async () =>
|
||||
{
|
||||
Process[] processes = Process.GetProcessesByName(processName);
|
||||
|
||||
await Task.Delay(msDelay);
|
||||
foreach (var process in processes)
|
||||
{
|
||||
if (!process.HasExited)
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
}
|
||||
}).Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Pipelines;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
using Update;
|
||||
|
||||
namespace RunnerV2.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// This class provides helper methods to interact with the PowerToys Settings window.
|
||||
/// </summary>
|
||||
internal static class SettingsHelper
|
||||
{
|
||||
private static readonly SettingsUtils _settingsUtils = SettingsUtils.Default;
|
||||
private static Process? _process;
|
||||
private static TwoWayPipeMessageIPCManaged? _ipc;
|
||||
|
||||
public static void OpenSettingsWindow(bool showOobeWindow = false, bool showScoobeWindow = false, bool showFlyout = false, Point? flyoutPosition = null, string? additionalArguments = null)
|
||||
{
|
||||
if (_process is not null && _ipc is not null && !_process.HasExited)
|
||||
{
|
||||
if (showFlyout)
|
||||
{
|
||||
_ipc.Send(@"{""ShowYourself"": ""flyout""}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_ipc.Send($@"{{""ShowYourself"": ""{additionalArguments ?? "Dashboard"}""}}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_ipc?.End();
|
||||
_ipc = null;
|
||||
|
||||
// Arg 1: Executable path
|
||||
string executablePath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) ?? throw new InvalidOperationException("No executable path found"), "WinUI3Apps", "PowerToys.Settings.exe");
|
||||
|
||||
// Arg 2,3: Pipe names
|
||||
Pipe settingsPipe = new();
|
||||
Pipe powertoysPipe = new();
|
||||
|
||||
string powerToysPipeName = @"\\.\pipe\powertoys_runner_" + Guid.NewGuid();
|
||||
string settingsPipeName = @"\\.\pipe\powertoys_settings_" + Guid.NewGuid();
|
||||
|
||||
// Arg 4: Process pid
|
||||
string currentProcessId = Environment.ProcessId.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
// Arg 5: Settings theme
|
||||
string theme = Program.GeneralSettings.Theme switch
|
||||
{
|
||||
"light" => "light",
|
||||
"dark" => "dark",
|
||||
"system" when ThemeHelpers.GetAppTheme() == AppTheme.Light => "light",
|
||||
"system" when ThemeHelpers.GetAppTheme() == AppTheme.Dark => "dark",
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
// Arg 6: Elevated status
|
||||
string isElevated = Program.GeneralSettings.IsElevated ? "true" : "false";
|
||||
|
||||
// Arg 7: Is user an administrator
|
||||
string isAdmin = Program.GeneralSettings.IsAdmin ? "true" : "false";
|
||||
|
||||
// Arg 8: Show OOBE window
|
||||
string showOobeArg = showOobeWindow ? "true" : "false";
|
||||
|
||||
// Arg 9: Show SCOOBE window
|
||||
string showScoobeArg = showScoobeWindow ? "true" : "false";
|
||||
|
||||
// Arg 10: Show flyout
|
||||
string showFlyoutArg = showFlyout ? "true" : "false";
|
||||
|
||||
// Arg 11: Are there additional settings window arguments
|
||||
string areThereadditionalArgs = string.IsNullOrEmpty(additionalArguments) ? "false" : "true";
|
||||
|
||||
// Arg 12: Are there flyout position arguments
|
||||
string areThereFlyoutPositionArgs = flyoutPosition.HasValue ? "true" : "false";
|
||||
|
||||
string executableArgs = $"{powerToysPipeName} {settingsPipeName} {currentProcessId} {theme} {isElevated} {isAdmin} {showOobeArg} {showScoobeArg} {showFlyoutArg} {areThereadditionalArgs} {areThereFlyoutPositionArgs}";
|
||||
|
||||
if (!string.IsNullOrEmpty(additionalArguments))
|
||||
{
|
||||
executableArgs += $" {additionalArguments}";
|
||||
}
|
||||
|
||||
if (flyoutPosition is not null)
|
||||
{
|
||||
executableArgs += $" {flyoutPosition.Value.X} {flyoutPosition.Value.Y}";
|
||||
}
|
||||
|
||||
_process = Process.Start(executablePath, executableArgs);
|
||||
|
||||
// Initialize listening to pipes
|
||||
_ipc = new TwoWayPipeMessageIPCManaged(powerToysPipeName, settingsPipeName, OnSettingsMessageReceived);
|
||||
_ipc.Start();
|
||||
}
|
||||
|
||||
private static void OnSettingsMessageReceived(string message)
|
||||
{
|
||||
JsonDocument messageDocument = JsonDocument.Parse(message);
|
||||
|
||||
foreach (var property in messageDocument.RootElement.EnumerateObject())
|
||||
{
|
||||
switch (property.Name)
|
||||
{
|
||||
case "action":
|
||||
foreach (var moduleName in property.Value.EnumerateObject())
|
||||
{
|
||||
_settingsUtils.SaveSettings(moduleName.Value.ToString(), moduleName.Name);
|
||||
if (moduleName.Name == "general")
|
||||
{
|
||||
switch (moduleName.Value.GetProperty("action_name").GetString())
|
||||
{
|
||||
case "restart_elevation":
|
||||
ElevationHelper.RestartScheduled = ElevationHelper.RestartScheduledMode.RestartElevatedWithOpenSettings;
|
||||
Runner.Close();
|
||||
break;
|
||||
case "restart_mentain_elevation":
|
||||
// Todo:
|
||||
break;
|
||||
case "check_for_updates":
|
||||
UpdateSettingsHelper.TriggerUpdateCheck();
|
||||
break;
|
||||
case "request_update_state_date":
|
||||
// Todo:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (IPowerToysModule ptModule in Runner.LoadedModules)
|
||||
{
|
||||
if (ptModule.CustomActions.TryGetValue(moduleName.Value.GetProperty("action_name").GetString() ?? string.Empty, out Action? action))
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case "get_all_hotkey_conflicts":
|
||||
// Todo: Handle hotkey conflict
|
||||
break;
|
||||
case "bugreport":
|
||||
TrayIconManager.ProcessTrayMenuCommand((nuint)TrayIconManager.TrayButton.ReportBug);
|
||||
break;
|
||||
case "bug_report_status":
|
||||
_ipc?.Send($@"{{""bug_report_running:"" {(TrayIconManager.IsBugReportToolRunning ? "true" : "false")}");
|
||||
break;
|
||||
case "killrunner":
|
||||
Runner.Close();
|
||||
break;
|
||||
case "general":
|
||||
try
|
||||
{
|
||||
_settingsUtils.SaveSettings(property.Value.ToString(), string.Empty);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// TODO: Log error
|
||||
}
|
||||
|
||||
NativeMethods.PostMessageW(Runner.RunnerHwnd, (uint)NativeMethods.WindowMessages.REFRESH_SETTINGS, 0, 0);
|
||||
|
||||
foreach (IPowerToysModule module in Runner.ModulesToLoad)
|
||||
{
|
||||
module.OnSettingsChanged("general", property.Value);
|
||||
}
|
||||
|
||||
break;
|
||||
case "powertoys":
|
||||
foreach (var powertoysSettingsPart in property.Value.EnumerateObject())
|
||||
{
|
||||
_settingsUtils.SaveSettings(powertoysSettingsPart.Value.ToString(), powertoysSettingsPart.Name);
|
||||
|
||||
if (Runner.LoadedModules.Find(m => m.Name == powertoysSettingsPart.Name) is IPowerToysModule module)
|
||||
{
|
||||
module.OnSettingsChanged(powertoysSettingsPart.Name, powertoysSettingsPart.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no specific module was found, notify all enabled modules
|
||||
foreach (IPowerToysModule module2 in Runner.LoadedModules.Where(m => m.Enabled))
|
||||
{
|
||||
module2.OnSettingsChanged(powertoysSettingsPart.Name, powertoysSettingsPart.Value);
|
||||
}
|
||||
}
|
||||
|
||||
NativeMethods.PostMessageW(Runner.RunnerHwnd, (uint)NativeMethods.WindowMessages.REFRESH_SETTINGS, 0, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"Unknown message received from Settings: {property.Name}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void CloseSettingsWindow()
|
||||
{
|
||||
using var closeEventWrapper = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.PowerToysRunnerTerminateSettingsEvent());
|
||||
closeEventWrapper.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using static RunnerV2.NativeMethods;
|
||||
|
||||
namespace RunnerV2.Helpers
|
||||
{
|
||||
internal static partial class TrayIconManager
|
||||
{
|
||||
internal static void StartTrayIcon()
|
||||
{
|
||||
NOTIFYICONDATA notifyicondata = new()
|
||||
{
|
||||
CbSize = (uint)Marshal.SizeOf<NOTIFYICONDATA>(),
|
||||
HWnd = Runner.RunnerHwnd,
|
||||
UId = 1,
|
||||
HIcon = Icon.ExtractAssociatedIcon(Environment.ProcessPath!)!.Handle,
|
||||
UFlags = 0x0000001 | 0x00000002 | 0x4,
|
||||
UCallbackMessage = (uint)WindowMessages.ICON_NOTIFY,
|
||||
SzTip = "PowerToys Runner",
|
||||
};
|
||||
|
||||
ChangeWindowMessageFilterEx(Runner.RunnerHwnd, 0x0111, 0x0001, IntPtr.Zero);
|
||||
|
||||
Shell_NotifyIcon(NIMADD, ref notifyicondata);
|
||||
}
|
||||
|
||||
internal static void StopTrayIcon()
|
||||
{
|
||||
NOTIFYICONDATA notifyicondata = new()
|
||||
{
|
||||
CbSize = (uint)Marshal.SizeOf<NOTIFYICONDATA>(),
|
||||
HWnd = Runner.RunnerHwnd,
|
||||
UId = 1,
|
||||
};
|
||||
|
||||
Shell_NotifyIcon(NIMDELETE, ref notifyicondata);
|
||||
}
|
||||
|
||||
internal enum TrayButton : uint
|
||||
{
|
||||
Settings = 1,
|
||||
Documentation,
|
||||
ReportBug,
|
||||
Close,
|
||||
}
|
||||
|
||||
private static bool _doubleClickTimerRunning;
|
||||
private static bool _doubleClickDetected;
|
||||
|
||||
private static IntPtr _trayIconMenu;
|
||||
|
||||
static TrayIconManager()
|
||||
{
|
||||
_trayIconMenu = CreatePopupMenu();
|
||||
AppendMenuW(_trayIconMenu, 0u, new UIntPtr((uint)TrayButton.Settings), "Settings\tDouble-click");
|
||||
AppendMenuW(_trayIconMenu, 0x00000800u, UIntPtr.Zero, string.Empty); // separator
|
||||
AppendMenuW(_trayIconMenu, 0u, new UIntPtr((uint)TrayButton.Documentation), "Documentation");
|
||||
AppendMenuW(_trayIconMenu, 0u, new UIntPtr((uint)TrayButton.ReportBug), "Report a Bug");
|
||||
AppendMenuW(_trayIconMenu, 0x00000800u, UIntPtr.Zero, string.Empty); // separator
|
||||
AppendMenuW(_trayIconMenu, 0u, new UIntPtr((uint)TrayButton.Close), "Close");
|
||||
}
|
||||
|
||||
internal static void ProcessTrayIconMessage(long lParam)
|
||||
{
|
||||
switch (lParam)
|
||||
{
|
||||
case 0x0205: // WM_RBUTTONDBLCLK
|
||||
case 0x007B: // WM_CONTEXTMENU
|
||||
SetForegroundWindow(Runner.RunnerHwnd);
|
||||
TrackPopupMenu(_trayIconMenu, 0x0004 | 0x0020, Cursor.Position.X, Cursor.Position.Y, 0, Runner.RunnerHwnd, IntPtr.Zero);
|
||||
break;
|
||||
case 0x0202: // WM_LBUTTONUP
|
||||
if (_doubleClickTimerRunning)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
_doubleClickTimerRunning = true;
|
||||
Task.Delay(SystemInformation.DoubleClickTime).ContinueWith(_ =>
|
||||
{
|
||||
if (!_doubleClickDetected)
|
||||
{
|
||||
SettingsHelper.OpenSettingsWindow(showFlyout: true, flyoutPosition: Cursor.Position);
|
||||
}
|
||||
|
||||
_doubleClickDetected = false;
|
||||
_doubleClickTimerRunning = false;
|
||||
});
|
||||
break;
|
||||
case 0x0203: // WM_LBUTTONDBLCLK
|
||||
_doubleClickDetected = true;
|
||||
SettingsHelper.OpenSettingsWindow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsBugReportToolRunning { get; set; }
|
||||
|
||||
internal static void ProcessTrayMenuCommand(nuint commandId)
|
||||
{
|
||||
switch ((TrayButton)commandId)
|
||||
{
|
||||
case TrayButton.Settings:
|
||||
SettingsHelper.OpenSettingsWindow();
|
||||
break;
|
||||
case TrayButton.Documentation:
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "https://aka.ms/PowerToysOverview",
|
||||
UseShellExecute = true,
|
||||
});
|
||||
break;
|
||||
case TrayButton.ReportBug:
|
||||
Process bugReportProcess = new();
|
||||
bugReportProcess.StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "Tools\\PowerToys.BugReportTool.exe",
|
||||
CreateNoWindow = true,
|
||||
};
|
||||
|
||||
bugReportProcess.EnableRaisingEvents = true;
|
||||
|
||||
EnableMenuItem(_trayIconMenu, (uint)TrayButton.ReportBug, 0x000000 | 0x00001);
|
||||
|
||||
bugReportProcess.Exited += (sender, e) =>
|
||||
{
|
||||
bugReportProcess.Dispose();
|
||||
EnableMenuItem(_trayIconMenu, (uint)TrayButton.ReportBug, 0x00000000);
|
||||
IsBugReportToolRunning = false;
|
||||
};
|
||||
|
||||
bugReportProcess.Start();
|
||||
IsBugReportToolRunning = true;
|
||||
|
||||
break;
|
||||
case TrayButton.Close:
|
||||
Runner.Close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
|
||||
namespace RunnerV2.Models
|
||||
{
|
||||
public interface IPowerToysModule
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the short name of the module. The same used as the name of the folder containing its settings.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This function is called when the module is enabled.
|
||||
/// </summary>
|
||||
public void Enable();
|
||||
|
||||
/// <summary>
|
||||
/// This function is called when the module is disabled.
|
||||
/// </summary>
|
||||
public void Disable();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the module is enabled.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This value shall be read from the settings of the module in the module interface implementation.
|
||||
/// </remarks>
|
||||
public bool Enabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GPO rule configured state for the module.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This value shall be read from the GPO settings with the <see cref="GPOWrapper"/> class.
|
||||
/// </remarks>
|
||||
public GpoRuleConfigured GpoRuleConfigured { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of shortcuts, that shall be registered in the keyboard hook, and their associated actions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this property is not overridden, the module is considered to not have shortcuts.
|
||||
/// </remarks>
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get => []; }
|
||||
|
||||
public Dictionary<string, Action> CustomActions { get => []; }
|
||||
|
||||
/// <summary>
|
||||
/// This function is called when the settings of the module or the general settings are changed.
|
||||
/// </summary>
|
||||
/// <param name="settingsKind">Value of <see cref="Name"/> or "general" indicating the type of change.</param>
|
||||
/// <param name="jsonProperties">The json element with the new settings.</param>
|
||||
public void OnSettingsChanged(string settingsKind, JsonElement jsonProperties)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using RunnerV2.Helpers;
|
||||
|
||||
namespace RunnerV2.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Base abstract class for modules that launch and manage external processes.
|
||||
/// </summary>
|
||||
internal abstract class ProcessModuleAbstractClass
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for launching a process.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ProcessLaunchOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Only a single instance of the process should be running.
|
||||
/// </summary>
|
||||
SingletonProcess = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Elevate the process if the current process is elevated.
|
||||
/// </summary>
|
||||
ElevateIfApplicable = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Provide the runner process ID as the first argument to the launched process.
|
||||
/// </summary>
|
||||
RunnerProcessIdAsFirstArgument = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the application should not launch automatically when the specified module is enabled.
|
||||
/// </summary>
|
||||
SupressLaunchOnModuleEnabled = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Specifies that the process should be started using the operating system shell.
|
||||
/// </summary>
|
||||
UseShellExecute = 16,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the process should never exit automatically.
|
||||
/// </summary>
|
||||
/// <remarks>Use this value when using a helper process to launch an application like explorer.exe.</remarks>
|
||||
NeverExit = 32,
|
||||
|
||||
/// <summary>
|
||||
/// Suppresses UI when process is launched.
|
||||
/// </summary>
|
||||
HideUI = 64,
|
||||
|
||||
/// <summary>
|
||||
/// Sets the launched process to realtime priority.
|
||||
/// </summary>
|
||||
RealtimePriority = 128,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative or absolute path to the process executable.
|
||||
/// </summary>
|
||||
public abstract string ProcessPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the process without the .exe extension.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Has no effect if the <see cref="LaunchOptions"/> has the <see cref="ProcessLaunchOptions.UseShellExecute"/> flag set.
|
||||
/// </remarks>
|
||||
public abstract string ProcessName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the arguments to pass to the process on launch.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If not overridden, no arguments are passed.
|
||||
/// If the <see cref="LaunchOptions"/> has the <see cref="ProcessLaunchOptions.RunnerProcessIdAsFirstArgument"/> flag is set, the runner process ID is prepended to these arguments.
|
||||
/// </remarks>
|
||||
public virtual string ProcessArguments { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the options used to configure how the process is launched.
|
||||
/// </summary>
|
||||
public abstract ProcessLaunchOptions LaunchOptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that atleast one process is launched. If the process is already running, does nothing.
|
||||
/// </summary>
|
||||
public void EnsureLaunched()
|
||||
{
|
||||
Process[] processes = Process.GetProcessesByName(ProcessName);
|
||||
if (processes.Length > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LaunchProcess();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Launches the process with the specified options.
|
||||
/// </summary>
|
||||
/// <param name="isModuleEnableProcess">Specifies if the <see cref="Runner"/> class is currently calling this function as part of a module startup</param>
|
||||
public void LaunchProcess(bool isModuleEnableProcess = false)
|
||||
{
|
||||
if (isModuleEnableProcess && LaunchOptions.HasFlag(ProcessLaunchOptions.SupressLaunchOnModuleEnabled))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (LaunchOptions.HasFlag(ProcessLaunchOptions.SingletonProcess))
|
||||
{
|
||||
Process[] processes = Process.GetProcessesByName(ProcessName);
|
||||
if (processes.Length > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string arguments = (LaunchOptions.HasFlag(ProcessLaunchOptions.RunnerProcessIdAsFirstArgument) ? Environment.ProcessId.ToString(CultureInfo.InvariantCulture) + (string.IsNullOrEmpty(ProcessArguments) ? string.Empty : " ") : string.Empty) + ProcessArguments;
|
||||
|
||||
Process? p = Process.Start(new ProcessStartInfo()
|
||||
{
|
||||
UseShellExecute = LaunchOptions.HasFlag(ProcessLaunchOptions.UseShellExecute),
|
||||
FileName = ProcessPath,
|
||||
Arguments = arguments,
|
||||
Verb = LaunchOptions.HasFlag(ProcessLaunchOptions.ElevateIfApplicable) && ElevationHelper.IsProcessElevated() ? "runas" : "open",
|
||||
});
|
||||
|
||||
if (LaunchOptions.HasFlag(ProcessLaunchOptions.RealtimePriority))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (p != null && !p.HasExited)
|
||||
{
|
||||
p.PriorityClass = ProcessPriorityClass.RealTime;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[ProcessModuleAbstractClass] Failed to set realtime priority for process {ProcessName}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedules all processes with the specified <see cref="ProcessName"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the <see cref="LaunchOptions"/> has the <see cref="ProcessLaunchOptions.NeverExit"/> flag set, this function does nothing.
|
||||
/// </remarks>
|
||||
public void ProcessExit()
|
||||
{
|
||||
if (LaunchOptions.HasFlag(ProcessLaunchOptions.NeverExit))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessHelper.ScheudleProcessKill(ProcessName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RunnerV2.Models
|
||||
{
|
||||
internal readonly struct RegistryChangeSet
|
||||
{
|
||||
public RegistryValueChange[] Changes { get; init; }
|
||||
|
||||
public readonly bool IsApplied => Changes.All(c => c.IsApplied);
|
||||
|
||||
public readonly bool Apply()
|
||||
{
|
||||
bool allApplied = true;
|
||||
foreach (var change in Changes)
|
||||
{
|
||||
allApplied = (change.Apply() || !change.Required) && allApplied;
|
||||
}
|
||||
|
||||
return allApplied;
|
||||
}
|
||||
|
||||
public readonly bool ApplyIfNotApplied() => IsApplied || Apply();
|
||||
|
||||
public readonly bool UnApplyIfApplied() => !IsApplied || UnApply();
|
||||
|
||||
public readonly bool UnApply()
|
||||
{
|
||||
bool allUnapplied = true;
|
||||
foreach (var change in Changes)
|
||||
{
|
||||
allUnapplied = (change.UnApply() || !change.Required) && allUnapplied;
|
||||
}
|
||||
|
||||
return allUnapplied;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace RunnerV2.Models
|
||||
{
|
||||
internal readonly struct RegistryValueChange
|
||||
{
|
||||
public RegistryValueChange()
|
||||
{
|
||||
}
|
||||
|
||||
public required string KeyPath { get; init; }
|
||||
|
||||
public required string? KeyName { get; init; }
|
||||
|
||||
public bool Required { get; init; } = true;
|
||||
|
||||
public required object Value { get; init; }
|
||||
|
||||
public RegistryHive Scope { get; init; } = RegistryHive.CurrentUser;
|
||||
|
||||
private static RegistryValueKind ValueTypeToRegistryValueKind(object value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
int => RegistryValueKind.DWord,
|
||||
long => RegistryValueKind.QWord,
|
||||
string => RegistryValueKind.String,
|
||||
string[] => RegistryValueKind.MultiString,
|
||||
byte[] => RegistryValueKind.Binary,
|
||||
_ => throw new ArgumentException("Unsupported value type"),
|
||||
};
|
||||
}
|
||||
|
||||
public readonly bool IsApplied
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
using RegistryKey? key = RegistryKey.OpenBaseKey(Scope, RegistryView.Default).OpenSubKey(KeyPath, false);
|
||||
return key != null && ValueTypeToRegistryValueKind(Value) == key.GetValueKind(KeyName) && Value.Equals(key.GetValue(KeyName));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Testing if registry change \"{this}\" is applied failed.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public readonly bool RequiresElevation
|
||||
{
|
||||
get => Scope == RegistryHive.LocalMachine;
|
||||
}
|
||||
|
||||
public readonly bool Apply()
|
||||
{
|
||||
try
|
||||
{
|
||||
using RegistryKey? key = RegistryKey.OpenBaseKey(Scope, RegistryView.Default).CreateSubKey(KeyPath, true);
|
||||
if (key == null)
|
||||
{
|
||||
Logger.LogError($"Applying registry change \"{this}\" failed because the registry key could not be created.");
|
||||
return false;
|
||||
}
|
||||
|
||||
key.SetValue(KeyName, Value, ValueTypeToRegistryValueKind(Value));
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Applying registry change \"{this}\" failed.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly bool UnApply()
|
||||
{
|
||||
try
|
||||
{
|
||||
using RegistryKey? key = RegistryKey.OpenBaseKey(Scope, RegistryView.Default).OpenSubKey(KeyPath, true);
|
||||
if (key == null)
|
||||
{
|
||||
Logger.LogError($"Unapplying registry change \"{this}\" failed because the registry key could not be opened.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (KeyName is not null)
|
||||
{
|
||||
key.DeleteValue(KeyName, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
key.SetValue(null, string.Empty); // Delete the default value
|
||||
}
|
||||
|
||||
// Check if the path doesn't contain anything and delete it if so
|
||||
if (key.GetValueNames().Length == 0 && key.GetSubKeyNames().Length == 0)
|
||||
{
|
||||
RegistryKey.OpenBaseKey(Scope, RegistryView.Default).DeleteSubKey(KeyPath, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Unapplying registry change \"{this}\" failed.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override readonly string ToString() => $"{RegistryKey.OpenBaseKey(Scope, RegistryView.Default).Name}\\{KeyPath}\\{KeyName}:{Value}";
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace RunnerV2.Models
|
||||
{
|
||||
internal enum SpecialMode
|
||||
{
|
||||
None,
|
||||
Win32ToastNotificationCOMServer,
|
||||
ToastNotificationHandler,
|
||||
UpdateNow,
|
||||
ReportSuccessfulUpdate,
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class AdvancedPasteModuleInterface : ProcessModuleAbstractClass, IPowerToysModule, IDisposable
|
||||
{
|
||||
public string Name => "AdvancedPaste";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.AdvancedPaste;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredAdvancedPasteEnabledValue();
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
if (_ipc != null)
|
||||
{
|
||||
_ipc.Send(Constants.AdvancedPasteTerminateAppMessage());
|
||||
_ipc.End();
|
||||
_ipc = null;
|
||||
}
|
||||
}
|
||||
|
||||
private const string IpcName = @"\\.\pipe\PowerToys.AdvancedPaste";
|
||||
private TwoWayPipeMessageIPCManaged? _ipc;
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
_ipc = new TwoWayPipeMessageIPCManaged(string.Empty, IpcName, (_) => { });
|
||||
_ipc.Start();
|
||||
|
||||
PopulateShortcuts();
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, JsonElement jsonProperties)
|
||||
{
|
||||
PopulateShortcuts();
|
||||
}
|
||||
|
||||
public void PopulateShortcuts()
|
||||
{
|
||||
_ipc ??= new TwoWayPipeMessageIPCManaged(string.Empty, @"\\.\pipe\PowerToys.AdvancedPaste", (_) => { });
|
||||
|
||||
Shortcuts.Clear();
|
||||
|
||||
AdvancedPasteSettings settings = SettingsUtils.Default.GetSettingsOrDefault<AdvancedPasteSettings>(Name);
|
||||
Shortcuts.Add((settings.Properties.AdvancedPasteUIShortcut, () =>
|
||||
_ipc.Send(Constants.AdvancedPasteShowUIMessage())
|
||||
));
|
||||
Shortcuts.Add((settings.Properties.PasteAsPlainTextShortcut, TryToPasteAsPlainText));
|
||||
Shortcuts.Add((settings.Properties.PasteAsMarkdownShortcut, () => _ipc.Send(Constants.AdvancedPasteMarkdownMessage())));
|
||||
Shortcuts.Add((settings.Properties.PasteAsJsonShortcut, () => _ipc.Send(Constants.AdvancedPasteJsonMessage())));
|
||||
|
||||
HotkeyAccessor[] hotkeyAccessors = settings.GetAllHotkeyAccessors();
|
||||
int additionalActionsCount = settings.Properties.AdditionalActions.GetAllActions().Count() - 2;
|
||||
for (int i = 0; i < additionalActionsCount; i++)
|
||||
{
|
||||
int scopedI = i;
|
||||
Shortcuts.Add((hotkeyAccessors[4 + i].Value, () => _ipc.Send(Constants.AdvancedPasteAdditionalActionMessage() + " " + (3 + scopedI))));
|
||||
}
|
||||
|
||||
for (int i = 4 + additionalActionsCount; i < hotkeyAccessors.Length; i++)
|
||||
{
|
||||
int scopedI = i;
|
||||
Shortcuts.Add((hotkeyAccessors[i].Value, () => _ipc.Send(Constants.AdvancedPasteCustomActionMessage() + " " + (scopedI - 4 - additionalActionsCount))));
|
||||
}
|
||||
}
|
||||
|
||||
private void TryToPasteAsPlainText()
|
||||
{
|
||||
if (Clipboard.ContainsText())
|
||||
{
|
||||
string text = Clipboard.GetText();
|
||||
SendKeys.SendWait(text);
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_ipc?.Dispose();
|
||||
}
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get; } = [];
|
||||
|
||||
public override string ProcessPath => "WinUI3Apps\\PowerToys.AdvancedPaste.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.AdvancedPaste";
|
||||
|
||||
public override string ProcessArguments => IpcName;
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class AlwaysOnTopModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.AlwaysOnTop;
|
||||
|
||||
public string Name => "AlwaysOnTop";
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredAlwaysOnTopEnabledValue();
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
using var terminateEventWrapper = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.AlwaysOnTopTerminateEvent());
|
||||
terminateEventWrapper.Set();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
InitializeHotkey();
|
||||
}
|
||||
|
||||
private void InitializeHotkey()
|
||||
{
|
||||
Shortcuts.Clear();
|
||||
Shortcuts.Add((SettingsUtils.Default.GetSettings<AlwaysOnTopSettings>(Name).Properties.Hotkey.Value, () =>
|
||||
{
|
||||
using var pinEventWrapper = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.AlwaysOnTopPinEvent());
|
||||
pinEventWrapper.Set();
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, JsonElement jsonProperties)
|
||||
{
|
||||
InitializeHotkey();
|
||||
}
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get; } = [];
|
||||
|
||||
public override string ProcessPath => "PowerToys.AlwaysOnTop.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.AlwaysOnTop";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument | ProcessLaunchOptions.ElevateIfApplicable;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class AwakeModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "Awake";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.Awake;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredAwakeEnabledValue();
|
||||
|
||||
public override string ProcessPath => "PowerToys.Awake.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.Awake";
|
||||
|
||||
public override string ProcessArguments => $"--use-pt-config --pid {Environment.ProcessId.ToString(CultureInfo.InvariantCulture)}";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
using var terminateEventWrapper = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.AwakeExitEvent());
|
||||
terminateEventWrapper.Set();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using ManagedCommon;
|
||||
using PowerToys.GPOWrapper;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class CmdNotFoundModuleInterface : IPowerToysModule
|
||||
{
|
||||
public string Name => "CmdNotFound";
|
||||
|
||||
public bool Enabled => true;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredCmdNotFoundEnabledValue();
|
||||
|
||||
public CmdNotFoundModuleInterface()
|
||||
{
|
||||
if (GpoRuleConfigured == GpoRuleConfigured.Disabled)
|
||||
{
|
||||
UninstallModule();
|
||||
}
|
||||
|
||||
if (GpoRuleConfigured == GpoRuleConfigured.Enabled)
|
||||
{
|
||||
InstallModule();
|
||||
}
|
||||
}
|
||||
|
||||
public void InstallModule()
|
||||
{
|
||||
Logger.LogInfo("Installing Command Not Found module invoked through GPO");
|
||||
|
||||
new Thread(async () =>
|
||||
{
|
||||
Process p = Process.Start("pwsh.exe", "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + Path.GetDirectoryName(Environment.ProcessPath) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\EnableModule.ps1" + "\"" + " -scriptPath \"" + Path.GetDirectoryName(Environment.ProcessPath) + "\"");
|
||||
await p.WaitForExitAsync();
|
||||
if (p.ExitCode == 0)
|
||||
{
|
||||
Logger.LogInfo("Command Not Found was successfully installed.");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInfo("Command Not Found failed to install with exit code: " + p.ExitCode);
|
||||
}).Start();
|
||||
}
|
||||
|
||||
public void UninstallModule()
|
||||
{
|
||||
Logger.LogInfo("Uninstalling Command Not Found module invoked through GPO");
|
||||
|
||||
new Thread(async () =>
|
||||
{
|
||||
Process p = Process.Start("pwsh.exe", "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + Path.GetDirectoryName(Environment.ProcessPath) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "\"" + " -scriptPath \"" + Path.GetDirectoryName(Environment.ProcessPath) + "\"");
|
||||
await p.WaitForExitAsync();
|
||||
if (p.ExitCode == 0)
|
||||
{
|
||||
Logger.LogInfo("Command Not Found was successfully uninstalled.");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInfo("Command Not Found failed to uninstall with exit code: " + p.ExitCode);
|
||||
}).Start();
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class ColorPickerModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "ColorPicker";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettingsOrDefault<GeneralSettings>().Enabled.ColorPicker;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredColorPickerEnabledValue();
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
using var terminateEventWrapper = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.TerminateColorPickerSharedEvent());
|
||||
terminateEventWrapper.Set();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
InitializeShortcuts();
|
||||
}
|
||||
|
||||
private void InitializeShortcuts()
|
||||
{
|
||||
Shortcuts.Clear();
|
||||
Shortcuts.Add((SettingsUtils.Default.GetSettings<ColorPickerSettings>(Name).Properties.ActivationShortcut, () =>
|
||||
{
|
||||
using var showUiEventWrapper = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowColorPickerSharedEvent());
|
||||
showUiEventWrapper.Set();
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, JsonElement jsonProperties)
|
||||
{
|
||||
InitializeShortcuts();
|
||||
}
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get; } = [];
|
||||
|
||||
public override string ProcessPath => "PowerToys.ColorPickerUI.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.ColorPickerUI";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument;
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using RunnerV2.Helpers;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class CommandPaletteModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
private const string PackageName = "Microsoft.CommandPalette"
|
||||
#if DEBUG
|
||||
+ ".Dev"
|
||||
#endif
|
||||
;
|
||||
|
||||
public string Name => "CmdPal";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettingsOrDefault<GeneralSettings>().Enabled.CmdPal;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredCmdPalEnabledValue();
|
||||
|
||||
public override string ProcessPath => "explorer.exe";
|
||||
|
||||
public override string ProcessArguments => "x-cmdpal://background";
|
||||
|
||||
public override string ProcessName => string.Empty;
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.UseShellExecute | ProcessLaunchOptions.NeverExit;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
if (PackageHelper.GetRegisteredPackage(PackageName, false) is null)
|
||||
{
|
||||
try
|
||||
{
|
||||
string architectureString = RuntimeInformation.ProcessArchitecture == Architecture.X64 ? "x64" : "ARM64";
|
||||
#if DEBUG
|
||||
string[] msixFiles = PackageHelper.FindMsixFiles(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\WinUI3Apps\\CmdPal\\AppPackages\\Microsoft.CmdPal.UI_0.0.1.0_Debug_Test\\", false);
|
||||
string[] dependencies = PackageHelper.FindMsixFiles(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\WinUI3Apps\\CmdPal\\AppPackages\\Microsoft.CmdPal.UI_0.0.1.0_Debug_Test\\Dependencies\\" + architectureString + "\\", true);
|
||||
#else
|
||||
string[] msixFiles = PackageHelper.FindMsixFiles(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\WinUI3Apps\\CmdPal\\", false);
|
||||
string[] dependencies = PackageHelper.FindMsixFiles(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\WinUI3Apps\\CmdPal\\Dependencies\\", true);
|
||||
#endif
|
||||
|
||||
if (msixFiles.Length > 0)
|
||||
{
|
||||
if (!PackageHelper.InstallPackage(msixFiles[0], dependencies))
|
||||
{
|
||||
Logger.LogError("Failed to register Command Palette package.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Exception occurred while enabling Command Palette package.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (PackageHelper.GetRegisteredPackage(PackageName, false) is null)
|
||||
{
|
||||
Logger.LogError("Command Palette package is not registered after attempting to enable it.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed partial class CropAndLockModuleInterface : ProcessModuleAbstractClass, IPowerToysModule, IDisposable
|
||||
{
|
||||
public string Name => "CropAndLock";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.CropAndLock;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredCropAndLockEnabledValue();
|
||||
|
||||
private EventWaitHandle _reparentEvent = new(false, EventResetMode.AutoReset, Constants.CropAndLockReparentEvent());
|
||||
private EventWaitHandle _thumbnailEvent = new(false, EventResetMode.AutoReset, Constants.CropAndLockThumbnailEvent());
|
||||
private EventWaitHandle _terminateEvent = new(false, EventResetMode.AutoReset, Constants.CropAndLockExitEvent());
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
_terminateEvent.Set();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
PopulateShortcuts();
|
||||
}
|
||||
|
||||
public void PopulateShortcuts()
|
||||
{
|
||||
Shortcuts.Clear();
|
||||
var settings = SettingsUtils.Default.GetSettings<CropAndLockSettings>(Name);
|
||||
Shortcuts.Add((settings.Properties.ThumbnailHotkey.Value, () => _thumbnailEvent.Set()));
|
||||
Shortcuts.Add((settings.Properties.ReparentHotkey.Value, () => _reparentEvent.Set()));
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, JsonElement jsonProperties)
|
||||
{
|
||||
PopulateShortcuts();
|
||||
}
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get; } = [];
|
||||
|
||||
public override string ProcessPath => "PowerToys.CropAndLock.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.CropAndLock";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
_reparentEvent.Dispose();
|
||||
_thumbnailEvent.Dispose();
|
||||
_terminateEvent.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class CursorWrapModuleInterface : IPowerToysModule
|
||||
{
|
||||
public string Name => "CursorWrap";
|
||||
|
||||
private bool _hookActive;
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.CursorWrap;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredCursorWrapEnabledValue();
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
_hookActive = false;
|
||||
CursorWrapStopMouseHook();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
InitializeShortcuts();
|
||||
_hookActive = true;
|
||||
CursorWrapStartMouseHook();
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, JsonElement jsonProperties)
|
||||
{
|
||||
InitializeShortcuts();
|
||||
}
|
||||
|
||||
private void InitializeShortcuts()
|
||||
{
|
||||
Shortcuts.Clear();
|
||||
Shortcuts.Add((SettingsUtils.Default.GetSettings<CursorWrapSettings>(Name).Properties.DefaultActivationShortcut, () =>
|
||||
{
|
||||
if (_hookActive)
|
||||
{
|
||||
CursorWrapStopMouseHook();
|
||||
_hookActive = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
CursorWrapStartMouseHook();
|
||||
_hookActive = true;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get; } = [];
|
||||
|
||||
[DllImport("PowerToys.CursorWrap.dll")]
|
||||
private static extern bool CursorWrapStartMouseHook();
|
||||
|
||||
[DllImport("PowerToys.CursorWrap.dll")]
|
||||
private static extern bool CursorWrapStopMouseHook();
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class EnvironmentVariablesModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "Environment Variables";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.EnvironmentVariables;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredEnvironmentVariablesEnabledValue();
|
||||
|
||||
public override string ProcessPath => "WinUI3Apps\\PowerToys.EnvironmentVariables.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.EnvironmentVariables";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SupressLaunchOnModuleEnabled | ProcessLaunchOptions.NeverExit;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class FancyZonesModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "FancyZones";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.FancyZones;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredFancyZonesEnabledValue();
|
||||
|
||||
public override string ProcessPath => "PowerToys.FancyZones.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.FancyZones";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument | ProcessLaunchOptions.HideUI;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
using var terminateEvent = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset, Constants.FZEExitEvent());
|
||||
terminateEvent.Set();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
}
|
||||
|
||||
public Dictionary<string, Action> CustomActions => new()
|
||||
{
|
||||
{
|
||||
"ToggledFZEditor",
|
||||
() =>
|
||||
{
|
||||
EnsureLaunched();
|
||||
using var invokeFZEditorEvent = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset, Constants.FZEToggleEvent());
|
||||
invokeFZEditorEvent.Set();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,331 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class FileExplorerModuleInterface : IPowerToysModule
|
||||
{
|
||||
private record struct FileExplorerModule(Func<bool> IsEnabled, GpoRuleConfigured GpoRule, RegistryChangeSet RegistryChanges);
|
||||
|
||||
private static readonly List<FileExplorerModule> _fileExplorerModules;
|
||||
|
||||
private static readonly string[] ExtSVG = { ".svg" };
|
||||
private static readonly string[] ExtMarkdown = { ".md", ".markdown", ".mdown", ".mkdn", ".mkd", ".mdwn", ".mdtxt", ".mdtext" };
|
||||
private static readonly string[] ExtPDF = { ".pdf" };
|
||||
private static readonly string[] ExtGCode = { ".gcode" };
|
||||
private static readonly string[] ExtBGCode = { ".bgcode" };
|
||||
private static readonly string[] ExtSTL = { ".stl" };
|
||||
private static readonly string[] ExtQOI = { ".qoi" };
|
||||
|
||||
static FileExplorerModuleInterface()
|
||||
{
|
||||
static PowerPreviewProperties GetProperties() => SettingsUtils.Default.GetSettings<PowerPreviewSettings>(PowerPreviewSettings.ModuleName).Properties;
|
||||
|
||||
string installationPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
||||
|
||||
_fileExplorerModules = [
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnableBgcodePreview,
|
||||
GPOWrapper.GetConfiguredBgcodePreviewEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.PreviewHandler, "{0e6d5bdd-d5f8-4692-a089-8bb88cdd37f4}", "BgcodePreviewHandler", "Binary G-code Preview Handler", ExtBGCode)),
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnableBgcodeThumbnail,
|
||||
GPOWrapper.GetConfiguredBgcodeThumbnailsEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.ThumbnailProvider, "{5c93a1e4-99d0-4fb3-991c-6c296a27be21}", "BgcodeThumbnailProvider", "Binary G-code Thumbnail Provider", ExtBGCode)),
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnableGcodePreview,
|
||||
GPOWrapper.GetConfiguredGcodePreviewEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.PreviewHandler, "{A0257634-8812-4CE8-AF11-FA69ACAEAFAE}", "GcodePreviewHandler", "G-code Preview Handler", ExtGCode)),
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnableGcodeThumbnail,
|
||||
GPOWrapper.GetConfiguredGcodeThumbnailsEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.ThumbnailProvider, "{F2847CBE-CD03-4C83-A359-1A8052C1B9D5}", "GcodeThumbnailProvider", "G-code Thumbnail Provider", ExtGCode)),
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnableMdPreview,
|
||||
GPOWrapper.GetConfiguredMarkdownPreviewEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.PreviewHandler, "{60789D87-9C3C-44AF-B18C-3DE2C2820ED3}", "MarkdownPreviewHandler", "Markdown Preview Handler", ExtMarkdown)),
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnablePdfPreview,
|
||||
GPOWrapper.GetConfiguredPdfPreviewEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.PreviewHandler, "{A5A41CC7-02CB-41D4-8C9B-9087040D6098}", "PdfPreviewHandler", "PDF Preview Handler", ExtPDF)),
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnablePdfThumbnail,
|
||||
GPOWrapper.GetConfiguredPdfThumbnailsEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.ThumbnailProvider, "{D8BB9942-93BD-412D-87E4-33FAB214DC1A}", "PdfThumbnailProvider", "PDF Thumbnail Provider", ExtPDF)),
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnableQoiPreview,
|
||||
GPOWrapper.GetConfiguredQoiPreviewEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.PreviewHandler, "{729B72CD-B72E-4FE9-BCBF-E954B33FE699}", "QoiPreviewHandler", "QOI Preview Handler", ExtQOI)),
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnableQoiThumbnail,
|
||||
GPOWrapper.GetConfiguredQoiThumbnailsEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.ThumbnailProvider, "{AD856B15-D25E-4008-AFB7-AFAA55586188}", "QoiThumbnailProvider", "QOI Thumbnail Provider", ExtQOI, "image", "Picture")),
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnableStlThumbnail,
|
||||
GPOWrapper.GetConfiguredStlThumbnailsEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.ThumbnailProvider, "{77257004-6F25-4521-B602-50ECC6EC62A6}", "StlThumbnailProvider", "STL Thumbnail Provider", ExtSTL)),
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnableSvgPreview,
|
||||
GPOWrapper.GetConfiguredSvgPreviewEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.PreviewHandler, "{FCDD4EED-41AA-492F-8A84-31A1546226E0}", "SvgPreviewHandler", "SVG Preview Handler", ExtSVG)),
|
||||
new FileExplorerModule(
|
||||
() => GetProperties().EnableSvgThumbnail,
|
||||
GPOWrapper.GetConfiguredSvgThumbnailsEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(FileExplorerAddOnType.ThumbnailProvider, "{10144713-1526-46C9-88DA-1FB52807A9FF}", "SvgThumbnailProvider", "SVG Thumbnail Provider", ExtSVG, "image", "Picture")),
|
||||
GetMonacoFileExplorerModule(installationPath)
|
||||
];
|
||||
}
|
||||
|
||||
private static FileExplorerModule GetMonacoFileExplorerModule(string installationPath)
|
||||
{
|
||||
// .svgz is a binary file type that Monaco cannot handle, so we exclude it from the preview handler
|
||||
string[] extExclusions = [..ExtMarkdown, ..ExtSVG, ".svgz"];
|
||||
List<string> extensions = [];
|
||||
|
||||
string languagesFilePath = Path.Combine(installationPath, "Assets\\Monaco\\monaco_languages.json");
|
||||
|
||||
if (!File.Exists(languagesFilePath))
|
||||
{
|
||||
Logger.LogError("PowerPreviewModuleInterface: Unable to find monaco_languages.json file at " + languagesFilePath);
|
||||
goto returnLabel;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
JsonDocument jsonDocument = JsonDocument.Parse(File.ReadAllText(languagesFilePath));
|
||||
var list = jsonDocument.RootElement.GetProperty("list");
|
||||
|
||||
foreach (var item in list.EnumerateArray())
|
||||
{
|
||||
if (item.TryGetProperty("extensions", out JsonElement extensionsElement))
|
||||
{
|
||||
foreach (var ext in extensionsElement.EnumerateArray())
|
||||
{
|
||||
string extension = ext.GetString() ?? string.Empty;
|
||||
if (!string.IsNullOrEmpty(extension) && !extensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
extensions.Add(extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("PowerPreviewModuleInterface: Failed to parse monaco_languages.json file.", ex);
|
||||
}
|
||||
|
||||
returnLabel:
|
||||
return new FileExplorerModule(
|
||||
() => SettingsUtils.Default.GetSettings<PowerPreviewSettings>(PowerPreviewSettings.ModuleName).Properties.EnableMonacoPreview,
|
||||
GPOWrapper.GetConfiguredMonacoPreviewEnabledValue(),
|
||||
GetFileExplorerAddOnChangeSet(
|
||||
FileExplorerAddOnType.PreviewHandler,
|
||||
"{D8034CFA-F34B-41FE-AD45-62FCBB52A6DA}",
|
||||
"MonacoPreviewHandler",
|
||||
"Monaco Preview Handler",
|
||||
[.. extensions.Where(ext => !extExclusions.Contains(ext))]));
|
||||
}
|
||||
|
||||
private enum FileExplorerAddOnType
|
||||
{
|
||||
ThumbnailProvider,
|
||||
PreviewHandler,
|
||||
}
|
||||
|
||||
private static RegistryChangeSet GetFileExplorerAddOnChangeSet(FileExplorerAddOnType type, string handlerClsid, string className, string displayName, string[] fileTypes, string? perceivedType = null, string? fileKindType = null)
|
||||
{
|
||||
string installationPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
||||
string pathToHandler = Path.Combine(installationPath, "PowerToys.FileExplorerDllExporter.dll");
|
||||
string clsidPath = "Software\\Classes\\CLSID\\" + handlerClsid;
|
||||
string inprocServer32Path = clsidPath + "\\InprocServer32";
|
||||
|
||||
string assemblyKeyValue;
|
||||
int lastDotPos = className.LastIndexOf('.');
|
||||
|
||||
if (lastDotPos != -1)
|
||||
{
|
||||
assemblyKeyValue = string.Concat("PowerToys.", className.AsSpan(lastDotPos + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
assemblyKeyValue = "PowerToys." + className;
|
||||
}
|
||||
|
||||
assemblyKeyValue += $", Version={Assembly.GetExecutingAssembly().GetName().Version!}, Culture=neutral";
|
||||
|
||||
List<RegistryValueChange> changes = [
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = clsidPath,
|
||||
KeyName = "DisplayName",
|
||||
Value = displayName,
|
||||
},
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = clsidPath,
|
||||
KeyName = null,
|
||||
Value = className,
|
||||
},
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = inprocServer32Path,
|
||||
KeyName = null,
|
||||
Value = pathToHandler,
|
||||
},
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = inprocServer32Path,
|
||||
KeyName = "Assembly",
|
||||
Value = assemblyKeyValue,
|
||||
},
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = inprocServer32Path,
|
||||
KeyName = "Class",
|
||||
Value = className,
|
||||
},
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = inprocServer32Path,
|
||||
KeyName = "ThreadingModel",
|
||||
Value = "Apartment",
|
||||
},
|
||||
];
|
||||
|
||||
foreach (string fileType in fileTypes)
|
||||
{
|
||||
string fileTypePath = "Software\\Classes\\" + fileType;
|
||||
string fileAssociationPath = fileTypePath + "\\shellex\\" + (type == FileExplorerAddOnType.PreviewHandler ? IPREVIEWHANDLERCLSID : ITHUMBNAILPROVIDERCLSID);
|
||||
|
||||
changes.Add(new RegistryValueChange
|
||||
{
|
||||
KeyPath = fileAssociationPath,
|
||||
KeyName = null,
|
||||
Value = handlerClsid,
|
||||
});
|
||||
|
||||
if (!string.IsNullOrEmpty(fileKindType))
|
||||
{
|
||||
// Registering a file type as a kind needs to be done at the HKEY_LOCAL_MACHINE level.
|
||||
// Make it optional as well so that we don't fail registering the handler if we can't write to HKEY_LOCAL_MACHINE.
|
||||
string kindPath = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\KindMap";
|
||||
changes.Add(new RegistryValueChange
|
||||
{
|
||||
Scope = Microsoft.Win32.RegistryHive.LocalMachine,
|
||||
KeyPath = kindPath,
|
||||
KeyName = fileType,
|
||||
Value = fileKindType,
|
||||
Required = false,
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(perceivedType))
|
||||
{
|
||||
changes.Add(new RegistryValueChange
|
||||
{
|
||||
KeyPath = fileTypePath,
|
||||
KeyName = "PerceivedType",
|
||||
Value = perceivedType,
|
||||
});
|
||||
}
|
||||
|
||||
// this regfile registry key has precedence over Software\Classes\.reg for .reg files
|
||||
if (type == FileExplorerAddOnType.PreviewHandler && fileType.Equals(".reg", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string regFilePath = "Software\\Classes\\regfile\\shellex\\" + IPREVIEWHANDLERCLSID;
|
||||
changes.Add(new RegistryValueChange
|
||||
{
|
||||
KeyPath = regFilePath,
|
||||
KeyName = null,
|
||||
Value = handlerClsid,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (type == FileExplorerAddOnType.PreviewHandler)
|
||||
{
|
||||
string previewHostClsid = "{6d2b5079-2f0b-48dd-ab7f-97cec514d30b}";
|
||||
string previewHandlerListPath = "(Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers)";
|
||||
changes.Add(new RegistryValueChange
|
||||
{
|
||||
KeyPath = clsidPath,
|
||||
KeyName = "AppID",
|
||||
Value = previewHostClsid,
|
||||
});
|
||||
|
||||
changes.Add(new RegistryValueChange
|
||||
{
|
||||
KeyPath = previewHandlerListPath,
|
||||
KeyName = handlerClsid,
|
||||
Value = displayName,
|
||||
});
|
||||
}
|
||||
|
||||
changes.Add(new RegistryValueChange
|
||||
{
|
||||
Scope = Microsoft.Win32.RegistryHive.LocalMachine,
|
||||
KeyPath = "Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
|
||||
KeyName = handlerClsid,
|
||||
Value = displayName,
|
||||
Required = false,
|
||||
});
|
||||
|
||||
return new RegistryChangeSet
|
||||
{
|
||||
Changes = [.. changes],
|
||||
};
|
||||
}
|
||||
|
||||
private const string ITHUMBNAILPROVIDERCLSID = "{E357FCCD-A995-4576-B01F-234630154E96}";
|
||||
private const string IPREVIEWHANDLERCLSID = "{8895b1c6-b41f-4c1c-a562-0d564250836f}";
|
||||
|
||||
public string Name => "File Explorer";
|
||||
|
||||
public bool Enabled => true;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GpoRuleConfigured.Unavailable;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
OnSettingsChanged("File Explorer", default);
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, JsonElement jsonProperties)
|
||||
{
|
||||
foreach (FileExplorerModule submodule in _fileExplorerModules)
|
||||
{
|
||||
if (submodule.GpoRule == GpoRuleConfigured.Disabled)
|
||||
{
|
||||
submodule.RegistryChanges.UnApply();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (submodule.IsEnabled() || submodule.GpoRule == GpoRuleConfigured.Enabled)
|
||||
{
|
||||
submodule.RegistryChanges.Apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
submodule.RegistryChanges.UnApply();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class FindMyMouseModuleInterface : IPowerToysModule
|
||||
{
|
||||
public string Name => "FindMyMouse";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.FindMyMouse;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredFindMyMouseEnabledValue();
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
FindMyMouseDisable();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
InitializeShortcuts();
|
||||
|
||||
var thread = new Thread(() =>
|
||||
{
|
||||
uint version = 0x00010008;
|
||||
int hr = MddBootstrapInitialize(version, 0, IntPtr.Zero);
|
||||
if (hr < 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Windows app sdk could not be initialized for MouseJump. HR code:{hr}");
|
||||
}
|
||||
|
||||
FindMyMouseMain();
|
||||
});
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, JsonElement jsonProperties)
|
||||
{
|
||||
InitializeShortcuts();
|
||||
NativeMethods.PostMessageW(GetSonarHwnd(), GetWmPrivSettingsChanged(), IntPtr.Zero, IntPtr.Zero);
|
||||
}
|
||||
|
||||
private void InitializeShortcuts()
|
||||
{
|
||||
Shortcuts.Clear();
|
||||
|
||||
Shortcuts.Add((SettingsUtils.Default.GetSettings<FindMyMouseSettings>(Name).Properties.ActivationShortcut, () =>
|
||||
{
|
||||
NativeMethods.PostMessageW(GetSonarHwnd(), GetWmPrivShortcut(), IntPtr.Zero, IntPtr.Zero);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get; } = [];
|
||||
|
||||
[DllImport("PowerToys.FindMyMouse.dll")]
|
||||
public static extern void FindMyMouseMain();
|
||||
|
||||
[DllImport("PowerToys.FindMyMouse.dll")]
|
||||
public static extern void FindMyMouseDisable();
|
||||
|
||||
[DllImport("PowerToys.FindMyMouse.dll")]
|
||||
public static extern IntPtr GetSonarHwnd();
|
||||
|
||||
[DllImport("PowerToys.FindMyMouse.dll")]
|
||||
public static extern uint GetWmPrivShortcut();
|
||||
|
||||
[DllImport("PowerToys.FindMyMouse.dll")]
|
||||
public static extern uint GetWmPrivSettingsChanged();
|
||||
|
||||
[DllImport("Microsoft.WindowsAppRuntime.Bootstrap.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern int MddBootstrapInitialize(uint majorMinorVersion, uint versionTag, IntPtr packageVersion);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class HostsModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public bool Enabled => SettingsUtils.Default.GetSettingsOrDefault<GeneralSettings>().Enabled.Hosts;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredHostsFileEditorEnabledValue();
|
||||
|
||||
public string Name => "Hosts";
|
||||
|
||||
public override string ProcessPath => "WinUI3Apps\\PowerToys.Hosts.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.Hosts";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SupressLaunchOnModuleEnabled | ProcessLaunchOptions.NeverExit;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class KeyboardManagerModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "Keyboard Manager";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.KeyboardManager;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredKeyboardManagerEnabledValue();
|
||||
|
||||
public override string ProcessPath => "KeyboardManagerEngine\\PowerToys.KeyboardManagerEngine.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.KeyboardManagerEngine";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument | ProcessLaunchOptions.RealtimePriority;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
using var terminateEvent = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.TerminateKBMSharedEvent());
|
||||
terminateEvent.Set();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
using Settings.UI.Library;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class LightSwitchModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "LightSwitch";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.LightSwitch;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredLightSwitchEnabledValue();
|
||||
|
||||
public override string ProcessPath => "LightSwitchService\\PowerToys.LightSwitchService.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.LightSwitchService";
|
||||
|
||||
public override string ProcessArguments => $"--pid {Environment.ProcessId}";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess;
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts
|
||||
{
|
||||
get => [(SettingsUtils.Default.GetSettings<LightSwitchSettings>(Name).Properties.ToggleThemeHotkey.Value, () =>
|
||||
{
|
||||
LightSwitchProperties properties = SettingsUtils.Default.GetSettings<LightSwitchSettings>(Name).Properties;
|
||||
EnsureLaunched();
|
||||
if (properties.ChangeSystem.Value)
|
||||
{
|
||||
ThemeHelper.SetSystemTheme(!ThemeHelper.GetCurrentSystemTheme());
|
||||
}
|
||||
|
||||
if (properties.ChangeApps.Value)
|
||||
{
|
||||
ThemeHelper.SetAppsTheme(!ThemeHelper.GetCurrentAppsTheme());
|
||||
}
|
||||
|
||||
using var manualOverrideEvent = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset, Constants.LightSwitchManualOverrideEvent());
|
||||
manualOverrideEvent.Set();
|
||||
})];
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class MeasureToolModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "Measure Tool";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.MeasureTool;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredScreenRulerEnabledValue();
|
||||
|
||||
public override string ProcessPath => "WinUI3Apps\\PowerToys.MeasureToolUI.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.MeasureToolUI";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument | ProcessLaunchOptions.SupressLaunchOnModuleEnabled;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
PopulateShortcuts();
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, System.Text.Json.JsonElement jsonProperties)
|
||||
{
|
||||
PopulateShortcuts();
|
||||
}
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get; } = [];
|
||||
|
||||
private void PopulateShortcuts()
|
||||
{
|
||||
Shortcuts.Clear();
|
||||
Shortcuts.Add((SettingsUtils.Default.GetSettings<MeasureToolSettings>(Name).Properties.ActivationShortcut, () =>
|
||||
{
|
||||
LaunchProcess();
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class MouseJumpModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "MouseJump";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.MouseJump;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredMouseJumpEnabledValue();
|
||||
|
||||
public override string ProcessPath => "PowerToys.MouseJumpUI.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.MouseJumpUI";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
using var terminateEvent = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset, Constants.TerminateMouseJumpSharedEvent());
|
||||
terminateEvent.Set();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
PopulateShortcuts();
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, System.Text.Json.JsonElement jsonProperties)
|
||||
{
|
||||
PopulateShortcuts();
|
||||
}
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get; } = [];
|
||||
|
||||
public void PopulateShortcuts()
|
||||
{
|
||||
Shortcuts.Clear();
|
||||
var settings = SettingsUtils.Default.GetSettings<MouseJumpSettings>(Name);
|
||||
Shortcuts.Add((settings.Properties.ActivationShortcut, () =>
|
||||
{
|
||||
EnsureLaunched();
|
||||
using var invokeMouseJumpEvent = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset, Constants.MouseJumpShowPreviewEvent());
|
||||
invokeMouseJumpEvent.Set();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class PowerAccentModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "PowerAccent";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettingsOrDefault<GeneralSettings>().Enabled.PowerAccent;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredQuickAccentEnabledValue();
|
||||
|
||||
public override string ProcessPath => "PowerToys.PowerAccent.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.PowerAccent";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class PowerOCRModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "TextExtractor";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.PowerOcr;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredTextExtractorEnabledValue();
|
||||
|
||||
public override string ProcessPath => "PowerToys.PowerOCR.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.PowerOCR";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument;
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get; } = [];
|
||||
|
||||
private void PopulateShortcuts()
|
||||
{
|
||||
Shortcuts.Clear();
|
||||
Shortcuts.Add((SettingsUtils.Default.GetSettings<PowerOcrSettings>(Name).Properties.ActivationShortcut, () =>
|
||||
{
|
||||
using var invokeOcrEvent = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowPowerOCRSharedEvent());
|
||||
invokeOcrEvent.Set();
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
using var terminateEvent = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.TerminatePowerOCRSharedEvent());
|
||||
terminateEvent.Set();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
PopulateShortcuts();
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, JsonElement jsonProperties)
|
||||
{
|
||||
PopulateShortcuts();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class PowerToysRunModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "PowerToys Run";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.PowerLauncher;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredPowerLauncherEnabledValue();
|
||||
|
||||
public override string ProcessPath => Path.GetFullPath("PowerToys.PowerLauncher.exe");
|
||||
|
||||
public override string ProcessName => "PowerToys.PowerLauncher";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.ElevateIfApplicable | ProcessLaunchOptions.SingletonProcess;
|
||||
|
||||
public override string ProcessArguments => $"-powerToysPid {Environment.ProcessId}";
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
using var terminateEvent = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset, Constants.RunExitEvent());
|
||||
terminateEvent.Set();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
}
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts =>
|
||||
[
|
||||
(
|
||||
SettingsUtils.Default.GetSettings<PowerLauncherSettings>(Name).Properties.OpenPowerLauncher,
|
||||
() =>
|
||||
{
|
||||
EnsureLaunched();
|
||||
using var invokeRunEvent = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset, Constants.PowerLauncherCentralizedHookSharedEvent());
|
||||
invokeRunEvent.Set();
|
||||
}
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.Win32;
|
||||
using PowerToys.GPOWrapper;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class RegistryPreviewModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public bool Enabled => SettingsUtils.Default.GetSettingsOrDefault<GeneralSettings>().Enabled.RegistryPreview;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredHostsFileEditorEnabledValue();
|
||||
|
||||
public string Name => "RegistryPreview";
|
||||
|
||||
public override string ProcessPath => "WinUI3Apps\\PowerToys.RegistryPreview.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.RegistryPreview";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SupressLaunchOnModuleEnabled | ProcessLaunchOptions.NeverExit;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
if (!RegistryPreviewChangeSet.UnApplyIfApplied())
|
||||
{
|
||||
Logger.LogError("Unapplying registry changes failed");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, JsonElement jsonProperties)
|
||||
{
|
||||
bool defaultRegApp = SettingsUtils.Default.GetSettings<RegistryPreviewSettings>(Name).Properties.DefaultRegApp;
|
||||
if (defaultRegApp && !RegistryPreviewSetDefaultAppChangeSet.IsApplied)
|
||||
{
|
||||
if (!RegistryPreviewSetDefaultAppChangeSet.Apply())
|
||||
{
|
||||
Logger.LogError("Applying reg default handler failed.");
|
||||
}
|
||||
|
||||
NativeMethods.SHChangeNotify(NativeMethods.SHCNE_ASSOCCHANGED, NativeMethods.SHCNF_IDLIST, IntPtr.Zero, IntPtr.Zero);
|
||||
}
|
||||
else if (!defaultRegApp && RegistryPreviewSetDefaultAppChangeSet.IsApplied)
|
||||
{
|
||||
if (RegistryPreviewSetDefaultAppChangeSet.UnApply())
|
||||
{
|
||||
Logger.LogError("Unapplying reg default handler failed.");
|
||||
}
|
||||
|
||||
NativeMethods.SHChangeNotify(NativeMethods.SHCNE_ASSOCCHANGED, NativeMethods.SHCNF_IDLIST, IntPtr.Zero, IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
if (!RegistryPreviewChangeSet.ApplyIfNotApplied())
|
||||
{
|
||||
Logger.LogError("Applying registry changes failed");
|
||||
}
|
||||
|
||||
OnSettingsChanged(null!, default);
|
||||
}
|
||||
|
||||
public Dictionary<string, Action> CustomActions
|
||||
{
|
||||
get => new() { { "Launch", () => LaunchProcess() } };
|
||||
}
|
||||
|
||||
private RegistryChangeSet RegistryPreviewSetDefaultAppChangeSet
|
||||
{
|
||||
get
|
||||
{
|
||||
string installationDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
||||
|
||||
string appName = "Registry Preview";
|
||||
string registryKeyPrefix = "Software\\Classes\\";
|
||||
RegistryValueChange[] changes =
|
||||
[
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = registryKeyPrefix + ProcessName + "\\Application",
|
||||
KeyName = "ApplicationName",
|
||||
Value = appName,
|
||||
},
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = registryKeyPrefix + ProcessName + "\\DefaultIcon",
|
||||
KeyName = null,
|
||||
Value = installationDir + "\\WinUI3Apps\\PowerToys.RegistryPreview.exe",
|
||||
},
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = registryKeyPrefix + ProcessName + "\\shell\\open\\command",
|
||||
KeyName = null,
|
||||
Value = installationDir + "\\WinUI3Apps\\PowerToys.RegistryPreview.exe \"----ms-protocol:ms-encodedlaunch:App?ContractId=Windows.File&Verb=open&File=%1\"",
|
||||
},
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = registryKeyPrefix + ".reg\\OpenWithProgIDs",
|
||||
KeyName = null,
|
||||
Value = ProcessName,
|
||||
}
|
||||
];
|
||||
return new RegistryChangeSet { Changes = changes };
|
||||
}
|
||||
}
|
||||
|
||||
private RegistryChangeSet RegistryPreviewChangeSet
|
||||
{
|
||||
get
|
||||
{
|
||||
string installationDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
||||
|
||||
RegistryValueChange[] changes =
|
||||
[
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = "Software\\Classes\\regfile\\shell\\preview\\command",
|
||||
KeyName = null,
|
||||
Value = installationDir + "\\WinUI3Apps\\PowerToys.RegistryPreview.exe \"%1\"",
|
||||
},
|
||||
new RegistryValueChange
|
||||
{
|
||||
KeyPath = "Software\\Classes\\regfile\\shell\\preview",
|
||||
KeyName = "icon",
|
||||
Value = installationDir + "\\WinUI3Apps\\Assets\\RegistryPreview\\RegistryPreview.ico",
|
||||
}
|
||||
];
|
||||
return new RegistryChangeSet() { Changes = changes };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class WorkspacesModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "Workspaces";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.Workspaces;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredWorkspacesEnabledValue();
|
||||
|
||||
public override string ProcessPath => "PowerToys.WorkspacesEditor.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.WorkspacesEditor";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SupressLaunchOnModuleEnabled | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
InitializeShortcuts();
|
||||
}
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, System.Text.Json.JsonElement jsonProperties)
|
||||
{
|
||||
InitializeShortcuts();
|
||||
}
|
||||
|
||||
private void InitializeShortcuts()
|
||||
{
|
||||
Shortcuts.Clear();
|
||||
Shortcuts.Add((SettingsUtils.Default.GetSettings<WorkspacesSettings>(Name).Properties.Hotkey, () =>
|
||||
{
|
||||
LaunchProcess();
|
||||
}));
|
||||
}
|
||||
|
||||
public List<(HotkeySettings Hotkey, Action Action)> Shortcuts { get; } = [];
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapper;
|
||||
using PowerToys.Interop;
|
||||
using RunnerV2.Models;
|
||||
|
||||
namespace RunnerV2.ModuleInterfaces
|
||||
{
|
||||
internal sealed class ZoomItModuleInterface : ProcessModuleAbstractClass, IPowerToysModule
|
||||
{
|
||||
public string Name => "ZoomIt";
|
||||
|
||||
public bool Enabled => SettingsUtils.Default.GetSettings<GeneralSettings>().Enabled.ZoomIt;
|
||||
|
||||
public GpoRuleConfigured GpoRuleConfigured => GPOWrapper.GetConfiguredZoomItEnabledValue();
|
||||
|
||||
public override string ProcessPath => "PowerToys.ZoomIt.exe";
|
||||
|
||||
public override string ProcessName => "PowerToys.ZoomIt";
|
||||
|
||||
public override ProcessLaunchOptions LaunchOptions => ProcessLaunchOptions.SingletonProcess | ProcessLaunchOptions.RunnerProcessIdAsFirstArgument;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
}
|
||||
|
||||
public Dictionary<string, Action> CustomActions { get => new() { { "refresh_settings", () => OnSettingsChanged(null!, default) } }; }
|
||||
|
||||
public void OnSettingsChanged(string settingsKind, System.Text.Json.JsonElement jsonProperties)
|
||||
{
|
||||
using var refreshSettingsEvent = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ZoomItRefreshSettingsEvent());
|
||||
refreshSettingsEvent.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using ManagedCommon;
|
||||
using Windows.ApplicationModel;
|
||||
|
||||
namespace RunnerV2
|
||||
{
|
||||
internal static partial class NativeMethods
|
||||
{
|
||||
[LibraryImport("advapi32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool OpenProcessToken(IntPtr processHandle, uint desiredAccess, out IntPtr tokenHandle);
|
||||
|
||||
[LibraryImport("advapi32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool GetTokenInformation(IntPtr tokenHandle, TOKEN_INFORMATION_CLASS tokenInformationClass, ref TokenElevation tokenInformation, uint tokenInformationLength, out uint returnLength);
|
||||
|
||||
[LibraryImport("Kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool CloseHandle(IntPtr hObject);
|
||||
|
||||
internal enum TOKEN_INFORMATION_CLASS
|
||||
{
|
||||
TOKEN_ELEVATION = 20,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct TokenElevation
|
||||
{
|
||||
public uint TokenIsElevated;
|
||||
}
|
||||
|
||||
internal const int TOKENQUERY = 0x0008;
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool AppendMenuW(IntPtr hMenu, uint uFlags, UIntPtr uIDNewItem, string lpNewItem);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
internal static partial IntPtr CreatePopupMenu();
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool TrackPopupMenu(IntPtr hMenu, uint uFlags, int x, int y, int nReserved, IntPtr hWnd, IntPtr prcRect);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
|
||||
|
||||
internal const uint NIMADD = 0x00000000;
|
||||
internal const uint NIMDELETE = 0x00000002;
|
||||
|
||||
internal struct NOTIFYICONDATA
|
||||
{
|
||||
public uint CbSize;
|
||||
public IntPtr HWnd;
|
||||
public uint UId;
|
||||
public uint UFlags;
|
||||
public uint UCallbackMessage;
|
||||
public IntPtr HIcon;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
|
||||
public string SzTip;
|
||||
public uint DwState;
|
||||
public uint DwStateMask;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
|
||||
public string SzInfo;
|
||||
public uint UTimeoutOrVersion;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
|
||||
public string SzInfoTitle;
|
||||
public uint DwInfoFlags;
|
||||
public Guid GuidItem;
|
||||
public IntPtr HBalloonIcon;
|
||||
}
|
||||
|
||||
[DllImport("shell32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool Shell_NotifyIcon(uint dwMessage, ref NOTIFYICONDATA lpdata);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool ChangeWindowMessageFilterEx(IntPtr hWnd, uint msg, uint action, IntPtr pChangeFilterStruct);
|
||||
|
||||
internal const uint CSVREDRAW = 0x0001;
|
||||
internal const uint CSHREDRAW = 0x0002;
|
||||
|
||||
internal const uint WSOVERLAPPEDWINDOW = 0x00CF0000;
|
||||
internal const uint WSPOPUP = 0x80000000;
|
||||
|
||||
internal const int CWUSEDEFAULT = unchecked((int)0x80000000);
|
||||
|
||||
internal static readonly IntPtr IDCARROW = new(32512);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern IntPtr GetMessageW(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool TranslateMessage(ref MSG lpMsg);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool DispatchMessageW(ref MSG lpMsg);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool PostMessageW(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
internal static partial ushort AddAtomW([MarshalAs(UnmanagedType.LPWStr)] string lpString);
|
||||
|
||||
internal struct MSG
|
||||
{
|
||||
public IntPtr HWnd;
|
||||
public uint Message;
|
||||
public UIntPtr WParam;
|
||||
public long LParam;
|
||||
public ulong Time;
|
||||
public Point Pt;
|
||||
}
|
||||
|
||||
internal enum WindowMessages : uint
|
||||
{
|
||||
COMMAND = 0x0111,
|
||||
HOTKEY = 0x0312,
|
||||
ICON_NOTIFY = 0x0800,
|
||||
WINDOWPOSCHANGING = 0x0046,
|
||||
DESTROY = 0x0002,
|
||||
REFRESH_SETTINGS = 0x0400 + 2,
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern ushort RegisterClassW(ref WNDCLASS lpWndClass);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = false)]
|
||||
internal static partial IntPtr DefWindowProcW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
internal static partial uint RegisterWindowMessageW([MarshalAs(UnmanagedType.LPWStr)] string lpString);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
|
||||
internal static partial nint CreateWindowExW(
|
||||
uint dwExStyle,
|
||||
string lpClassName,
|
||||
string lpWindowName,
|
||||
uint dwStyle,
|
||||
int x,
|
||||
int y,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
IntPtr hWndParent,
|
||||
IntPtr hMenu,
|
||||
IntPtr hInstance,
|
||||
IntPtr lpParam);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct WNDCLASS
|
||||
{
|
||||
public uint Style;
|
||||
public WndProc LpfnWndProc;
|
||||
public int CbClsExtra;
|
||||
public int CbWndExtra;
|
||||
public IntPtr HInstance;
|
||||
public IntPtr HIcon;
|
||||
public IntPtr HCursor;
|
||||
public IntPtr HbrBackground;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string LpszMenuName;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string LpszClassName;
|
||||
}
|
||||
|
||||
[DllImport("Advapi32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptorW(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string StringSecurityDescriptor,
|
||||
uint StringSDRevision,
|
||||
out IntPtr SecurityDescriptor,
|
||||
out uint SecurityDescriptorSize);
|
||||
|
||||
[DllImport("Advapi32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool MakeAbsoluteSD(
|
||||
IntPtr pSelfRelativeSD,
|
||||
IntPtr pAbsoluteSD,
|
||||
ref uint lpdwAbsoluteSDSize,
|
||||
IntPtr pDacl,
|
||||
ref uint lpdwDaclSize,
|
||||
IntPtr pSacl,
|
||||
ref uint lpdwSaclSize,
|
||||
IntPtr pOwner,
|
||||
ref uint lpdwOwnerSize,
|
||||
IntPtr pPrimaryGroup,
|
||||
ref uint lpdwPrimaryGroupSize);
|
||||
|
||||
[DllImport("ole32.dll", SetLastError = true)]
|
||||
internal static extern int CoInitializeSecurity(
|
||||
IntPtr pSecDesc,
|
||||
int cAuthSvc,
|
||||
IntPtr asAuthSvc,
|
||||
IntPtr pReserved1,
|
||||
uint dwAuthnLevel,
|
||||
uint dwImpLevel,
|
||||
IntPtr pAuthList,
|
||||
uint dwCapabilities,
|
||||
IntPtr pReserved3);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern uint SendInput(uint nInputs, NativeKeyboardHelper.INPUT[] pInputs, int cbSize);
|
||||
|
||||
[DllImport("PowerToys.Interop.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern bool GetPackageNameAndVersionFromAppx(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string appxPath,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] out string outName,
|
||||
out PackageVersion outVersion);
|
||||
|
||||
[LibraryImport("Shell32.dll", SetLastError = true)]
|
||||
internal static partial void SHChangeNotify(
|
||||
uint wEventId,
|
||||
uint uFlags,
|
||||
IntPtr dwItem1,
|
||||
IntPtr dwItem2);
|
||||
|
||||
internal const uint SHCNE_ASSOCCHANGED = 0x8000000;
|
||||
internal const uint SHCNF_IDLIST = 0x0;
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.GPOWrapperProjection;
|
||||
using RunnerV2;
|
||||
using RunnerV2.Helpers;
|
||||
using RunnerV2.Models;
|
||||
using Settings.UI.Library;
|
||||
|
||||
internal sealed class Program
|
||||
{
|
||||
private static readonly SettingsUtils _settingsUtils = SettingsUtils.Default;
|
||||
|
||||
internal static GeneralSettings GeneralSettings => _settingsUtils.GetSettings<GeneralSettings>();
|
||||
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
Logger.InitializeLogger("\\RunnerLogs");
|
||||
|
||||
string securityDescriptor =
|
||||
"O:BA" // Owner: Builtin (local) administrator
|
||||
+ "G:BA" // Group: Builtin (local) administrator
|
||||
+ "D:"
|
||||
+ "(A;;0x7;;;PS)" // Access allowed on COM_RIGHTS_EXECUTE, _LOCAL, & _REMOTE for Personal self
|
||||
+ "(A;;0x7;;;IU)" // Access allowed on COM_RIGHTS_EXECUTE for Interactive Users
|
||||
+ "(A;;0x3;;;SY)" // Access allowed on COM_RIGHTS_EXECUTE, & _LOCAL for Local system
|
||||
+ "(A;;0x7;;;BA)" // Access allowed on COM_RIGHTS_EXECUTE, _LOCAL, & _REMOTE for Builtin (local) administrator
|
||||
+ "(A;;0x3;;;S-1-15-3-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646)" // Access allowed on COM_RIGHTS_EXECUTE, & _LOCAL for Win32WebViewHost package capability
|
||||
+ "S:"
|
||||
+ "(ML;;NX;;;LW)"; // Integrity label on No execute up for Low mandatory level
|
||||
|
||||
COMUtils.InitializeCOMSecurity(securityDescriptor);
|
||||
|
||||
switch (ShouldRunInSpecialMode(args))
|
||||
{
|
||||
case SpecialMode.None:
|
||||
break;
|
||||
case SpecialMode.UpdateNow:
|
||||
UpdateNow();
|
||||
return;
|
||||
default:
|
||||
throw new NotImplementedException("Special modes are not implemented yet.");
|
||||
}
|
||||
|
||||
bool shouldOpenSettings = args.Any(s => s.StartsWith("--open-settings", StringComparison.InvariantCulture));
|
||||
bool shouldOpenSettingsToSpecificPage = args.Any(s => s.StartsWith("--open-settings=", StringComparison.InvariantCulture));
|
||||
|
||||
// Check if PowerToys is already running
|
||||
if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
|
||||
{
|
||||
throw new NotImplementedException("Opening another instance window is not supported yet.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Todo: Data diagnotics
|
||||
*/
|
||||
|
||||
bool isElevated = ElevationHelper.IsProcessElevated();
|
||||
bool hasDontElevateArgument = args.Contains("--dont-elevate");
|
||||
bool runElevatedSetting = GeneralSettings.RunElevated;
|
||||
bool hasRestartedElevatedArgment = args.Contains("--restartedElevated");
|
||||
|
||||
Action afterInitializationAction = () => { };
|
||||
Version version = Assembly.GetExecutingAssembly().GetName().Version!;
|
||||
|
||||
if ($"v{version.Major}.{version.Minor}.{version.Build}" != _settingsUtils.GetSettings<LastVersionRunSettings>(fileName: "last_version_run.json").LastVersion && (!GeneralSettings.ShowWhatsNewAfterUpdates || GPOWrapper.GetDisableShowWhatsNewAfterUpdatesValue() != GpoRuleConfigured.Disabled))
|
||||
{
|
||||
afterInitializationAction += () =>
|
||||
{
|
||||
SettingsHelper.OpenSettingsWindow(showScoobeWindow: true);
|
||||
};
|
||||
}
|
||||
|
||||
if (!_settingsUtils.GetSettings<OOBESettings>(fileName: "oobe_settings.json").OpenedAtFirstLaunch)
|
||||
{
|
||||
afterInitializationAction += () =>
|
||||
{
|
||||
SettingsHelper.OpenSettingsWindow(showOobeWindow: true);
|
||||
};
|
||||
}
|
||||
|
||||
if (shouldOpenSettings)
|
||||
{
|
||||
afterInitializationAction += () =>
|
||||
{
|
||||
SettingsHelper.OpenSettingsWindow(additionalArguments: shouldOpenSettingsToSpecificPage ? args.First(s => s.StartsWith("--open-settings=", StringComparison.InvariantCulture)).Replace("--open-settings=", string.Empty, StringComparison.InvariantCulture) : null);
|
||||
};
|
||||
}
|
||||
|
||||
// Set last version run
|
||||
_settingsUtils.SaveSettings(new LastVersionRunSettings() { LastVersion = $"v{version.Major}.{version.Minor}.{version.Build}" }.ToJsonString(), fileName: "last_version_run.json");
|
||||
|
||||
switch ((isElevated, hasDontElevateArgument, runElevatedSetting, hasRestartedElevatedArgment))
|
||||
{
|
||||
case (true, true, false, _):
|
||||
// Todo: Scheudle restart as non elevated
|
||||
throw new NotImplementedException();
|
||||
case (true, _, _, _):
|
||||
case (_, _, false, _):
|
||||
case (_, true, _, _):
|
||||
case (false, _, _, true):
|
||||
GeneralSettings tempGeneralSettings = GeneralSettings;
|
||||
tempGeneralSettings.IsElevated = isElevated;
|
||||
_settingsUtils.SaveSettings(tempGeneralSettings.ToJsonString());
|
||||
|
||||
Runner.Run(afterInitializationAction);
|
||||
break;
|
||||
default:
|
||||
ElevationHelper.RestartScheduled = ElevationHelper.RestartScheduledMode.RestartElevated;
|
||||
break;
|
||||
}
|
||||
|
||||
ElevationHelper.RestartIfScheudled();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the application should run in a special mode based on the provided arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments passed to <see cref="Main(string[])"/></param>
|
||||
/// <returns>The <see cref="SpecialMode"/> the app should run in.</returns>
|
||||
private static SpecialMode ShouldRunInSpecialMode(string[] args)
|
||||
{
|
||||
if (args.Length > 0 && args[0].StartsWith("powertoys://", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
Uri uri = new(args[0]);
|
||||
string host = uri.Host.ToLowerInvariant();
|
||||
return host switch
|
||||
{
|
||||
"update_now" => SpecialMode.UpdateNow,
|
||||
_ => SpecialMode.None,
|
||||
};
|
||||
}
|
||||
|
||||
return SpecialMode.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the update process for PowerToys.
|
||||
/// </summary>
|
||||
private static void UpdateNow()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo()
|
||||
{
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
FileName = "PowerToys.Update.exe",
|
||||
Arguments = "-update_now",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using ManagedCommon;
|
||||
using RunnerV2.Helpers;
|
||||
using RunnerV2.Models;
|
||||
using RunnerV2.ModuleInterfaces;
|
||||
using Update;
|
||||
using static RunnerV2.NativeMethods;
|
||||
|
||||
namespace RunnerV2
|
||||
{
|
||||
internal static partial class Runner
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the window handle for the Runner main window that hosts the tray icon and receives system messages.
|
||||
/// </summary>
|
||||
public static nint RunnerHwnd { get; private set; }
|
||||
|
||||
private const string TrayWindowClassName = "pt_tray_icon_window_class";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the currently loaded modules.
|
||||
/// </summary>
|
||||
public static List<IPowerToysModule> LoadedModules { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of all available PowerToys modules.
|
||||
/// </summary>
|
||||
public static FrozenSet<IPowerToysModule> ModulesToLoad { get; } =
|
||||
[
|
||||
new ColorPickerModuleInterface(),
|
||||
new AlwaysOnTopModuleInterface(),
|
||||
new HostsModuleInterface(),
|
||||
new PowerAccentModuleInterface(),
|
||||
new AdvancedPasteModuleInterface(),
|
||||
new AwakeModuleInterface(),
|
||||
new CmdNotFoundModuleInterface(),
|
||||
new CommandPaletteModuleInterface(),
|
||||
new CropAndLockModuleInterface(),
|
||||
new EnvironmentVariablesModuleInterface(),
|
||||
new RegistryPreviewModuleInterface(),
|
||||
new FileExplorerModuleInterface(),
|
||||
new ZoomItModuleInterface(),
|
||||
new PowerOCRModuleInterface(),
|
||||
new MeasureToolModuleInterface(),
|
||||
new MouseJumpModuleInterface(),
|
||||
new FancyZonesModuleInterface(),
|
||||
new PowerToysRunModuleInterface(),
|
||||
new KeyboardManagerModuleInterface(),
|
||||
new LightSwitchModuleInterface(),
|
||||
new CursorWrapModuleInterface(),
|
||||
new FindMyMouseModuleInterface(),
|
||||
new WorkspacesModuleInterface(),
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Runs the main message loop for Runner.
|
||||
/// </summary>
|
||||
/// <param name="afterInitializationAction">A function to execute after initialization.</param>
|
||||
internal static void Run(Action afterInitializationAction)
|
||||
{
|
||||
Logger.LogInfo("Runner started");
|
||||
|
||||
InitializeTrayWindow();
|
||||
TrayIconManager.StartTrayIcon();
|
||||
|
||||
Task.Run(UpdateUtilities.UninstallPreviousMsixVersions);
|
||||
|
||||
foreach (IPowerToysModule module in ModulesToLoad)
|
||||
{
|
||||
ToggleModuleStateBasedOnEnabledProperty(module);
|
||||
}
|
||||
|
||||
CentralizedKeyboardHookManager.Start();
|
||||
|
||||
afterInitializationAction();
|
||||
|
||||
MessageLoop();
|
||||
}
|
||||
|
||||
private static readonly uint _taskbarCreatedMessage = RegisterWindowMessageW("TaskbarCreated");
|
||||
|
||||
/// <summary>
|
||||
/// The main message loop that processes Windows messages.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
private static void MessageLoop()
|
||||
{
|
||||
while (GetMessageW(out MSG msg, IntPtr.Zero, 0, 0) != 0 || true)
|
||||
{
|
||||
TranslateMessage(ref msg);
|
||||
DispatchMessageW(ref msg);
|
||||
|
||||
// Supress duplicate handling of HOTKEY messages
|
||||
if (msg.Message == (uint)WindowMessages.HOTKEY)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
HandleMessage(msg.HWnd, msg.Message, (nint)msg.WParam, (nint)msg.LParam);
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes Runner and all loaded modules.
|
||||
/// </summary>
|
||||
[DoesNotReturn]
|
||||
internal static void Close()
|
||||
{
|
||||
TrayIconManager.StopTrayIcon();
|
||||
SettingsHelper.CloseSettingsWindow();
|
||||
ElevationHelper.RestartIfScheudled();
|
||||
|
||||
foreach (IPowerToysModule module in LoadedModules)
|
||||
{
|
||||
try
|
||||
{
|
||||
module.Disable();
|
||||
|
||||
if (module is ProcessModuleAbstractClass pmac)
|
||||
{
|
||||
pmac.ProcessExit();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show($"The module {module.Name} failed to unload: \n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the state of a module based on its enabled property and GPO rules.
|
||||
/// </summary>
|
||||
/// <param name="module">The module to toggle</param>
|
||||
public static void ToggleModuleStateBasedOnEnabledProperty(IPowerToysModule module)
|
||||
{
|
||||
try
|
||||
{
|
||||
if ((module.Enabled && (module.GpoRuleConfigured != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)) || module.GpoRuleConfigured == PowerToys.GPOWrapper.GpoRuleConfigured.Enabled)
|
||||
{
|
||||
/* Todo: conflict manager */
|
||||
|
||||
if (!LoadedModules.Contains(module))
|
||||
{
|
||||
module.Enable();
|
||||
if (module is ProcessModuleAbstractClass pmac)
|
||||
{
|
||||
pmac.LaunchProcess(true);
|
||||
}
|
||||
|
||||
LoadedModules.Add(module);
|
||||
}
|
||||
|
||||
CentralizedKeyboardHookManager.RemoveAllHooksFromModule(module.Name);
|
||||
|
||||
foreach (var shortcut in module.Shortcuts.ToArray())
|
||||
{
|
||||
CentralizedKeyboardHookManager.AddKeyboardHook(module.Name, shortcut.Hotkey, shortcut.Action);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show($"The module {module.Name} failed to load: \n" + e.Message, "Error: " + e.GetType().Name, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
module.Disable();
|
||||
|
||||
if (module is ProcessModuleAbstractClass pmac)
|
||||
{
|
||||
pmac.ProcessExit();
|
||||
}
|
||||
|
||||
CentralizedKeyboardHookManager.RemoveAllHooksFromModule(module.Name);
|
||||
|
||||
LoadedModules.Remove(module);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show($"The module {module.Name} failed to unload: \n" + e.Message, "Error: " + e.GetType().Name, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the tray window to receive system messages.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
private static void InitializeTrayWindow()
|
||||
{
|
||||
IntPtr hInstance = Process.GetCurrentProcess().MainModule!.BaseAddress;
|
||||
IntPtr hCursor = Cursors.Arrow.Handle;
|
||||
IntPtr hIcon = SystemIcons.Application.Handle;
|
||||
|
||||
var wc = new WNDCLASS
|
||||
{
|
||||
HCursor = hCursor,
|
||||
HInstance = hInstance,
|
||||
LpszClassName = TrayWindowClassName,
|
||||
Style = CSHREDRAW | CSVREDRAW,
|
||||
LpfnWndProc = HandleMessage,
|
||||
HIcon = hIcon,
|
||||
HbrBackground = IntPtr.Zero,
|
||||
LpszMenuName = string.Empty,
|
||||
CbClsExtra = 0,
|
||||
CbWndExtra = 0,
|
||||
};
|
||||
|
||||
_ = RegisterClassW(ref wc);
|
||||
|
||||
RunnerHwnd = CreateWindowExW(
|
||||
0,
|
||||
wc.LpszClassName,
|
||||
TrayWindowClassName,
|
||||
WSOVERLAPPEDWINDOW | WSPOPUP,
|
||||
CWUSEDEFAULT,
|
||||
CWUSEDEFAULT,
|
||||
CWUSEDEFAULT,
|
||||
CWUSEDEFAULT,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
wc.HInstance,
|
||||
IntPtr.Zero);
|
||||
|
||||
if (RunnerHwnd == IntPtr.Zero)
|
||||
{
|
||||
var err = Marshal.GetLastPInvokeError();
|
||||
MessageBox.Show($"CreateWindowExW failed. LastError={err}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles Windows messages sent to the tray window.
|
||||
/// </summary>
|
||||
private static IntPtr HandleMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case (uint)WindowMessages.ICON_NOTIFY:
|
||||
TrayIconManager.ProcessTrayIconMessage(lParam);
|
||||
break;
|
||||
case (uint)WindowMessages.COMMAND:
|
||||
TrayIconManager.ProcessTrayMenuCommand((nuint)wParam);
|
||||
break;
|
||||
case (uint)WindowMessages.WINDOWPOSCHANGING:
|
||||
TrayIconManager.StartTrayIcon();
|
||||
break;
|
||||
case (uint)WindowMessages.DESTROY:
|
||||
Close();
|
||||
break;
|
||||
case (uint)WindowMessages.REFRESH_SETTINGS:
|
||||
foreach (IPowerToysModule module in ModulesToLoad)
|
||||
{
|
||||
ToggleModuleStateBasedOnEnabledProperty(module);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
if (msg == _taskbarCreatedMessage)
|
||||
{
|
||||
TrayIconManager.StartTrayIcon();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return DefWindowProcW(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\Common.SelfContained.props" />
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<Description>PowerToys Runner</Description>
|
||||
<AssemblyName>PowerToys</AssemblyName>
|
||||
<OutputPath>..\..\..\$(Platform)\$(Configuration)</OutputPath>
|
||||
<Nullable>enable</Nullable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\common\ManagedCsWin32\ManagedCsWin32.csproj" />
|
||||
<ProjectReference Include="..\..\modules\poweraccent\PowerAccent.UI\PowerAccent.UI.csproj" />
|
||||
<ProjectReference Include="..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\..\Update\Update.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 52 KiB |
36
src/Update/PowerToys.Update.base.rc
Normal file
36
src/Update/PowerToys.Update.base.rc
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <windows.h>
|
||||
#include "resource.h"
|
||||
#include "../common/version/version.h"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", FILE_DESCRIPTION
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", INTERNAL_NAME
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", ORIGINAL_FILENAME
|
||||
VALUE "ProductName", PRODUCT_NAME
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
|
||||
END
|
||||
END
|
||||
234
src/Update/PowerToys.Update.cpp
Normal file
234
src/Update/PowerToys.Update.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include "Generated Files/resource.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
|
||||
#include <common/updating/updating.h>
|
||||
#include <common/updating/updateState.h>
|
||||
#include <common/updating/installer.h>
|
||||
|
||||
#include <common/utils/elevation.h>
|
||||
#include <common/utils/HttpClient.h>
|
||||
#include <common/utils/process_path.h>
|
||||
#include <common/utils/resources.h>
|
||||
#include <common/utils/timeutil.h>
|
||||
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <Msi.h>
|
||||
|
||||
#include "../runner/tray_icon.h"
|
||||
#include "../runner/UpdateUtils.h"
|
||||
|
||||
using namespace cmdArg;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::optional<fs::path> CopySelfToTempDir()
|
||||
{
|
||||
std::error_code error;
|
||||
auto dst_path = fs::temp_directory_path() / "PowerToys.Update.exe";
|
||||
fs::copy_file(get_module_filename(), dst_path, fs::copy_options::overwrite_existing, error);
|
||||
if (error)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return std::move(dst_path);
|
||||
}
|
||||
|
||||
std::optional<fs::path> ObtainInstaller(bool& isUpToDate)
|
||||
{
|
||||
using namespace updating;
|
||||
|
||||
isUpToDate = false;
|
||||
|
||||
auto state = UpdateState::read();
|
||||
|
||||
const auto new_version_info = get_github_version_info_async().get();
|
||||
if (std::holds_alternative<version_up_to_date>(*new_version_info))
|
||||
{
|
||||
isUpToDate = true;
|
||||
Logger::error("Invoked with -update_now argument, but no update was available");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (state.state == UpdateState::readyToDownload || state.state == UpdateState::errorDownloading)
|
||||
{
|
||||
if (!new_version_info)
|
||||
{
|
||||
Logger::error(L"Couldn't obtain github version info: {}", new_version_info.error());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Cleanup old updates before downloading the latest
|
||||
updating::cleanup_updates();
|
||||
|
||||
auto downloaded_installer = download_new_version(std::get<new_version_download_info>(*new_version_info)).get();
|
||||
if (!downloaded_installer)
|
||||
{
|
||||
Logger::error("Couldn't download new installer");
|
||||
}
|
||||
|
||||
return downloaded_installer;
|
||||
}
|
||||
else if (state.state == UpdateState::readyToInstall)
|
||||
{
|
||||
fs::path installer{ get_pending_updates_path() / state.downloadedInstallerFilename };
|
||||
if (fs::is_regular_file(installer))
|
||||
{
|
||||
return std::move(installer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error(L"Couldn't find a downloaded installer {}", installer.native());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
else if (state.state == UpdateState::upToDate)
|
||||
{
|
||||
isUpToDate = true;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Logger::error("Invoked with -update_now argument, but update state was invalid");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool InstallNewVersionStage1(fs::path installer)
|
||||
{
|
||||
if (auto copy_in_temp = CopySelfToTempDir())
|
||||
{
|
||||
// Detect if PT was running
|
||||
const auto pt_main_window = FindWindowW(pt_tray_icon_window_class, nullptr);
|
||||
|
||||
if (pt_main_window != nullptr)
|
||||
{
|
||||
SendMessageW(pt_main_window, WM_CLOSE, 0, 0);
|
||||
}
|
||||
|
||||
std::wstring arguments{ UPDATE_NOW_LAUNCH_STAGE2 };
|
||||
arguments += L" \"";
|
||||
arguments += installer.c_str();
|
||||
arguments += L"\"";
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
|
||||
sei.lpFile = copy_in_temp->c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
|
||||
sei.lpParameters = arguments.c_str();
|
||||
return ShellExecuteExW(&sei) == TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool InstallNewVersionStage2(std::wstring installer_path)
|
||||
{
|
||||
std::transform(begin(installer_path), end(installer_path), begin(installer_path), ::towlower);
|
||||
|
||||
bool success = true;
|
||||
|
||||
if (installer_path.ends_with(L".msi"))
|
||||
{
|
||||
success = MsiInstallProductW(installer_path.data(), nullptr) == ERROR_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it's not .msi, then it's a wix bootstrapper
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE };
|
||||
sei.lpFile = installer_path.c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
std::wstring parameters = L"/passive /norestart";
|
||||
sei.lpParameters = parameters.c_str();
|
||||
|
||||
success = ShellExecuteExW(&sei) == TRUE;
|
||||
|
||||
// Wait for the install completion
|
||||
if (success)
|
||||
{
|
||||
WaitForSingleObject(sei.hProcess, INFINITE);
|
||||
DWORD exitCode = 0;
|
||||
GetExitCodeProcess(sei.hProcess, &exitCode);
|
||||
success = exitCode == 0;
|
||||
CloseHandle(sei.hProcess);
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateState::store([&](UpdateState& state) {
|
||||
state = {};
|
||||
state.githubUpdateLastCheckedDate.emplace(timeutil::now());
|
||||
state.state = UpdateState::upToDate;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
{
|
||||
int nArgs = 0;
|
||||
LPWSTR* args = CommandLineToArgvW(GetCommandLineW(), &nArgs);
|
||||
if (!args || nArgs < 2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::wstring_view action{ args[1] };
|
||||
|
||||
std::filesystem::path logFilePath(PTSettingsHelper::get_root_save_folder_location());
|
||||
logFilePath.append(LogSettings::updateLogPath);
|
||||
Logger::init(LogSettings::updateLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
|
||||
|
||||
if (action == UPDATE_NOW_LAUNCH_STAGE1)
|
||||
{
|
||||
bool isUpToDate = false;
|
||||
auto installerPath = ObtainInstaller(isUpToDate);
|
||||
bool failed = !installerPath.has_value();
|
||||
failed = failed || !InstallNewVersionStage1(std::move(*installerPath));
|
||||
if (failed)
|
||||
{
|
||||
UpdateState::store([&](UpdateState& state) {
|
||||
state = {};
|
||||
state.githubUpdateLastCheckedDate.emplace(timeutil::now());
|
||||
state.state = isUpToDate ? UpdateState::upToDate : UpdateState::errorDownloading;
|
||||
});
|
||||
}
|
||||
return failed;
|
||||
}
|
||||
else if (action == UPDATE_NOW_LAUNCH_STAGE2)
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
const bool failed = !InstallNewVersionStage2(args[2]);
|
||||
if (failed)
|
||||
{
|
||||
UpdateState::store([&](UpdateState& state) {
|
||||
state = {};
|
||||
state.githubUpdateLastCheckedDate.emplace(timeutil::now());
|
||||
state.state = UpdateState::errorDownloading;
|
||||
});
|
||||
}
|
||||
return failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
81
src/Update/PowerToys.Update.vcxproj
Normal file
81
src/Update/PowerToys.Update.vcxproj
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
|
||||
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h PowerToys.Update.base.rc PowerToys.Update.rc" />
|
||||
</Target>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{44CE9AE1-4390-42C5-BACC-0FD6B40AA203}</ProjectGuid>
|
||||
<RootNamespace>Update</RootNamespace>
|
||||
<ProjectName>PowerToys.Update</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\deps\expected.props" />
|
||||
<PropertyGroup>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup>
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>WindowsApp.lib;Msi.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="PowerToys.Update.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\common\logger\logger.vcxproj">
|
||||
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\notifications\notifications.vcxproj">
|
||||
<Project>{1d5be09d-78c0-4fd7-af00-ae7c1af7c525}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\updating\updating.vcxproj">
|
||||
<Project>{17da04df-e393-4397-9cf0-84dabe11032e}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PowerToys.Update.base.rc" />
|
||||
<ResourceCompile Include="Generated Files\PowerToys.Update.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,183 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading.Tasks;
|
||||
using Update;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal sealed partial class Program
|
||||
{
|
||||
private static readonly string _installerPath = Path.Combine(Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"Microsoft",
|
||||
"PowerToys",
|
||||
"Updates"));
|
||||
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
string action = args[0];
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case UpdateStage.UPDATENOWLAUNCHSTAGE1:
|
||||
await PerformUpdateNowStage1();
|
||||
break;
|
||||
case UpdateStage.UPDATENOWLAUNCHSTAGE2:
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
await PerformUpdateNowStage2(args[1]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task PerformUpdateNowStage2(string installerPath)
|
||||
{
|
||||
Process installerProcess = new()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = installerPath,
|
||||
Arguments = "/passive /norestart",
|
||||
UseShellExecute = true,
|
||||
},
|
||||
};
|
||||
|
||||
installerProcess.Start();
|
||||
await installerProcess.WaitForExitAsync();
|
||||
|
||||
if (installerProcess.ExitCode == 0)
|
||||
{
|
||||
UpdateSettingsHelper.ProcessNoUpdateAvailable();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateSettingsHelper.SetUpdateState(UpdatingSettings.UpdatingState.ErrorDownloading);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task PerformUpdateNowStage1()
|
||||
{
|
||||
UpdateSettingsHelper.TriggerUpdateCheck();
|
||||
UpdateSettingsHelper.UpdateInfo updateInfo = await UpdateSettingsHelper.GetUpdateAvailableInfo();
|
||||
|
||||
if (updateInfo is not UpdateSettingsHelper.UpdateInfo.UpdateAvailable ua)
|
||||
{
|
||||
// No update found
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy itsself to the temp folder
|
||||
File.Copy("PowerToys.Update.exe", Path.Combine(Path.GetTempPath(), "PowerToys.Update.exe"), true);
|
||||
|
||||
string? installerFilePath = null;
|
||||
|
||||
switch (UpdateSettingsHelper.GetUpdateState())
|
||||
{
|
||||
case UpdatingSettings.UpdatingState.ReadyToDownload:
|
||||
case UpdatingSettings.UpdatingState.ErrorDownloading:
|
||||
CleanupUpdates();
|
||||
installerFilePath = await DownloadFile(ua.InstallerDownloadUrl.ToString(), ua.InstallerFilename);
|
||||
break;
|
||||
case UpdatingSettings.UpdatingState.ReadyToInstall:
|
||||
installerFilePath = Path.Combine(_installerPath, ua.InstallerFilename);
|
||||
if (!File.Exists(installerFilePath))
|
||||
{
|
||||
// Installer not found
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case UpdatingSettings.UpdatingState.UpToDate:
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (installerFilePath == null)
|
||||
{
|
||||
UpdateSettingsHelper.SetUpdateState(UpdatingSettings.UpdatingState.ErrorDownloading);
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
IntPtr runnerHwnd = FindWindowW("pt_tray_icon_window_class");
|
||||
|
||||
if (runnerHwnd != IntPtr.Zero)
|
||||
{
|
||||
SendMessageW(runnerHwnd, 0x0010, IntPtr.Zero, IntPtr.Zero); // Send WM_CLOSE
|
||||
}
|
||||
|
||||
string arguments = $"{UpdateStage.UPDATENOWLAUNCHSTAGE2} \"{installerFilePath}\"";
|
||||
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = Path.Combine(Path.GetTempPath(), "PowerToys.Update.exe"),
|
||||
Arguments = arguments,
|
||||
UseShellExecute = true,
|
||||
CreateNoWindow = true,
|
||||
WorkingDirectory = Environment.CurrentDirectory,
|
||||
});
|
||||
}
|
||||
|
||||
private static async Task<string?> DownloadFile(string downloadUri, string downloadFileName)
|
||||
{
|
||||
HttpClient httpClient = new();
|
||||
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("PowerToys Runner"); // GitHub API requires a user-agent
|
||||
|
||||
// 3 Attempts to download the file
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
using FileStream fileStream = new(Path.Combine(_installerPath, downloadFileName), FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
await (await httpClient.GetStreamAsync(downloadUri)).CopyToAsync(fileStream);
|
||||
return fileStream.Name;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void CleanupUpdates()
|
||||
{
|
||||
if (!Path.Exists(_installerPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string file in Directory.GetFiles(_installerPath).Where(f => f.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
|
||||
private static partial IntPtr FindWindowW(string lpClassName);
|
||||
|
||||
[LibraryImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool SendMessageW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
}
|
||||
82
src/Update/Resources.resx
Normal file
82
src/Update/Resources.resx
Normal file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="DOTNET_CORE_DOWNLOAD_FAILURE" xml:space="preserve">
|
||||
<value>Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.</value>
|
||||
</data>
|
||||
<data name="DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" xml:space="preserve">
|
||||
<value>PowerToys installation error</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_AVAILABLE" xml:space="preserve">
|
||||
<value>An update to PowerToys is available.</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_UPDATE_NOW" xml:space="preserve">
|
||||
<value>Update now</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT" xml:space="preserve">
|
||||
<value>An update to PowerToys is available. Visit our GitHub page to update.</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_MORE_INFO" xml:space="preserve">
|
||||
<value>More info...</value>
|
||||
</data>
|
||||
<data name="TOAST_TITLE" xml:space="preserve">
|
||||
<value>PowerToys Update</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,22 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\Common.SelfContained.props" />
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<Description>PowerToys Runner</Description>
|
||||
<AssemblyName>PowerToys.Update</AssemblyName>
|
||||
<OutputPath>..\..\$(Platform)\$(Configuration)</OutputPath>
|
||||
<Nullable>enable</Nullable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<PublishAot>true</PublishAot>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<_IsPublishing Condition="'$(_IsPublishing)'==''">false</_IsPublishing>
|
||||
</PropertyGroup>
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(_IsPublishing)'!='true'">
|
||||
<Exec Command="dotnet publish "$(ProjectPath)" -c $(Configuration) -r $(RuntimeIdentifier) --self-contained -o "$(OutputPath)"" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,227 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Update
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static class UpdateSettingsHelper
|
||||
{
|
||||
private static Thread? _updateThread;
|
||||
|
||||
private const string INSTALLERFILENAME = "powertoyssetup";
|
||||
private const string USERINSTALLERFILENAME = "powertoysusersetup";
|
||||
|
||||
public static void TriggerUpdateCheck()
|
||||
{
|
||||
if (_updateThread is not null && _updateThread.IsAlive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_updateThread = new Thread(async () =>
|
||||
{
|
||||
UpdateInfo updateInfo = await GetUpdateAvailableInfo();
|
||||
switch (updateInfo)
|
||||
{
|
||||
case UpdateInfo.UpdateCheckFailed ucf:
|
||||
ProcessUpdateCheckFailed(ucf);
|
||||
break;
|
||||
case UpdateInfo.UpdateAvailable ua:
|
||||
ProcessUpdateAvailable(ua);
|
||||
break;
|
||||
case UpdateInfo.NoUpdateAvailable:
|
||||
ProcessNoUpdateAvailable();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
_updateThread.Start();
|
||||
}
|
||||
|
||||
internal record UpdateInfo
|
||||
{
|
||||
private UpdateInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public sealed record NoUpdateAvailable : UpdateInfo;
|
||||
|
||||
public sealed record UpdateAvailable(Uri ReleasePageUri, Version AvailableVersion, Uri InstallerDownloadUrl, string InstallerFilename) : UpdateInfo;
|
||||
|
||||
public sealed record UpdateCheckFailed(Exception Exception) : UpdateInfo;
|
||||
}
|
||||
|
||||
internal static async Task<UpdateInfo> GetUpdateAvailableInfo()
|
||||
{
|
||||
Version? currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
|
||||
if (currentVersion is null)
|
||||
{
|
||||
// Todo: Log
|
||||
return new UpdateInfo.NoUpdateAvailable();
|
||||
}
|
||||
|
||||
if (currentVersion is { Major: 0, Minor: 0 })
|
||||
{
|
||||
// Pre-release or local build, skip update check
|
||||
return new UpdateInfo.NoUpdateAvailable();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
HttpClient httpClient = new();
|
||||
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("PowerToys Runner"); // GitHub API requires a user-agent
|
||||
Stream body = await httpClient.GetStreamAsync("https://api.github.com/repos/microsoft/PowerToys/releases/latest").ConfigureAwait(false);
|
||||
JsonElement releaseObject = (await JsonDocument.ParseAsync(body)).RootElement;
|
||||
Version latestVersion = new(releaseObject.GetProperty("tag_name").GetString()?.TrimStart('V', 'v') ?? throw new FormatException("The \"tag_name\" field could not be found"));
|
||||
string architectureString = RuntimeInformation.OSArchitecture switch
|
||||
{
|
||||
Architecture.X64 => "x64",
|
||||
Architecture.Arm64 => "arm64",
|
||||
_ => throw new InvalidDataException("Unknown architecture"),
|
||||
};
|
||||
|
||||
if (latestVersion > currentVersion)
|
||||
{
|
||||
Uri releasePageUri = new(releaseObject.GetProperty("html_url").GetString() ?? throw new FormatException("The \"html_url\" field could not be found"));
|
||||
|
||||
string requiredFilename = GetInstallScope() == InstallScope.PerMachine ? INSTALLERFILENAME : USERINSTALLERFILENAME;
|
||||
|
||||
Uri? installerDownloadUrl = null;
|
||||
string? installerFilename = null;
|
||||
|
||||
foreach (JsonElement asset in releaseObject.GetProperty("assets").EnumerateArray())
|
||||
{
|
||||
string? name = asset.GetProperty("name").GetString();
|
||||
string? browserDownloadUrl = asset.GetProperty("browser_download_url").GetString();
|
||||
|
||||
if (name is null
|
||||
|| browserDownloadUrl is null
|
||||
|| !name.Contains(requiredFilename, StringComparison.InvariantCultureIgnoreCase)
|
||||
|| !name.Contains(".exe", StringComparison.InvariantCultureIgnoreCase)
|
||||
|| !name.Contains(architectureString, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
installerDownloadUrl = new Uri(browserDownloadUrl);
|
||||
installerFilename = name;
|
||||
break;
|
||||
}
|
||||
|
||||
return installerDownloadUrl is null || installerFilename is null
|
||||
? new UpdateInfo.UpdateCheckFailed(new InvalidDataException("No installer found in GitHub release"))
|
||||
: new UpdateInfo.UpdateAvailable(releasePageUri, latestVersion, installerDownloadUrl, installerFilename);
|
||||
}
|
||||
|
||||
return new UpdateInfo.NoUpdateAvailable();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new UpdateInfo.UpdateCheckFailed(e);
|
||||
}
|
||||
}
|
||||
|
||||
private enum InstallScope
|
||||
{
|
||||
PerMachine,
|
||||
PerUser,
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
private static InstallScope GetInstallScope()
|
||||
{
|
||||
if (Registry.LocalMachine.OpenSubKey(@"Software\Classes\powertoys\", false) is not RegistryKey machineKey)
|
||||
{
|
||||
if (Registry.CurrentUser.OpenSubKey(@"Software\Classes\powertoys\", false) is not RegistryKey userKey)
|
||||
{
|
||||
// Both keys are missing
|
||||
return InstallScope.PerMachine;
|
||||
}
|
||||
|
||||
if (userKey.GetValue("InstallScope") is not string installScope)
|
||||
{
|
||||
userKey.Close();
|
||||
return InstallScope.PerMachine;
|
||||
}
|
||||
|
||||
userKey.Close();
|
||||
|
||||
return installScope.Contains("perUser") ? InstallScope.PerUser : InstallScope.PerMachine;
|
||||
}
|
||||
|
||||
machineKey.Close();
|
||||
|
||||
return InstallScope.PerMachine;
|
||||
}
|
||||
|
||||
private static readonly string _settingsPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"Microsoft",
|
||||
"PowerToys");
|
||||
|
||||
private static readonly string _updatingSettingsFile = Path.Combine(_settingsPath, "UpdateState.json");
|
||||
|
||||
private static void ProcessUpdateAvailable(UpdateInfo.UpdateAvailable updateAvailable)
|
||||
{
|
||||
UpdatingSettings updatingSettings = UpdatingSettings.LoadSettings();
|
||||
Console.WriteLine($"Update available: {updateAvailable.AvailableVersion}");
|
||||
|
||||
updatingSettings.State = UpdatingSettings.UpdatingState.ReadyToDownload;
|
||||
updatingSettings.ReleasePageLink = updateAvailable.ReleasePageUri.ToString();
|
||||
updatingSettings.DownloadedInstallerFilename = updateAvailable.InstallerFilename;
|
||||
updatingSettings.ReleasePageLink = updateAvailable.ReleasePageUri.ToString();
|
||||
updatingSettings.LastCheckedDate = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
File.WriteAllText(_updatingSettingsFile, updatingSettings.ToJsonString());
|
||||
}
|
||||
|
||||
internal static void ProcessNoUpdateAvailable()
|
||||
{
|
||||
UpdatingSettings updatingSettings = UpdatingSettings.LoadSettings();
|
||||
|
||||
updatingSettings.State = UpdatingSettings.UpdatingState.UpToDate;
|
||||
updatingSettings.ReleasePageLink = string.Empty;
|
||||
updatingSettings.DownloadedInstallerFilename = string.Empty;
|
||||
updatingSettings.ReleasePageLink = string.Empty;
|
||||
updatingSettings.LastCheckedDate = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture);
|
||||
File.WriteAllText(_updatingSettingsFile, updatingSettings.ToJsonString());
|
||||
}
|
||||
|
||||
private static void ProcessUpdateCheckFailed(UpdateInfo.UpdateCheckFailed updateCheckFailed)
|
||||
{
|
||||
// Todo: Log failed attempt
|
||||
UpdatingSettings updatingSettings = UpdatingSettings.LoadSettings();
|
||||
|
||||
updatingSettings.State = UpdatingSettings.UpdatingState.NetworkError;
|
||||
updatingSettings.ReleasePageLink = string.Empty;
|
||||
updatingSettings.DownloadedInstallerFilename = string.Empty;
|
||||
updatingSettings.ReleasePageLink = string.Empty;
|
||||
updatingSettings.LastCheckedDate = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture);
|
||||
File.WriteAllText(_updatingSettingsFile, updatingSettings.ToJsonString());
|
||||
}
|
||||
|
||||
internal static void SetUpdateState(UpdatingSettings.UpdatingState state)
|
||||
{
|
||||
UpdatingSettings updatingSettings = UpdatingSettings.LoadSettings();
|
||||
|
||||
updatingSettings.State = state;
|
||||
File.WriteAllText(_updatingSettingsFile, updatingSettings.ToJsonString());
|
||||
}
|
||||
|
||||
internal static UpdatingSettings.UpdatingState GetUpdateState() => UpdatingSettings.LoadSettings().State;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Update
|
||||
{
|
||||
internal static class UpdateStage
|
||||
{
|
||||
internal const string UPDATENOWLAUNCHSTAGE1 = "-update_now";
|
||||
internal const string UPDATENOWLAUNCHSTAGE2 = "-update_now_stage_2";
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Update
|
||||
{
|
||||
public static class UpdateUtilities
|
||||
{
|
||||
public static async void UninstallPreviousMsixVersions()
|
||||
{
|
||||
try
|
||||
{
|
||||
Windows.Management.Deployment.PackageManager packageManager = new();
|
||||
var packages = packageManager.FindPackagesForUser(string.Empty, "Microsoft.PowerToys", "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US");
|
||||
|
||||
Version? currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
|
||||
if (currentVersion == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var package in packages)
|
||||
{
|
||||
Version msixVersion = new Version(package.Id.Version.Major, package.Id.Version.Minor, package.Id.Version.Revision);
|
||||
if (msixVersion < currentVersion)
|
||||
{
|
||||
await packageManager.RemovePackageAsync(package.Id.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Update
|
||||
{
|
||||
public sealed class UpdatingSettings
|
||||
{
|
||||
public enum UpdatingState
|
||||
{
|
||||
UpToDate = 0,
|
||||
ErrorDownloading,
|
||||
ReadyToDownload,
|
||||
ReadyToInstall,
|
||||
NetworkError,
|
||||
}
|
||||
|
||||
// Gets or sets a value of the updating state
|
||||
[JsonPropertyName("state")]
|
||||
public UpdatingState State { get; set; }
|
||||
|
||||
// Gets or sets a value of the release page url
|
||||
[JsonPropertyName("releasePageUrl")]
|
||||
public string ReleasePageLink { get; set; } = string.Empty;
|
||||
|
||||
// Gets or sets a value of the github last checked date
|
||||
[JsonPropertyName("githubUpdateLastCheckedDate")]
|
||||
public string LastCheckedDate { get; set; } = string.Empty;
|
||||
|
||||
// Gets or sets a value of the updating state
|
||||
[JsonPropertyName("downloadedInstallerFilename")]
|
||||
public string DownloadedInstallerFilename { get; set; } = string.Empty;
|
||||
|
||||
// Non-localizable strings: Files
|
||||
public const string SettingsFilePath = "\\Microsoft\\PowerToys\\";
|
||||
public const string SettingsFile = "UpdateState.json";
|
||||
|
||||
public string NewVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ReleasePageLink == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string version = ReleasePageLink.Substring(ReleasePageLink.LastIndexOf('/') + 1);
|
||||
return version.Trim();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public string LastCheckedDateLocalized
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
if (LastCheckedDate == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
long seconds = long.Parse(LastCheckedDate, CultureInfo.CurrentCulture);
|
||||
var date = DateTimeOffset.FromUnixTimeSeconds(seconds).UtcDateTime;
|
||||
return date.ToLocalTime().ToString(CultureInfo.CurrentCulture);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public UpdatingSettings()
|
||||
{
|
||||
State = UpdatingState.UpToDate;
|
||||
}
|
||||
|
||||
public static UpdatingSettings LoadSettings()
|
||||
{
|
||||
var localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
var file = localAppDataDir + SettingsFilePath + SettingsFile;
|
||||
|
||||
if (File.Exists(file))
|
||||
{
|
||||
try
|
||||
{
|
||||
FileStream inputStream = File.Open(file, FileMode.Open);
|
||||
StreamReader reader = new(inputStream);
|
||||
string data = reader.ReadToEnd();
|
||||
inputStream.Close();
|
||||
reader.Dispose();
|
||||
|
||||
return JsonSerializer.Deserialize(data, UpdatingsSettingsSourceGenerationContext.Default.UpdatingSettings)!;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return new UpdatingSettings();
|
||||
}
|
||||
|
||||
public string ToJsonString()
|
||||
{
|
||||
return JsonSerializer.Serialize(this, UpdatingsSettingsSourceGenerationContext.Default.UpdatingSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Update
|
||||
{
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(UpdatingSettings))]
|
||||
|
||||
internal sealed partial class UpdatingsSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
}
|
||||
5
src/Update/packages.config
Normal file
5
src/Update/packages.config
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
</packages>
|
||||
11
src/Update/resource.base.h
Normal file
11
src/Update/resource.base.h
Normal file
@@ -0,0 +1,11 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by PowerToys.Update.rc
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "PowerToys Update"
|
||||
#define INTERNAL_NAME "PowerToys.Update"
|
||||
#define ORIGINAL_FILENAME "PowerToys.Update.exe"
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Microsoft.PowerToys.FilePreviewCommon
|
||||
var softlineBreak = new Markdig.Extensions.Hardlines.SoftlineBreakAsHardlineExtension();
|
||||
|
||||
MarkdownPipelineBuilder pipelineBuilder;
|
||||
pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseEmojiAndSmiley().UseYamlFrontMatter().UseMathematics();
|
||||
pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseEmojiAndSmiley().UseYamlFrontMatter().UseMathematics().DisableHtml();
|
||||
pipelineBuilder.Extensions.Add(extension);
|
||||
pipelineBuilder.Extensions.Add(softlineBreak);
|
||||
|
||||
|
||||
@@ -66,15 +66,5 @@ namespace PowerToys.GPOWrapperProjection
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredWorkspacesEnabledValue();
|
||||
}
|
||||
|
||||
public static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue()
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredLightSwitchEnabledValue();
|
||||
}
|
||||
|
||||
public static GpoRuleConfigured GetDisableShowWhatsNewAfterUpdatesValue()
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetDisableShowWhatsNewAfterUpdatesValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace ManagedCommon
|
||||
{
|
||||
public record HotkeyEx(ushort ModifiersMask, ushort VkCode, int Identifier);
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace ManagedCommon
|
||||
internal sealed class OutGoingLanguageSettings
|
||||
{
|
||||
[JsonPropertyName("language")]
|
||||
public string? LanguageTag { get; set; }
|
||||
public string LanguageTag { get; set; }
|
||||
}
|
||||
|
||||
public static string LoadLanguage()
|
||||
@@ -36,7 +36,7 @@ namespace ManagedCommon
|
||||
inputStream.Close();
|
||||
reader.Dispose();
|
||||
|
||||
return JsonSerializer.Deserialize<OutGoingLanguageSettings>(data, SourceGenerationContext.Default.OutGoingLanguageSettings)!.LanguageTag!;
|
||||
return JsonSerializer.Deserialize<OutGoingLanguageSettings>(data, SourceGenerationContext.Default.OutGoingLanguageSettings).LanguageTag;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
@@ -29,17 +29,17 @@ namespace ManagedCommon
|
||||
/// <summary>
|
||||
/// Gets the path to the log directory for the current version of the app.
|
||||
/// </summary>
|
||||
public static string? CurrentVersionLogDirectoryPath { get; private set; }
|
||||
public static string CurrentVersionLogDirectoryPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the current log file.
|
||||
/// </summary>
|
||||
public static string? CurrentLogFile { get; private set; }
|
||||
public static string CurrentLogFile { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the log directory for the app.
|
||||
/// </summary>
|
||||
public static string? AppLogDirectoryPath { get; private set; }
|
||||
public static string AppLogDirectoryPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the logger and sets the path for logging.
|
||||
@@ -50,7 +50,7 @@ namespace ManagedCommon
|
||||
public static void InitializeLogger(string applicationLogPath, bool isLocalLow = false)
|
||||
{
|
||||
string versionedPath = LogDirectoryPath(applicationLogPath, isLocalLow);
|
||||
string basePath = Path.GetDirectoryName(versionedPath)!;
|
||||
string basePath = Path.GetDirectoryName(versionedPath);
|
||||
|
||||
if (!Directory.Exists(versionedPath))
|
||||
{
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<PropertyGroup>
|
||||
<Description>PowerToys ManagedCommon</Description>
|
||||
<AssemblyName>PowerToys.ManagedCommon</AssemblyName>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->
|
||||
@@ -21,7 +20,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GPOWrapperProjection\GPOWrapperProjection.csproj" />
|
||||
<ProjectReference Include="..\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -36,6 +36,5 @@ namespace ManagedCommon
|
||||
PowerOCR,
|
||||
Workspaces,
|
||||
ZoomIt,
|
||||
GeneralSettings,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace ManagedCommon
|
||||
{
|
||||
internal static partial class NativeMethods
|
||||
internal static class NativeMethods
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
|
||||
@@ -21,13 +21,6 @@ namespace ManagedCommon
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
internal static extern bool QueryFullProcessImageName(IntPtr hProcess, uint dwFlags, System.Text.StringBuilder lpExeName, ref uint lpdwSize);
|
||||
|
||||
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
|
||||
internal static partial IntPtr CreateEventW(IntPtr lpEventAttributes, [MarshalAs(UnmanagedType.Bool)] bool bManualReset, [MarshalAs(UnmanagedType.Bool)] bool bInitialState, string lpName);
|
||||
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool SetEvent(IntPtr hEvent);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace ManagedCommon
|
||||
/// Gets the PowerToys installation path by checking registry entries
|
||||
/// </summary>
|
||||
/// <returns>The path to PowerToys installation directory, or null if not found</returns>
|
||||
public static string? GetPowerToysInstallPath()
|
||||
public static string GetPowerToysInstallPath()
|
||||
{
|
||||
#if DEBUG
|
||||
// In debug builds, resolve directly from the running process (no installer/registry involved).
|
||||
@@ -44,14 +44,14 @@ namespace ManagedCommon
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string? GetPathFromRegistry(RegistryHive hive)
|
||||
private static string GetPathFromRegistry(RegistryHive hive)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var baseKey = RegistryKey.OpenBaseKey(hive, RegistryView.Registry64);
|
||||
|
||||
// First try to get path from the powertoys protocol registration
|
||||
string? path = GetPathFromProtocolRegistration(baseKey);
|
||||
string path = GetPathFromProtocolRegistration(baseKey);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
return path;
|
||||
@@ -65,7 +65,7 @@ namespace ManagedCommon
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string? GetPathFromProtocolRegistration(RegistryKey baseKey)
|
||||
private static string GetPathFromProtocolRegistration(RegistryKey baseKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -73,7 +73,7 @@ namespace ManagedCommon
|
||||
|
||||
if (key != null)
|
||||
{
|
||||
string? command = key.GetValue(string.Empty)?.ToString();
|
||||
string command = key.GetValue(string.Empty)?.ToString();
|
||||
if (!string.IsNullOrEmpty(command))
|
||||
{
|
||||
// Parse command like: "C:\Program Files\PowerToys\PowerToys.exe" "%1"
|
||||
@@ -89,7 +89,7 @@ namespace ManagedCommon
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string? GetPathFromCurrentProcess()
|
||||
private static string GetPathFromCurrentProcess()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -125,7 +125,7 @@ namespace ManagedCommon
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string? ExtractPathFromCommand(string command)
|
||||
private static string ExtractPathFromCommand(string command)
|
||||
{
|
||||
if (string.IsNullOrEmpty(command))
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace ManagedCommon
|
||||
// based on https://stackoverflow.com/questions/51334674/how-to-detect-windows-10-light-dark-mode-in-win32-application
|
||||
public static AppTheme GetAppTheme()
|
||||
{
|
||||
int value = (int)Registry.GetValue($"{HKeyRoot}\\{HkeyWindowsPersonalizeTheme}", HValueAppTheme, 1)!;
|
||||
int value = (int)Registry.GetValue($"{HKeyRoot}\\{HkeyWindowsPersonalizeTheme}", HValueAppTheme, 1);
|
||||
return (AppTheme)value;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user