Initial commit (1.0.0)
This commit is contained in:
commit
26cde137c9
38
.github/workflows/build-manual.yml
vendored
Normal file
38
.github/workflows/build-manual.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
name: Build Project
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-release:
|
||||||
|
runs-on: windows-2022
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
#Check out the code from the repository
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
#Verify installed .NET Framework version (Optional Debug Step)
|
||||||
|
- name: Check .NET Framework version
|
||||||
|
run: |
|
||||||
|
reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" /v Release
|
||||||
|
|
||||||
|
#Restore NuGet packages
|
||||||
|
- name: Restore NuGet packages
|
||||||
|
run: nuget restore SoraV2Utils.sln
|
||||||
|
|
||||||
|
#Build the project
|
||||||
|
- name: Build the solution
|
||||||
|
run: msbuild SoraV2Utils.sln /p:Configuration=Release /t:Rebuild
|
||||||
|
|
||||||
|
#Build the installer
|
||||||
|
- name: Build the installer
|
||||||
|
run: devenv SoraV2Utils.sln /Project SoraV2Utils_Setup\SoraV2Utils_Setup.vdproj /Build "Release"
|
||||||
|
|
||||||
|
#Upload the build artifact
|
||||||
|
- name: Upload build artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: build-artifact
|
||||||
|
path: build/Release/**
|
78
.github/workflows/build-release.yml
vendored
Normal file
78
.github/workflows/build-release.yml
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
name: Build and Relase
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '[0-9]+.[0-9]+.[0-9]+'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-release:
|
||||||
|
runs-on: windows-2022
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
#Check out the code from the repository
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
#Verify installed .NET Framework version (Optional Debug Step)
|
||||||
|
- name: Check .NET Framework version
|
||||||
|
run: |
|
||||||
|
reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" /v Release
|
||||||
|
|
||||||
|
- name: Run Replace-Version-Strings.ps1
|
||||||
|
run: |
|
||||||
|
.\Scripts\Replace-Version-Strings.ps1 -version "${{github.ref_name}}"
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
#Restore NuGet packages
|
||||||
|
- name: Restore NuGet packages
|
||||||
|
run: nuget restore SoraV2BatteryHelper.sln
|
||||||
|
|
||||||
|
#Build the project
|
||||||
|
- name: Build the solution
|
||||||
|
run: msbuild SoraV2Utils.sln /p:Configuration=Release /t:Rebuild
|
||||||
|
|
||||||
|
#Build the installer
|
||||||
|
- name: Build the installer
|
||||||
|
run: devenv SoraV2Utils.sln /Project SoraV2Utils_Setup\SoraV2Utils_Setup.vdproj /Build "Release"
|
||||||
|
|
||||||
|
#Upload the build artifact
|
||||||
|
- name: Upload build artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: build-artifact
|
||||||
|
path: build/Release/
|
||||||
|
|
||||||
|
create-release:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
#Checkout repository to access release-related metadata
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
#Download the build artifact
|
||||||
|
- name: Download build artifact
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: build-artifact
|
||||||
|
path: ${{ github.workspace }}/artifact
|
||||||
|
|
||||||
|
- name: Archive Manual Release
|
||||||
|
run: (cd ${{ github.workspace }}/artifact && zip -r ${{ github.workspace }}/artifact/SoraV2UtilsBinarys${{github.ref_name}}.zip . -x "/*Installer/*")
|
||||||
|
|
||||||
|
- name: Rename Release Installer file
|
||||||
|
run: mv ${{ github.workspace }}/artifact/Installer/SoraV2Utils_Setup.msi ${{ github.workspace }}/artifact/Installer/SoraV2Utils_Setup${{github.ref_name}}.msi
|
||||||
|
|
||||||
|
- name: Release
|
||||||
|
uses: akkuman/gitea-release-action@v1
|
||||||
|
with:
|
||||||
|
name: "SoraV2Battery Helper ${{github.ref_name}}"
|
||||||
|
files: |-
|
||||||
|
${{ github.workspace }}/artifact/Installer/SoraV2UtilsSetup${{github.ref_name}}.msi
|
||||||
|
${{ github.workspace }}/artifact/SoraV2UtilsBinarys${{github.ref_name}}.zip
|
||||||
|
token: '${{secrets.KRJAN02_RELEASE_TOKEN}}'
|
||||||
|
tag_name: ${{github.ref_name}}
|
402
.gitignore
vendored
Normal file
402
.gitignore
vendored
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/csharp
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=csharp
|
||||||
|
|
||||||
|
### Csharp ###
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.tlog
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||||
|
*.vbp
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||||
|
*.dsw
|
||||||
|
*.dsp
|
||||||
|
|
||||||
|
# Visual Studio 6 technical files
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# Visual Studio History (VSHistory) files
|
||||||
|
.vshistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# VS Code files for those working on multiple tools
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Windows Installer files from build outputs
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/csharp
|
69
README.md
Normal file
69
README.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
## SoraV2 Battery Helper
|
||||||
|
|
||||||
|
This program is designed for the Ninjutsu Sora V2 mouse. Unfortunately, the manufacturer doesn't provide a Windows program to check the battery status. This often left me with a dead mouse at inconvenient times, like in the middle of a game. To solve this, I created an application that monitors the mouse's battery status and sends notifications based on your settings. It also estimates the remaining battery life by tracking the time it takes for the battery level to drop by one percent.
|
||||||
|
|
||||||
|
### SoraV2BatteryHelperSvc
|
||||||
|
- Retrieves USB data
|
||||||
|
- Triggers notifications
|
||||||
|
- Calculates battery status
|
||||||
|
|
||||||
|
### SoraV2BatteryHelperNotification
|
||||||
|
- Sends and displays notifications
|
||||||
|
- Provides a tray icon for a quick overview
|
||||||
|
- Shows the remaining battery time
|
||||||
|
|
||||||
|
|
||||||
|
# Installing SoraV2 Battery Helper
|
||||||
|
|
||||||
|
To install the SoraV2 Battery Helper using the installer, please follow the steps below.
|
||||||
|
|
||||||
|
## Steps to Install
|
||||||
|
|
||||||
|
1. **Download the Installer:**
|
||||||
|
- Download the `.msi` file from the latest release of the SoraV2 Battery Helper on GitHub.
|
||||||
|
- You can find the latest release [here](https://git.jan.sx/krjan02/SoraV2BatteryHelper/releases/latest).
|
||||||
|
|
||||||
|
2. **Run the Installer:**
|
||||||
|
- Locate the downloaded `.msi` file on your computer and double-click it to start the installation process.
|
||||||
|
|
||||||
|
3. **Follow the Installation Wizard:**
|
||||||
|
- Follow the on-screen instructions provided by the installation wizard to complete the setup.
|
||||||
|
|
||||||
|
4. **Verify Installation:**
|
||||||
|
- Once the installation is complete, ensure that the SoraV2 Battery Helper service is running correctly.
|
||||||
|
|
||||||
|
By following these steps, you will have successfully installed the SoraV2 Battery Helper on your system.
|
||||||
|
|
||||||
|
|
||||||
|
# Manual Service Installation for SoraV2 Battery Helper
|
||||||
|
|
||||||
|
This guide explains how to manually install the SoraV2 Battery Helper service using the provided PowerShell script.
|
||||||
|
|
||||||
|
## Steps to manually Install
|
||||||
|
|
||||||
|
1. **Download the Build Files:**
|
||||||
|
- Download the `SoraV2BatteryHelperManualX.X.X.zip` file from the latest release of the SoraV2 Battery Helper on GitHub.
|
||||||
|
- You can find the latest release [here](https://git.jan.sx/krjan02/SoraV2BatteryHelper/releases/latest).
|
||||||
|
|
||||||
|
2. **Open PowerShell as an Administrator:**
|
||||||
|
- Ensure you have the necessary administrative privileges to install services.
|
||||||
|
|
||||||
|
3. **Navigate to the Script Directory:**
|
||||||
|
- Open PowerShell and change the directory to where the script is located:
|
||||||
|
```powershell
|
||||||
|
cd C:\Path\To\Script
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Run the Script to Install the Service:**
|
||||||
|
- Execute the script with the `install` action:
|
||||||
|
```powershell
|
||||||
|
.\Install-Service.ps1 -Action install
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Verify the Service Installation:**
|
||||||
|
- To ensure the service has been installed correctly, you can check the status of the service:
|
||||||
|
```powershell
|
||||||
|
.\Install-Service.ps1 -Action status
|
||||||
|
```
|
||||||
|
|
||||||
|
By following these steps, you will have successfully installed the SoraV2 Battery Helper on your system.
|
97
Scripts/Install-Service.ps1
Normal file
97
Scripts/Install-Service.ps1
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
param (
|
||||||
|
[string]$Action
|
||||||
|
)
|
||||||
|
|
||||||
|
# Default values for the service
|
||||||
|
$ServicePath = ".\SoraV2Utils_Service.exe"
|
||||||
|
$ServiceName = "SoraV2Utils_Service"
|
||||||
|
$Description = "SoraV2 Utils Service"
|
||||||
|
$DisplayName = "SoraV2 Utils Service"
|
||||||
|
|
||||||
|
# Function to install the service
|
||||||
|
function Install-Service {
|
||||||
|
param (
|
||||||
|
[string]$ServicePath,
|
||||||
|
[string]$ServiceName,
|
||||||
|
[string]$Description,
|
||||||
|
[string]$DisplayName
|
||||||
|
)
|
||||||
|
|
||||||
|
$FullServicePath = (Resolve-Path -Path $ServicePath).Path
|
||||||
|
|
||||||
|
New-Service -Name $ServiceName -BinaryPathName $FullServicePath -Description $Description -DisplayName $DisplayName -StartupType Automatic
|
||||||
|
|
||||||
|
Write-Host "Service '$ServiceName' installed successfully."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to remove the service
|
||||||
|
function Remove-Service {
|
||||||
|
param (
|
||||||
|
[string]$ServiceName
|
||||||
|
)
|
||||||
|
|
||||||
|
Stop-Service -Name $ServiceName -Force
|
||||||
|
sc.exe delete $ServiceName
|
||||||
|
|
||||||
|
Write-Host "Service '$ServiceName' removed successfully."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to start the service
|
||||||
|
function Start-Service {
|
||||||
|
param (
|
||||||
|
[string]$ServiceName
|
||||||
|
)
|
||||||
|
|
||||||
|
Start-Service -Name $ServiceName
|
||||||
|
|
||||||
|
Write-Host "Service '$ServiceName' started successfully."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to stop the service
|
||||||
|
function Stop-Service {
|
||||||
|
param (
|
||||||
|
[string]$ServiceName
|
||||||
|
)
|
||||||
|
|
||||||
|
Stop-Service -Name $ServiceName
|
||||||
|
|
||||||
|
Write-Host "Service '$ServiceName' stopped successfully."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to get the status of the service
|
||||||
|
function Get-ServiceStatus {
|
||||||
|
param (
|
||||||
|
[string]$ServiceName
|
||||||
|
)
|
||||||
|
|
||||||
|
$Service = Get-Service -Name $ServiceName
|
||||||
|
Write-Host "Service '$ServiceName' is $($Service.Status)."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script logic
|
||||||
|
switch ($Action.ToLower()) {
|
||||||
|
"install" {
|
||||||
|
Install-Service -ServicePath $ServicePath -ServiceName $ServiceName -Description $Description -DisplayName $DisplayName
|
||||||
|
break
|
||||||
|
}
|
||||||
|
"remove" {
|
||||||
|
Remove-Service -ServiceName $ServiceName
|
||||||
|
break
|
||||||
|
}
|
||||||
|
"start" {
|
||||||
|
Start-Service -ServiceName $ServiceName
|
||||||
|
break
|
||||||
|
}
|
||||||
|
"stop" {
|
||||||
|
Stop-Service -ServiceName $ServiceName
|
||||||
|
break
|
||||||
|
}
|
||||||
|
"status" {
|
||||||
|
Get-ServiceStatus -ServiceName $ServiceName
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
Write-Host "Usage: .\Install-Service.ps1 -Action <install|remove|start|stop|status>"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
41
Scripts/Replace-Version-Strings.ps1
Normal file
41
Scripts/Replace-Version-Strings.ps1
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
param (
|
||||||
|
[string]$version
|
||||||
|
)
|
||||||
|
|
||||||
|
# Function to replace version in a file
|
||||||
|
function Replace-Version {
|
||||||
|
param (
|
||||||
|
[string]$filePath,
|
||||||
|
[string]$regexPattern,
|
||||||
|
[string]$replacement
|
||||||
|
)
|
||||||
|
|
||||||
|
if (Test-Path $filePath) {
|
||||||
|
(Get-Content $filePath) -replace $regexPattern, $replacement | Set-Content $filePath
|
||||||
|
Write-Host "Updated $filePath"
|
||||||
|
} else {
|
||||||
|
Write-Host "File not found: $filePath"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update SoraV2BatteryHelperSetup.vdproj
|
||||||
|
$vdprojFile = "SoraV2BatteryHelperSetup\SoraV2BatteryHelperSetup.vdproj"
|
||||||
|
$vdprojPattern = '(\s*)"ProductVersion" = "8:\d+\.\d+\.\d+"'
|
||||||
|
$vdprojReplacement = '$1"ProductVersion" = "8:' + $version + '"'
|
||||||
|
Replace-Version -filePath $vdprojFile -regexPattern $vdprojPattern -replacement $vdprojReplacement
|
||||||
|
|
||||||
|
# Update AssemblyInfo.cs files
|
||||||
|
$assemblyFiles = @(
|
||||||
|
"SoraV2BatteryHelperSvc\Properties\AssemblyInfo.cs",
|
||||||
|
"SoraV2BatteryHelperNotification\Properties\AssemblyInfo.cs"
|
||||||
|
)
|
||||||
|
|
||||||
|
$assemblyVersionPattern = '(\s*)\[assembly: AssemblyVersion\("(\d+\.\d+\.\d+\.\d+)"\)\]'
|
||||||
|
$assemblyFileVersionPattern = '(\s*)\[assembly: AssemblyFileVersion\("(\d+\.\d+\.\d+\.\d+)"\)\]'
|
||||||
|
$assemblyVersionReplacement = '$1[assembly: AssemblyVersion("' + $version + '")]'
|
||||||
|
$assemblyFileVersionReplacement = '$1[assembly: AssemblyFileVersion("' + $version + '")]'
|
||||||
|
|
||||||
|
foreach ($assemblyFile in $assemblyFiles) {
|
||||||
|
Replace-Version -filePath $assemblyFile -regexPattern $assemblyVersionPattern -replacement $assemblyVersionReplacement
|
||||||
|
Replace-Version -filePath $assemblyFile -regexPattern $assemblyFileVersionPattern -replacement $assemblyFileVersionReplacement
|
||||||
|
}
|
77
SoraV2Utils.sln
Normal file
77
SoraV2Utils.sln
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.10.35027.167
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoraV2Utils_Service", "SoraV2Utils_Service\SoraV2Utils_Service.csproj", "{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoraV2Utils_Agent", "SoraV2Utils_Agent\SoraV2Utils_Agent.csproj", "{763D3776-C431-44DC-81EA-721B66F5FE65}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Projektmappenelemente", "Projektmappenelemente", "{F828EC4B-96A0-464D-86D3-12B66C40D238}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.github\workflows\build-manual.yml = .github\workflows\build-manual.yml
|
||||||
|
.github\workflows\build-release.yml = .github\workflows\build-release.yml
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "SoraV2Utils_Setup", "SoraV2Utils_Setup\SoraV2Utils_Setup.vdproj", "{0323BB43-7266-452C-A2C2-2DC5A85A7BF7}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|ARM64 = Debug|ARM64
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|ARM64 = Release|ARM64
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Release|ARM64.Build.0 = Release|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Release|ARM64.Build.0 = Release|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{763D3776-C431-44DC-81EA-721B66F5FE65}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{0323BB43-7266-452C-A2C2-2DC5A85A7BF7}.Debug|Any CPU.ActiveCfg = Debug
|
||||||
|
{0323BB43-7266-452C-A2C2-2DC5A85A7BF7}.Debug|ARM64.ActiveCfg = Debug
|
||||||
|
{0323BB43-7266-452C-A2C2-2DC5A85A7BF7}.Debug|x64.ActiveCfg = Debug
|
||||||
|
{0323BB43-7266-452C-A2C2-2DC5A85A7BF7}.Debug|x86.ActiveCfg = Debug
|
||||||
|
{0323BB43-7266-452C-A2C2-2DC5A85A7BF7}.Release|Any CPU.ActiveCfg = Release
|
||||||
|
{0323BB43-7266-452C-A2C2-2DC5A85A7BF7}.Release|Any CPU.Build.0 = Release
|
||||||
|
{0323BB43-7266-452C-A2C2-2DC5A85A7BF7}.Release|ARM64.ActiveCfg = Release
|
||||||
|
{0323BB43-7266-452C-A2C2-2DC5A85A7BF7}.Release|x64.ActiveCfg = Release
|
||||||
|
{0323BB43-7266-452C-A2C2-2DC5A85A7BF7}.Release|x86.ActiveCfg = Release
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {FA78BF41-9AC0-4075-AF23-F604F5765917}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
6
SoraV2Utils_Agent/App.config
Normal file
6
SoraV2Utils_Agent/App.config
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
||||||
|
</startup>
|
||||||
|
</configuration>
|
40
SoraV2Utils_Agent/NotificationInstaller.Designer.cs
generated
Normal file
40
SoraV2Utils_Agent/NotificationInstaller.Designer.cs
generated
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
namespace SoraV2BatteryHelperNotification
|
||||||
|
{
|
||||||
|
partial class NotificationInstaller
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Erforderliche Designervariable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verwendete Ressourcen bereinigen.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Vom Komponenten-Designer generierter Code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Erforderliche Methode für die Designerunterstützung.
|
||||||
|
/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Installer1
|
||||||
|
//
|
||||||
|
this.BeforeUninstall += new System.Configuration.Install.InstallEventHandler(this.NotificationInstaller_BeforeUninstall);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
25
SoraV2Utils_Agent/NotificationInstaller.cs
Normal file
25
SoraV2Utils_Agent/NotificationInstaller.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Configuration.Install;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace SoraV2BatteryHelperNotification
|
||||||
|
{
|
||||||
|
[RunInstaller(true)]
|
||||||
|
public partial class NotificationInstaller : System.Configuration.Install.Installer
|
||||||
|
{
|
||||||
|
public NotificationInstaller()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NotificationInstaller_BeforeUninstall(object sender, InstallEventArgs e)
|
||||||
|
{
|
||||||
|
System.Environment.Exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
123
SoraV2Utils_Agent/NotificationInstaller.resx
Normal file
123
SoraV2Utils_Agent/NotificationInstaller.resx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<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>
|
||||||
|
<metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>False</value>
|
||||||
|
</metadata>
|
||||||
|
</root>
|
28
SoraV2Utils_Agent/NotificationSender.cs
Normal file
28
SoraV2Utils_Agent/NotificationSender.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Windows.Data.Xml.Dom;
|
||||||
|
using Windows.UI.Notifications;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Agent
|
||||||
|
{
|
||||||
|
public interface IToastNotification
|
||||||
|
{
|
||||||
|
void SendToastNotification(string xml, string title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NotificationSender : IToastNotification
|
||||||
|
{
|
||||||
|
public void SendToastNotification(string xml, string title)
|
||||||
|
{
|
||||||
|
XmlDocument tileXml = new XmlDocument();
|
||||||
|
tileXml.LoadXml(xml);
|
||||||
|
var toastNotification = new ToastNotification(tileXml);
|
||||||
|
toastNotification.Priority = ToastNotificationPriority.High;
|
||||||
|
|
||||||
|
ToastNotificationManager.CreateToastNotifier(title).Show(toastNotification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
SoraV2Utils_Agent/Program.cs
Normal file
33
SoraV2Utils_Agent/Program.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using EasyPipes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Agent
|
||||||
|
{
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
new ServiceIPC();
|
||||||
|
Server server = new Server("SoraV2UtilsNotifcation");
|
||||||
|
server.RegisterService<IToastNotification>(new NotificationSender());
|
||||||
|
server.Start();
|
||||||
|
|
||||||
|
var serviceMonitor = new ServiceMonitor("SoraV2Utils_Service");
|
||||||
|
serviceMonitor.ServiceStopped += (sender, e) =>
|
||||||
|
{
|
||||||
|
server.Stop();
|
||||||
|
System.Environment.Exit(1);
|
||||||
|
};;
|
||||||
|
|
||||||
|
var trayicon = new TrayIcon();
|
||||||
|
trayicon.Display();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
SoraV2Utils_Agent/Properties/AssemblyInfo.cs
Normal file
36
SoraV2Utils_Agent/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||||
|
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||||
|
// die einer Assembly zugeordnet sind.
|
||||||
|
[assembly: AssemblyTitle("SoraV2Utils_Agent")]
|
||||||
|
[assembly: AssemblyDescription("Agent for SoraV2 Utils")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("SoraV2Utils_Agent")]
|
||||||
|
[assembly: AssemblyCopyright("")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
|
||||||
|
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
|
||||||
|
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||||
|
[assembly: Guid("763d3776-c431-44dc-81ea-721b66f5fe65")]
|
||||||
|
|
||||||
|
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||||
|
//
|
||||||
|
// Hauptversion
|
||||||
|
// Nebenversion
|
||||||
|
// Buildnummer
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
|
||||||
|
// indem Sie "*" wie unten gezeigt eingeben:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
87
SoraV2Utils_Agent/ServiceIPC.cs
Normal file
87
SoraV2Utils_Agent/ServiceIPC.cs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
using EasyPipes;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Agent
|
||||||
|
{
|
||||||
|
public struct DeviceStatus
|
||||||
|
{
|
||||||
|
public byte Battery;
|
||||||
|
public byte Charging;
|
||||||
|
public byte FullCharge;
|
||||||
|
public byte Online;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IMouseData
|
||||||
|
{
|
||||||
|
byte[] GetDeviceStatus();
|
||||||
|
double GetBatteryRuntime();
|
||||||
|
void Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ServiceIPC
|
||||||
|
{
|
||||||
|
public static ServiceIPC Instance;
|
||||||
|
public static DeviceStatus DeviceStatus;
|
||||||
|
public static double BatteryRuntime = 0;
|
||||||
|
|
||||||
|
public ServiceIPC()
|
||||||
|
{
|
||||||
|
ThreadPool.QueueUserWorkItem(state =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
GetDeviceDataFromService();
|
||||||
|
Thread.Sleep(5000); // Periodically fetch data every 5 seconds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void GetDeviceDataFromService()
|
||||||
|
{
|
||||||
|
var client = new Client("SoraV2UtilsDeviceData");
|
||||||
|
var service = client.GetServiceProxy<IMouseData>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Allocate memory and copy device status data
|
||||||
|
int size = Marshal.SizeOf(typeof(DeviceStatus));
|
||||||
|
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] statusData = service.GetDeviceStatus();
|
||||||
|
Marshal.Copy(statusData, 0, ptr, size);
|
||||||
|
|
||||||
|
// Marshal data into DeviceStatus struct
|
||||||
|
DeviceStatus = (DeviceStatus)Marshal.PtrToStructure(ptr, typeof(DeviceStatus));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Clean up allocated memory
|
||||||
|
Marshal.FreeHGlobal(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
BatteryRuntime = service.GetBatteryRuntime();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
//TODO: Log or handle exceptions properly
|
||||||
|
Console.WriteLine($"Error fetching device data: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Exit()
|
||||||
|
{
|
||||||
|
var client = new Client("SoraV2UtilsDeviceData");
|
||||||
|
var service = client.GetServiceProxy<IMouseData>();
|
||||||
|
service.Exit();
|
||||||
|
|
||||||
|
// Exit the application gracefully
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
SoraV2Utils_Agent/ServiceMonitor.cs
Normal file
55
SoraV2Utils_Agent/ServiceMonitor.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Agent
|
||||||
|
{
|
||||||
|
public class ServiceMonitor
|
||||||
|
{
|
||||||
|
// Event to notify when the service stops
|
||||||
|
public event EventHandler ServiceStopped;
|
||||||
|
|
||||||
|
private Timer _timer;
|
||||||
|
private string _serviceName;
|
||||||
|
|
||||||
|
public ServiceMonitor(string serviceName)
|
||||||
|
{
|
||||||
|
_serviceName = serviceName;
|
||||||
|
|
||||||
|
// Set up the timer to check service status periodically
|
||||||
|
_timer = new Timer(1000);
|
||||||
|
_timer.Elapsed += CheckServiceStatus;
|
||||||
|
_timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckServiceStatus(object sender, ElapsedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ServiceController serviceController = new ServiceController(_serviceName);
|
||||||
|
|
||||||
|
// Check the current status of the service
|
||||||
|
if (serviceController.Status == ServiceControllerStatus.Stopped)
|
||||||
|
{
|
||||||
|
// Raise the ServiceStopped event
|
||||||
|
OnServiceStopped();
|
||||||
|
_timer.Stop(); // Stop the timer after detecting that the service has stopped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error checking service status: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event handler to raise the ServiceStopped event
|
||||||
|
protected virtual void OnServiceStopped()
|
||||||
|
{
|
||||||
|
ServiceStopped?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
120
SoraV2Utils_Agent/SoraV2Utils_Agent.csproj
Normal file
120
SoraV2Utils_Agent/SoraV2Utils_Agent.csproj
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{763D3776-C431-44DC-81EA-721B66F5FE65}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RootNamespace>SoraV2Utils_Agent</RootNamespace>
|
||||||
|
<AssemblyName>SoraV2Utils_Agent</AssemblyName>
|
||||||
|
<targetplatformversion>8.0</targetplatformversion>
|
||||||
|
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||||
|
<PublishUrl>publish\</PublishUrl>
|
||||||
|
<Install>true</Install>
|
||||||
|
<InstallFrom>Disk</InstallFrom>
|
||||||
|
<UpdateEnabled>false</UpdateEnabled>
|
||||||
|
<UpdateMode>Foreground</UpdateMode>
|
||||||
|
<UpdateInterval>7</UpdateInterval>
|
||||||
|
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||||
|
<UpdatePeriodically>false</UpdatePeriodically>
|
||||||
|
<UpdateRequired>false</UpdateRequired>
|
||||||
|
<MapFileExtensions>true</MapFileExtensions>
|
||||||
|
<ApplicationRevision>0</ApplicationRevision>
|
||||||
|
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||||
|
<UseApplicationTrust>false</UseApplicationTrust>
|
||||||
|
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<RuntimeIdentifiers>win</RuntimeIdentifiers>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<targetplatformversion>8.0</targetplatformversion>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>..\build\Release\</OutputPath>
|
||||||
|
<targetplatformversion>8.0</targetplatformversion>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<StartupObject />
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Configuration.Install" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Management" />
|
||||||
|
<Reference Include="System.ServiceProcess" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Windows.Data" />
|
||||||
|
<Reference Include="Windows.UI" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="NotificationInstaller.cs">
|
||||||
|
<SubType>Component</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="NotificationInstaller.Designer.cs">
|
||||||
|
<DependentUpon>NotificationInstaller.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="ServiceIPC.cs" />
|
||||||
|
<Compile Include="NotificationSender.cs" />
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="ServiceMonitor.cs" />
|
||||||
|
<Compile Include="TrayIcon.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="EasyPipes">
|
||||||
|
<Version>1.3.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
|
||||||
|
<Visible>False</Visible>
|
||||||
|
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 und x64%29</ProductName>
|
||||||
|
<Install>true</Install>
|
||||||
|
</BootstrapperPackage>
|
||||||
|
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||||
|
<Visible>False</Visible>
|
||||||
|
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||||
|
<Install>false</Install>
|
||||||
|
</BootstrapperPackage>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="NotificationInstaller.resx">
|
||||||
|
<DependentUpon>NotificationInstaller.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<PostBuildEvent>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
93
SoraV2Utils_Agent/TrayIcon.cs
Normal file
93
SoraV2Utils_Agent/TrayIcon.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Agent
|
||||||
|
{
|
||||||
|
public class TrayIcon
|
||||||
|
{
|
||||||
|
public TrayIcon()
|
||||||
|
{
|
||||||
|
// Create a NotifyIcon instance
|
||||||
|
using (NotifyIcon trayIcon = new NotifyIcon())
|
||||||
|
{
|
||||||
|
// Set up the tray icon properties
|
||||||
|
trayIcon.Icon = new Icon(SystemIcons.WinLogo, 40, 40); // Choose your own icon
|
||||||
|
trayIcon.Visible = true;
|
||||||
|
|
||||||
|
// Create a context menu
|
||||||
|
var contextMenu = new ContextMenu();
|
||||||
|
|
||||||
|
// Add a non-clickable dynamic text item
|
||||||
|
var batteryLevelItem = new MenuItem("Battery Level: Loading...");
|
||||||
|
var batteryRuntimeItem = new MenuItem("Battery Runtime: Loading...");
|
||||||
|
batteryLevelItem.Enabled = false;
|
||||||
|
batteryRuntimeItem.Enabled = false;
|
||||||
|
|
||||||
|
|
||||||
|
// Add an exit menu item
|
||||||
|
contextMenu.MenuItems.Add("Exit", (sender, e) => ServiceIPC.Instance.Exit());
|
||||||
|
|
||||||
|
contextMenu.MenuItems.Add(batteryLevelItem);
|
||||||
|
contextMenu.MenuItems.Add(batteryRuntimeItem);
|
||||||
|
|
||||||
|
trayIcon.ContextMenu = contextMenu;
|
||||||
|
|
||||||
|
// Update the dynamic text in the context menu
|
||||||
|
ThreadPool.QueueUserWorkItem(state =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Format battery level with charging status
|
||||||
|
string batteryLevelText = ServiceIPC.DeviceStatus.Charging == 1
|
||||||
|
? $"Battery Level: {ServiceIPC.DeviceStatus.Battery} (Charging...)"
|
||||||
|
: $"Battery Level: {ServiceIPC.DeviceStatus.Battery}";
|
||||||
|
|
||||||
|
// Format battery runtime (convert minutes to hours and minutes)
|
||||||
|
string batteryRuntimeText = FormatBatteryRuntime(ServiceIPC.BatteryRuntime);
|
||||||
|
|
||||||
|
// Update the context menu items
|
||||||
|
batteryLevelItem.Text = batteryLevelText;
|
||||||
|
batteryRuntimeItem.Text = $"Battery Runtime: {batteryRuntimeText}";
|
||||||
|
|
||||||
|
// Update every second
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Application.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formats the runtime in hours and minutes.
|
||||||
|
private string FormatBatteryRuntime(double runtimeInMinutes)
|
||||||
|
{
|
||||||
|
if(runtimeInMinutes < 0)
|
||||||
|
{
|
||||||
|
return "No data yet";
|
||||||
|
}
|
||||||
|
|
||||||
|
int hours = (int)(runtimeInMinutes / 60);
|
||||||
|
int minutes = (int)(runtimeInMinutes % 60);
|
||||||
|
|
||||||
|
// If runtime is greater than 1 hour, display hours and minutes
|
||||||
|
if (hours > 0)
|
||||||
|
{
|
||||||
|
return $"{hours} hour{(hours > 1 ? "s" : "")} {minutes} minute{(minutes > 1 ? "s" : "")}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return $"{minutes} minute{(minutes > 1 ? "s" : "")}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Display()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
SoraV2Utils_Service/AgentProcessHandler.cs
Normal file
94
SoraV2Utils_Service/AgentProcessHandler.cs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
internal class AgentProcessHandler
|
||||||
|
{
|
||||||
|
private Process _notificationHandlerProcess;
|
||||||
|
|
||||||
|
public AgentProcessHandler()
|
||||||
|
{
|
||||||
|
RunAgentHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunAgentHandler()
|
||||||
|
{
|
||||||
|
string notificationHandlerPath = GetNotificationHandlerPath();
|
||||||
|
|
||||||
|
if (LaunchProcess(notificationHandlerPath))
|
||||||
|
{
|
||||||
|
_notificationHandlerProcess = GetRunningProcess("SoraV2Utils_Agent");
|
||||||
|
|
||||||
|
if (_notificationHandlerProcess != null)
|
||||||
|
{
|
||||||
|
_notificationHandlerProcess.Exited += OnNotificationHandlerExited;
|
||||||
|
_notificationHandlerProcess.EnableRaisingEvents = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to start the notification handler process.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetNotificationHandlerPath()
|
||||||
|
{
|
||||||
|
return AppDomain.CurrentDomain.BaseDirectory + "\\SoraV2Utils_Agent.exe";
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool LaunchProcess(string path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ApplicationLauncher.CreateProcessInConsoleSession(path, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error launching process: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Process GetRunningProcess(string processName)
|
||||||
|
{
|
||||||
|
return Process.GetProcessesByName(processName).FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNotificationHandlerExited(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Process exited. Restarting...");
|
||||||
|
RestartNotificationHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RestartNotificationHandler()
|
||||||
|
{
|
||||||
|
Thread.Sleep(1000); // Delay before restarting the process
|
||||||
|
RunAgentHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopNotificationHandler()
|
||||||
|
{
|
||||||
|
if (_notificationHandlerProcess != null && !_notificationHandlerProcess.HasExited)
|
||||||
|
{
|
||||||
|
_notificationHandlerProcess.Kill();
|
||||||
|
_notificationHandlerProcess.WaitForExit(); // Ensure the process is fully terminated
|
||||||
|
Console.WriteLine("Process stopped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
UnsubscribeFromExitEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnsubscribeFromExitEvent()
|
||||||
|
{
|
||||||
|
if (_notificationHandlerProcess != null)
|
||||||
|
{
|
||||||
|
_notificationHandlerProcess.Exited -= OnNotificationHandlerExited;
|
||||||
|
Console.WriteLine("Event handler unregistered.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
SoraV2Utils_Service/App.config
Normal file
6
SoraV2Utils_Service/App.config
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
||||||
|
</startup>
|
||||||
|
</configuration>
|
469
SoraV2Utils_Service/ApplicationLauncher.cs
Normal file
469
SoraV2Utils_Service/ApplicationLauncher.cs
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
internal class ApplicationLauncher
|
||||||
|
{
|
||||||
|
public enum TOKEN_INFORMATION_CLASS
|
||||||
|
{
|
||||||
|
TokenUser = 1,
|
||||||
|
TokenGroups,
|
||||||
|
TokenPrivileges,
|
||||||
|
TokenOwner,
|
||||||
|
TokenPrimaryGroup,
|
||||||
|
TokenDefaultDacl,
|
||||||
|
TokenSource,
|
||||||
|
TokenType,
|
||||||
|
TokenImpersonationLevel,
|
||||||
|
TokenStatistics,
|
||||||
|
TokenRestrictedSids,
|
||||||
|
TokenSessionId,
|
||||||
|
TokenGroupsAndPrivileges,
|
||||||
|
TokenSessionReference,
|
||||||
|
TokenSandBoxInert,
|
||||||
|
TokenAuditPolicy,
|
||||||
|
TokenOrigin,
|
||||||
|
MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum
|
||||||
|
}
|
||||||
|
|
||||||
|
public const int READ_CONTROL = 0x00020000;
|
||||||
|
|
||||||
|
public const int STANDARD_RIGHTS_REQUIRED = 0x000F0000;
|
||||||
|
|
||||||
|
public const int STANDARD_RIGHTS_READ = READ_CONTROL;
|
||||||
|
public const int STANDARD_RIGHTS_WRITE = READ_CONTROL;
|
||||||
|
public const int STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
|
||||||
|
|
||||||
|
public const int STANDARD_RIGHTS_ALL = 0x001F0000;
|
||||||
|
|
||||||
|
public const int SPECIFIC_RIGHTS_ALL = 0x0000FFFF;
|
||||||
|
|
||||||
|
public const int TOKEN_ASSIGN_PRIMARY = 0x0001;
|
||||||
|
public const int TOKEN_DUPLICATE = 0x0002;
|
||||||
|
public const int TOKEN_IMPERSONATE = 0x0004;
|
||||||
|
public const int TOKEN_QUERY = 0x0008;
|
||||||
|
public const int TOKEN_QUERY_SOURCE = 0x0010;
|
||||||
|
public const int TOKEN_ADJUST_PRIVILEGES = 0x0020;
|
||||||
|
public const int TOKEN_ADJUST_GROUPS = 0x0040;
|
||||||
|
public const int TOKEN_ADJUST_DEFAULT = 0x0080;
|
||||||
|
public const int TOKEN_ADJUST_SESSIONID = 0x0100;
|
||||||
|
|
||||||
|
public const int TOKEN_ALL_ACCESS_P = (STANDARD_RIGHTS_REQUIRED |
|
||||||
|
TOKEN_ASSIGN_PRIMARY |
|
||||||
|
TOKEN_DUPLICATE |
|
||||||
|
TOKEN_IMPERSONATE |
|
||||||
|
TOKEN_QUERY |
|
||||||
|
TOKEN_QUERY_SOURCE |
|
||||||
|
TOKEN_ADJUST_PRIVILEGES |
|
||||||
|
TOKEN_ADJUST_GROUPS |
|
||||||
|
TOKEN_ADJUST_DEFAULT);
|
||||||
|
|
||||||
|
public const int TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;
|
||||||
|
|
||||||
|
public const int TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;
|
||||||
|
|
||||||
|
public const int TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
|
||||||
|
TOKEN_ADJUST_PRIVILEGES |
|
||||||
|
TOKEN_ADJUST_GROUPS |
|
||||||
|
TOKEN_ADJUST_DEFAULT;
|
||||||
|
|
||||||
|
public const int TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;
|
||||||
|
|
||||||
|
public const uint MAXIMUM_ALLOWED = 0x2000000;
|
||||||
|
|
||||||
|
public const int CREATE_NEW_PROCESS_GROUP = 0x00000200;
|
||||||
|
public const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
|
||||||
|
|
||||||
|
public const int IDLE_PRIORITY_CLASS = 0x40;
|
||||||
|
public const int NORMAL_PRIORITY_CLASS = 0x20;
|
||||||
|
public const int HIGH_PRIORITY_CLASS = 0x80;
|
||||||
|
public const int REALTIME_PRIORITY_CLASS = 0x100;
|
||||||
|
|
||||||
|
public const int CREATE_NEW_CONSOLE = 0x00000010;
|
||||||
|
|
||||||
|
public const string SE_DEBUG_NAME = "SeDebugPrivilege";
|
||||||
|
public const string SE_RESTORE_NAME = "SeRestorePrivilege";
|
||||||
|
public const string SE_BACKUP_NAME = "SeBackupPrivilege";
|
||||||
|
|
||||||
|
public const int SE_PRIVILEGE_ENABLED = 0x0002;
|
||||||
|
|
||||||
|
public const int ERROR_NOT_ALL_ASSIGNED = 1300;
|
||||||
|
|
||||||
|
private const uint TH32CS_SNAPPROCESS = 0x00000002;
|
||||||
|
|
||||||
|
public static int INVALID_HANDLE_VALUE = -1;
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError = true)]
|
||||||
|
public static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpname,
|
||||||
|
[MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi,
|
||||||
|
CallingConvention = CallingConvention.StdCall)]
|
||||||
|
public static extern bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine,
|
||||||
|
ref SECURITY_ATTRIBUTES lpProcessAttributes,
|
||||||
|
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
|
||||||
|
String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
public static extern bool DuplicateToken(IntPtr ExistingTokenHandle,
|
||||||
|
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
|
||||||
|
public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
|
||||||
|
ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
|
||||||
|
int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError = true)]
|
||||||
|
public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges,
|
||||||
|
ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError = true)]
|
||||||
|
public static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass,
|
||||||
|
ref uint TokenInformation, uint TokenInformationLength);
|
||||||
|
|
||||||
|
[DllImport("userenv.dll", SetLastError = true)]
|
||||||
|
public static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
|
||||||
|
|
||||||
|
public static bool CreateProcessInConsoleSession(String CommandLine, bool bElevate)
|
||||||
|
{
|
||||||
|
|
||||||
|
PROCESS_INFORMATION pi;
|
||||||
|
|
||||||
|
bool bResult = false;
|
||||||
|
uint dwSessionId, winlogonPid = 0;
|
||||||
|
IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
|
||||||
|
|
||||||
|
Debug.Print("CreateProcessInConsoleSession");
|
||||||
|
// Log the client on to the local computer.
|
||||||
|
dwSessionId = WTSGetActiveConsoleSessionId();
|
||||||
|
|
||||||
|
// Find the winlogon process
|
||||||
|
var procEntry = new PROCESSENTRY32();
|
||||||
|
|
||||||
|
uint hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
|
if (hSnap == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
procEntry.dwSize = (uint)Marshal.SizeOf(procEntry); //sizeof(PROCESSENTRY32);
|
||||||
|
|
||||||
|
if (Process32First(hSnap, ref procEntry) == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String strCmp = "explorer.exe";
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (strCmp.IndexOf(procEntry.szExeFile) == 0)
|
||||||
|
{
|
||||||
|
// We found a winlogon process...make sure it's running in the console session
|
||||||
|
uint winlogonSessId = 0;
|
||||||
|
if (ProcessIdToSessionId(procEntry.th32ProcessID, ref winlogonSessId) &&
|
||||||
|
winlogonSessId == dwSessionId)
|
||||||
|
{
|
||||||
|
winlogonPid = procEntry.th32ProcessID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (Process32Next(hSnap, ref procEntry) != 0);
|
||||||
|
|
||||||
|
//Get the user token used by DuplicateTokenEx
|
||||||
|
WTSQueryUserToken(dwSessionId, ref hUserToken);
|
||||||
|
|
||||||
|
var si = new STARTUPINFO();
|
||||||
|
si.cb = Marshal.SizeOf(si);
|
||||||
|
si.lpDesktop = "winsta0\\default";
|
||||||
|
var tp = new TOKEN_PRIVILEGES();
|
||||||
|
var luid = new LUID();
|
||||||
|
hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!OpenProcessToken(hProcess,
|
||||||
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY
|
||||||
|
| TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE, ref hPToken))
|
||||||
|
{
|
||||||
|
Debug.Print(String.Format("CreateProcessInConsoleSession OpenProcessToken error: {0}",
|
||||||
|
Marshal.GetLastWin32Error()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LookupPrivilegeValue(IntPtr.Zero, SE_DEBUG_NAME, ref luid))
|
||||||
|
{
|
||||||
|
Debug.Print(String.Format("CreateProcessInConsoleSession LookupPrivilegeValue error: {0}",
|
||||||
|
Marshal.GetLastWin32Error()));
|
||||||
|
}
|
||||||
|
|
||||||
|
var sa = new SECURITY_ATTRIBUTES();
|
||||||
|
sa.Length = Marshal.SizeOf(sa);
|
||||||
|
|
||||||
|
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa,
|
||||||
|
(int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary,
|
||||||
|
ref hUserTokenDup))
|
||||||
|
{
|
||||||
|
Debug.Print(
|
||||||
|
String.Format(
|
||||||
|
"CreateProcessInConsoleSession DuplicateTokenEx error: {0} Token does not have the privilege.",
|
||||||
|
Marshal.GetLastWin32Error()));
|
||||||
|
CloseHandle(hProcess);
|
||||||
|
CloseHandle(hUserToken);
|
||||||
|
CloseHandle(hPToken);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bElevate)
|
||||||
|
{
|
||||||
|
//tp.Privileges[0].Luid = luid;
|
||||||
|
//tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||||
|
|
||||||
|
tp.PrivilegeCount = 1;
|
||||||
|
tp.Privileges = new int[3];
|
||||||
|
tp.Privileges[2] = SE_PRIVILEGE_ENABLED;
|
||||||
|
tp.Privileges[1] = luid.HighPart;
|
||||||
|
tp.Privileges[0] = luid.LowPart;
|
||||||
|
|
||||||
|
//Adjust Token privilege
|
||||||
|
if (
|
||||||
|
!SetTokenInformation(hUserTokenDup, TOKEN_INFORMATION_CLASS.TokenSessionId, ref dwSessionId,
|
||||||
|
(uint)IntPtr.Size))
|
||||||
|
{
|
||||||
|
Debug.Print(
|
||||||
|
String.Format(
|
||||||
|
"CreateProcessInConsoleSession SetTokenInformation error: {0} Token does not have the privilege.",
|
||||||
|
Marshal.GetLastWin32Error()));
|
||||||
|
//CloseHandle(hProcess);
|
||||||
|
//CloseHandle(hUserToken);
|
||||||
|
//CloseHandle(hPToken);
|
||||||
|
//CloseHandle(hUserTokenDup);
|
||||||
|
//return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!AdjustTokenPrivileges(hUserTokenDup, false, ref tp, Marshal.SizeOf(tp), /*(PTOKEN_PRIVILEGES)*/
|
||||||
|
IntPtr.Zero, IntPtr.Zero))
|
||||||
|
{
|
||||||
|
int nErr = Marshal.GetLastWin32Error();
|
||||||
|
|
||||||
|
if (nErr == ERROR_NOT_ALL_ASSIGNED)
|
||||||
|
{
|
||||||
|
Debug.Print(
|
||||||
|
String.Format(
|
||||||
|
"CreateProcessInConsoleSession AdjustTokenPrivileges error: {0} Token does not have the privilege.",
|
||||||
|
nErr));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Print(String.Format("CreateProcessInConsoleSession AdjustTokenPrivileges error: {0}", nErr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
|
||||||
|
IntPtr pEnv = IntPtr.Zero;
|
||||||
|
if (CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true))
|
||||||
|
{
|
||||||
|
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pEnv = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
// Launch the process in the client's logon session.
|
||||||
|
bResult = CreateProcessAsUser(hUserTokenDup, // client's access token
|
||||||
|
CommandLine, // file to execute
|
||||||
|
null, // command line
|
||||||
|
ref sa, // pointer to process SECURITY_ATTRIBUTES
|
||||||
|
ref sa, // pointer to thread SECURITY_ATTRIBUTES
|
||||||
|
false, // handles are not inheritable
|
||||||
|
(int)dwCreationFlags, // creation flags
|
||||||
|
pEnv, // pointer to new environment block
|
||||||
|
null, // name of current directory
|
||||||
|
ref si, // pointer to STARTUPINFO structure
|
||||||
|
out pi // receives information about new process
|
||||||
|
);
|
||||||
|
// End impersonation of client.
|
||||||
|
|
||||||
|
//GetLastError should be 0
|
||||||
|
int iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
|
||||||
|
|
||||||
|
//Close handles task
|
||||||
|
CloseHandle(hProcess);
|
||||||
|
CloseHandle(hUserToken);
|
||||||
|
CloseHandle(hUserTokenDup);
|
||||||
|
CloseHandle(hPToken);
|
||||||
|
|
||||||
|
return (iResultOfCreateProcessAsUser == 0) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern int Process32First(uint hSnapshot, ref PROCESSENTRY32 lppe);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern int Process32Next(uint hSnapshot, ref PROCESSENTRY32 lppe);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
private static extern uint CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
private static extern bool CloseHandle(IntPtr hSnapshot);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern uint WTSGetActiveConsoleSessionId();
|
||||||
|
|
||||||
|
[DllImport("Wtsapi32.dll")]
|
||||||
|
private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
|
||||||
|
|
||||||
|
[DllImport("advapi32", SetLastError = true)]
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
private static extern bool OpenProcessToken(IntPtr ProcessHandle, // handle to process
|
||||||
|
int DesiredAccess, // desired access to process
|
||||||
|
ref IntPtr TokenHandle);
|
||||||
|
|
||||||
|
#region Nested type: LUID
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct LUID
|
||||||
|
{
|
||||||
|
public int LowPart;
|
||||||
|
public int HighPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
//end struct
|
||||||
|
|
||||||
|
#region Nested type: LUID_AND_ATRIBUTES
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct LUID_AND_ATRIBUTES
|
||||||
|
{
|
||||||
|
public LUID Luid;
|
||||||
|
public int Attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested type: PROCESSENTRY32
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct PROCESSENTRY32
|
||||||
|
{
|
||||||
|
public uint dwSize;
|
||||||
|
public readonly uint cntUsage;
|
||||||
|
public readonly uint th32ProcessID;
|
||||||
|
public readonly IntPtr th32DefaultHeapID;
|
||||||
|
public readonly uint th32ModuleID;
|
||||||
|
public readonly uint cntThreads;
|
||||||
|
public readonly uint th32ParentProcessID;
|
||||||
|
public readonly int pcPriClassBase;
|
||||||
|
public readonly uint dwFlags;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
|
||||||
|
public readonly string szExeFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested type: PROCESS_INFORMATION
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct PROCESS_INFORMATION
|
||||||
|
{
|
||||||
|
public IntPtr hProcess;
|
||||||
|
public IntPtr hThread;
|
||||||
|
public uint dwProcessId;
|
||||||
|
public uint dwThreadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested type: SECURITY_ATTRIBUTES
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct SECURITY_ATTRIBUTES
|
||||||
|
{
|
||||||
|
public int Length;
|
||||||
|
public IntPtr lpSecurityDescriptor;
|
||||||
|
public bool bInheritHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested type: SECURITY_IMPERSONATION_LEVEL
|
||||||
|
|
||||||
|
private enum SECURITY_IMPERSONATION_LEVEL
|
||||||
|
{
|
||||||
|
SecurityAnonymous = 0,
|
||||||
|
SecurityIdentification = 1,
|
||||||
|
SecurityImpersonation = 2,
|
||||||
|
SecurityDelegation = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested type: STARTUPINFO
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct STARTUPINFO
|
||||||
|
{
|
||||||
|
public int cb;
|
||||||
|
public String lpReserved;
|
||||||
|
public String lpDesktop;
|
||||||
|
public String lpTitle;
|
||||||
|
public uint dwX;
|
||||||
|
public uint dwY;
|
||||||
|
public uint dwXSize;
|
||||||
|
public uint dwYSize;
|
||||||
|
public uint dwXCountChars;
|
||||||
|
public uint dwYCountChars;
|
||||||
|
public uint dwFillAttribute;
|
||||||
|
public uint dwFlags;
|
||||||
|
public short wShowWindow;
|
||||||
|
public short cbReserved2;
|
||||||
|
public IntPtr lpReserved2;
|
||||||
|
public IntPtr hStdInput;
|
||||||
|
public IntPtr hStdOutput;
|
||||||
|
public IntPtr hStdError;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested type: TOKEN_PRIVILEGES
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct TOKEN_PRIVILEGES
|
||||||
|
{
|
||||||
|
internal int PrivilegeCount;
|
||||||
|
//LUID_AND_ATRIBUTES
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||||
|
internal int[] Privileges;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Nested type: TOKEN_TYPE
|
||||||
|
|
||||||
|
private enum TOKEN_TYPE
|
||||||
|
{
|
||||||
|
TokenPrimary = 1,
|
||||||
|
TokenImpersonation = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// handle to open access token
|
||||||
|
}
|
||||||
|
}
|
135
SoraV2Utils_Service/DeviceTracker.cs
Normal file
135
SoraV2Utils_Service/DeviceTracker.cs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
using EasyPipes;
|
||||||
|
using SoraV2Tools;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
public class DeviceTracker
|
||||||
|
{
|
||||||
|
public static DeviceTracker Instance;
|
||||||
|
private bool BatteryWarningSent = false;
|
||||||
|
private bool BatteryCriticalSent = false;
|
||||||
|
private bool RechargeCompletedSent = false;
|
||||||
|
private Dictionary<DateTime, byte> BatteryStats = new Dictionary<DateTime, byte>();
|
||||||
|
public DeviceStatus _DeviceStatus;
|
||||||
|
|
||||||
|
public DeviceTracker()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processMouseData(DeviceStatus deviceStatus)
|
||||||
|
{
|
||||||
|
_DeviceStatus = deviceStatus;
|
||||||
|
|
||||||
|
if (deviceStatus.Charging == 0)
|
||||||
|
{
|
||||||
|
BatteryStats.Add(DateTime.Now, deviceStatus.Battery);
|
||||||
|
RechargeCompletedSent = false;
|
||||||
|
|
||||||
|
if (deviceStatus.Battery <= ServiceSettings.Instance.criticalThreshold && !BatteryCriticalSent && deviceStatus.Charging != 1)
|
||||||
|
{
|
||||||
|
Notification.TwoLine(String.Format("SoraV2 battery is at {0}%",deviceStatus.Battery), "Please recharge now!");
|
||||||
|
BatteryCriticalSent = true;
|
||||||
|
}
|
||||||
|
else if (deviceStatus.Battery <= ServiceSettings.Instance.warningThreshold && !BatteryCriticalSent && !BatteryWarningSent && deviceStatus.Charging != 1)
|
||||||
|
{
|
||||||
|
Notification.TwoLine(String.Format("SoraV2 battery is at {0}%", deviceStatus.Battery), "Please consider recharching.");
|
||||||
|
BatteryWarningSent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (deviceStatus.Charging == 1 && !RechargeCompletedSent)
|
||||||
|
{
|
||||||
|
BatteryStats.Clear();
|
||||||
|
BatteryWarningSent = false;
|
||||||
|
BatteryCriticalSent = false;
|
||||||
|
RechargeCompletedSent = true;
|
||||||
|
|
||||||
|
if (deviceStatus.Battery == 100)
|
||||||
|
{
|
||||||
|
Notification.SingleLine(String.Format("SoraV2 battery fully recharged!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double CalculateConsumptionRate()
|
||||||
|
{
|
||||||
|
// Ensure there's enough data to calculate a rate (at least two points)
|
||||||
|
if (this.BatteryStats.Count > 2)
|
||||||
|
{
|
||||||
|
// Get the last two entries in the dictionary
|
||||||
|
var latestEntry = this.BatteryStats.Last();
|
||||||
|
var previousEntry = this.BatteryStats.ElementAt(this.BatteryStats.Count - 2);
|
||||||
|
|
||||||
|
// Calculate the difference in battery level and time
|
||||||
|
double batteryLevelDifference = previousEntry.Value - latestEntry.Value;
|
||||||
|
double timeDifferenceMinutes = (latestEntry.Key - previousEntry.Key).TotalMinutes;
|
||||||
|
|
||||||
|
// Calculate consumption rate (percentage per minute)
|
||||||
|
return batteryLevelDifference / timeDifferenceMinutes;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double EstimateRemainingRuntime()
|
||||||
|
{
|
||||||
|
if (this.BatteryStats == null || this.BatteryStats.Count < 2)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sortedStats = new SortedDictionary<DateTime, byte>(this.BatteryStats);
|
||||||
|
|
||||||
|
double totalConsumptionRate = 0;
|
||||||
|
int numberOfIntervals = 0;
|
||||||
|
|
||||||
|
DateTime previousTime = DateTime.MinValue;
|
||||||
|
byte previousBatteryLevel = 0;
|
||||||
|
|
||||||
|
// Calculate the average consumption rate per second
|
||||||
|
foreach (var entry in sortedStats)
|
||||||
|
{
|
||||||
|
if (previousTime != DateTime.MinValue)
|
||||||
|
{
|
||||||
|
// Calculate the time difference (in seconds)
|
||||||
|
double timeDifferenceInSeconds = (entry.Key - previousTime).TotalSeconds;
|
||||||
|
|
||||||
|
// Calculate the battery consumption for this interval
|
||||||
|
int batteryDifference = previousBatteryLevel - entry.Value;
|
||||||
|
|
||||||
|
// Ensure the consumption is positive (in case of rounding errors)
|
||||||
|
if (batteryDifference > 0)
|
||||||
|
{
|
||||||
|
// Calculate consumption rate per second
|
||||||
|
double consumptionRate = batteryDifference / timeDifferenceInSeconds;
|
||||||
|
totalConsumptionRate += consumptionRate;
|
||||||
|
numberOfIntervals++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousTime = entry.Key;
|
||||||
|
previousBatteryLevel = entry.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the average consumption rate
|
||||||
|
double averageConsumptionRate = totalConsumptionRate / numberOfIntervals;
|
||||||
|
|
||||||
|
// Get the current battery level (from the last entry in the sorted dictionary)
|
||||||
|
byte currentBatteryLevel = sortedStats.Values.Last();
|
||||||
|
|
||||||
|
// Calculate the remaining runtime (in seconds)
|
||||||
|
double remainingRuntimeInSeconds = currentBatteryLevel / averageConsumptionRate;
|
||||||
|
|
||||||
|
// Convert seconds to a more human-readable format (hours, minutes, seconds)
|
||||||
|
TimeSpan remainingTime = TimeSpan.FromSeconds(remainingRuntimeInSeconds);
|
||||||
|
Console.WriteLine($"Estimated remaining battery runtime: {remainingTime.Hours} hours, {remainingTime.Minutes} minutes, {remainingTime.Seconds} seconds.");
|
||||||
|
|
||||||
|
return remainingRuntimeInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
75
SoraV2Utils_Service/Notification.cs
Normal file
75
SoraV2Utils_Service/Notification.cs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
using EasyPipes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
public interface IToastNotification
|
||||||
|
{
|
||||||
|
void SendToastNotification(string xml, string title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Notification
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void SingleLine(string line1)
|
||||||
|
{
|
||||||
|
var client = new Client("SoraV2UtilsNotifcation");
|
||||||
|
var service = client.GetServiceProxy<IToastNotification>();
|
||||||
|
|
||||||
|
string xml = @"
|
||||||
|
<toast>
|
||||||
|
<visual>
|
||||||
|
<binding template='ToastImageAndText01'>
|
||||||
|
<text id='1'>{0}</text>
|
||||||
|
</binding>
|
||||||
|
</visual>
|
||||||
|
<audio src='ms-winsoundevent:Notification.Default' />
|
||||||
|
</toast>";
|
||||||
|
|
||||||
|
xml = String.Format(xml, line1);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
service.SendToastNotification(xml, "SoraV2 Utils");
|
||||||
|
}
|
||||||
|
catch (System.TimeoutException)
|
||||||
|
{
|
||||||
|
ServiceLogger.Instance.Log("SoraV2Utils_Agent is not responding...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void TwoLine(string line1, string line2)
|
||||||
|
{
|
||||||
|
var client = new Client("SoraV2UtilsNotifcation");
|
||||||
|
var service = client.GetServiceProxy<IToastNotification>();
|
||||||
|
|
||||||
|
string xml = @"
|
||||||
|
<toast>
|
||||||
|
<visual>
|
||||||
|
<binding template='ToastImageAndText02'>
|
||||||
|
<text id='1'>{0}</text>
|
||||||
|
<text id='2'>{1}</text>
|
||||||
|
</binding>
|
||||||
|
</visual>
|
||||||
|
<audio src='ms-winsoundevent:Notification.Default' />
|
||||||
|
</toast>";
|
||||||
|
|
||||||
|
xml = String.Format(xml, line1, line2);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
service.SendToastNotification(xml, "SoraV2 Utils");
|
||||||
|
}
|
||||||
|
catch (System.TimeoutException)
|
||||||
|
{
|
||||||
|
ServiceLogger.Instance.Log("SoraV2Utils_Agent is not responding...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
32
SoraV2Utils_Service/Program.cs
Normal file
32
SoraV2Utils_Service/Program.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
new ServiceLogger();
|
||||||
|
new ServiceSettings();
|
||||||
|
new AgentProcessHandler();
|
||||||
|
new ServiceIPC();
|
||||||
|
|
||||||
|
//System.Threading.Thread.Sleep(1000);
|
||||||
|
//Service1 service = new Service1();
|
||||||
|
//service.OnDebug();
|
||||||
|
//System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
|
||||||
|
|
||||||
|
ServiceBase[] ServicesToRun;
|
||||||
|
ServicesToRun = new ServiceBase[]
|
||||||
|
{
|
||||||
|
new SoraV2UtilsService()
|
||||||
|
};
|
||||||
|
ServiceBase.Run(ServicesToRun);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
SoraV2Utils_Service/Properties/AssemblyInfo.cs
Normal file
36
SoraV2Utils_Service/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||||
|
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||||
|
// die einer Assembly zugeordnet sind.
|
||||||
|
[assembly: AssemblyTitle("SoraV2Utils_Service")]
|
||||||
|
[assembly: AssemblyDescription("Service for SoraV2 Utils")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("SoraV2Utils_Service")]
|
||||||
|
[assembly: AssemblyCopyright("")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
|
||||||
|
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
|
||||||
|
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||||
|
[assembly: Guid("8eab0571-6603-4ecf-ad49-93c2b8f6c94c")]
|
||||||
|
|
||||||
|
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||||
|
//
|
||||||
|
// Hauptversion
|
||||||
|
// Nebenversion
|
||||||
|
// Buildnummer
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
|
||||||
|
// indem Sie "*" wie unten gezeigt eingeben:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
135
SoraV2Utils_Service/ServiceIPC.cs
Normal file
135
SoraV2Utils_Service/ServiceIPC.cs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
using EasyPipes;
|
||||||
|
using SoraV2Tools;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Pipes;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.AccessControl;
|
||||||
|
using System.Security.Principal;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
// Interface for mouse data interaction
|
||||||
|
public interface IMouseData
|
||||||
|
{
|
||||||
|
byte[] GetDeviceStatus();
|
||||||
|
double GetBatteryRuntime();
|
||||||
|
void Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ServiceIPC
|
||||||
|
{
|
||||||
|
// Creates pipe security for system IO
|
||||||
|
private PipeSecurity CreateSystemIOPipeSecurity()
|
||||||
|
{
|
||||||
|
PipeSecurity pipeSecurity = new PipeSecurity();
|
||||||
|
|
||||||
|
// Assign authenticated user read-write access
|
||||||
|
var authenticatedUserSid = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
|
||||||
|
pipeSecurity.SetAccessRule(new PipeAccessRule(authenticatedUserSid, PipeAccessRights.ReadWrite, AccessControlType.Allow));
|
||||||
|
|
||||||
|
return pipeSecurity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opens a custom named pipe with the specified name and buffer size
|
||||||
|
private NamedPipeServerStream OpenCustomPipe(string pipeName, int bufferSize)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pipeSecurity = CreateSystemIOPipeSecurity();
|
||||||
|
var pipeStream = new NamedPipeServerStream(pipeName,
|
||||||
|
PipeDirection.InOut,
|
||||||
|
1,
|
||||||
|
PipeTransmissionMode.Message,
|
||||||
|
PipeOptions.Asynchronous,
|
||||||
|
bufferSize,
|
||||||
|
0x400,
|
||||||
|
pipeSecurity,
|
||||||
|
HandleInheritability.Inheritable);
|
||||||
|
|
||||||
|
Console.WriteLine($"Named Pipe '{pipeName}' successfully opened.");
|
||||||
|
return pipeStream;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Error opening pipe {pipeName}: {ex.Message}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor for initializing the IPC service and server
|
||||||
|
public ServiceIPC()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var server = new Server("SoraV2UtilsDeviceData", OpenCustomPipe);
|
||||||
|
server.RegisterService<IMouseData>(new ServiceIPCHandler());
|
||||||
|
server.Start();
|
||||||
|
Console.WriteLine("IPC Service started successfully.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Failed to start IPC Service: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler class to implement the IMouseData interface
|
||||||
|
public class ServiceIPCHandler : IMouseData
|
||||||
|
{
|
||||||
|
public byte[] GetDeviceStatus()
|
||||||
|
{
|
||||||
|
var status = DeviceTracker.Instance._DeviceStatus;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int size = Marshal.SizeOf(status);
|
||||||
|
byte[] arr = new byte[size];
|
||||||
|
|
||||||
|
IntPtr ptr = IntPtr.Zero;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ptr = Marshal.AllocHGlobal(size);
|
||||||
|
Marshal.StructureToPtr(status, ptr, true);
|
||||||
|
Marshal.Copy(ptr, arr, 0, size);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(ptr);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Error retrieving battery level: {ex.Message}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetBatteryRuntime()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return DeviceTracker.Instance?.EstimateRemainingRuntime() ?? 0.0;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Error estimating battery runtime: {ex.Message}");
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Exit()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Exit the application with code 1
|
||||||
|
Console.WriteLine("Exiting application...");
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Error during exit: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
SoraV2Utils_Service/ServiceInstaller.Designer.cs
generated
Normal file
62
SoraV2Utils_Service/ServiceInstaller.Designer.cs
generated
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
partial class ServiceInstaller
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Erforderliche Designervariable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verwendete Ressourcen bereinigen.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Vom Komponenten-Designer generierter Code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Erforderliche Methode für die Designerunterstützung.
|
||||||
|
/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
|
||||||
|
this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
|
||||||
|
//
|
||||||
|
// serviceProcessInstaller1
|
||||||
|
//
|
||||||
|
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
|
||||||
|
this.serviceProcessInstaller1.Password = null;
|
||||||
|
this.serviceProcessInstaller1.Username = null;
|
||||||
|
//
|
||||||
|
// serviceInstaller1
|
||||||
|
//
|
||||||
|
this.serviceInstaller1.Description = "SoraV2 Utils Service";
|
||||||
|
this.serviceInstaller1.DisplayName = "SoraV2 Utils Service";
|
||||||
|
this.serviceInstaller1.ServiceName = "SoraV2Utils_Service";
|
||||||
|
this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
|
||||||
|
//
|
||||||
|
// ProjectInstaller
|
||||||
|
//
|
||||||
|
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
|
||||||
|
this.serviceProcessInstaller1,
|
||||||
|
this.serviceInstaller1});
|
||||||
|
this.AfterInstall += new System.Configuration.Install.InstallEventHandler(this.ProjectInstaller_AfterInstall);
|
||||||
|
this.BeforeUninstall += new System.Configuration.Install.InstallEventHandler(this.ProjectInstaller_BeforeUninstall);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
|
||||||
|
private System.ServiceProcess.ServiceInstaller serviceInstaller1;
|
||||||
|
}
|
||||||
|
}
|
46
SoraV2Utils_Service/ServiceInstaller.cs
Normal file
46
SoraV2Utils_Service/ServiceInstaller.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Configuration.Install;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
[RunInstaller(true)]
|
||||||
|
public partial class ServiceInstaller : System.Configuration.Install.Installer
|
||||||
|
{
|
||||||
|
public ServiceInstaller()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProjectInstaller_AfterInstall(object sender, InstallEventArgs e)
|
||||||
|
{
|
||||||
|
System.ServiceProcess.ServiceController sc = new System.ServiceProcess.ServiceController(serviceInstaller1.ServiceName);
|
||||||
|
sc.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProjectInstaller_BeforeUninstall(object sender, InstallEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (ServiceController sv = new ServiceController(serviceInstaller1.ServiceName))
|
||||||
|
{
|
||||||
|
if (sv.Status != ServiceControllerStatus.Stopped)
|
||||||
|
{
|
||||||
|
sv.Stop();
|
||||||
|
sv.WaitForStatus(ServiceControllerStatus.Stopped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
EventLog.WriteEntry("SoraV2Utils_Service", ex.Message, EventLogEntryType.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
129
SoraV2Utils_Service/ServiceInstaller.resx
Normal file
129
SoraV2Utils_Service/ServiceInstaller.resx
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<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>
|
||||||
|
<metadata name="serviceProcessInstaller1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 54</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="serviceInstaller1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>194, 17</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>False</value>
|
||||||
|
</metadata>
|
||||||
|
</root>
|
36
SoraV2Utils_Service/ServiceLogger.cs
Normal file
36
SoraV2Utils_Service/ServiceLogger.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
public class ServiceLogger
|
||||||
|
{
|
||||||
|
public static ServiceLogger Instance { get; private set; }
|
||||||
|
|
||||||
|
string LogFilePath = "";
|
||||||
|
|
||||||
|
public ServiceLogger()
|
||||||
|
{
|
||||||
|
var logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
|
||||||
|
|
||||||
|
Directory.CreateDirectory(logDirectory); // Ensures directory exists
|
||||||
|
|
||||||
|
string logFileName = $"ServiceLog_{DateTime.Now:yyyy_MM_dd}.txt";
|
||||||
|
this.LogFilePath = Path.Combine(logDirectory, logFileName);
|
||||||
|
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log(string message)
|
||||||
|
{
|
||||||
|
using (StreamWriter sw = new StreamWriter(this.LogFilePath, append: true))
|
||||||
|
{
|
||||||
|
sw.WriteLine($"[{DateTime.Now}] {message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
SoraV2Utils_Service/ServiceSettings.cs
Normal file
94
SoraV2Utils_Service/ServiceSettings.cs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
using IniParser;
|
||||||
|
using IniParser.Model;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using static System.Collections.Specialized.BitVector32;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
public class ServiceSettings
|
||||||
|
{
|
||||||
|
public static ServiceSettings Instance { get; private set; }
|
||||||
|
|
||||||
|
private IniData data = null;
|
||||||
|
private FileIniDataParser parser = null;
|
||||||
|
|
||||||
|
public int intervall = 60000;
|
||||||
|
public int warningThreshold = 30;
|
||||||
|
public int criticalThreshold = 10;
|
||||||
|
|
||||||
|
public ServiceSettings()
|
||||||
|
{
|
||||||
|
string serviceSettingsFile = AppDomain.CurrentDomain.BaseDirectory + "\\SoraV2Utils.ini";
|
||||||
|
|
||||||
|
this.parser = new FileIniDataParser();
|
||||||
|
|
||||||
|
this.data = ReadOrCreateConfigFile(serviceSettingsFile);
|
||||||
|
|
||||||
|
TryParseConfig<int>("SoraV2Utils", "interval", ref this.intervall);
|
||||||
|
TryParseConfig<int>("SoraV2Utils", "warningThreshold", ref this.warningThreshold);
|
||||||
|
TryParseConfig<int>("SoraV2Utils", "criticalThreshold", ref this.criticalThreshold);
|
||||||
|
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryParseConfig<T>(string section, string key, ref T variable)
|
||||||
|
{
|
||||||
|
bool parseResult = false;
|
||||||
|
|
||||||
|
string value = this.data[section][key];
|
||||||
|
if (!string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
variable = (T)Convert.ChangeType(value, typeof(T)); // Convert the value to the appropriate type.
|
||||||
|
parseResult = true;
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
ServiceLogger.Instance.Log($"{key} setting could not be read from config! (Using default: {variable})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IniData ReadOrCreateConfigFile(string filePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = parser.ReadFile(filePath);
|
||||||
|
if (data.Sections.Count == 0)
|
||||||
|
{
|
||||||
|
CreateServiceFile(data, filePath);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (IniParser.Exceptions.ParsingException)
|
||||||
|
{
|
||||||
|
var newData = new IniParser.Parser.IniDataParser().Parse("");
|
||||||
|
CreateServiceFile(newData,filePath);
|
||||||
|
return newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IniData CreateServiceFile(IniData _data, string path)
|
||||||
|
{
|
||||||
|
_data.Sections.AddSection("SoraV2Utils");
|
||||||
|
_data["SoraV2Utils"].AddKey("interval", "60000");
|
||||||
|
_data["SoraV2Utils"].GetKeyData("interval").Comments.Add("Interval to check the mouse battery");
|
||||||
|
|
||||||
|
_data["SoraV2Utils"].AddKey("warningThreshold", "30");
|
||||||
|
_data["SoraV2Utils"].GetKeyData("warningThreshold").Comments.Add("Threshold for first warning");
|
||||||
|
|
||||||
|
_data["SoraV2Utils"].AddKey("criticalThreshold", "10");
|
||||||
|
_data["SoraV2Utils"].GetKeyData("criticalThreshold").Comments.Add("Threshold for second (critial) warning");
|
||||||
|
this.parser.WriteFile("SoraV2Utils.ini", _data);
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
SoraV2Utils_Service/SoraV2Interface.cs
Normal file
102
SoraV2Utils_Service/SoraV2Interface.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
using HidLibrary;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SoraV2Tools
|
||||||
|
{
|
||||||
|
internal class SoraV2Interface
|
||||||
|
{
|
||||||
|
private const ushort VID = 0x1915;
|
||||||
|
private const ushort PID_WIRELESS = 0xAE1C;
|
||||||
|
private const ushort PID_WIRED = 0xAE11;
|
||||||
|
|
||||||
|
public static List<HidDevice> GetDevice()
|
||||||
|
{
|
||||||
|
var devices = HidDevices.Enumerate(VID, PID_WIRELESS).ToList();
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DeviceStatus GetDeviceStatus()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var device in GetDevice())
|
||||||
|
{
|
||||||
|
// Get the HID device
|
||||||
|
if (device == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Device not found.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the device for communication
|
||||||
|
device.OpenDevice();
|
||||||
|
|
||||||
|
if (!device.IsOpen || !device.IsConnected)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the report to send to the device
|
||||||
|
byte[] report = new byte[32];
|
||||||
|
report[0] = 5; // Report ID
|
||||||
|
report[1] = 21; // Command or action type
|
||||||
|
report[4] = 1; // Action flag
|
||||||
|
|
||||||
|
// Send the feature report to request battery info
|
||||||
|
var success = device.WriteFeatureData(report);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to send feature report.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait a bit for the device to respond
|
||||||
|
System.Threading.Thread.Sleep(90);
|
||||||
|
|
||||||
|
// Get the response feature report
|
||||||
|
device.ReadFeatureData(out byte[] responseData, reportId: 5);
|
||||||
|
if (responseData == null || responseData.Length < 13)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to read feature report.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract battery information from the response
|
||||||
|
byte battery = responseData[9];
|
||||||
|
byte charging = responseData[10];
|
||||||
|
byte fullCharge = responseData[11];
|
||||||
|
byte online = responseData[12];
|
||||||
|
|
||||||
|
return new DeviceStatus(battery, charging, fullCharge, online);
|
||||||
|
}
|
||||||
|
return new DeviceStatus();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error: {ex.Message}");
|
||||||
|
return new DeviceStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct DeviceStatus
|
||||||
|
{
|
||||||
|
public byte Battery;
|
||||||
|
public byte Charging;
|
||||||
|
public byte FullCharge;
|
||||||
|
public byte Online;
|
||||||
|
|
||||||
|
public DeviceStatus(byte battery, byte charging, byte fullCharge, byte online)
|
||||||
|
{
|
||||||
|
Battery = battery;
|
||||||
|
Charging = charging;
|
||||||
|
FullCharge = fullCharge;
|
||||||
|
Online = online;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
SoraV2Utils_Service/SoraV2UtilsService.Designer.cs
generated
Normal file
37
SoraV2Utils_Service/SoraV2UtilsService.Designer.cs
generated
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
partial class SoraV2UtilsService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Erforderliche Designervariable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verwendete Ressourcen bereinigen.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Vom Komponenten-Designer generierter Code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Erforderliche Methode für die Designerunterstützung.
|
||||||
|
/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
components = new System.ComponentModel.Container();
|
||||||
|
this.ServiceName = "SoraV2Utils_Service";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
65
SoraV2Utils_Service/SoraV2UtilsService.cs
Normal file
65
SoraV2Utils_Service/SoraV2UtilsService.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using EasyPipes;
|
||||||
|
using IniParser.Model;
|
||||||
|
using SoraV2Tools;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
namespace SoraV2Utils_Service
|
||||||
|
{
|
||||||
|
|
||||||
|
public partial class SoraV2UtilsService : ServiceBase
|
||||||
|
{
|
||||||
|
Timer timer = new Timer();
|
||||||
|
DeviceTracker deviceTracker = new DeviceTracker();
|
||||||
|
|
||||||
|
public SoraV2UtilsService()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnStart(string[] args)
|
||||||
|
{
|
||||||
|
ServiceLogger.Instance.Log("Started SoraV2Utils_Service");
|
||||||
|
Notification.SingleLine("SoraV2 Utils running...");
|
||||||
|
|
||||||
|
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
|
||||||
|
timer.Interval = ServiceSettings.Instance.intervall;
|
||||||
|
timer.Enabled = true;
|
||||||
|
OnElapsedTime(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnStop()
|
||||||
|
{
|
||||||
|
ServiceLogger.Instance.Log("Stopped SoraV2Utils_Service");
|
||||||
|
Notification.SingleLine("SoraV2 Utils stopped...");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnElapsedTime(object source, ElapsedEventArgs e)
|
||||||
|
{
|
||||||
|
var ds = SoraV2Interface.GetDeviceStatus();
|
||||||
|
|
||||||
|
//byte charging = 0;
|
||||||
|
//byte battery = 0;
|
||||||
|
//new ServiceSettings().TryParseConfig<byte>("debug", "charging", ref charging);
|
||||||
|
//new ServiceSettings().TryParseConfig<byte>("debug", "battery", ref battery);
|
||||||
|
//ds.Charging = charging;
|
||||||
|
//ds.Battery = battery;
|
||||||
|
|
||||||
|
deviceTracker.processMouseData(ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDebug()
|
||||||
|
{
|
||||||
|
OnStart(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
136
SoraV2Utils_Service/SoraV2Utils_Service.csproj
Normal file
136
SoraV2Utils_Service/SoraV2Utils_Service.csproj
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{8EAB0571-6603-4ECF-AD49-93C2B8F6C94C}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RootNamespace>SoraV2Utils_Service</RootNamespace>
|
||||||
|
<targetplatformversion>8.0</targetplatformversion>
|
||||||
|
<AssemblyName>SoraV2Utils_Service</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||||
|
<Install>true</Install>
|
||||||
|
<InstallFrom>Disk</InstallFrom>
|
||||||
|
<UpdateEnabled>false</UpdateEnabled>
|
||||||
|
<UpdateMode>Foreground</UpdateMode>
|
||||||
|
<UpdateInterval>7</UpdateInterval>
|
||||||
|
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||||
|
<UpdatePeriodically>false</UpdatePeriodically>
|
||||||
|
<UpdateRequired>false</UpdateRequired>
|
||||||
|
<MapFileExtensions>true</MapFileExtensions>
|
||||||
|
<ApplicationRevision>1</ApplicationRevision>
|
||||||
|
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||||
|
<UseApplicationTrust>false</UseApplicationTrust>
|
||||||
|
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<RuntimeIdentifiers>win</RuntimeIdentifiers>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<targetplatformversion>8.0</targetplatformversion>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<targetplatformversion>8.0</targetplatformversion>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>..\build\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Configuration.Install" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Management" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.ServiceProcess" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Windows.Data" />
|
||||||
|
<Reference Include="Windows.UI" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="ApplicationLauncher.cs" />
|
||||||
|
<Compile Include="DeviceTracker.cs" />
|
||||||
|
<Compile Include="ServiceIPC.cs" />
|
||||||
|
<Compile Include="AgentProcessHandler.cs" />
|
||||||
|
<Compile Include="Notification.cs" />
|
||||||
|
<Compile Include="ServiceInstaller.cs">
|
||||||
|
<SubType>Component</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="ServiceInstaller.Designer.cs">
|
||||||
|
<DependentUpon>ServiceInstaller.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="SoraV2UtilsService.cs">
|
||||||
|
<SubType>Component</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="SoraV2UtilsService.Designer.cs">
|
||||||
|
<DependentUpon>SoraV2UtilsService.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="ServiceLogger.cs" />
|
||||||
|
<Compile Include="ServiceSettings.cs" />
|
||||||
|
<Compile Include="SoraV2Interface.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\Scripts\Install-Service.ps1">
|
||||||
|
<Link>Install-Service.ps1</Link>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="ServiceInstaller.resx">
|
||||||
|
<DependentUpon>ServiceInstaller.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="EasyPipes">
|
||||||
|
<Version>1.3.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="hidlibrary">
|
||||||
|
<Version>3.3.40</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="ini-parser">
|
||||||
|
<Version>2.5.2</Version>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
|
||||||
|
<Visible>False</Visible>
|
||||||
|
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 und x64%29</ProductName>
|
||||||
|
<Install>true</Install>
|
||||||
|
</BootstrapperPackage>
|
||||||
|
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||||
|
<Visible>False</Visible>
|
||||||
|
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||||
|
<Install>false</Install>
|
||||||
|
</BootstrapperPackage>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<PostBuildEvent>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
1738
SoraV2Utils_Setup/SoraV2Utils_Setup.vdproj
Normal file
1738
SoraV2Utils_Setup/SoraV2Utils_Setup.vdproj
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user