[
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Ww][Ii][Nn]32/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Oo]ut/\n[Ll]og/\n[Ll]ogs/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# ASP.NET Scaffolding\nScaffoldingReadMe.txt\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Coverlet is a free, cross platform Code Coverage Tool\ncoverage*.json\ncoverage*.xml\ncoverage*.info\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n\n# Fody - auto-generated XML schema\nFodyWeavers.xsd\n/LKY_OfficeTools/Resource/Json/LKY_OfficeTools_AppInfo.json\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "LKY_OfficeTools/App.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.7.2\"/>\n    </startup>\n</configuration>\n"
  },
  {
    "path": "LKY_OfficeTools/Common/Com_ExeOS.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Com_ExeOS.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Common\n{\n    internal class Com_ExeOS\n    {\n        internal class Run\n        {\n            internal static int Exe(string file_path, string args)\n            {\n                try\n                {\n                    Process p = new Process();\n                    var result = Process(file_path, args, out p, true);     //默认等待完成\n\n                    //是否执行了\n                    if (!result)\n                    {\n                        throw new Exception($\"执行 {file_path} 异常！\");\n                    }\n\n                    return p.ExitCode;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return -920921;\n                }\n            }\n\n            internal static bool Process(string file_path, string args, out Process ProcessInfo, bool WaitForExit)\n            {\n                try\n                {\n                    Console.ForegroundColor = ConsoleColor.Gray;\n\n                    ProcessInfo = new Process();\n                    ProcessInfo.StartInfo.FileName = file_path;             //需要启动的程序名       \n                    ProcessInfo.StartInfo.Arguments = args;                   //启动参数\n\n                    //是否使用操作系统shell启动\n                    ProcessInfo.StartInfo.UseShellExecute = false;\n\n                    //启动\n                    ProcessInfo.Start();\n\n                    //接收返回值\n                    //p.StandardInput.AutoFlush = true;\n\n                    //获取输出信息\n                    //string strOuput = p.StandardOutput.ReadToEnd();\n\n                    //等待程序执行完退出进程\n                    if (WaitForExit)\n                    {\n                        ProcessInfo.WaitForExit();\n                    }\n\n                    return true;\n                }\n                catch (Exception Ex)\n                {\n                    ProcessInfo = null;\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static string Cmd(string args)\n            {\n                try\n                {\n                    Console.ForegroundColor = ConsoleColor.Gray;\n\n                    Process p = new Process();\n                    //设置要启动的应用程序\n                    p.StartInfo.FileName = \"cmd.exe\";\n\n                    //是否使用操作系统shell启动\n                    p.StartInfo.UseShellExecute = false;\n\n                    // 接受来自调用程序的输入信息\n                    p.StartInfo.RedirectStandardInput = true;\n\n                    //输出信息\n                    p.StartInfo.RedirectStandardOutput = true;\n\n                    // 输出错误\n                    p.StartInfo.RedirectStandardError = true;\n\n                    //不显示程序窗口\n                    p.StartInfo.CreateNoWindow = true;\n\n                    //启动程序\n                    p.Start();\n\n                    //向cmd窗口发送输入信息\n                    p.StandardInput.WriteLine(args + \"&exit\");\n\n                    p.StandardInput.AutoFlush = true;\n\n                    //获取输出信息\n                    string strOuput = p.StandardOutput.ReadToEnd();\n\n                    //等待程序执行完退出进程\n                    p.WaitForExit();\n                    p.Close();\n\n                    return strOuput;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return null;\n                }\n            }\n        }\n\n        internal class KillExe\n        {\n            internal enum KillMode\n            {\n                Try_Friendly = 1,\n\n                Only_Friendly = 2,\n\n                Only_Force = 4,\n            }\n\n            internal static bool ByExeName(string exe_name, KillMode kill_mode, bool isWait)\n            {\n                try\n                {\n                    //先判断是否存在进程\n                    if (Info.IsRun(exe_name))\n                    {\n                        Process[] p = Process.GetProcessesByName(exe_name);\n                        foreach (Process now_p in p)\n                        {\n                            ByProcessID(now_p.Id, kill_mode, isWait);\n                        }\n                        return true;\n                    }\n                    else\n                    {\n                        //不存在时，直接返回 true\n                        return true;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static bool ByProcessID(int exe_id, KillMode kill_mode, bool isWait)\n            {\n                try\n                {\n                    //先判断是否存在进程\n                    if (Info.IsRun(exe_id))\n                    {\n                        //进程存在，获取对象\n                        Process now_p = Process.GetProcessById(exe_id);\n\n                        switch (kill_mode)\n                        {\n                            case KillMode.Try_Friendly:\n                                {\n                                    //先尝试友好关闭\n                                    if (!now_p.CloseMainWindow())\n                                    {\n                                        //有好关闭失败时，启用强制结束\n                                        now_p.Kill();\n                                    }\n                                    break;\n                                }\n                            case KillMode.Only_Friendly:\n                                {\n                                    //只友好关闭\n                                    now_p.CloseMainWindow();\n                                    break;\n                                }\n                            case KillMode.Only_Force:\n                                {\n                                    //只强制结束\n                                    now_p.Kill();\n                                    break;\n                                }\n                        }\n\n                        //判断是否等待进程结束\n                        if (isWait)\n                        {\n                            //等待进程被结束\n                            now_p.WaitForExit();\n                        }\n\n                        return true;    //如果不是等待结束进程，中途未出现catch时，也返回true\n                    }\n                    else\n                    {\n                        //不存在时，直接返回 true\n                        return true;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n        }\n\n        internal class Info\n        {\n            internal static bool IsRun(string exe_name)\n            {\n                try\n                {\n                    Process[] p = Process.GetProcesses();\n                    foreach (Process now_p in p)\n                    {\n                        if (now_p.ProcessName.Equals(exe_name, StringComparison.OrdinalIgnoreCase))\n                        {\n                            return true;\n                        }\n                    }\n                    return false;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static bool IsRun(int exe_id)\n            {\n                try\n                {\n                    Process[] p = Process.GetProcesses();\n                    foreach (Process now_p in p)\n                    {\n                        if (now_p.Id == exe_id)\n                        {\n                            return true;\n                        }\n                    }\n                    return false;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static List<Process> GetProcessByTitle(string window_title, bool need_equal = false)\n            {\n                try\n                {\n                    Process[] process_list = Process.GetProcesses();\n\n                    List<Process> result = new List<Process>();\n                    foreach (var now_p in process_list)\n                    {\n                        if (need_equal)\n                        {\n                            //严格相等\n                            if (now_p.MainWindowTitle == window_title)\n                            {\n                                result.Add(now_p);\n                            }\n                        }\n                        else\n                        {\n                            //包含即可\n                            if (now_p.MainWindowTitle.Contains(window_title))\n                            {\n                                result.Add(now_p);\n                            }\n                        }\n                    }\n                    return result;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return null;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Common/Com_FileOS.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Com_FileOS.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Security.Cryptography;\nusing System.Text;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Common\n{\n    internal class Com_FileOS\n    {\n        internal class XML\n        {\n            internal static bool SetValue(string xml_path, string Key_str, string new_Value)\n            {\n                try\n                {\n                    //读取文件\n                    string xml_content = File.ReadAllText(xml_path, Encoding.UTF8);\n\n                    //获得当前值\n                    string current_value = Com_TextOS.GetCenterText(xml_content, $\"{Key_str}=\\\"\", \"\\\"\");\n\n                    //替换值\n                    string xml_new_content = xml_content.Replace(current_value, new_Value);\n\n                    //判断是否替换成功\n                    if (string.IsNullOrEmpty(xml_new_content))\n                    {\n                        //new Log(\"替换失败！\");\n                        return false;\n                    }\n\n                    //写入文件\n                    File.WriteAllText(xml_path, xml_new_content, Encoding.UTF8);\n\n                    return true;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n        }\n\n        internal class ScanFiles\n        {\n            internal List<string> FilesList = new List<string>();\n\n            internal void GetFilesByExtension(string dirPath, string fileType = \"*\", bool isRoot = false)\n            {\n                if (Directory.Exists(dirPath))     //目录存在\n                {\n                    DirectoryInfo folder = new DirectoryInfo(dirPath);\n\n                    //获取当前目录下的文件名\n                    foreach (FileInfo file in folder.GetFiles())\n                    {\n                        //如果没有限制文件后缀名，或者满足了特定后缀名，开始写入List\n                        if (fileType == \"*\" || file.Extension == (fileType))\n                        {\n                            //扫描到的文件添加到列表中\n                            FilesList.Add(file.FullName);\n                        }\n                    }\n\n                    //如果是根目录先排除掉 回收站目录\n                    if (isRoot)\n                    {\n                        foreach (DirectoryInfo dir in folder.GetDirectories())\n                        {\n                            if (dir.FullName.Contains(\"$RECYCLE.BIN\") || dir.FullName.Contains(\"System Volume Information\"))\n                            {\n                                //Console.ForegroundColor = ConsoleColor.Gray;\n                                //new Log(\"跳过: \" + dir.FullName);\n                            }\n                            else\n                            {\n                                //new Log(\"----->: \" + dir.FullName);\n                                GetFilesByExtension(dir.FullName, fileType);\n                            }\n                        }\n                    }\n                    else\n                    {\n                        //遍历下一个子目录\n                        foreach (DirectoryInfo subFolders in folder.GetDirectories())\n                        {\n                            //new Log(subFolders.FullName);\n                            GetFilesByExtension(subFolders.FullName, fileType);\n                        }\n                    }\n                }\n                else\n                {\n                    /*Console.ForegroundColor = ConsoleColor.Gray;\n                    new Log(\"不存在: \" + dirPath);*/\n                }\n            }\n        }\n\n        internal class Covert\n        {\n            /* - - - - - - - - - - - - - - - - - - - - - - - - \n             * Stream 和 byte[] 之间的转换\n             * - - - - - - - - - - - - - - - - - - - - - - - */\n            internal static byte[] StreamToBytes(Stream stream)\n            {\n                byte[] bytes = new byte[stream.Length];\n                stream.Read(bytes, 0, bytes.Length);\n\n                // 设置当前流的位置为流的开始\n                stream.Seek(0, SeekOrigin.Begin);\n                return bytes;\n            }\n        }\n\n        internal class Write\n        {\n            internal static bool TextToFile(string file_path, string content, bool is_append = true)\n            {\n                try\n                {\n                    // 创建文件所在目录\n                    Directory.CreateDirectory(new FileInfo(file_path).DirectoryName);\n\n                    //非追加模式，并且已经存在目标文件，则先删除原有文件\n                    if (!is_append && File.Exists(file_path))\n                    {\n                        File.Delete(file_path);\n                    }\n\n                    var fs = new FileStream(file_path, FileMode.OpenOrCreate, FileAccess.Write);\n                    var sw = new StreamWriter(fs);\n                    sw.BaseStream.Seek(0, SeekOrigin.End);\n                    sw.WriteLine(content);\n\n                    sw.Flush();\n                    sw.Close();\n                    fs.Close();\n\n                    return true;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static bool FromStream(Stream stream, string to_path)\n            {\n                try\n                {\n                    byte[] Save = Covert.StreamToBytes(stream);\n\n                    //创建文件所在目录\n                    Directory.CreateDirectory(new FileInfo(to_path).DirectoryName);\n\n                    FileStream fsObj = new FileStream(to_path, FileMode.Create);\n                    fsObj.Write(Save, 0, Save.Length);\n                    fsObj.Close();\n\n                    return true;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n        }\n\n        internal class Info\n        {\n            internal static string GetHash(string file_path)\n            {\n                try\n                {\n                    //文件不存在，返回null\n                    if (!File.Exists(file_path))\n                    {\n                        return null;\n                    }\n\n                    //为了防止文件被占用，无法校验，这里先拷贝一份副本，校验副本的值\n                    string file_copied = DateTime.Now.ToFileTime().ToString() + \".hash\";\n                    File.Copy(file_path, file_copied, true);\n                    if (!File.Exists(file_copied))\n                    {\n                        return null;        //副本文件拷贝失败，返回null\n                    }\n\n                    //var hash = SHA256.Create();\n                    //var hash = MD5.Create();\n                    var hash = SHA1.Create();\n                    var stream = new FileStream(file_copied, FileMode.Open);\n                    byte[] hashByte = hash.ComputeHash(stream);\n                    stream.Close();\n\n                    //校验完成，删除副本\n                    if (File.Exists(file_copied))\n                    {\n                        File.Delete(file_copied);\n                    }\n\n                    return BitConverter.ToString(hashByte).Replace(\"-\", \"\");\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return null;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Common/Com_InstallerOS.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Com_InstallerOS.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing WindowsInstaller;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Common\n{\n    internal class Com_InstallerOS\n    {\n        internal enum MsiInfoType\n        {\n            ProductName,\n\n            ProductCode,\n\n            ProductVersion\n        }\n\n        internal static string GetProductInfo(string msi_path, MsiInfoType msi_info)\n        {\n            try\n            {\n                Type oType = Type.GetTypeFromProgID(\"WindowsInstaller.Installer\");\n                if (oType == null)\n                {\n                    return null;\n                }\n\n                Installer inst = Activator.CreateInstance(oType) as Installer;\n                if (inst == null)\n                {\n                    return null;\n                }\n\n                Database DB = inst.OpenDatabase(msi_path, MsiOpenDatabaseMode.msiOpenDatabaseModeReadOnly);\n                if (DB == null)\n                {\n                    return null;\n                }\n\n                //生成要查询的内容\n                string str = $\" SELECT * FROM Property WHERE Property = '{msi_info}' \";\n\n                View thisView = DB.OpenView(str);\n                if (thisView == null)\n                {\n                    return null;\n                }\n\n                thisView.Execute();\n\n                Record thisRecord = thisView.Fetch();\n                if (thisRecord == null)\n                {\n                    return null;\n                }\n\n                string result = thisRecord.get_StringData(2);\n\n                return result;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Common/Com_NetworkOS.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Com_NetworkOS.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.InteropServices;\nusing System.Text.RegularExpressions;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Common\n{\n    internal class Com_NetworkOS\n    {\n        internal class Check\n        {\n            //导入判断网络是否连接的 .dll\n            [DllImport(\"wininet.dll\", EntryPoint = \"InternetGetConnectedState\")]\n            //判断网络状况的方法,返回值true为连接，false为未连接\n            private extern static bool InternetGetConnectedState(out int conState, int reder);\n\n            internal static bool IsConnected\n            {\n                get\n                {\n                    return InternetGetConnectedState(out int n, 0);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Common/Com_PrivilegeOS.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Com_PrivilegeOS.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing System.IO;\nusing System.Security.Principal;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\nusing static LKY_OfficeTools.Lib.Lib_AppMessage;\nusing static LKY_OfficeTools.Lib.Lib_AppState;\n\nnamespace LKY_OfficeTools.Common\n{\n    internal class Com_PrivilegeOS\n    {\n        internal static bool IsRunByAdmin()\n        {\n            WindowsIdentity identity = WindowsIdentity.GetCurrent();\n            WindowsPrincipal principal = new WindowsPrincipal(identity);\n            return principal.IsInRole(WindowsBuiltInRole.Administrator);\n        }\n\n        internal static bool CanWriteProgramDataDir()\n        {\n            try\n            {\n                string time_sn = $\"{DateTime.UtcNow.Hour}{DateTime.UtcNow.Minute}{DateTime.UtcNow.Second}{DateTime.UtcNow.Millisecond}\";\n                string test_file_dir = AppPath.Documents.Temp + $\"\\\\{time_sn}\";\n                string test_file_path = test_file_dir + \"\\\\time_sn.tmp\";\n                var isSuccess = Com_FileOS.Write.TextToFile(test_file_path, \"test\", false);\n\n                //删除测试的文件和目录\n                if (Directory.Exists(test_file_dir))\n                {\n                    Directory.Delete(test_file_dir, true);\n                }\n\n                return isSuccess;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return false;\n            }\n        }\n\n        internal static void PrivilegeAttention()\n        {\n            new Log($\"\\n------> 正在进行 {AppAttribute.AppName} 权限检查 ...\", ConsoleColor.DarkCyan);\n\n            //提权检验，非提权，激活会出问题\n            if (!IsRunByAdmin())\n            {\n                new Log($\"     × 权限错误，请以管理员身份运行此文件！\", ConsoleColor.DarkRed);\n\n                Current_StageType = ProcessStage.Finish_Fail;     //设置为失败模式\n\n                //退出提示\n                KeyMsg.Quit(-1);\n            }\n\n            //ProgramData目录权限检查\n            if (!CanWriteProgramDataDir())\n            {\n                //ProgramData目录缺少权限，已改用“我的文档”目录。服务维护流程已伴随关闭！\n                Must_Use_PersonalDir = true;\n                new Log($\"      * 已采用备用权限策略！\", ConsoleColor.DarkMagenta);\n            }\n\n            new Log($\"     √ 已通过 {AppAttribute.AppName} 权限检查。\", ConsoleColor.DarkGreen);\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Common/Com_ServiceOS.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Com_ServiceOS.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing System.ServiceProcess;\nusing System.Threading;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Common\n{\n    internal class Com_ServiceOS\n    {\n        internal class Query\n        {\n            internal static int RunState(string serv_name)\n            {\n                try\n                {\n                    using (var control = new ServiceController(serv_name))\n                    {\n                        return (int)control.Status;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return 0;\n                }\n            }\n\n            internal static bool IsCreated(string serv_name)\n            {\n                try\n                {\n                    if (GetService(serv_name) != null)\n                    {\n                        //服务不为空，服务存在\n                        return true;\n                    }\n                    else\n                    {\n                        //服务为空，服务不存在\n                        return false;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static ServiceController GetService(string serv_name)\n            {\n                try\n                {\n                    ServiceController service_info = null;                                   //即将获得的服务对象\n                    ServiceController[] services_list = ServiceController.GetServices();     //获得所有服务\n                    foreach (var now_service in services_list)                               //遍历搜索服务\n                    {\n                        if (now_service.ServiceName.ToLower() == serv_name.ToLower())        //全部取小写，防止判断麻烦\n                        {\n                            service_info = now_service;\n                            break;\n                        }\n                    }\n\n                    return service_info;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return null;\n                }\n            }\n\n            internal static bool CompareBinPath(string serv_name, string compare_path)\n            {\n                try\n                {\n                    //服务未创建，不相等\n                    if (!IsCreated(serv_name))\n                    {\n                        return false;\n                    }\n\n                    string cmd_query = $\"sc qc {serv_name}\";\n                    string query_result = Com_ExeOS.Run.Cmd(cmd_query);\n\n                    //返回值为空，不相等\n                    if (string.IsNullOrEmpty(query_result))\n                    {\n                        return false;\n                    }\n\n                    if (query_result.Replace(@\"\\\\\", @\"\\\").Contains(compare_path.Replace(@\"\\\\\", @\"\\\")))  //替换两个斜杠，为单斜杠之后在比对   \n                    {\n                        //包含指定路径（含命令行）\n                        return true;\n                    }\n                    else\n                    {\n                        //不包含指定路径（含命令行）\n                        return false;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static bool CompareDescription(string serv_name, string compare_description)\n            {\n                try\n                {\n                    //服务未创建，不相等\n                    if (!IsCreated(serv_name))\n                    {\n                        return false;\n                    }\n\n                    string cmd_query = $\"sc qdescription {serv_name}\";\n                    string query_result = Com_ExeOS.Run.Cmd(cmd_query);\n\n                    //返回值为空，不相等\n                    if (string.IsNullOrEmpty(query_result))\n                    {\n                        return false;\n                    }\n\n                    if (query_result.Contains(compare_description))\n                    {\n                        //包含描述\n                        return true;\n                    }\n                    else\n                    {\n                        //不包含描述\n                        return false;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n        }\n\n        internal class Action\n        {\n            internal static bool Start(string serv_name)\n            {\n                try\n                {\n                    //未创建服务，不启动！\n                    if (!Query.IsCreated(serv_name))\n                    {\n                        throw new Exception($\"启动服务 {serv_name} 时失败。未找到该服务！\");\n                    }\n\n                    //已安装服务，开始启动\n                    using (var control = new ServiceController(serv_name))\n                    {\n                        //没有处于运行状态的服务，才运行。\n                        if (control.Status != ServiceControllerStatus.Running)\n                        {\n                            control.Start();\n                        }\n\n                        //判断状态，是否是开启了\n                        int wait_time = 0;                          //等待时间总计\n                        while (wait_time <= (10 * 1000))            //最多等待10秒\n                        {\n                            //获取服务状态\n                            if (Query.RunState(serv_name) == (int)ServiceControllerStatus.Running)\n                            {\n                                //已经处于运行状态\n                                return true;\n                            }\n                            else\n                            {\n                                //不是运行状态\n                                Thread.Sleep(1000);      //延迟 1s 后，再度查询\n                                wait_time += 1000;       //追加等待时间\n                                continue;\n                            }\n                        }\n\n                        //如果在轮询期间，没有返回true，那么最终返回false\n                        return false;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static bool Stop(string serv_name)\n            {\n                try\n                {\n                    //未创建服务，不能停止！\n                    if (!Query.IsCreated(serv_name))\n                    {\n                        throw new Exception($\"停止服务 {serv_name} 时失败。未找到该服务！\");\n                    }\n\n                    //已安装服务，开始停止\n                    using (var control = new ServiceController(serv_name))\n                    {\n                        //只要不是停止状态，就发送停止指令\n                        if (control.Status != ServiceControllerStatus.Stopped)\n                        {\n                            control.Stop();\n                        }\n\n                        //判断状态，是否是停止了\n                        int wait_time = 0;                          //等待时间总计\n                        while (wait_time <= (10 * 1000))            //最多等待10秒\n                        {\n                            //获取服务状态\n                            if (Query.RunState(serv_name) == (int)ServiceControllerStatus.Stopped)\n                            {\n                                //已经处于停止状态\n                                return true;\n                            }\n                            else\n                            {\n                                //不是停止状态\n                                Thread.Sleep(1000);      //延迟 1s 后，再度查询\n                                wait_time += 1000;       //追加等待时间\n                                continue;\n                            }\n                        }\n\n                        //如果在轮询期间，没有返回true，那么最终返回false\n                        return false;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static bool Restart(string serv_name)\n            {\n                try\n                {\n                    //未创建服务，不能停止！\n                    if (!Query.IsCreated(serv_name))\n                    {\n                        throw new Exception($\"重启服务 {serv_name} 时失败。未找到该服务！\");\n                    }\n\n                    //已安装服务，开始重启\n                    if (Stop(serv_name))\n                    {\n                        if (Start(serv_name))\n                        {\n                            return true;        //当且仅当停止成功，开启成功，返回true。除此之外，均为false\n                        }\n                    }\n\n                    return false;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n        }\n\n        internal class Config\n        {\n            internal static bool Create(string serv_name, string serv_runpath, string serv_displayname, string serv_description = null)\n            {\n                try\n                {\n                    if (Query.IsCreated(serv_name))\n                    {\n                        //已经创建服务，直接返回真\n                        return true;\n                    }\n                    else\n                    {\n                        //未安装，开始安装\n                        string cmd_install = $\"sc create \\\"{serv_name}\\\" binPath=\\\"{serv_runpath}\\\" start=auto DisplayName=\\\"{serv_displayname}\\\"\";\n                        var install_result = Com_ExeOS.Run.Cmd(cmd_install);\n\n                        //非空判断，若返回值为空，则为假\n                        if (string.IsNullOrEmpty(install_result))\n                        {\n                            throw new Exception($\"创建服务 {serv_name} 时，返回值为空！\");\n                        }\n\n                        //判断是否安装成功\n                        if (install_result.Contains(\"成功\") || install_result.ToLower().Contains(\"success\"))\n                        {\n                            //描述信息不为空，配置描述信息\n                            if (!string.IsNullOrEmpty(serv_description))\n                            {\n                                Modify.Description(serv_name, serv_description);\n                            }\n\n                            //无论修改描述信息是否成功，均返回成功\n                            return true;\n                        }\n                        else\n                        {\n                            return false;\n                        }\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static bool Delete(string serv_name)\n            {\n                if (Query.IsCreated(serv_name))\n                {\n                    //已经创建服务，才能删除\n\n                    //先停止服务\n                    if (!Action.Stop(serv_name))\n                    {\n                        throw new Exception($\"服务 {serv_name} 因无法停止，导致卸载失败！\");\n                    }\n\n                    string cmd_del = $\"sc delete \\\"{serv_name}\\\"\";\n                    var del_result = Com_ExeOS.Run.Cmd(cmd_del);\n\n                    //非空判断，若返回值为空，则为假\n                    if (string.IsNullOrEmpty(del_result))\n                    {\n                        throw new Exception($\"删除服务 {serv_name} 时，返回值为空！\");\n                    }\n\n                    //判断是否删除成功\n                    if (del_result.Contains(\"成功\") || del_result.ToLower().Contains(\"success\"))\n                    {\n                        return true;\n                    }\n                    else\n                    {\n                        throw new Exception($\"删除服务 {serv_name} 失败！\");\n                    }\n                }\n                else\n                {\n                    throw new Exception($\"没有找到名称为 {serv_name} 的服务，无法删除该服务！\");\n                }\n            }\n\n            internal class Modify\n            {\n                internal static bool BinPath(string serv_name, string serv_binpath)\n                {\n                    try\n                    {\n                        if (Query.IsCreated(serv_name))\n                        {\n                            //已经创建服务，才进行修改\n                            string cmd_modify_binpath = $\"sc config \\\"{serv_name}\\\" binPath=\\\"{serv_binpath}\\\"\";\n                            var modify_result = Com_ExeOS.Run.Cmd(cmd_modify_binpath);\n\n                            //非空判断，若返回值为空，则为假\n                            if (string.IsNullOrEmpty(modify_result))\n                            {\n                                throw new Exception($\"执行修改 {serv_name} binPath 参数时，返回值为空！\");\n                            }\n\n                            //判断是否修改 binPath 成功\n                            if (modify_result.Contains(\"成功\") || modify_result.ToLower().Contains(\"success\"))\n                            {\n                                return true;\n                            }\n                            else\n                            {\n                                throw new Exception($\"修改服务 {serv_name} 的 binPath 信息为 {serv_binpath} 失败！\");\n                            }\n                        }\n                        else\n                        {\n                            throw new Exception($\"尝试修改服务 {serv_name} 的 binPath 信息为 {serv_binpath}，但该服务未安装！\");\n                        }\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(Ex.ToString());\n                        return false;\n                    }\n                }\n\n                internal static bool DisplayName(string serv_name, string serv_displayname)\n                {\n                    try\n                    {\n                        if (Query.IsCreated(serv_name))\n                        {\n                            //已经创建服务，才进行修改\n                            string cmd_modify_displayname = $\"sc config \\\"{serv_name}\\\" DisplayName=\\\"{serv_displayname}\\\"\";\n                            var modify_result = Com_ExeOS.Run.Cmd(cmd_modify_displayname);\n\n                            //非空判断，若返回值为空，则为假\n                            if (string.IsNullOrEmpty(modify_result))\n                            {\n                                throw new Exception($\"执行修改 {serv_name} DisplayName 参数时，返回值为空！\");\n                            }\n\n                            //判断是否修改 DisplayName 成功\n                            if (modify_result.Contains(\"成功\") || modify_result.ToLower().Contains(\"success\"))\n                            {\n                                return true;\n                            }\n                            else\n                            {\n                                throw new Exception($\"修改服务 {serv_name} 的 DisplayName 信息为 {serv_displayname} 失败！\");\n                            }\n                        }\n                        else\n                        {\n                            throw new Exception($\"尝试修改服务 {serv_name} 的 DisplayName 信息为 {serv_displayname}，但该服务未安装！\");\n                        }\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(Ex.ToString());\n                        return false;\n                    }\n                }\n\n                internal static bool Description(string serv_name, string serv_description)\n                {\n                    try\n                    {\n                        if (Query.IsCreated(serv_name))\n                        {\n                            //已经创建服务，才进行修改\n                            string cmd_modify_desc = $\"sc description \\\"{serv_name}\\\" \\\"{serv_description}\\\"\";\n                            var modify_result = Com_ExeOS.Run.Cmd(cmd_modify_desc);\n\n                            //非空判断，若返回值为空，则为假\n                            if (string.IsNullOrEmpty(modify_result))\n                            {\n                                throw new Exception($\"执行修改 {serv_name} 描述信息时，返回值为空！\");\n                            }\n\n                            //判断是否修改描述成功\n                            if (modify_result.Contains(\"成功\") || modify_result.ToLower().Contains(\"success\"))\n                            {\n                                return true;\n                            }\n                            else\n                            {\n                                throw new Exception($\"修改服务 {serv_name} 的描述信息为 {serv_description} 失败！\");\n                            }\n                        }\n                        else\n                        {\n                            throw new Exception($\"尝试修改服务 {serv_name} 的描述信息为 {serv_description}，但该服务未安装！\");\n                        }\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(Ex.ToString());\n                        return false;\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Common/Com_SystemOS.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Com_SystemOS.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing Microsoft.Win32;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Common\n{\n    internal class Com_SystemOS\n    {\n        internal class OSVersion\n        {\n            internal enum OSType\n            {\n                LowVersion,\n                WinXP,\n                WinVista,\n                Win7,       //支持.NET 4.8\n                Win8_1,     //支持.NET 4.8\n                Win10,      //1607开始，支持.NET 4.8\n                Win11,\n                UnKnow\n            }\n\n            internal static readonly IDictionary<string, string> WinPublishType = new Dictionary<string, string>\n                {\n                    //win10版本号\n                    { \"10240\", \"1507\" },\n                    { \"10586\", \"1511\" },\n                    { \"14393\", \"1607\" },        //.NET 4.8从此版本开始支持\n                    { \"15063\", \"1703\" },\n                    { \"16299\", \"1709\" },\n                    { \"17134\", \"1803\" },\n                    { \"17763\", \"1809\" },\n                    { \"18362\", \"1903\" },\n                    { \"18363\", \"1909\" },\n                    { \"19041\", \"2004\" },\n                    { \"19042\", \"20H2\" },\n                    { \"19043\", \"21H1\" },\n                    { \"19044\", \"21H2\" },\n                    { \"19045\", \"22H2\" },\n\n                    //win11版本号\n                    { \"22000\", \"21H2\" },\n                    { \"22621\", \"22H2\" },\n\n                    //{ \"1111111111111111\", \"LTSB\" },\n                    //{ \"1111111111111111\", \"LTSC\" },\n                    //{ \"1111111111111111\", \"ARM\" },\n                };\n\n            internal static OSType GetPublishType()\n            {\n                try\n                {\n                    Version ver = Environment.OSVersion.Version;\n\n                    if (ver.Major < 5)\n                    {\n                        return OSType.LowVersion;\n                    }\n                    else if (ver.Major == 5 && ver.Minor == 1)\n                    {\n                        return OSType.WinXP;\n                    }\n                    else if (ver.Major == 6 && ver.Minor == 0)\n                    {\n                        return OSType.WinVista;\n                    }\n                    else if (ver.Major == 6 && ver.Minor == 1)\n                    {\n                        return OSType.Win7;\n                    }\n                    else if (ver.Major == 6 && ver.Minor == 2)\n                    {\n                        return OSType.Win8_1;\n                    }\n                    else if (ver.Major == 10 && ver.Minor == 0)     //正确获取win10版本号，需要在exe里面加入app.manifest\n                    {\n                        //检查注册表，因为win10和11的主版本号都为10，只能用buildID来判断了\n                        string curr_ver = Register.Read.ValueBySystem(RegistryHive.LocalMachine, @\"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\", \"CurrentBuild\");\n\n                        if (!string.IsNullOrEmpty(curr_ver) && int.Parse(curr_ver) < 22000)       //Win11目前内部版本号\n                        {\n                            return OSType.Win10;\n                        }\n                        else\n                        {\n                            return OSType.Win11;\n                        }\n                    }\n                    else\n                    {\n                        return OSType.UnKnow;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return OSType.UnKnow;\n                }\n            }\n\n            internal static string GetBuildNumber(bool isCoreVersion = true)\n            {\n                try\n                {\n                    //检查注册表\n                    string curr_mode = Register.Read.ValueBySystem(RegistryHive.LocalMachine, @\"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\", \"CurrentBuild\");\n\n                    //为空返回未知\n                    if (string.IsNullOrEmpty(curr_mode))\n                    {\n                        return \"unknow\";\n                    }\n\n                    //判断返回的内容\n                    if (isCoreVersion)      //返回内部版本号\n                    {\n                        return curr_mode;\n                    }\n                    else                    //返回发行版本\n                    {\n                        return WinPublishType[curr_mode];\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return \"error!\";\n                }\n            }\n        }\n\n        internal class Register\n        {\n            internal class Read\n            {\n                internal static string Value(RegistryHive reg_root, RegistryView reg_view, string path, string key)\n                {\n                    try\n                    {\n                        RegistryKey HK_Root = RegistryKey.OpenBaseKey(reg_root, reg_view);\n\n                        RegistryKey path_reg = HK_Root.OpenSubKey(path);    //先获取路径\n\n                        if (path_reg == null)\n                        {\n                            //找不到注册表路径\n                            return null;\n                        }\n                        else\n                        {\n                            object value = path_reg.GetValue(key);\n                            if (value != null)      //必须先判断不为null，否则会抛出异常\n                            {\n                                //一切正常\n                                return value.ToString();\n                            }\n                            else\n                            {\n                                //Key不存在或值为空\n                                return null;\n                            }\n                        }\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(Ex.ToString());\n                        return null;\n                    }\n                }\n\n                internal static string ValueBySystem(RegistryHive reg_root, string path, string key)\n                {\n                    try\n                    {\n                        if (Environment.Is64BitOperatingSystem)\n                        {\n                            //x64系统，访问x64注册表\n                            return Value(reg_root, RegistryView.Registry64, path, key);\n                        }\n                        else\n                        {\n                            //x32系统，访问x32注册表\n                            return Value(reg_root, RegistryView.Registry32, path, key);\n                        }\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(Ex.ToString());\n                        return null;\n                    }\n                }\n\n                internal static List<string> AllValues(RegistryHive reg_root, string path, string key)\n                {\n                    try\n                    {\n                        List<string> result = new List<string>();\n\n                        //获取x32的结构值\n                        string value_x32 = Value(reg_root, RegistryView.Registry32, path, key);\n                        if (!string.IsNullOrWhiteSpace(value_x32))\n                        {\n                            result.Add(value_x32);\n                        }\n\n                        //获取x64的结构值（仅在当前计算机为 x64 系统时，才获取）\n                        if (Environment.Is64BitOperatingSystem)\n                        {\n                            string value_x64 = Value(reg_root, RegistryView.Registry64, path, key);\n                            if (!string.IsNullOrWhiteSpace(value_x64))\n                            {\n                                result.Add(value_x64);\n                            }\n                        }\n\n                        return result;\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(Ex.ToString());\n                        return null;\n                    }\n                }\n            }\n\n            internal static bool ExistItem(RegistryHive reg_root, RegistryView reg_view, string item_path)\n            {\n                try\n                {\n                    RegistryKey reg = RegistryKey.OpenBaseKey(reg_root, reg_view);\n                    var result = reg.OpenSubKey(item_path);\n\n                    if (result != null)\n                    {\n                        return true;\n                    }\n\n                    result.Close();\n\n                    return false;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static bool DeleteItem(RegistryHive reg_root, RegistryView reg_view, string path, string item)\n            {\n                try\n                {\n                    RegistryKey HK_Root = RegistryKey.OpenBaseKey(reg_root, reg_view);\n                    RegistryKey path_reg = HK_Root.OpenSubKey(path, true);              //先获取路径，启动可写模式\n\n                    //找不到注册表路径，默认已删除，返回true\n                    if (path_reg == null)\n                    {\n                        return true;\n                    }\n\n                    path_reg.DeleteSubKeyTree(item, false);\n\n                    return true;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n\n            internal static bool ExportReg(string reg_path, string save_to)\n            {\n                try\n                {\n                    //先删除已经存在的文件\n                    if (File.Exists(save_to))\n                    {\n                        File.Delete(save_to);\n                    }\n\n                    //开始导出\n                    Directory.CreateDirectory(new FileInfo(save_to).DirectoryName);         //先创建文件所在目录\n                    Process.Start(\"regedit\", $\" /E \\\"{save_to}\\\" \\\"{reg_path}\\\"\").WaitForExit();\n\n                    if (File.Exists(save_to))\n                    {\n                        return true;\n                    }\n                    else\n                    {\n                        return false;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "LKY_OfficeTools/Common/Com_TextOS.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Com_TextOS.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Common\n{\n    internal class Com_TextOS\n    {\n        internal static string GetCenterText(string str, string strLeft, string strRight)\n        {\n            try\n            {\n                if (str == null || str.Length == 0) return \"\";\n                if (strLeft != \"\")\n                {\n                    int indexLeft = str.IndexOf(strLeft);//左边字符串位置\n                    if (indexLeft < 0) return \"\";\n                    indexLeft = indexLeft + strLeft.Length;//左边字符串长度\n                    if (strRight != \"\")\n                    {\n                        int indexRight = str.IndexOf(strRight, indexLeft);//右边字符串位置\n                        if (indexRight < 0) return \"\";\n                        return str.Substring(indexLeft, indexRight - indexLeft);//indexRight - indexLeft是取中间字符串长度\n                    }\n                    else return str.Substring(indexLeft, str.Length - indexLeft);//取字符串右边\n                }\n                else\n                {\n                    //取字符串左边\n                    int indexRight = str.IndexOf(strRight);\n                    if (indexRight <= 0) return \"\";\n                    else return str.Substring(0, indexRight);\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return null;\n            }\n        }\n\n        internal static int GetStringTimes(string str, string scan_str)\n        {\n            try\n            {\n                int index = 0;\n                int count = 0;\n                while ((index = str.IndexOf(scan_str, index)) != -1)\n                {\n                    count++;\n                    index += scan_str.Length;\n                }\n                return count;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return 0;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Common/Com_Timer.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Com_Timer.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing System.Threading;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Common\n{\n    internal class Com_Timer\n    {\n        internal class Countdown_Timer\n        {\n            internal int Remaining_Time\n            { get; set; }\n\n            internal bool isRun\n            { get; set; }\n\n            internal void Start(int total_time)\n            {\n                try\n                {\n                    //此处不放入线程，否则使用isRun判断是否在运行时，会导致isRun状态无法判断\n                    Remaining_Time = total_time;\n                    isRun = true;\n\n                    Thread time_t = new Thread(() =>\n                    {\n                        Update();\n\n                        //迭代完成后，自动停止运行\n                        isRun = false;\n                    });\n\n                    //time_t.SetApartmentState(ApartmentState.STA);\n                    time_t.Start();\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return;\n                }\n            }\n\n            void Update()\n            {\n                try\n                {\n                    //倒计时不为0，且给出运行指令\n                    if (Remaining_Time > 0 & isRun)\n                    {\n                        Thread.Sleep(1000);\n                        Remaining_Time--;\n                        Update();  //轮询继续\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return;\n                }\n            }\n\n            internal void Pause()\n            {\n                try\n                {\n                    isRun = false;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return;\n                }\n            }\n\n            internal void Continue()\n            {\n                try\n                {\n                    if (Remaining_Time != 0)\n                    {\n                        isRun = true;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return;\n                }\n            }\n\n            internal void Stop()\n            {\n                try\n                {\n                    Pause();\n                    Remaining_Time = 0;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Common/Com_WebOS.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Com_WebOS.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing System.Net;\nusing System.Text;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Common\n{\n    internal class Com_WebOS\n    {\n        internal static string Visit_WebClient(string url, Encoding encoding = null)\n        {\n            try\n            {\n                if (encoding == null)\n                {\n                    encoding = Encoding.UTF8;\n                }\n\n                using (WebClient WC = new WebClient())\n                {\n                    WC.Headers.Add(\"User-Agent\", \"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36\");\n                    WC.Credentials = CredentialCache.DefaultCredentials;//获取或设置用于向Internet资源的请求进行身份验证的网络凭据\n                    Byte[] pageData = WC.DownloadData(url); //从指定网站下载数据\n                    return encoding.GetString(pageData);\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/LKY_OfficeTools.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{6D00F508-106A-42D1-BE0F-048762434E69}</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <RootNamespace>LKY_OfficeTools</RootNamespace>\n    <AssemblyName>LKY_OfficeTools</AssemblyName>\n    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n    <Deterministic>true</Deterministic>\n    <TargetFrameworkProfile />\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>bin\\Debug\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>bin\\Release\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n    <Prefer32Bit>false</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Debug|x86'\">\n    <PlatformTarget>x86</PlatformTarget>\n    <OutputPath>bin\\x86\\Debug\\</OutputPath>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)' == 'Release|x86'\">\n    <PlatformTarget>x86</PlatformTarget>\n    <OutputPath>bin\\x86\\Release\\</OutputPath>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationIcon>logo.ico</ApplicationIcon>\n  </PropertyGroup>\n  <PropertyGroup>\n    <TargetZone>Custom</TargetZone>\n  </PropertyGroup>\n  <PropertyGroup>\n    <GenerateManifests>false</GenerateManifests>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationManifest>Properties\\app.manifest</ApplicationManifest>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"PresentationCore\" />\n    <Reference Include=\"PresentationFramework\" />\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Drawing\" />\n    <Reference Include=\"System.IO.Compression\" />\n    <Reference Include=\"System.IO.Compression.FileSystem\" />\n    <Reference Include=\"System.ServiceProcess\" />\n    <Reference Include=\"System.Windows.Forms\" />\n    <Reference Include=\"System.Xaml\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Net.Http\" />\n    <Reference Include=\"System.Xml\" />\n    <Reference Include=\"WindowsBase\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Common\\Com_ExeOS.cs\" />\n    <Compile Include=\"Common\\Com_FileOS.cs\" />\n    <Compile Include=\"Common\\Com_InstallerOS.cs\" />\n    <Compile Include=\"Common\\Com_NetworkOS.cs\" />\n    <Compile Include=\"Common\\Com_PrivilegeOS.cs\" />\n    <Compile Include=\"Common\\Com_ServiceOS.cs\" />\n    <Compile Include=\"Common\\Com_SystemOS.cs\" />\n    <Compile Include=\"Common\\Com_TextOS.cs\" />\n    <Compile Include=\"Common\\Com_Timer.cs\" />\n    <Compile Include=\"Common\\Com_WebOS.cs\" />\n    <Compile Include=\"Lib\\Lib_AppClosing.cs\" />\n    <Compile Include=\"Lib\\Lib_AppCommand.cs\" />\n    <Compile Include=\"Lib\\Lib_AppState.cs\" />\n    <Compile Include=\"Lib\\Lib_AppInfo.cs\" />\n    <Compile Include=\"Lib\\Lib_AppMessage.cs\" />\n    <Compile Include=\"Lib\\Lib_AppSdk.cs\" />\n    <Compile Include=\"Lib\\Lib_AppServiceHub.cs\">\n      <SubType>Component</SubType>\n    </Compile>\n    <Compile Include=\"Lib\\Lib_AppServiceHub.Designer.cs\">\n      <DependentUpon>Lib_AppServiceHub.cs</DependentUpon>\n    </Compile>\n    <Compile Include=\"Lib\\Lib_AppServiceConfig.cs\" />\n    <Compile Include=\"Lib\\Lib_AppSignCert.cs\" />\n    <Compile Include=\"Lib\\Lib_OfficeActivate.cs\" />\n    <Compile Include=\"Lib\\Lib_OfficeDownload.cs\" />\n    <Compile Include=\"Lib\\Lib_OfficeInfo.cs\" />\n    <Compile Include=\"Lib\\Lib_OfficeInstall.cs\" />\n    <Compile Include=\"Lib\\Lib_AppLog.cs\" />\n    <Compile Include=\"Lib\\Lib_AppUpdate.cs\" />\n    <Compile Include=\"Lib\\Lib_OfficeClean.cs\" />\n    <Compile Include=\"Lib\\Lib_OfficeProcess.cs\" />\n    <Compile Include=\"OfficeTools.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n    <Compile Include=\"Lib\\Lib_Aria2c.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n    <None Include=\"Resource\\Json\\LKY_OfficeTools_AppInfo.json\" />\n    <None Include=\"Resource\\Json\\OfficeChannels.txt\" />\n    <None Include=\"Properties\\app.manifest\" />\n    <EmbeddedResource Include=\"Resource\\Office_Processes.list\" />\n    <EmbeddedResource Include=\"Resource\\SDK\\Activate.lotp\" />\n    <EmbeddedResource Include=\"Resource\\SDK\\Aria2c.lotp\" />\n    <EmbeddedResource Include=\"Resource\\SDK\\ODT.lotp\" />\n    <EmbeddedResource Include=\"Resource\\SDK\\SaRA.lotp\" />\n    <EmbeddedResource Include=\"Resource\\PublisherCert.cer\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"logo.ico\" />\n    <EmbeddedResource Include=\"Resource\\SDK\\SDK_Processes.list\" />\n  </ItemGroup>\n  <ItemGroup>\n    <COMReference Include=\"WindowsInstaller\">\n      <Guid>{000C1092-0000-0000-C000-000000000046}</Guid>\n      <VersionMajor>1</VersionMajor>\n      <VersionMinor>0</VersionMinor>\n      <Lcid>1033</Lcid>\n      <WrapperTool>tlbimp</WrapperTool>\n      <Isolated>False</Isolated>\n      <EmbedInteropTypes>True</EmbedInteropTypes>\n    </COMReference>\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n</Project>"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppClosing.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_AppClosing.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing System.Runtime.InteropServices;\nusing static LKY_OfficeTools.Common.Com_ExeOS.KillExe;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\nusing static LKY_OfficeTools.Lib.Lib_AppState;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_AppClosing\n    {\n        internal class CloseWindow\n        {\n            internal delegate bool ControlCtrlDelegate(int CtrlType);\n\n            [DllImport(\"kernel32.dll\")]\n            internal static extern bool SetConsoleCtrlHandler(ControlCtrlDelegate HandlerRoutine, bool Add);\n\n            internal static ControlCtrlDelegate newDelegate = new ControlCtrlDelegate(HandlerRoutine);\n\n            internal static bool HandlerRoutine(int CtrlType)\n            {\n                //只要程序不是已完成（无论成功与否），手动关闭，就会显示文字并打点\n                if (Current_StageType != ProcessStage.Finish_Fail && Current_StageType != ProcessStage.Finish_Success)\n                {\n                    new Log($\"\\n     × 正在尝试 取消部署，请稍候 ...\", ConsoleColor.DarkRed);\n\n                    Current_StageType = ProcessStage.Interrupt;         //设置中断状态。非完成情况下，关闭，属于 中断部署 状态，此处用于停止 下载 office 进程\n                    Console.ForegroundColor = ConsoleColor.Gray;        //重置颜色，如果第一次失败，颜色还是可以正常的\n\n                    //结束残存进程\n                    Lib_AppSdk.KillAllSdkProcess(KillMode.Try_Friendly);\n\n                    /*Pointing(ProcessStage.Interrupt); 暂停中断打点 */   //中断 点位。下载时触发该逻辑，打点会失败。\n                }\n                else\n                {\n                    //完成状态时，清理文件夹\n                    Lib_AppSdk.Clean();     //清理SDK目录\n                }\n\n                return false;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppCommand.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_AppCommand.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\nusing static LKY_OfficeTools.Lib.Lib_AppState;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_AppCommand\n    {\n        internal static ArgsFlag AppCommandFlag { get; set; }\n\n        internal Lib_AppCommand(string[] args)\n        {\n            try\n            {\n                //非空判断\n                if (args != null && args.Length > 0)\n                {\n                    foreach (var now_arg in args)\n                    {\n                        //单独arg非空判断\n                        if (!string.IsNullOrWhiteSpace(now_arg))\n                        {\n                            //对于 /passive 模式，自动跳过所有需要确认的步骤\n                            if (now_arg.Contains(\"/passive\"))\n                            {\n                                SkipAllConfirm();\n                                Current_RunMode = RunMode.Passive;  //设置为被动模式\n                                break;\n                            }\n                            //服务模式。该模式 与 passive 是互斥的。\n                            else if (now_arg.Contains(\"/service\"))\n                            {\n                                Current_RunMode = RunMode.Service;  //设置为服务模式\n\n                                //以服务模式运行\n                                Lib_AppServiceConfig.Start();       //在Hub类库设置中，service模式将被添加passive标记。\n\n                                //找到服务模式，不再执行后续的\n                                Environment.Exit(-100);\n                            }\n                            //非服务、非被动模式\n                            else\n                            {\n                                Current_RunMode = RunMode.Manual;   //设置为手动模式\n\n                                if (now_arg.ToLower().Contains(\"/none_welcome_confirm\"))\n                                {\n                                    AppCommandFlag |= ArgsFlag.None_Welcome_Confirm;\n                                }\n\n                                if (now_arg.ToLower().Contains(\"/ignore_manual_update_msg\"))\n                                {\n                                    AppCommandFlag |= ArgsFlag.Ignore_Manual_Update_Msg;\n                                }\n\n                                if (now_arg.ToLower().Contains(\"/auto_remove_conflict_office\"))\n                                {\n                                    AppCommandFlag |= ArgsFlag.Auto_Remove_Conflict_Office;\n                                }\n\n                                if (now_arg.ToLower().Contains(\"/none_finish_presskey\"))\n                                {\n                                    AppCommandFlag |= ArgsFlag.None_Finish_PressKey;\n                                }\n\n                                continue;\n                            }\n                        }\n                    }\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return;\n            }\n        }\n\n        [Flags]\n        internal enum ArgsFlag\n        {\n            None_Welcome_Confirm = 2,\n\n            Ignore_Manual_Update_Msg = 4,\n\n            Auto_Remove_Conflict_Office = 8,\n\n            None_Finish_PressKey = 16,\n        }\n\n        private static bool SkipAllConfirm()\n        {\n            try\n            {\n                AppCommandFlag |=\n                    ArgsFlag.None_Welcome_Confirm |\n                    ArgsFlag.Auto_Remove_Conflict_Office |\n                    ArgsFlag.Ignore_Manual_Update_Msg |\n                    ArgsFlag.None_Finish_PressKey;\n                return true;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return false;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppInfo.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_AppInfo.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing System;\nusing System.Diagnostics;\nusing System.IO;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_AppInfo\n    {\n        internal class AppAttribute\n        {\n            internal const string AppName = \"LKY Office Tools\";\n\n            internal const string AppName_Short = \"LOT\";\n\n            internal static readonly string ServiceName = ServiceDisplayName.Replace(\" \", \"\");\n\n            internal const string ServiceDisplayName = AppName + \" Service\";\n\n            internal const string AppFilename = \"LKY_OfficeTools.exe\";\n\n            internal const string AppVersion = \"1.3.0.223\";\n\n            internal const string Developer = \"LiuKaiyuan\";\n        }\n\n        internal class AppJson\n        {\n            private static string AppJsonInfo = null;\n\n            internal static string Info\n            {\n                get\n                {\n                    try\n                    {\n                        //内部变量非空时，直接返回其值\n                        if (!string.IsNullOrEmpty(AppJsonInfo))\n                        {\n                            return AppJsonInfo;\n                        }\n\n#if (!DEBUG)\n                        //release模式地址\n                        string json_url = $\"https://gitee.com/OdysseusYuan/LKY_OfficeTools/releases/download/AppInfo/LKY_OfficeTools_AppInfo.json\";\n#else\n                        //debug模式地址\n                        string json_url = $\"https://gitee.com/OdysseusYuan/LOT_OnlyTest/releases/download/AppInfo_Test/test.json\";\n#endif\n\n                        //获取 Json 信息\n                        string result = Com_WebOS.Visit_WebClient(json_url);\n\n                        AppJsonInfo = result;\n                        return AppJsonInfo;\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(Ex.ToString());\n                        //new Log($\"     × 获取 AppJson 信息失败！\", ConsoleColor.DarkRed);\n                        new Log($\"获取 AppJson 信息失败！\");\n                        return null;\n                    }\n                }\n            }\n        }\n\n        internal class AppDevelop\n        {\n            internal const string NameSpace_Top = \"LKY_OfficeTools\";\n        }\n\n        internal class AppPath\n        {\n            internal static string ExecuteDir\n            {\n                get\n                {\n                    //使用 BaseDirectory 获取路径时，结尾会多一个 \\ 符号，为了替换之，先在结果处增加一个斜杠，变成 \\\\ 结尾，然后替换掉这个 双斜杠\n                    return (AppDomain.CurrentDomain.BaseDirectory + @\"\\\").Replace(@\"\\\\\", @\"\");\n                }\n            }\n\n            internal static string Executer\n            {\n                get\n                {\n                    return new FileInfo(Process.GetCurrentProcess().MainModule.FileName).FullName;\n                }\n            }\n\n            internal class Documents\n            {\n                internal static string Documents_Root\n                {\n                    get\n                    {\n                        if (Lib_AppState.Must_Use_PersonalDir)\n                        {\n                            return $\"{Environment.GetFolderPath(Environment.SpecialFolder.Personal)}\\\\{AppAttribute.AppName}\";            //我的文档\n                        }\n                        else\n                        {\n                            return $\"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}\\\\{AppAttribute.AppName}\";     //ProgramData\n                        }\n                    }\n                }\n\n                internal class Update\n                {\n                    internal static string Update_Root\n                    {\n                        get\n                        {\n                            return $\"{Documents_Root}\\\\Update\";\n                        }\n                    }\n\n                    internal static string UpdateTrash\n                    {\n                        get\n                        {\n                            if (Path.GetPathRoot(Executer) == Path.GetPathRoot(Documents_Root))\n                            {\n                                //相同盘符，使用默认回收站目录\n                                return $\"{Update_Root}\\\\Trash\";\n                            }\n                            else\n                            {\n                                //不同盘符，使用子目录\n                                return $\"{ExecuteDir}\\\\Trash\";\n                            }\n                        }\n                    }\n                }\n\n                internal class Services\n                {\n                    internal static string Services_Root\n                    {\n                        get\n                        {\n                            return $\"{Documents_Root}\\\\Services\";\n                        }\n                    }\n\n                    internal static string ServicesTrash\n                    {\n                        get\n                        {\n                            return $\"{Services_Root}\\\\Trash\";\n                        }\n                    }\n\n                    internal static string ServiceAutorun\n                    {\n                        get\n                        {\n                            return $\"{Services_Root}\\\\Autorun\";\n                        }\n                    }\n\n                    internal static string ServiceAutorun_Exe\n                    {\n                        get\n                        {\n                            return $\"{ServiceAutorun}\\\\{AppAttribute.AppFilename}\";\n                        }\n                    }\n\n                    internal static string PassiveProcessInfo\n                    {\n                        get\n                        {\n                            return $\"{Services_Root}\\\\PassiveProcess.info\";\n                        }\n                    }\n                }\n\n                internal static string Logs\n                {\n                    get\n                    {\n                        return $\"{Documents_Root}\\\\Logs\";\n                    }\n                }\n\n                internal static string Temp\n                {\n                    get\n                    {\n                        return $\"{Documents_Root}\\\\Temp\";\n                    }\n                }\n\n                internal class SDKs\n                {\n                    internal static string SDKs_Root\n                    {\n                        get\n                        {\n                            return $\"{Documents_Root}\\\\SDKs\";\n                        }\n                    }\n\n                    internal static string Activate\n                    {\n                        get\n                        {\n                            return $\"{SDKs_Root}\\\\Activate\";\n                        }\n                    }\n\n                    internal static string Activate_OSPP\n                    {\n                        get\n                        {\n                            return $\"{Activate}\\\\OSPP.VBS\";\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppLog.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_AppLog.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing System;\nusing System.IO;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath;\nusing static LKY_OfficeTools.Lib.Lib_OfficeInfo.OfficeLocalInfo;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_AppLog\n    {\n        internal class Log\n        {\n            internal static string log_filepath = null;\n\n            internal static string reg_install_error { get; set; }\n\n            internal static string log_info { get; set; }\n\n            /*\n                                                internal static List<string> error_screen_path = new List<string>();\n            */\n\n            internal enum Output_Type\n            {\n                Display,\n\n                Write,\n\n                Display_Write\n            }\n\n            internal Log(string str, ConsoleColor str_color, Output_Type output_type = Output_Type.Display_Write)\n            {\n                try\n                {\n                    //判断是否需要显示文字\n                    if (output_type == Output_Type.Display || output_type == Output_Type.Display_Write)\n                    {\n                        Console.ForegroundColor = str_color;\n                        Console.WriteLine(str);\n\n                        //输出后恢复颜色\n                        Console.ForegroundColor = ConsoleColor.Gray;\n                    }\n\n                    //需要输出日志文件时，进行判断\n                    if (output_type == Output_Type.Write || output_type == Output_Type.Display_Write)\n                    {\n                        string datatime_format = DateTime.Now.ToString(\"s\").Replace(\"T\", \"_\").Replace(\":\", \"-\");\n\n\n                        //服务模式，写出到日志文件\n                        if (Lib_AppState.Current_RunMode == Lib_AppState.RunMode.Service)\n                        {\n                            //为空时，创建日志路径\n                            if (string.IsNullOrEmpty(log_filepath))\n                            {\n                                string file_name = \"service_\" + datatime_format + \".log\";\n                                log_filepath = $\"{Documents.Logs}\\\\{file_name}\";\n                            }\n\n                            //目录不存在时创建目录\n                            Directory.CreateDirectory(new FileInfo(log_filepath).DirectoryName);\n\n                            //文件不存在时创建&写入\n                            File.AppendAllText(log_filepath, $\"{datatime_format}, {str}\\n\");\n                        }\n\n\n                        //将日志记录在内存中\n                        string now_log = $\"{datatime_format}, {str.Replace(\"\\n\", \"\")}\";\n                        if (str.Contains(\"×\"))\n                        {\n                            now_log = $\"<font color=red><b>{now_log}</b></font>\";          //有错误标红、加粗\n                        }\n                        else if (str.Contains(\"Exception\"))\n                        {\n                            now_log = $\"<font color=\\\"#ff4c00\\\">{now_log}</font>\";       //抛出异常用橙红色\n                        }\n\n                        log_info += now_log + \"<br />\";\n\n                        /*\n                        //出现错误时，增加附加信息\n                        if (str.Contains(\"×\"))\n                        {\n                            string err_filename = datatime_format + \".png\";\n                            err_filename = $\"{Lib_AppInfo.Path.Dir_Log}\\\\{err_filename}\";\n                            if (Com_SystemOS.Screen.CaptureToSave(err_filename))\n                            {\n                                error_screen_path.Add(err_filename);\n                            }\n                        }\n                        */\n                    }\n                }\n                catch\n                {\n                    return;\n                }\n            }\n\n            internal Log(string err_str)\n            {\n                try\n                {\n                    //整合格式\n                    string msg = $\"---------- [Error Log: BEGIN] ----------\\n{err_str}\\n---------- [END] ----------\";\n\n                    //非服务模式，替换换行符\n                    if (Lib_AppState.Current_RunMode != Lib_AppState.RunMode.Service)\n                    {\n                        msg = msg.Replace(\"\\n\", \"<br />\");\n                    }\n\n                    //输出日志\n                    new Log(msg, ConsoleColor.Gray, Output_Type.Write);\n                }\n                catch\n                {\n                    return;\n                }\n            }\n\n            internal Log(InstallState install_error)\n            {\n                try\n                {\n                    //安装出错后，会记录系统目前office注册表情况\n                    //导出注册表 Office 信息\n                    string office_reg_path = @\"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Office\";\n\n                    //依据不同的安装错误生成不同的注册表文件名\n                    string reg_filename = \"error\";\n\n                    //包含未安装标记\n                    if (install_error == InstallState.None)\n                    {\n                        reg_filename += \"_none\";\n                    }\n\n                    //包含不同版本标记\n                    if (install_error == InstallState.Diff)\n                    {\n                        reg_filename += \"_diff\";\n                    }\n\n                    //包含多版本标记\n                    if (install_error == InstallState.Multi)\n                    {\n                        reg_filename += \"_multi\";\n                    }\n\n                    //包含安装正确标记\n                    if (install_error == InstallState.Correct)\n                    {\n                        reg_filename += \"_correct\";\n                    }\n\n                    //合成最终注册表路径\n                    reg_install_error = Documents.Logs + $@\"\\{reg_filename}.reg\";\n\n                    //生成注册表信息\n                    Com_SystemOS.Register.ExportReg(office_reg_path, reg_install_error);\n\n                    return;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return;\n                }\n            }\n\n            internal static bool Clean()\n            {\n                try\n                {\n                    /*\n                    //清理日志\n                    if (log_filepath != null)\n                    {\n                        try\n                        {\n                            File.Delete(log_filepath);\n                        }\n                        catch (Exception Ex)\n                        {\n                            new Log(Ex.ToString());\n                        }\n                    }\n                    */\n\n                    /*\n                    //清理错误截屏\n                    if (error_screen_path != null)\n                    {\n                        foreach (var now_file in error_screen_path)\n                        {\n                            try\n                            {\n                                File.Delete(now_file);\n                            }\n                            catch (Exception Ex)\n                            {\n                                new Log(Ex.ToString());\n                            }\n                        }\n                    }\n                    */\n\n                    //清理整个Log文件夹\n                    if (Directory.Exists(Documents.Logs))\n                    {\n                        try\n                        {\n                            Directory.Delete(Documents.Logs, true);\n                        }\n                        catch (Exception Ex)\n                        {\n                            new Log(Ex.ToString());\n                        }\n                    }\n\n                    return true;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppMessage.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_AppMessage.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing System.Threading;\nusing static LKY_OfficeTools.Common.Com_Timer;\nusing static LKY_OfficeTools.Lib.Lib_AppCommand;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_AppMessage\n    {\n        internal class KeyMsg\n        {\n            internal static void Quit(int exit_code)\n            {\n                //清理SDK缓存\n                Lib_AppSdk.Clean();\n\n                //退出机制\n                if (!AppCommandFlag.HasFlag(ArgsFlag.None_Finish_PressKey))\n                {\n                    //不包含“结束无需确认”命令行，需要人工按键结束\n                    Console.ForegroundColor = ConsoleColor.Gray;\n                    Console.Write(\"\\n请按 任意键 退出 ...\");\n                    Console.ReadKey();\n\n                    Environment.Exit(exit_code);\n                }\n            }\n\n            internal static bool Confirm(string msg_str = null)\n            {\n                Console.ForegroundColor = ConsoleColor.Gray;\n\n                //判断是否为空\n                if (string.IsNullOrWhiteSpace(msg_str))\n                {\n                    //msg为空，直接展示回车键继续，并且前面不空格\n                    msg_str = $\"\\n请按 回车键（Enter）继续 ...\";\n                }\n                else\n                {\n                    //msg不为空，一般在运行过程中的确认，有空格，并且增加逗号\n                    msg_str = $\"        {msg_str}，请按 回车键（Enter）继续 ...\";\n                }\n\n                Console.Write(msg_str);     //提示信息\n                new Log(msg_str, ConsoleColor.Gray, Log.Output_Type.Write);     //写入日志\n\n                if (Console.ReadKey().Key == ConsoleKey.Enter)\n                {\n                    Console.WriteLine();    //增加一个空白行\n                    return true;\n                }\n                else\n                {\n                    Console.WriteLine();    //增加一个空白行\n                    return false;\n                }\n            }\n\n            internal static void DoByTime(string msg_str, int countdown_time)\n            {\n                Console.ForegroundColor = ConsoleColor.Gray;\n                Console.WriteLine();    //插入一个空白行\n\n                //设置一个倒计时组件\n                Countdown_Timer timer = new Countdown_Timer();\n                timer.Start(countdown_time);\n\n                //循环输出\n                while (timer.isRun)\n                {\n                    string msg = $\"{msg_str}将在 {timer.Remaining_Time} 秒内开始 ...\";\n                    new Log(msg, ConsoleColor.Gray, Log.Output_Type.Write);     //写入日志\n                    Thread.Sleep(100);\n\n                    //输出消息倒计时\n                    Console.Write($\"\\r{msg}\");\n                }\n\n                //倒计时结束后，告知开始\n                Console.Write($\"\\r{msg_str}启动 ...                \");\n\n                //完成等待\n                Console.WriteLine();    //增加一个空白行\n                return;\n            }\n\n            internal static bool Choose(string todo_thing)\n            {\n                new Log($\"\\n     ★ {todo_thing}\", ConsoleColor.Gray);\n\n                Console.ForegroundColor = ConsoleColor.Gray;\n                string msg = $\"        按 回车键（Enter）确认执行上述操作，按 其它键 跳过此环节 ...\";\n\n                Console.Write(msg);\n                new Log(msg, ConsoleColor.Gray, Log.Output_Type.Write);     //写入日志\n\n                if (Console.ReadKey().Key == ConsoleKey.Enter)\n                {\n                    Console.WriteLine();    //增加一个空白行\n                    return true;\n                }\n                else\n                {\n                    Console.WriteLine();    //增加一个空白行\n                    return false;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppSdk.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_AppSdk.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.IO.Compression;\nusing System.Linq;\nusing System.Reflection;\nusing System.Threading;\nusing static LKY_OfficeTools.Common.Com_ExeOS;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\nusing static LKY_OfficeTools.Lib.Lib_AppMessage;\nusing static LKY_OfficeTools.Lib.Lib_AppState;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_AppSdk\n    {\n        enum SdkPackage\n        {\n            Activate,\n\n            Aria2c,\n\n            ODT,\n\n            SaRA,\n        }\n\n        private static Dictionary<SdkPackage, Stream> SdkPackageDic\n        {\n            get\n            {\n                try\n                {\n                    //初始字典\n                    Dictionary<SdkPackage, Stream> res_dic = new Dictionary<SdkPackage, Stream>();\n\n                    var asm = Assembly.GetExecutingAssembly();\n\n                    res_dic[SdkPackage.Activate] = asm.GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时，此值也需要调整 */\n                                                    + $\".Resource.SDK.{SdkPackage.Activate}.lotp\");\n                    res_dic[SdkPackage.Aria2c] = asm.GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时，此值也需要调整 */\n                                                    + $\".Resource.SDK.{SdkPackage.Aria2c}.lotp\");\n                    res_dic[SdkPackage.ODT] = asm.GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时，此值也需要调整 */\n                                                    + $\".Resource.SDK.{SdkPackage.ODT}.lotp\");\n                    res_dic[SdkPackage.SaRA] = asm.GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时，此值也需要调整 */\n                                                    + $\".Resource.SDK.{SdkPackage.SaRA}.lotp\");\n                    return res_dic;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return null;\n                }\n            }\n        }\n\n        private static Dictionary<SdkPackage, string> SdkPkgPath\n        {\n            get\n            {\n                try\n                {\n                    //初始字典\n                    Dictionary<SdkPackage, string> extra_dic = new Dictionary<SdkPackage, string>();\n\n                    extra_dic[SdkPackage.Activate] = Documents.SDKs.SDKs_Root + $\"\\\\{SdkPackage.Activate}.lotp\";\n                    extra_dic[SdkPackage.Aria2c] = Documents.SDKs.SDKs_Root + $\"\\\\{SdkPackage.Aria2c}.lotp\";\n                    extra_dic[SdkPackage.ODT] = Documents.SDKs.SDKs_Root + $\"\\\\{SdkPackage.ODT}.lotp\";\n                    extra_dic[SdkPackage.SaRA] = Documents.SDKs.SDKs_Root + $\"\\\\{SdkPackage.SaRA}.lotp\";\n\n                    return extra_dic;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return null;\n                }\n            }\n        }\n\n        internal static bool Initial()\n        {\n            try\n            {\n                new Log($\"\\n------> 正在配置 {AppAttribute.AppName} 基础组件 ...\", ConsoleColor.DarkCyan);\n                Thread.Sleep(1000);      //短暂间隔，提升下体验\n                new Log($\"     >> 此过程会持续些许时间，这取决于您的电脑硬件配置，请耐心等待 ...\", ConsoleColor.DarkYellow);\n\n                //初始化前先清理SDK目录，防止因为文件已经存在，引发解压的catch\n                Clean();\n\n                //释放文件\n                if (SdkPackageDic == null)\n                {\n                    throw new Exception(\"读取 SDK 内存资源失败！\");\n                }\n                foreach (var now_pkg in SdkPackageDic)\n                {\n                    string pkg_path = SdkPkgPath[now_pkg.Key];\n                    bool isToDisk = Com_FileOS.Write.FromStream(now_pkg.Value, pkg_path);\n                    if (!isToDisk)\n                    {\n                        //写出异常，抛出\n                        throw new IOException($\"无法写出 SDK 文件 {pkg_path} 到硬盘！\");\n                    }\n\n                    //无异常，解压包\n                    ZipFile.ExtractToDirectory(pkg_path, Documents.SDKs.SDKs_Root + $@\"\\{now_pkg.Key}\");\n                }\n\n                new Log($\"     √ 已完成 {AppAttribute.AppName} 组件配置。\", ConsoleColor.DarkGreen);\n\n                return true;\n            }\n            catch (IOException IO_Ex)\n            {\n                new Log(IO_Ex.ToString());\n\n                //读写出现意外\n                new Log($\"     × 配置 {AppAttribute.AppName} 基础组件失败。请确保您的系统盘具备足够的可写空间！\", ConsoleColor.DarkRed);\n\n                //清理SDK缓存\n                Clean();\n\n                Current_StageType = ProcessStage.Finish_Fail;     //设置为失败模式\n\n                //退出提示\n                KeyMsg.Quit(-2);\n\n                return false;\n            }\n            catch (UnauthorizedAccessException Au_Ex)\n            {\n                new Log(Au_Ex.ToString());\n\n                //不具备读写权限\n                new Log($\"     × 配置 {AppAttribute.AppName} 基础组件失败。请确保您具备对 {Documents.SDKs.SDKs_Root} 目录的写入权限！\", ConsoleColor.DarkRed);\n\n                //清理SDK缓存\n                Clean();\n\n                Current_StageType = ProcessStage.Finish_Fail;     //设置为失败模式\n\n                //退出提示\n                KeyMsg.Quit(-2);\n\n                return false;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n\n                //其它未知问题\n                new Log($\"     × 配置 {AppAttribute.AppName} 基础组件失败，无法继续。请重新下载本软件或联系开发者！\", ConsoleColor.DarkRed);\n\n                //清理SDK缓存\n                Clean();\n\n                Current_StageType = ProcessStage.Finish_Fail;     //设置为失败模式\n\n                //退出提示\n                KeyMsg.Quit(-10);\n\n                return false;\n            }\n            finally\n            {\n                //清理 SDK pkg文件\n                var extra_sdk_list = SdkPkgPath.Values.ToList();\n                foreach (var now_path in extra_sdk_list)\n                {\n                    if (File.Exists(now_path))\n                    {\n                        try\n                        {\n                            File.Delete(now_path);\n                        }\n                        catch (Exception Ex)\n                        {\n                            new Log(Ex.ToString());\n                            new Log($\"Exception: 清理 SDK 的 {now_path} 文件失败！\");\n                        }\n                    }\n                }\n            }\n        }\n\n        internal static bool Clean()\n        {\n            try\n            {\n                //目录不存在时，自动返回为真\n                if (!Directory.Exists(Documents.SDKs.SDKs_Root))\n                {\n                    return true;\n                }\n\n                Directory.Delete(Documents.SDKs.SDKs_Root, true);\n\n                return true;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                new Log($\"清理 SDK 目录失败！\");\n                return false;\n            }\n        }\n\n        internal static List<string> Process_List\n        {\n            get\n            {\n                try\n                {\n                    Stream sdk_processes_res = Assembly.GetExecutingAssembly().\n                    GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时，此值也需要调整 */\n                    + \".Resource.SDK.SDK_Processes.list\");\n                    StreamReader sdk_processes_sr = new StreamReader(sdk_processes_res);\n                    string sdk_processes = sdk_processes_sr.ReadToEnd();\n                    if (!string.IsNullOrWhiteSpace(sdk_processes))\n                    {\n                        List<string> sdk_processes_list = new List<string>();\n                        string[] p_info = sdk_processes.Replace(\"\\r\", \"\").Split('\\n');      //分割出进程数组\n                        if (p_info != null && p_info.Length > 0)\n                        {\n                            foreach (var now_process in p_info)\n                            {\n                                sdk_processes_list.Add(now_process);\n                            }\n\n                            return sdk_processes_list;\n                        }\n                    }\n                    return null;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return null;\n                }\n            }\n        }\n\n        internal static bool KillAllSdkProcess(KillExe.KillMode mode)\n        {\n            try\n            {\n                //轮询结束每个进程（不等待）\n                foreach (var now_p in Process_List)\n                {\n                    KillExe.ByExeName(now_p, mode, false);\n                }\n\n                return true;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return false;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppServiceConfig.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_AppServiceConfig.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing System;\nusing System.IO;\nusing System.ServiceProcess;\nusing static LKY_OfficeTools.Common.Com_ServiceOS;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\nusing static LKY_OfficeTools.Lib.Lib_AppMessage;\nusing static LKY_OfficeTools.Lib.Lib_AppState;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_AppServiceConfig\n    {\n        internal static void Setup()\n        {\n            try\n            {\n                //无论手动、被动模式，只要安装了服务，就自动更新之\n                if (Com_ServiceOS.Query.IsCreated(AppAttribute.ServiceName))\n                {\n                    AddOrUpdate();\n                }\n                else\n                {\n                    //未安装过。根据模式不同，给出判断\n\n                    //手动模式，提示添加\n                    if (Current_RunMode == RunMode.Manual)\n                    {\n                        //让用户选择是否添加自检服务\n                        if (KeyMsg.Choose(\"为保证 Office 始终处于最新正版，即将添加 Office 自动更新/正版激活 服务。\"))\n                        {\n                            AddOrUpdate();\n                        }\n                        else\n                        {\n                            new Log($\"     × 您已拒绝添加 Office 自动更新/正版激活 服务，若要重新添加，请再次运行本软件！\", ConsoleColor.DarkRed);\n                            return;\n                        }\n                    }\n                    //被动模式，自动安装服务信息\n                    else if (Current_RunMode == RunMode.Passive)\n                    {\n                        AddOrUpdate();\n                    }\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return;\n            }\n        }\n\n        enum Add_Result\n        {\n            Add_Success,\n\n            Add_Fail,\n\n            Add_Error,\n\n            Update_Success,\n\n            Update_Fail,\n\n            Update_Error,\n\n            Unknow,\n        }\n\n        static Add_Result AddOrUpdate()\n        {\n            try\n            {\n                //构建服务基本信息\n                string serv_name = AppAttribute.ServiceName;\n                string serv_execmd = AppPath.Documents.Services.ServiceAutorun_Exe + \" /service\";\n                string serv_displayname = AppAttribute.ServiceDisplayName;\n                string serv_desc = \"用于检测、下载、安装和激活正版 Office 的重要服务。如果此服务被禁用，这台计算机的用户将无法获取 Office 更新，也无法使其处于最新正版激活状态。\";\n\n                //判断是否安装\n                if (Com_ServiceOS.Query.IsCreated(serv_name))\n                {\n                    //已安装服务\n                    new Log($\"\\n------> 正在更新 {AppAttribute.ServiceDisplayName} 服务 ...\", ConsoleColor.DarkCyan);\n\n                    //校验其属性是否一致\n\n                    //修改 exe 运行信息\n                    if (!Com_ServiceOS.Query.CompareBinPath(serv_name, serv_execmd))\n                    {\n                        Com_ServiceOS.Config.Modify.BinPath(serv_name, serv_execmd);\n                    }\n\n                    //修改 DisplayName\n                    var service_info = Com_ServiceOS.Query.GetService(serv_name);      //无需判断是否为空，IsCreated() 已经判断过\n                    if (service_info.DisplayName != serv_displayname)\n                    {\n                        //DisplayName 不同时，修改之\n                        Com_ServiceOS.Config.Modify.DisplayName(serv_name, serv_displayname);\n                    }\n\n                    //修改 描述信息\n                    if (!Com_ServiceOS.Query.CompareDescription(serv_name, serv_desc))\n                    {\n                        Com_ServiceOS.Config.Modify.Description(serv_name, serv_desc);\n                    }\n\n                    //信息修改完成后，需要更新服务对应的文件\n                    string serv_filepath = AppPath.Documents.Services.ServiceAutorun_Exe;\n                    if (File.Exists(serv_filepath))\n                    {\n                        //运行的文件和服务目录下的文件 哈希值 一致时，跳过替换旧版本，否则要升级替换旧版本（常用于更新场景下）\n                        if (Com_FileOS.Info.GetHash(AppPath.Executer) == Com_FileOS.Info.GetHash(serv_filepath))\n                        {\n                            //哈希值一致，说明，服务文件和当前运行的文件是相同的，无需替换\n                            new Log($\"     √ 无需升级服务，已刷新 {AppAttribute.ServiceDisplayName} 信息。\", ConsoleColor.DarkGreen);\n                            return Add_Result.Update_Success;\n                        }\n\n                        //文件哈希值不一样，开始替换升级\n                        /* 替换逻辑\n                         * 1、如果运行路径 = 服务路径，【因为服务配置发生在更新之后，相等时，文件已经是最新的，达到了预期（更新服务文件）的目的。不做任何操作】\n                         * 2、运行路径 != 服务路径，【移动旧文件到trash目录，拷贝运行路径文件到服务路径】\n                         */\n                        if (AppPath.Executer != serv_filepath)      //用户在非服务目录下运行\n                        {\n                            //move方式，解决服务启动状态中，文件无法被替换的问题。（停止服务会导致自身进程被结束）\n                            string dest_filepath = AppPath.Documents.Services.ServicesTrash + $\"\\\\{DateTime.Now.ToFileTime()}.old\";\n                            Directory.CreateDirectory(Path.GetDirectoryName(dest_filepath));       //创建计划移动到的目录\n                            File.Move(serv_filepath, dest_filepath);\n\n                            //复制自身文件到服务目录下\n                            File.Copy(AppPath.Executer, serv_filepath, true);\n                        }\n                    }\n                    else\n                    {\n                        //如果服务安装了，但文件丢失了，此处将自身文件 copy 到服务专用目录\n                        Directory.CreateDirectory(Path.GetDirectoryName(serv_filepath));        //先创建服务目录，否则copy文件会异常\n                        File.Copy(AppPath.Executer, serv_filepath, true);\n                    }\n\n                    new Log($\"     √ 已更新 {AppAttribute.ServiceDisplayName} 服务。\", ConsoleColor.DarkGreen);\n                    return Add_Result.Update_Success;\n                }\n                else\n                {\n                    //未安装服务时，添加服务\n                    new Log($\"\\n------> 正在安装 Office 自动更新/正版激活 服务 ...\", ConsoleColor.DarkCyan);\n\n                    var create_result = Com_ServiceOS.Config.Create(serv_name, serv_execmd, serv_displayname, serv_desc);\n\n                    //判断是否成功安装\n                    if (create_result)\n                    {\n                        Directory.CreateDirectory(Path.GetDirectoryName(AppPath.Documents.Services.ServiceAutorun_Exe));    //先创建服务目录，否则copy文件会异常\n                        File.Copy(AppPath.Executer, AppPath.Documents.Services.ServiceAutorun_Exe, true);                   //将当前文件复制到服务专用文件夹\n\n                        new Log($\"     √ 已安装 Office 自动更新/正版激活 服务。\", ConsoleColor.DarkGreen);\n                        return Add_Result.Add_Success;\n                    }\n                    else\n                    {\n                        new Log($\"     × Office 自动更新/正版激活 服务安装失败！若要添加，请重新运行本软件。\", ConsoleColor.DarkRed);\n                        return Add_Result.Add_Fail;\n                    }\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                new Log($\"     × 因特殊原因，服务配置失败！如有问题，可联系开发者。\", ConsoleColor.DarkRed);\n                return Add_Result.Unknow;\n            }\n        }\n\n        internal static bool Start()\n        {\n            try\n            {\n                //服务没有被创建时，不运行\n                if (!Com_ServiceOS.Query.IsCreated(AppAttribute.ServiceName))\n                {\n                    return false;\n                }\n\n                //启动服务\n                ServiceBase[] ServicesToRun;\n                ServicesToRun = new ServiceBase[]\n                {\n                        new Lib_AppServiceHub()\n                };\n                ServiceBase.Run(ServicesToRun);\n\n                return true;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return false;\n            }\n        }\n\n        internal static bool Stop()\n        {\n            try\n            {\n                //服务存在时，展示停止文字提示\n                if (Com_ServiceOS.Query.IsCreated(AppAttribute.ServiceName))\n                {\n                    new Log($\"\\n------> 正在停止 {AppAttribute.ServiceDisplayName} 服务 ...\", ConsoleColor.DarkCyan);\n\n                    //停止服务\n                    if (Com_ServiceOS.Action.Stop(AppAttribute.ServiceName))\n                    {\n                        new Log($\"     √ 已停止 {AppAttribute.ServiceDisplayName} 服务。\", ConsoleColor.DarkGreen);\n                        return true;\n                    }\n                    else\n                    {\n                        new Log($\"     × 无法停止 {AppAttribute.ServiceDisplayName} 服务。\", ConsoleColor.DarkGreen);\n                        return false;\n                    }\n                }\n                else\n                {\n                    //服务不存在，直接返回真\n                    return true;\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return false;\n            }\n        }\n\n        internal static void RestartSelf()\n        {\n            try\n            {\n                //自身服务名称\n                string serv_name = AppAttribute.ServiceName;\n\n                //未创建服务，不能停止！\n                if (!Query.IsCreated(serv_name))\n                {\n                    throw new Exception($\"重启服务 {serv_name} 时失败。未找到该服务！\");\n                }\n\n                //已安装服务，开始重启\n                string cmd = $\"(net stop {serv_name})&(net start {serv_name})\";\n                string result = Com_ExeOS.Run.Cmd(cmd);\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppServiceHub.Designer.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_AppService.Designer.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nnamespace LKY_OfficeTools.Lib\n{\n    partial class Lib_AppServiceHub\n    {\n                                private System.ComponentModel.IContainer components = null;\n\n                                        protected override void Dispose(bool disposing)\n        {\n            if (disposing && (components != null))\n            {\n                components.Dispose();\n            }\n            base.Dispose(disposing);\n        }\n\n        #region 组件设计器生成的代码\n\n                                        private void InitializeComponent()\n        {\n            components = new System.ComponentModel.Container();\n            this.ServiceName = \"Lib_AppService\";\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppServiceHub.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_AppService.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.ServiceProcess;\nusing static LKY_OfficeTools.Common.Com_FileOS;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Lib\n{\n    partial class Lib_AppServiceHub : ServiceBase\n    {\n        public Lib_AppServiceHub()\n        {\n            InitializeComponent();\n        }\n\n        protected override void OnStart(string[] args)\n        {\n            try\n            {\n                //每次启动服务时，自动删除 Services Trash 目录中的 .old 文件\n                ScanFiles oldFiles = new ScanFiles();\n                oldFiles.GetFilesByExtension(AppPath.Documents.Services.ServicesTrash, \".old\");\n                if (oldFiles.FilesList != null && oldFiles.FilesList.Count > 0)\n                {\n                    foreach (var now_file in oldFiles.FilesList)\n                    {\n                        //使用 try catch 模式。以防异常。\n                        try\n                        {\n                            File.Delete(now_file);\n                        }\n                        catch { }\n                    }\n                }\n\n                //以无人值守的模式，隐式的运行本程序。\n                Process process_info = new Process();\n                Com_ExeOS.Run.Process(AppPath.Executer, \"/passive\", out process_info, false);      //异步运行\n\n                //保存进程信息\n                Directory.CreateDirectory(AppPath.Documents.Services.Services_Root);\n                string info_file = AppPath.Documents.Services.PassiveProcessInfo;\n                File.WriteAllText(info_file, process_info.Id.ToString());\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return;\n            }\n        }\n\n        protected override void OnStop()\n        {\n            try\n            {\n                //服务停止时，如果有正在运行的 passive 进程，立即结束。\n                string info_path = AppPath.Documents.Services.PassiveProcessInfo;\n                if (File.Exists(info_path))\n                {\n                    string info = File.ReadAllText(info_path);\n                    if (!string.IsNullOrWhiteSpace(info))\n                    {\n                        Com_ExeOS.KillExe.ByProcessID(int.Parse(info), Com_ExeOS.KillExe.KillMode.Try_Friendly, true);       //尝试友好的结束进程\n                    }\n\n                    File.Delete(info_path);\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppSignCert.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_AppSignCert.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing System;\nusing System.IO;\nusing System.Reflection;\nusing System.Security.Cryptography.X509Certificates;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_AppSignCert\n    {\n        internal Lib_AppSignCert()\n        {\n            try\n            {\n                if (!AlreadyImported(\"12EA025393C6D19347EFB7C71313A9DD\"))\n                {\n                    string cer_filename = \"PublisherCert.cer\";\n                    string cer_path = Documents.Temp + $\"\\\\{cer_filename}\";\n\n                    //cer文件不存在时，写出到运行目录\n                    if (!File.Exists(cer_path))\n                    {\n                        Assembly assm = Assembly.GetExecutingAssembly();\n                        Stream istr = assm.GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时，词值也需要调整 */ + $\".Resource.{cer_filename}\");\n                        Com_FileOS.Write.FromStream(istr, cer_path);\n                    }\n\n                    //导入证书\n                    ImportCert(cer_path);\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return;\n            }\n        }\n\n        internal static bool AlreadyImported(string serial_number)\n        {\n            try\n            {\n                X509Store store2 = new X509Store(StoreName.Root, StoreLocation.LocalMachine);\n                store2.Open(OpenFlags.MaxAllowed);\n                X509Certificate2Collection certs = store2.Certificates.Find(X509FindType.FindBySerialNumber, serial_number, false);  //用序列号作为检索\n                store2.Close();\n\n                if (certs.Count == 0 || certs[0].NotAfter < DateTime.Now)\n                {\n                    return false;\n                }\n                else\n                {\n                    return true;\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return false;\n            }\n        }\n\n        internal static bool ImportCert(string cert_filepath, string cert_password = null)\n        {\n            try\n            {\n                //根据是否有密码决定导入方式\n                X509Certificate2 certificate = null;\n                if (string.IsNullOrEmpty(cert_password))\n                {\n                    //无密码\n                    certificate = new X509Certificate2(cert_filepath);\n                }\n                else\n                {\n                    //有密码\n                    certificate = new X509Certificate2(cert_filepath, cert_password);\n                }\n\n                certificate.FriendlyName = AppAttribute.Developer + \" DigiCert\";   //设置有友好名字\n\n                X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);\n                store.Open(OpenFlags.ReadWrite);\n                store.Remove(certificate);              //先移除\n                store.Add(certificate);\n                store.Close();\n\n                //安装后删除\n                File.Delete(cert_filepath);\n\n                return true;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return false;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppState.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_AppState.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_AppState\n    {\n        internal enum RunMode\n        {\n            Manual,\n\n            Passive,\n\n            Service\n        }\n\n        internal static RunMode Current_RunMode = RunMode.Manual;\n\n        internal enum ProcessStage\n        {\n            Starting = 1,\n\n            Process = 2,\n\n            Update_Success = 4,\n\n            Update_Fail = 8,\n\n            Interrupt = 16,\n\n            RestartPC = 32,\n\n            Finish_Success = 64,\n\n            Finish_Fail = 128,\n        }\n\n        internal static ProcessStage Current_StageType = ProcessStage.Process;\n\n        internal static bool Must_Use_PersonalDir\n        {\n            get; set;\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_AppUpdate.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_SelfUpdate.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.IO.Compression;\nusing System.Threading;\nusing static LKY_OfficeTools.Common.Com_FileOS;\nusing static LKY_OfficeTools.Lib.Lib_AppCommand;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\nusing static LKY_OfficeTools.Lib.Lib_AppMessage;\nusing static LKY_OfficeTools.Lib.Lib_AppState;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_AppUpdate\n    {\n        internal static string Latest_Version\n        { get; set; }\n\n        internal static string Latest_Url\n        { get; set; }\n\n        internal static bool Check()\n        {\n            try\n            {\n                new Log($\"\\n------> 正在进行 {AppAttribute.AppName} 初始化检查 ...\", ConsoleColor.DarkCyan);\n\n                //当更新完成自重启时，自动删除 Update Trash 目录中的 .old 文件\n                ScanFiles oldFiles = new ScanFiles();\n                oldFiles.GetFilesByExtension(AppPath.Documents.Update.UpdateTrash, \".old\");\n                if (oldFiles.FilesList != null && oldFiles.FilesList.Count > 0)\n                {\n                    foreach (var now_file in oldFiles.FilesList)\n                    {\n                        //使用 try catch 模式。在服务模式下，更新完成后，old 文件可能还会被服务占用，此时跳过该文件，避免占用报错，添加 try 模式。\n                        try\n                        {\n                            File.Delete(now_file);\n                        }\n                        catch (Exception Ex)\n                        {\n                            new Log(Ex.ToString());\n                            new Log($\"Exception: 无法删除 {now_file} 文件！\");\n                        }\n                    }\n                }\n\n                //当Trash目录是 运行目录的子目录 时（Trash运行盘符 != 程序文档的盘符时），需要回收Trash目录，否则体验不好。\n                string trash_dir = AppPath.Documents.Update.UpdateTrash;\n                if (Path.GetPathRoot(trash_dir) != Path.GetPathRoot(AppPath.Documents.Documents_Root))\n                {\n                    try\n                    {\n                        //文件夹存在时删除\n                        if (Directory.Exists(trash_dir))\n                        {\n                            Directory.Delete(trash_dir, true);\n                        }\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(Ex.ToString());\n                        new Log($\"Exception: 无法删除 {AppPath.Documents.Update.UpdateTrash} 目录！\");\n                    }\n                }\n\n                new Log($\"     >> 初始化完成 {new Random().Next(1, 10)}% ...\", ConsoleColor.DarkYellow);\n\n                //截取获得最新版本和下载地址\n                Latest_Version = Com_TextOS.GetCenterText(AppJson.Info, \"\\\"Latest_Version\\\": \\\"\", \"\\\"\");\n                Latest_Url = Com_TextOS.GetCenterText(AppJson.Info, \"\\\"Latest_Version_Update_Url\\\": \\\"\", \"\\\"\");\n\n                new Log($\"     >> 初始化完成 {new Random().Next(11, 30)}% ...\", ConsoleColor.DarkYellow);\n\n                new Log($\"     >> 初始化完成 {new Random().Next(91, 100)}% ...\", ConsoleColor.DarkYellow);\n\n                new Log($\"     √ 已完成 {AppAttribute.AppName} 初始化检查。\", ConsoleColor.DarkGreen);\n\n                string now_ver = AppAttribute.AppVersion;\n                if (new Version(Latest_Version) > new Version(now_ver))\n                {\n                    //发现新版本\n                    new Log($\"\\n------> 正在更新 {AppAttribute.AppName} 至 v{Latest_Version} 版本 ...\", ConsoleColor.DarkCyan);\n\n                    new Log($\"\\n     >> 下载 v{Latest_Version} 更新包中 ...\", ConsoleColor.DarkYellow);\n\n                    //下载文件\n                    string save_to = AppPath.Documents.Update.Update_Root + $\"\\\\v{Latest_Version}.zip\";\n\n                    //下载前先删除旧的文件（禁止续传），否则一旦意外中断，再次启动下载将出现异常\n                    try\n                    {\n                        //删除主体文件\n                        if (File.Exists(save_to))\n                        {\n                            File.Delete(save_to);\n                        }\n\n                        //删除主体对应的描述文件\n                        string des_file = save_to + \".aria2\";\n                        if (File.Exists(des_file))\n                        {\n                            File.Delete(des_file);\n                        }\n                    }\n                    catch\n                    {\n                        //仅用于日志记录\n                        new Log($\"清理冗余更新包文件失败，后续下载可能会出现异常！\");\n                    }\n\n                    //开始下载\n                    int down_result = Lib_Aria2c.DownFile(Latest_Url, save_to);\n\n                    //下载不成功时，抛出\n                    if (down_result != 1)\n                    {\n                        throw new Exception();\n                    }\n\n                    new Log($\"\\n     >> 更新 v{Latest_Version} 文件中 ...\", ConsoleColor.DarkYellow);\n\n                    //解压文件\n                    string extra_to = Path.GetDirectoryName(save_to) + \"\\\\\" + $\"v{Latest_Version}\";\n                    if (Directory.Exists(extra_to))\n                    {\n                        Directory.Delete(extra_to, true);\n                    }\n                    ZipFile.ExtractToDirectory(save_to, extra_to);\n                    File.Delete(save_to);\n\n                    //扫描文件\n                    ScanFiles new_files = new ScanFiles();\n                    new_files.GetFilesByExtension(extra_to);\n                    if (new_files.FilesList == null)\n                    {\n                        throw new Exception();\n                    }\n\n                    //获得自身主程序路径\n                    string self_RunPath = AppPath.Executer;\n\n                    //复制新文件\n                    foreach (var now_file in new_files.FilesList)\n                    {\n                        //获得文件相对路径\n                        string file_relative_path = now_file.Replace(extra_to, \"\\\\\");\n                        //合成移动路径\n                        string move_to = AppPath.ExecuteDir + file_relative_path;\n\n                        //如果新文件在现有路径中存在，则将旧的文件先 move 到 Trash 目录，解决文件被占用的情况\n                        if (File.Exists(move_to))\n                        {\n                            string dest_filepath = AppPath.Documents.Update.UpdateTrash + $\"\\\\{DateTime.Now.ToFileTime()}.old\";\n                            Directory.CreateDirectory(Path.GetDirectoryName(dest_filepath));        //创建目录\n                            File.Move(move_to, dest_filepath);\n                        }\n\n                        //增加目录创建，否则目标文件拷贝将会失败\n                        Directory.CreateDirectory(Path.GetDirectoryName(move_to));                    //目录已经存在时，重复创建，不会引发异常\n\n                        //拷贝、覆盖新文件\n                        File.Copy(now_file, move_to, true);\n                    }\n\n                    //若旧的 exe 文件名和默认 exe 文件名不一致时，将自身 exe 文件 move 到 Trash 目录。\n                    //解决用户修改旧 exe 文件名，复制新文件后，会出现两个 exe 的情况。\n                    if (Path.GetFileName(AppPath.Executer) != AppAttribute.AppFilename)\n                    {\n                        string exe_moveto = AppPath.Documents.Update.UpdateTrash + $\"\\\\{DateTime.Now.ToFileTime()}.old\";\n                        Directory.CreateDirectory(Path.GetDirectoryName(exe_moveto));        //创建目录\n                        File.Move(AppPath.Executer, exe_moveto);\n                    }\n\n                    //更新后，删除更新目录\n                    if (Directory.Exists(extra_to))\n                    {\n                        Directory.Delete(extra_to, true);\n                    }\n\n                    //重启自身完成更新\n                    new Log($\"\\n     √ 已更新至 {AppAttribute.AppName} v{Latest_Version} 版本，程序即将自动重启，请稍候。\", ConsoleColor.DarkGreen);\n\n                    //升级成功打点\n                    Current_StageType = ProcessStage.Update_Success;\n\n                    //延迟稍许\n                    Thread.Sleep(2000);\n\n                    //重启进程\n                    RestartProcess();\n                }\n\n                return true;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                new Log($\"      * 暂时跳过更新检查！\", ConsoleColor.DarkMagenta);\n\n                //没有忽略手动更新的标记时，提示手动升级。\n                if (!AppCommandFlag.HasFlag(ArgsFlag.Ignore_Manual_Update_Msg))\n                {\n                    ManualUpdate();\n                }\n\n                return false;\n            }\n\n        }\n\n        static void ManualUpdate()\n        {\n            try\n            {\n                //当获取Latest_Url失败时，不提示。\n                if (!string.IsNullOrEmpty(Latest_Url))\n                {\n                    if (KeyMsg.Choose($\"自动更新异常，是否手动下载 v{Latest_Version}（最新版）{AppAttribute.AppName} 软件？\"))\n                    {\n                        //确认后，打开浏览器下载。否则跳过更新使用旧版本。\n                        new Log($\"     >> 等待 系统默认浏览器 运行 ...\", ConsoleColor.DarkYellow);\n                        var p = Process.Start(Latest_Url);\n\n                        //等待启动\n                        while (string.IsNullOrEmpty(p.ProcessName)) { }\n\n                        new Log($\"     √ 已启动 您的默认浏览器（{p.ProcessName}），以下载 {AppAttribute.AppName} v{Latest_Version}。\", ConsoleColor.DarkGreen);\n\n                        //自动升级失败\n                        Current_StageType = ProcessStage.Update_Fail;     //设置为失败\n\n                        KeyMsg.Quit(-20);\n                    }\n                    else\n                    {\n                        new Log($\"     × 您已拒绝下载最新版 {AppAttribute.AppName}。如需下载最新版，您可重新运行本软件。\", ConsoleColor.DarkRed);\n                        Thread.Sleep(1000);     //延迟1s\n                    }\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                new Log($\"Exception: 手动更新异常！\");\n                return;\n            }\n        }\n\n        static bool RestartProcess()\n        {\n            try\n            {\n                /* \n                 * 【设计背景】\n                 *  1、自动更新在服务模式下的 confirm 卡进程问题。\n                 *  2、服务自行升级时，用户手动停止服务，无法关闭更新后文件再运行的进程ID。\n                 * \n                 * 【情况判断】\n                 *  一、已安装服务\n                 *  （1）服务文件存在：\n                 *      A. 自定义位置：服务文件没有被更新，即使执行 passive 指令，也是显示的运行，故，进程重启\n                 *      B. 服务位置：文件已被更新，且通常为 passive 模式，需要继承 passive 指令，否则会卡在 confirm 环节，故，重启服务\n                 *  （2）服务文件不存在：服务无法执行 passvive，因此不会卡在 confirm 环节，故，进程重启\n                 *  二、没有安装服务：只能手动模式，即使执行 passive 指令，也是显示的运行，不会卡进程。故，进程重启\n                 * \n                 * 【结论】\n                 *  综上，当且仅当 服务已安装 & 在服务目录运行（服务文件存在） 时，更新后重启服务，除此之外，全部 只重启进程\n                */\n\n                if (//重启服务条件\n                    Com_ServiceOS.Query.IsCreated(AppAttribute.ServiceName) &&              //服务已被创建\n                    AppPath.Executer == AppPath.Documents.Services.ServiceAutorun_Exe       //当前运行位置为服务文件位置（满足该条件，服务文件天然存在）\n                    )\n                {\n                    //满足服务重启的条件，重启服务，运行新的exe\n                    Lib_AppServiceConfig.RestartSelf();                                     //重启服务后，软件已经是最新版exe，并带着 passive 指令运行\n                }\n                else\n                {\n                    //未安装服务 OR 在非服务文件路径运行，运行进程\n                    /*\n                     * 当用户手动修改了升级旧exe的名字，这时 Executer 的名字 和 更新后文件exe的名字不一致。使用 Executer 重启，会导致重启进程失败。\n                     * 因此，应按照默认文件名，重启进程。这要求后续每个升级版本的默认主执行文件 exe 文件名，不得发生改变。\n                     */\n\n                    //定义路径\n                    string run_path = AppPath.ExecuteDir + $\"\\\\{AppAttribute.AppFilename}\";\n\n                    //启动实例\n                    Process p = new Process();\n                    p.StartInfo.FileName = run_path;                         //需要启动的程序名      \n                    p.StartInfo.Arguments = \"/none_welcome_confirm\";         //启动参数\n                    p.Start();\n\n                    /*\n                     * 暂时不使用 Process.StartInfo.UseShellExecute = false 模式，否则在执行时，会偶发性出现：应用程序无法正常启动(0xc0000142) 的错误\n                    Com_ExeOS.Run.Exe(run_path, \"/none_welcome_confirm\", false);    //二者 一般为 手动模式运行，无需 passive，默认使用 跳过欢迎确认 指令\n                    */\n                }\n\n                //无论何种模式，均要关闭当前旧的实例\n                Process.GetCurrentProcess().Kill();\n\n                return true;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                new Log($\"      * 暂时跳过更新步骤！\", ConsoleColor.DarkMagenta);\n                return false;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_Aria2c.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_Aria2c.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing System;\nusing System.IO;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_Aria2c\n    {\n        internal static int DownFile(string uri, string save_to)\n        {\n            try\n            {\n                //指定路径\n                string aria2c_path = Documents.SDKs.SDKs_Root + @\"\\Aria2c\\lot_aria2c.exe\";\n\n                if (!File.Exists(aria2c_path))\n                {\n                    new Log($\"     × {aria2c_path} 文件丢失！\", ConsoleColor.DarkRed);\n                    return 0;\n                }\n\n                string file_path = new FileInfo(save_to).DirectoryName;     //保存的文件路径，不含文件名\n                string filename = new FileInfo(save_to).Name;               //保存的文件名\n\n                //设置命令行\n                string aria2c_params = $\"{uri} --dir=\\\"{file_path}\\\" --out=\\\"{filename}\\\"\" +\n                    $\" --continue=true --max-connection-per-server=5 --check-integrity=true --file-allocation=none --console-log-level=error\";\n                //new Log(aria2c_params);\n\n                var down_result = Com_ExeOS.Run.Exe(aria2c_path, aria2c_params);\n                if (down_result == -920921)\n                {\n                    throw new Exception();\n                }\n\n                return 1;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return -1;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_OfficeActivate.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_OfficeActivate.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing Microsoft.Win32;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing static LKY_OfficeTools.Common.Com_SystemOS;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\nusing static LKY_OfficeTools.Lib.Lib_OfficeInfo;\nusing static LKY_OfficeTools.Lib.Lib_OfficeInfo.OfficeLocalInfo;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_OfficeActivate\n    {\n        internal static List<string> KMS_List = new List<string>();\n\n        internal static void Activating()\n        {\n            string KMS_info = Com_TextOS.GetCenterText(AppJson.Info, \"\\\"KMS_List\\\": \\\"\", \"\\\"\");\n\n            //为空抛出异常\n            if (!string.IsNullOrEmpty(KMS_info))\n            {\n                int try_times = 1;                                  //激活尝试的次数，初始值为1\n                KMS_List = new List<string>(KMS_info.Split(';'));\n                foreach (var now_kms in KMS_List)\n                {\n                    //激活成功时，结束；未安装Office导致不成功，也跳出。其余问题多次尝试不同激活服务器\n                    int act_state = StartActivate(now_kms.Replace(\" \", \"\"));   //替换空格并激活\n                    if (act_state == 1 || act_state < -2)\n                    {\n                        //激活成功（1），或者安装本身存在问题（< -11），亦或者安装序列号本身有问题（-3），直接结束激活。\n                        break;\n                    }\n                    else\n                    {\n                        if (try_times < KMS_List.Count)\n                        {\n                            new Log($\"\\n     >> 即将尝试第 {++try_times} 次激活 ...\", ConsoleColor.DarkYellow);\n                        }\n                        continue;\n                    }\n                }\n            }\n            else\n            {\n                //获取失败时，使用默认值\n                StartActivate();\n            }\n        }\n\n        internal static int StartActivate(string kms_server = \"kms.chinancce.com\")\n        {\n            //检查安装情况\n            InstallState install_state = GetOfficeState();\n            if (install_state == InstallState.Correct)              //必须安装最新版，才能激活\n            {\n                //检查 ospp.vbs 文件是否存在\n                if (!File.Exists(Documents.SDKs.Activate_OSPP))\n                {\n                    new Log($\"     × 目录 {Documents.SDKs.Activate} 下文件丢失！\", ConsoleColor.DarkRed);\n                    return -4;\n                }\n\n                //只要安装了 Office 新版本，就用KMS开始激活\n                string cmd_switch_cd = $\"pushd \\\"{Documents.SDKs.Activate}\\\"\";          //切换至OSPP文件目录\n                string cmd_kms_url = $\"cscript ospp.vbs /sethst:{kms_server}\";                          //设置激活KMS地址\n                string cmd_activate = \"cscript ospp.vbs /act\";                                              //开始激活\n\n                new Log($\"\\n------> 正在激活 Office v{OfficeNetInfo.OfficeLatestVersion} ...\", ConsoleColor.DarkCyan);\n\n                //执行：设置激活KMS地址\n                string kms_flag = kms_server.Replace(\"kms.\", \"\");\n                new Log($\"\\n     >> 设置 Office [{kms_flag}] 激活载体 ...\", ConsoleColor.DarkYellow);\n                string log_kms_url = Com_ExeOS.Run.Cmd($\"({cmd_switch_cd})&({cmd_kms_url})\");\n                if (!log_kms_url.ToLower().Contains(\"successful\"))\n                {\n                    new Log(log_kms_url);    //保存错误原因\n                    new Log($\"     × 设置激活载体失败，激活停止\", ConsoleColor.DarkRed);\n                    return -2;\n                }\n                new Log($\"     √ 已完成 Office 激活载体设置。\", ConsoleColor.DarkGreen);\n\n                //执行：开始激活\n                new Log($\"\\n     >> 执行 Office 激活 ...\", ConsoleColor.DarkYellow);\n                string log_activate = Com_ExeOS.Run.Cmd($\"({cmd_switch_cd})&({cmd_activate})\");\n\n                //先判断是几个SKU项目，以及成功数量\n                int sku_count = Com_TextOS.GetStringTimes(log_activate.ToLower(), \"sku id\");\n                //获取成功的数量\n                int success_count = Com_TextOS.GetStringTimes(log_activate.ToLower(), \"successful\");\n\n                bool activate_success;      //激活成功标志\n                if (success_count > 0 & sku_count == success_count)\n                {\n                    //全部激活成功\n                    activate_success = true;\n                }\n                else\n                {\n                    //至少有1个激活失败\n                    activate_success = false;\n                    new Log($\"     × 有 {sku_count - success_count} 个（共 {sku_count} 个）产品架构未能成功激活。\", ConsoleColor.DarkRed);\n                }\n\n                //判断原因\n                if (!activate_success)\n                {\n                    //继续判断失败原因，并给出方案\n\n                    //0x80080005\n                    if (log_activate.Contains(\"0x80080005\"))\n                    {\n                        //0x80080005错误：劫持问题，自动修复\n                        new Log($\"     >> 尝试修复 0x80080005 问题中 ...\", ConsoleColor.DarkYellow);\n                        string base_reg = @\"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\";\n                        string spp_reg = \"SppExtComObj.exe\";\n\n                        //清除x32劫持\n                        var x32_spp = Register.ExistItem(RegistryHive.LocalMachine, RegistryView.Registry32, $@\"{base_reg}\\{spp_reg}\");\n                        if (x32_spp)\n                        {\n                            Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry32, base_reg, spp_reg);\n                        }\n\n                        //清除x64劫持\n                        var x64_spp = Register.ExistItem(RegistryHive.LocalMachine, RegistryView.Registry64, $@\"{base_reg}\\{spp_reg}\");\n                        if (x64_spp)\n                        {\n                            Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry64, base_reg, spp_reg);\n                        }\n\n                        new Log($\"     √ 已完成 0x80080005 修复，稍后将自动重试激活。\", ConsoleColor.DarkGreen);\n                    }\n                    //0x8007000D\n                    else if (log_activate.Contains(\"0x8007000D\"))\n                    {\n                        //0x8007000D错误：软件保护、日期时间问题，自动修复\n                        new Log($\"     >> 尝试修复 0x8007000D 问题中，请同时确保您的计算机 日期/时间 正确 ...\", ConsoleColor.DarkYellow);\n\n                        //--------------------------------------- 软件保护修复 ---------------------------------------\n                        //先停止软件保护服务（sppsvc）\n                        Com_ServiceOS.Action.Stop(\"sppsvc\");    //无论是否成功都继续\n\n                        //准备清除注册表路径\n                        string base_reg = @\"SOFTWARE\\Microsoft\";\n                        string sub_reg = \"OfficeSoftwareProtectionPlatform\";\n\n                        //清除x32\n                        var x32_spp = Register.ExistItem(RegistryHive.LocalMachine, RegistryView.Registry32, $@\"{base_reg}\\{sub_reg}\");\n                        if (x32_spp)\n                        {\n                            Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry32, base_reg, sub_reg);\n                        }\n\n                        //清除x64\n                        var x64_spp = Register.ExistItem(RegistryHive.LocalMachine, RegistryView.Registry64, $@\"{base_reg}\\{sub_reg}\");\n                        if (x64_spp)\n                        {\n                            Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry64, base_reg, sub_reg);\n                        }\n\n                        //再次启动软件保护服务（sppsvc）\n                        Com_ServiceOS.Action.Start(\"sppsvc\");   //无论是否成功都继续\n\n                        //--------------------------------------- 软件保护修复（完成） ---------------------------------------\n\n\n                        //--------------------------------------- 系统日期/时间/时区修复 ---------------------------------------\n\n                        //--------------------------------------- 系统日期/时间/时区修复（完成） ---------------------------------------\n\n                        new Log($\"     √ 已完成 0x8007000D 修复，稍后将自动重试激活。\", ConsoleColor.DarkGreen);\n                    }\n                    //0x80040154\n                    else if (log_activate.Contains(\"0x80040154\"))\n                    {\n                        //0x80040154错误：没有注册类\n                        new Log($\"     × 系统可能存在损坏，建议您重新安装操作系统后重试！\", ConsoleColor.DarkRed);\n                        return -101;    //返回无限小，不再重试\n                    }\n                    //0xC004F074\n                    else if (log_activate.Contains(\"0xC004F074\"))\n                    {\n                        //0xC004F074错误：与KMS服务器通讯失败\n                        new Log($\"     × 激活失败！若此消息频频复现，强烈建议您重置网卡设置 或 重新安装操作系统！\", ConsoleColor.DarkRed);\n                    }\n                    else\n                    {\n                        //非已知问题\n                        new Log(log_activate);    //保存错误原因\n                        new Log($\"     × 意外的错误导致激活失败！\", ConsoleColor.DarkRed);\n                    }\n\n                    return -1;\n                }\n\n                new Log($\"     √ 已完成 Office v{OfficeNetInfo.OfficeLatestVersion} 正版激活。\", ConsoleColor.DarkGreen);\n                Lib_AppState.Current_StageType = Lib_AppState.ProcessStage.Finish_Success;   //设置整体运行状态为成功\n\n                return 1;\n            }\n            else if (install_state == InstallState.Diff)\n            {\n                new Log($\"     × 当前系统未安装最新版本的 Office，激活停止！\", ConsoleColor.DarkRed);\n                return -12;\n            }\n            else if (install_state == InstallState.Multi)\n            {\n                new Log($\"     × 当前系统存在多个 Office 版本，无法完成激活！\", ConsoleColor.DarkRed);    //这种多版本出错是指，未正确安装最新版，而且系统还有多个版本\n                return -14;\n            }\n            else if (install_state == InstallState.None)\n            {\n                new Log($\"     × 当前系统未安装任何 Office 版本，不需要激活！\", ConsoleColor.DarkRed);\n                return -18;\n            }\n            else\n            {\n                new Log($\"     × 因其它问题，Office 激活被迫停止！\", ConsoleColor.DarkRed);\n                return -99;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_OfficeClean.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_OfficeClean.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing Microsoft.Win32;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Threading;\nusing static LKY_OfficeTools.Common.Com_InstallerOS;\nusing static LKY_OfficeTools.Common.Com_SystemOS;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\nusing static LKY_OfficeTools.Lib.Lib_OfficeInfo;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_OfficeClean\n    {\n        internal static bool RemoveAllOffice()\n        {\n            try\n            {\n                //检查是否存在运行中的进程\n                var office_p_info = Lib_OfficeProcess.GetRuningProcess();\n                if (office_p_info != null && office_p_info.Count > 0)\n                {\n                    new Log($\"\\n------> 正在关闭 Office 组件（如果您有未保存的 Office 文档，请立即保存并关闭）...\", ConsoleColor.DarkCyan);\n                    new Log($\"        注意：如果您卡在上述流程达到 1 分钟以上，请您重启计算机，并再次运行本软件！\", ConsoleColor.Gray);\n                    Lib_OfficeProcess.KillOffice.All();       //友好的结束进程\n                    new Log($\"     √ 已完成 Office 进程处理。\", ConsoleColor.DarkGreen);\n                }\n\n                //先使用 ODT 模式卸载，其只能卸载使用 ODT 安装的2016及其以上版本的 Office，但是其耗时短。\n                Uninstall.ByODT();\n\n                //再使用 卸载早期版本 模式，可帮助后面 SaRA 模式省时间。\n                Uninstall.RemovePreviousVersion();\n\n                //然后使用 SaRA 模式，因为它可以尽可能卸载所有 Office 版本（非ODT），但是耗时长\n                Uninstall.BySaRA();\n\n                //无论哪种方式清理，都要再检查一遍是否卸载干净。如果 当前系统 Office 版本数量 > 0，启动强制模式\n                var installed_office = OfficeLocalInfo.GetArchiDir();\n                var license_list = OfficeLocalInfo.LicenseInfo();\n                if (\n                    (installed_office != null && installed_office.Count > 0) ||     //存在残留的Office注册表/文件目录\n                    license_list != null && license_list.Count > 0                  //存在残留的许可证信息\n                    )                                                               //二者满足任意一个时，执行强行清理\n                {\n                    Uninstall.ForceDelete();    //无论清除是否成功，都继续安装新 office\n                }\n\n                return true;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return false;\n            }\n        }\n\n        internal class Activate\n        {\n            internal static bool Delete()\n            {\n                try\n                {\n                    //获取激活信息\n                    var office_installed_key = OfficeLocalInfo.LicenseInfo();\n\n                    if (office_installed_key != null && office_installed_key.Count > 0)\n                    {\n                        foreach (var now_key in office_installed_key)\n                        {\n                            string cmd_switch_cd = $\"pushd \\\"{Documents.SDKs.SDKs_Root + @\"\\Activate\"}\\\"\";                  //切换至OSPP文件目录\n                            string cmd_remove = $\"cscript ospp.vbs /unpkey:{now_key}\";\n                            string result_log = Com_ExeOS.Run.Cmd($\"({cmd_switch_cd})&({cmd_remove})\");\n                            if (result_log.Contains(\"success\"))\n                            {\n                                new Log($\"     √ 已移除 {now_key} 激活信息。\", ConsoleColor.DarkGreen);\n                            }\n                            else\n                            {\n                                new Log(result_log);    //获取失败原因\n                                new Log($\"     × {now_key} 激活信息移除失败！\", ConsoleColor.DarkRed);\n                                return false;   //有一个产品卸载失败了，就直接返回 false。\n                            }\n                        }\n\n                        //逐一卸载后，若都为 success，则再执行一次检查\n                        office_installed_key = OfficeLocalInfo.LicenseInfo();   //再度获取list\n                        if (office_installed_key.Count == 0)   //为0，视为成功\n                        {\n                            return true;\n                        }\n                        else\n                        {\n                            return false;\n                        }\n                    }\n\n                    return true;    //激活信息为空，或者移除成功时 返回 true\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n        }\n\n        internal class Uninstall\n        {\n            internal static bool ForceDelete()\n            {\n                try\n                {\n                    new Log($\"\\n------> 正在执行 Office 强行清理 ...\", ConsoleColor.DarkCyan);\n\n                    //移除激活信息\n                    Activate.Delete();\n\n                    //删除文件\n                    try\n                    {\n                        //有些文件可能无法彻底删除，但如果后面复查时，不影响安装，则不会返回 false\n                        var office_installed_dir = OfficeLocalInfo.GetArchiDir();\n                        if (office_installed_dir != null && office_installed_dir.Count > 0)\n                        {\n                            foreach (var now_dir in office_installed_dir)     //遍历查询所有目录，将其删除\n                            {\n                                if (Directory.Exists(now_dir))\n                                {\n                                    Directory.Delete(now_dir, true);\n                                }\n                            }\n                        }\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(Ex.ToString());\n                    }\n\n                    //清理注册表残余。Office 有些注册表是无法清理干净的，所以用try\n                    try\n                    {\n                        //清理x32注册表\n                        Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry32, @\"SOFTWARE\\Microsoft\", \"Office\");\n\n                        //清理x64注册表。当且仅当，系统是x64系统时，清理x64的注册表节点\n                        if (Environment.Is64BitOperatingSystem)\n                        {\n                            Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry64, @\"SOFTWARE\\Microsoft\", \"Office\");\n                        }\n                    }\n                    catch {/*注册表 common 项目 铁定删不掉*/}\n\n                    //清除开始菜单\n                    try\n                    {\n                        string root = $@\"{Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu)}\\Programs\";\n                        var root_dir = Directory.GetDirectories(root, \"Microsoft Office*\", SearchOption.TopDirectoryOnly);\n                        foreach (var now_dir in root_dir)\n                        {\n                            if (Directory.Exists(now_dir))\n                            {\n                                Directory.Delete(now_dir, true);\n                            }\n                        }\n\n                        root = $@\"{Environment.GetFolderPath(Environment.SpecialFolder.StartMenu)}\\Programs\";\n                        root_dir = Directory.GetDirectories(root, \"Microsoft Office*\", SearchOption.TopDirectoryOnly);\n                        foreach (var now_dir in root_dir)\n                        {\n                            if (Directory.Exists(now_dir))\n                            {\n                                Directory.Delete(now_dir, true);\n                            }\n                        }\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(Ex.ToString());\n                    }\n\n                    //复查是否干净了\n                    var installed_info = OfficeLocalInfo.GetArchiDir();\n                    if (installed_info != null && installed_info.Count > 0)\n                    {\n                        throw new Exception();\n                    }\n                    else\n                    {\n                        new Log($\"     √ 已彻底清除 Office 所有组件。\", ConsoleColor.DarkGreen);\n                        return true;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    new Log($\"     × 暂时无法彻底清除 Office 所有组件！\", ConsoleColor.DarkRed);\n                    return false;\n                }\n            }\n\n            internal static bool RemovePreviousVersion()\n            {\n                try\n                {\n                    new Log($\"\\n------> 正在卸载 Office 早期版本 ...\", ConsoleColor.DarkCyan);\n\n                    //获取 installer 目录\n                    string installer_dir = Environment.GetFolderPath(Environment.SpecialFolder.Windows) + \"\\\\Installer\";\n\n                    //获取 installer 目录下所有msi文件\n                    var msi_files = Directory.GetFiles(installer_dir, \"*.msi\", SearchOption.TopDirectoryOnly);\n\n                    //生成 产品ID 与 命令行 的字典\n                    Dictionary<string, string> msi_id_cmd_dic = new Dictionary<string, string>();\n                    //生成 产品ID 与 MSI名称 的字典\n                    Dictionary<string, string> msi_id_name_dic = new Dictionary<string, string>();\n\n                    //非空判断\n                    if (msi_files == null)\n                    {\n                        new Log($\"      * 未发现 Office 早期版本的维护信息，已跳过此流程。\", ConsoleColor.DarkMagenta);\n                        return true;\n                    }\n\n                    new Log($\"     >> 启动 Office 早期版本筛查 ...\", ConsoleColor.DarkYellow);\n\n                    //找出符合条件的 Office MSI 文件，并添加至字典\n                    foreach (var now_msi in msi_files)\n                    {\n                        var now_msi_name = GetProductInfo(now_msi, MsiInfoType.ProductName);\n\n                        //非空判断\n                        if (string.IsNullOrEmpty(now_msi_name))\n                        {\n                            new Log($\"     × 无法获取 Office 早期版本信息！\", ConsoleColor.DarkRed);\n                            return false;\n                        }\n\n                        if (\n                            now_msi_name.Contains(\"Microsoft Office\") &&                        //只查找 Office MSI\n                            (now_msi_name.Contains(\"2003\") || now_msi_name.Contains(\"2007\")) && //只查找03、07版本。2010版本（含）以上无法使用本方法卸载\n                            !now_msi_name.Contains(\"MUI\") &&                                    //剔除 MUI 语言包\n                            !now_msi_name.Contains(\"Component\") &&                              //过滤子组件MSI，否则会导致卸载主程序失败\n                            !now_msi_name.Contains(\"(\") &&                                      //过滤其他的语言包，这种MSI通常会用 (English) 这种描述\n                            !now_msi_name.Contains(\"-\")                                         //过滤 Microsoft Office Proofing Tools 2013 - English 这种组件\n                           )\n                        {\n                            //获得 MSI 的 ID 号\n                            string id = GetProductInfo(now_msi, MsiInfoType.ProductCode);\n\n                            //组成命令行\n                            string cmd = $\"/uninstall {now_msi} /passive /norestart\";\n\n                            //设置 ID/命令行字典\n                            msi_id_cmd_dic[id] = cmd;\n\n                            //设置 ID/产品名字典\n                            msi_id_name_dic[id] = now_msi_name;\n                        }\n                    }\n\n                    //非空判断\n                    if (msi_id_cmd_dic.Count == 0 || msi_id_name_dic.Count == 0)\n                    {\n                        new Log($\"      * 未发现 Office 早期版本，已跳过此流程。\", ConsoleColor.DarkMagenta);\n                        return true;\n                    }\n                    else\n                    {\n                        new Log($\"     √ 已完成 Office 早期版本筛查。\", ConsoleColor.DarkGreen);\n                    }\n\n                    //逐一卸载 Office\n                    foreach (var now_id_cmd in msi_id_cmd_dic)\n                    {\n                        string product_name = msi_id_name_dic[now_id_cmd.Key];      //完整的产品名\n                        new Log($\"\\n     >> 开始移除 {product_name} 及其组件 ...\", ConsoleColor.DarkYellow);\n\n                        //运行卸载\n                        var msi_result = Com_ExeOS.Run.Exe(\"msiexec.exe\", now_id_cmd.Value);\n                        if (msi_result == -920921)\n                        {\n                            throw new Exception();\n                        }\n\n                        new Log($\"        若长时间无 {product_name.Replace(\"Microsoft Office \", \"\")} 卸载界面，您可到: 控制面板 -> 程序和功能 列表中手动卸载。\", ConsoleColor.Gray);\n\n                        //循环等待结束\n                        while (true)\n                        {\n                            //一直获取对应 产品ID 的注册表\n                            var uninstall_reg = Register.Read.AllValues(RegistryHive.LocalMachine,\n                                $@\"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{now_id_cmd.Key}\", \"UninstallString\");\n\n                            //一直获取标题包含 Microsoft Office 的进程信息\n                            var uninstall_pro_list = Com_ExeOS.Info.GetProcessByTitle(\"Microsoft Office\");\n                            if (\n                                (uninstall_reg == null || uninstall_reg.Count == 0) &&              //注册表的卸载信息已清空\n                                (uninstall_pro_list == null || uninstall_pro_list.Count == 0)       //进程中不存在 Microsoft Office 标题的进程\n                                )\n                            {\n                                break;\n                            }\n\n                            Thread.Sleep(3000);     //延迟，防止轮询资源占用过大\n                        }\n\n                        new Log($\"     √ 已完成 {product_name} 组件移除。\", ConsoleColor.DarkGreen);\n                    }\n\n                    Thread.Sleep(1000);     //基于体验短暂延迟\n\n                    new Log($\"\\n     √ 已完成 所有 Office 早期版本卸载。\", ConsoleColor.DarkGreen);\n\n                    return true;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    new Log($\"     × 卸载 Office 早期版本出现意外！\", ConsoleColor.DarkRed);\n                    return false;\n                }\n            }\n\n            internal static bool BySaRA()\n            {\n                try\n                {\n                    new Log($\"\\n------> 正在卸载 Office 冗余版本 ...\", ConsoleColor.DarkCyan);\n\n                    //获取目前存留的版本\n                    var install_list = OfficeLocalInfo.GetArchiDir();\n                    if (install_list == null || install_list.Count == 0)\n                    {\n                        //已经不存在残留版本时，直接返回卸载成功\n                        new Log($\"      * 未发现 Office 冗余版本，已跳过此流程。\", ConsoleColor.DarkMagenta);\n                        return true;\n                    }\n\n                    //定义SaRA文件位置\n                    string SaRA_path_root = Documents.SDKs.SDKs_Root + @\"\\SaRA\";\n                    string SaRA_path_exe = SaRA_path_root + @\"\\SaRACmd.exe\";\n\n                    //检查SaRA文件是否存在\n                    if (!File.Exists(SaRA_path_exe))\n                    {\n                        new Log($\"     × 目录 {SaRA_path_root} 下文件丢失！\", ConsoleColor.DarkRed);\n                        return false;\n                    }\n\n                    //实际测试，平均卸载1个Office需要5分钟的时间，留出1.5倍时间。\n                    new Log($\"     >> 此过程大约会在 {Math.Ceiling(install_list.Count * 5 * 1.5f)} 分钟内完成，实际用时取决于您的电脑配置，请耐心等待 ...\", ConsoleColor.DarkYellow);\n\n                    //执行卸载命令\n                    string cmd_switch_cd = $\"pushd \\\"{SaRA_path_root}\\\"\";             //切换至SaRA文件目录\n                    string cmd_uninstall = $\"SaRACmd.exe -S OfficeScrubScenario -AcceptEula -Officeversion All\";\n                    string uninstall_result = Com_ExeOS.Run.Cmd($\"({cmd_switch_cd})&({cmd_uninstall})\");\n                    if (!uninstall_result.ToLower().Contains(\"successful\"))\n                    {\n                        new Log($\"SaRA Exception: \\n{uninstall_result}\");\n                        new Log($\"     × 卸载 Office 冗余版本失败！\", ConsoleColor.DarkRed);\n                        return false;\n                    }\n\n                    /* 暂时不启用\n\n                    //判断是否需要重启\n                    if (uninstall_result.ToLower().Contains(\"restart\"))\n                    {\n                        //需要重启\n                        new Log($\"     √ 已完成 Office 卸载操作。为确保卸载干净，请您重启电脑后再次运行本程序。\", ConsoleColor.DarkGreen);\n                    }\n                    else\n                    {\n                        //不需要重启\n                        new Log($\"     √ 已完成 Office 卸载操作。\", ConsoleColor.DarkGreen);\n                    }\n\n                    */\n\n                    new Log($\"     √ 已完成 Office 冗余版本卸载。\", ConsoleColor.DarkGreen);\n\n                    return true;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    new Log($\"     × 卸载 Office 冗余版本出现异常！\", ConsoleColor.DarkRed);\n                    return false;\n                }\n            }\n\n            internal static bool ByODT()\n            {\n                try\n                {\n                    new Log($\"\\n------> 正在卸载 Office ODT 版本 ...\", ConsoleColor.DarkCyan);\n\n                    //获取目前存留的 ODT 版本\n                    var install_list = Register.Read.AllValues(RegistryHive.LocalMachine, @\"SOFTWARE\\Microsoft\\Office\\ClickToRun\\Configuration\", \"ProductReleaseIds\");\n                    if (install_list == null || install_list.Count == 0)\n                    {\n                        //已经不存在残留 ODT 版本时，直接返回卸载成功\n                        new Log($\"      * 未发现 Office ODT 版本，已跳过此流程。\", ConsoleColor.DarkMagenta);\n                        return true;\n                    }\n\n                    //定义ODT文件位置\n                    string ODT_path_root = Documents.SDKs.SDKs_Root + @\"\\ODT\";\n                    string ODT_path_exe = ODT_path_root + @\"\\ODT.exe\";\n                    string ODT_path_xml = ODT_path_root + @\"\\uninstall.xml\";    //此文件需要新生成\n\n                    //生成卸载xml\n                    string xml_content = \"<Configuration>\\n  <Remove All=\\\"TRUE\\\" />\\n  <Display Level=\\\"NONE\\\" AcceptEULA=\\\"TRUE\\\"/>\\n</Configuration>\";\n                    File.WriteAllText(ODT_path_xml, xml_content);\n\n                    //检查ODT文件是否存在\n                    if (!File.Exists(ODT_path_exe) || !File.Exists(ODT_path_xml))\n                    {\n                        new Log($\"     × 目录 {ODT_path_root} 下文件丢失！\", ConsoleColor.DarkRed);\n                        return false;\n                    }\n\n                    //测试表明，卸载1个ODT版本大约需要2分钟时间，留出2倍的富余\n                    new Log($\"     >> 此过程大约会在 {Math.Ceiling(install_list.Count * 2 * 2.0f)} 分钟内完成，具体时间取决于您的电脑配置，请稍候 ...\", ConsoleColor.DarkYellow);\n\n                    //移除所有激活信息，即使有错误也继续执行后续卸载。\n                    if (!Activate.Delete())\n                    {\n                        new Log(\"移除激活信息失败！\");    //纯打点\n                    }\n\n                    new Log($\"     >> 卸载仍在继续，请等待其自动完成 ...\", ConsoleColor.DarkYellow);\n                    //执行卸载命令\n                    string uninstall_args = $\"/configure \\\"{ODT_path_xml}\\\"\";\n                    var uninstall_code = Com_ExeOS.Run.Exe(ODT_path_exe, uninstall_args);      //卸载\n\n                    var reg_info = Register.Read.AllValues(RegistryHive.LocalMachine, @\"SOFTWARE\\Microsoft\\Office\\ClickToRun\\Configuration\", \"ProductReleaseIds\");\n\n                    //未正常结束卸载，视为卸载失败\n                    if (uninstall_code == -920921)\n                    {\n                        new Log($\"     × 无法卸载 Office ODT 版本！\", ConsoleColor.DarkRed);\n                        return false;\n                    }\n\n                    //注册表ODT至少存在1个版本时，视为卸载失败\n                    if (reg_info != null && reg_info.Count > 0)\n                    {\n                        //卸载存在问题\n                        string err_msg = $\"ODT UnInstalling Exception, ExitCode: {uninstall_code}\";\n                        if (uninstall_code > 0)\n                        {\n                            //只解析错误码大于0的情况\n                            string err_string = string.Empty;\n                            if (Lib_OfficeInstall.ODT_Error.TryGetValue(((uint)uninstall_code), out err_string))\n                            {\n                                err_msg += $\"{err_string}\";\n                            }\n                        }\n\n                        new Log(err_msg);         //回调错误码\n\n                        new Log($\"     × 已尝试卸载 Office ODT 版本，但系统仍存在 {reg_info.Count} 个无法卸载的版本！\", ConsoleColor.DarkRed);\n                        return false;\n                    }\n\n                    //卸载正常 + 注册表已清空时，开始安装新版本\n                    new Log($\"     √ 已完成 Office ODT 版本卸载。\", ConsoleColor.DarkGreen);\n                    return true;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    new Log($\"     × 卸载 Office ODT 版本出现异常！\", ConsoleColor.DarkRed);\n                    return false;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_OfficeDownload.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_OfficeDownload.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Threading;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\nusing static LKY_OfficeTools.Lib.Lib_OfficeInfo;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_OfficeDownload\n    {\n        internal static int StartDownload()\n        {\n            try\n            {\n                //定义下载目标地。文件必须位于 \\Office\\Data\\下。ODT安装必须在 Office 上一级目录上执行。\n                string save_to = AppPath.ExecuteDir + @\"\\Office\\Data\\\";\n\n                //计划保存的地址\n                List<string> save_files = new List<string>();\n\n                //下载开始\n                new Log($\"\\n------> 开始下载 Office v{OfficeNetInfo.OfficeLatestVersion} 文件 ...\", ConsoleColor.DarkCyan);\n                //延迟，让用户看到开始下载\n                Thread.Sleep(1000);\n\n                //轮询下载所有文件\n                foreach (var a in OfficeNetInfo.OfficeFileList)\n                {\n                    //根据官方目录，来调整下载保存位置\n                    string save_path = save_to + a.Substring(OfficeNetInfo.OfficeUrlRoot.Length).Replace(\"/\", \"\\\\\");\n\n                    //保存到List里面，用于后续检查\n                    save_files.Add(save_path);\n\n                    new Log($\"\\n     >> 下载 {new FileInfo(save_path).Name} 文件中，请稍候 ...\", ConsoleColor.DarkYellow);\n\n                    //遇到重复的文件可以断点续传\n                    int down_result = Lib_Aria2c.DownFile(a, save_path);\n                    if (down_result != 1)\n                    {\n                        //如果因为核心下载exe丢失，导致下载失败，直接中止\n                        throw new Exception($\"下载 {a} 异常！\");\n                    }\n\n                    //如果用户中断了下载，则直接跳出\n                    if (Lib_AppState.Current_StageType == Lib_AppState.ProcessStage.Interrupt)\n                    {\n                        return -1;\n                    }\n\n                    new Log($\"     √ 已下载 {new FileInfo(save_path).Name} 文件。\", ConsoleColor.DarkGreen);\n                }\n\n                new Log($\"\\n------> 正在检查 Office v{OfficeNetInfo.OfficeLatestVersion} 文件 ...\", ConsoleColor.DarkCyan);\n\n                foreach (var b in save_files)\n                {\n                    string aria_tmp_file = b + \".aria2c\";\n\n                    //下载完成的标志：文件存在，且不存在临时文件\n                    if (File.Exists(b) && !File.Exists(aria_tmp_file))\n                    {\n                        new Log($\"     >> 检查 {new FileInfo(b).Name} 文件中 ...\", ConsoleColor.DarkYellow);\n                    }\n                    else\n                    {\n                        new Log($\"     >> 文件 {new FileInfo(b).Name} 存在异常，重试中 ...\", ConsoleColor.DarkRed);\n                        return StartDownload();\n                    }\n                }\n\n                new Log($\"     √ 已完成 Office v{OfficeNetInfo.OfficeLatestVersion} 下载。\", ConsoleColor.DarkGreen);\n\n                return 1;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return 0;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_OfficeInfo.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_OfficeInfo.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing Microsoft.Win32;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing static LKY_OfficeTools.Common.Com_SystemOS;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_OfficeInfo\n    {\n        internal enum OfficeArchi\n        {\n            Office_2003 = 2003,\n\n            Office_2007_x32 = 2007 * 32,\n\n            Office_2007_x64 = 2007 * 64,\n\n            Office_2010_x32 = 2010 * 32,\n\n            Office_2010_x64 = 2010 * 64,\n\n            Office_2013_x32 = 2013 * 32,\n\n            Office_2013_x64 = 2013 * 64,\n\n            Office_ODT_x32 = 2016 * 32,\n\n            Office_ODT_x64 = 2016 * 64,\n        }\n\n        internal class OfficeNetInfo\n        {\n            private static string _office_channel_info;\n            internal static string OfficeChannelInfo\n            {\n                get\n                {\n                    try\n                    {\n                        //私有值非空判断\n                        if (!string.IsNullOrWhiteSpace(_office_channel_info))\n                        {\n                            return _office_channel_info;\n                        }\n\n                        new Log(\"\\n------> 正在获取 最新可用 Office 信息 ...\", ConsoleColor.DarkCyan);\n\n                        //获取频道信息，得到可用 Url 列表\n                        string channel_info = Com_TextOS.GetCenterText(AppJson.Info, \"\\\"OfficeChannels_Url\\\": \\\"\", \"\\\"\");\n                        if (string.IsNullOrEmpty(channel_info))\n                        {\n                            throw new Exception(\"OfficeChannels_Url 信息获取失败！\");\n                        }\n                        List<string> channel_list = channel_info.Split(';').ToList();\n\n                        //遍历获取有效地址，并下载channel信息\n                        string office_info = null;\n                        int try_times = 1;                                                          //尝试获取次数，初始值为1\n                        foreach (var now_url in channel_list)\n                        {\n                            office_info = Com_WebOS.Visit_WebClient(now_url.Replace(\" \", \"\"));      //替换网址空格，并获得当前网址的访问信息\n                            if (!string.IsNullOrEmpty(office_info))\n                            {\n                                new Log($\"     √ 已获得 Office 最新正版信息。\", ConsoleColor.DarkGreen);\n\n                                //info有值，返回该值。\n                                return _office_channel_info = office_info;\n                            }\n\n                            //访问当前网址返回数据为空，或者无法截取获得有效字段时，遍历。\n                            if (try_times < channel_list.Count)\n                            {\n                                new Log($\"     >> 尝试使用 {++try_times} 号服务器获取中 ...\", ConsoleColor.DarkYellow);\n                                continue;\n                            }\n                        }\n\n                        //全部遍历完，依旧没返回，就说明全部失败\n                        throw new Exception(\"Office 信息获取失败！\");\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(\"     × 最新 Office 信息获取失败，请稍后重试！\", ConsoleColor.DarkRed);\n                        new Log(Ex.ToString());\n                        return null;\n                    }\n                }\n            }\n\n            private static Version _office_latest_version;\n            internal static Version OfficeLatestVersion\n            {\n                get\n                {\n                    try\n                    {\n                        //非空返回\n                        if (_office_latest_version != null)\n                        {\n                            return _office_latest_version;\n                        }\n\n                        //非空判断\n                        if (string.IsNullOrWhiteSpace(OfficeChannelInfo))\n                        {\n                            return null;\n                        }\n\n                        new Log(\"\\n------> 正在解析 最新可用 Office 版本 ...\", ConsoleColor.DarkCyan);\n\n                        //获取版本信息\n                        string latest_info = Com_TextOS.GetCenterText(OfficeChannelInfo, \"\\\"PerpetualVL2021\\\",\", \"name\");                     //获取 2021 LTSC\n                        if (!string.IsNullOrEmpty(latest_info))\n                        {\n                            //获取版本号\n                            var ver = new Version(Com_TextOS.GetCenterText(latest_info, \"latestUpdateVersion\\\":\\\"\", \"\\\"\"));      //官方Json取值，格式和自己的不一样，千万注意\n\n                            new Log($\"     √ 已获得 Office 最新版本为：v{ver}\", ConsoleColor.DarkGreen);\n\n                            return _office_latest_version = ver;\n                        }\n\n                        throw new Exception(\"无法获取 PerpetualVL2021 对应的版本号！\");\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(\"     × 无法获取 最新可用 Office 版本，请稍后重试！\", ConsoleColor.DarkRed);\n                        new Log(Ex.ToString());\n                        return null;\n                    }\n                }\n            }\n\n            private static string _office_url_root;\n            internal static string OfficeUrlRoot\n            {\n                get\n                {\n                    try\n                    {\n                        //非空返回\n                        if (!string.IsNullOrWhiteSpace(_office_url_root))\n                        {\n                            return _office_url_root;\n                        }\n\n                        //非空判断\n                        if (string.IsNullOrWhiteSpace(OfficeChannelInfo))\n                        {\n                            return null;\n                        }\n\n                        new Log(\"\\n------> 正在解析 最新可用 Office 下载服务器 ...\", ConsoleColor.DarkCyan);\n\n                        //获取版本信息\n                        string latest_info = Com_TextOS.GetCenterText(OfficeChannelInfo, \"\\\"PerpetualVL2021\\\",\", \"name\");                     //获取 2021 LTSC\n                        if (!string.IsNullOrEmpty(latest_info))\n                        {\n                            //获取office下载地址\n                            var url = Com_TextOS.GetCenterText(latest_info, \"baseUrl\\\":\\\"\", \"\\\"\");                         //官方Json取值，格式和自己的不一样，千万注意\n\n                            new Log($\"     √ 已获得 Office 最新下载服务器信息\", ConsoleColor.DarkGreen);\n\n                            return _office_url_root = url + \"/office/data\";\n                        }\n\n                        throw new Exception(\"无法获取 PerpetualVL2021 对应的下载地址！\");\n                    }\n                    catch (Exception Ex)\n                    {\n                        new Log(\"     × 无法获取 最新可用 Office 下载服务器\", ConsoleColor.DarkRed);\n                        new Log(Ex.ToString());\n                        return null;\n                    }\n                }\n            }\n\n            private static List<string> _office_file_list;\n            internal static List<string> OfficeFileList\n            {\n                get\n                {\n                    try\n                    {\n                        //非空返回\n                        if (_office_file_list != null && _office_file_list.Count > 0)\n                        {\n                            return _office_file_list;\n                        }\n\n                        //下载根地址为空时，视为失败\n                        if (OfficeLatestVersion == null || string.IsNullOrEmpty(OfficeUrlRoot))\n                        {\n                            return null;\n                        }\n\n                        new Log(\"\\n------> 正在解析 Office 下载列表 ...\", ConsoleColor.DarkCyan);\n\n                        //获取文件列表\n                        List<string> result = new List<string>();\n\n                        //获取当前系统位数\n                        int sys_bit;\n                        if (Environment.Is64BitOperatingSystem)\n                        {\n                            sys_bit = 64;\n                        }\n                        else\n                        {\n                            sys_bit = 32;\n                        }\n\n                        //获得版本号的字符串\n                        string ver = OfficeLatestVersion.ToString();\n\n                        //x32系统也需要下载 64 的 i64****.cab文件，但必须放在 {ver} 目录下。\n                        result.Add($\"{OfficeUrlRoot}/{ver}/i640.cab\");\n                        result.Add($\"{OfficeUrlRoot}/{ver}/i642052.cab\");\n\n                        switch (sys_bit)\n                        {\n                            case 64:\n                                result.Add($\"{OfficeUrlRoot}/{ver}/stream.x{sys_bit}.x-none.dat\");\n                                result.Add($\"{OfficeUrlRoot}/{ver}/stream.x{sys_bit}.zh-cn.dat\");\n                                break;\n                            case 32:\n                                result.Add($\"{OfficeUrlRoot}/{ver}/stream.x86.x-none.dat\");\n                                result.Add($\"{OfficeUrlRoot}/{ver}/stream.x86.zh-cn.dat\");\n                                result.Add($\"{OfficeUrlRoot}/{ver}/i{sys_bit}0.cab\");\n                                result.Add($\"{OfficeUrlRoot}/{ver}/i{sys_bit}2052.cab\");\n                                break;\n                        }\n\n                        //按版本下载其余文件\n                        result.Add($\"{OfficeUrlRoot}/v{sys_bit}.cab\");\n                        result.Add($\"{OfficeUrlRoot}/v{sys_bit}_{ver}.cab\");\n                        result.Add($\"{OfficeUrlRoot}/{ver}/s{sys_bit}0.cab\");\n                        result.Add($\"{OfficeUrlRoot}/{ver}/s{sys_bit}2052.cab\");\n\n                        new Log($\"     √ 已解析 Office 下载列表。\", ConsoleColor.DarkGreen);\n\n                        return _office_file_list = result;\n                    }\n\n                    catch (Exception Ex)\n                    {\n                        new Log(Ex.ToString());\n                        return null;\n                    }\n                }\n            }\n        }\n\n        internal class OfficeLocalInfo\n        {\n            [Flags]\n            internal enum InstallState\n            {\n                Correct = 1,\n\n                Diff = 2,\n\n                Multi = 4,\n\n                None = 8,\n            }\n\n            internal static InstallState GetOfficeState()\n            {\n                var version_info = GetArchiDir();\n\n                if (version_info != null && version_info.Count > 0)     //系统已安装至少1个Office版本\n                {\n                    //判断是否只安装了一个版本\n                    if (version_info.Count > 1)\n                    {\n                        //安装了多个版本\n                        return InstallState.Multi;\n                    }\n                    else\n                    {\n                        //系统只有一个版本\n\n                        //获取ODT双位数的版本信息列表（x32、x64）\n                        var office_reg_ver_list = Register.Read.AllValues(RegistryHive.LocalMachine, @\"SOFTWARE\\Microsoft\\Office\\ClickToRun\\Configuration\", \"VersionToReport\");\n                        if (office_reg_ver_list != null && office_reg_ver_list.Count > 0)\n                        {\n                            //遍历查询安装目录\n                            if (office_reg_ver_list.Count == 1)\n                            {\n                                //只安装了1个ODT版本\n                                //获取其版本号\n                                Version installed_ver = null;\n\n                                //解析版本号\n                                try\n                                {\n                                    installed_ver = new Version(office_reg_ver_list[0]);\n                                }\n                                catch\n                                {\n                                    //ODT 只安装了1个版本，但 now_ver 为空，或版本号解析失败，视为版本不同\n                                    return InstallState.Diff;\n                                }\n\n                                //获取目标ID\n                                string Pop_Office_ID = \"2021Volume\";\n\n                                //获取本机已经安装的ID（如果x64系统，安装了x32的 Office 在 WOW6432Node 目录，该值将为null）\n                                string Current_Office_ID = Register.Read.ValueBySystem(RegistryHive.LocalMachine, @\"SOFTWARE\\Microsoft\\Office\\ClickToRun\\Configuration\", \"ProductReleaseIds\");\n\n                                //替换支持的版本ID。如果用户还安装了其它架构，进行替换后，该值不是空\n                                var diff_products = Current_Office_ID\n                                    .Replace($\"ProjectPro{Pop_Office_ID}\", \"\")\n                                    .Replace($\"ProPlus{Pop_Office_ID}\", \"\")\n                                    .Replace($\"VisioPro{Pop_Office_ID}\", \"\")\n                                    .Replace(\",\", \"\");\n\n                                if (\n                                    //本地版本号非空 & 本地版本号 >= 官方最新版本号，就视为安装了最新版（因为可能会有安全更新补丁）\n                                    (installed_ver != null && installed_ver >= OfficeNetInfo.OfficeLatestVersion)\n                                    &&\n                                    string.IsNullOrWhiteSpace(diff_products)    //是否还安装了其它ID\n                                    )\n                                {\n                                    //x64系统还得校验下 Office 注册表的 平台信息 是否一致\n                                    if (Environment.Is64BitOperatingSystem)\n                                    {\n                                        string platform = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, @\"SOFTWARE\\Microsoft\\Office\\ClickToRun\\Configuration\", \"Platform\");\n                                        if (string.IsNullOrWhiteSpace(platform) || platform == \"x86\")\n                                        {\n                                            return InstallState.Diff;       //虽然出现在了与x64系统注册表匹配的路径，但是Office平台版本并非x64\n                                        }\n                                        else\n                                        {\n                                            //版本号不小于官方最新版、产品ID一致、注册表显示的位数一致\n                                            return InstallState.Correct;    //已经正确安装最新版\n                                        }\n                                    }\n                                    else\n                                    {\n                                        //x32系统，直接满足了\n                                        return InstallState.Correct;    //已经正确安装最新版\n                                    }\n                                }\n                                else\n                                {\n                                    //ODT 只安装了1个版本，但版本号小于预期版本号，或者还安装了其它版本，\n                                    //如：ProPlus2016Volume，视为版本不同\n                                    return InstallState.Diff;\n                                }\n                            }\n                            else\n                            {\n                                //存在多个ODT版本（如：version信息为1，但odt的path信息有2个），不管版本是否相同，都设置 Multi 标记\n                                return InstallState.Multi;\n                            }\n                        }\n                        else\n                        {\n                            //安装了1个Office版本，但ODT版本安装数量为0，则版本不同\n                            return InstallState.Diff;\n                        }\n                    }\n                }\n                else\n                {\n                    //未安装任何\n                    return InstallState.None;\n                }\n            }\n\n            internal static Dictionary<OfficeArchi, string> GetArchiDirFull()\n            {\n                try\n                {\n                    //注册表根路径\n                    string office_reg_root = \"SOFTWARE\\\\Microsoft\\\\Office\";\n\n                    //获取所有版本 office 的安装目录\n\n                    //先获取 x32 的，并填充 null 到 x64\n                    Dictionary<OfficeArchi, string> office_installed_dir = new Dictionary<OfficeArchi, string>\n                    {\n                        //2016及其以上版本，通过odt安装的office\n                        {OfficeArchi.Office_ODT_x64, null},\n                        {OfficeArchi.Office_ODT_x32, Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry32, $@\"{office_reg_root}\\ClickToRun\", \"InstallPath\") },\n\n                        //2013版\n                        {OfficeArchi.Office_2013_x64, null},\n                        {OfficeArchi.Office_2013_x32, Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry32, $@\"{office_reg_root}\\15.0\\Common\\InstallRoot\", \"Path\" ) },\n\n                        //2010版\n                        {OfficeArchi.Office_2010_x64, null},\n                        {OfficeArchi.Office_2010_x32, Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry32, $@\"{office_reg_root}\\14.0\\Common\\InstallRoot\", \"Path\") },\n\n                        //2007版\n                        {OfficeArchi.Office_2007_x64, null},\n                        {OfficeArchi.Office_2007_x32, Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry32, $@\"{office_reg_root}\\12.0\\Common\\InstallRoot\", \"Path\") },\n\n                        //2003版\n                        {OfficeArchi.Office_2003, Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry32, $@\"{office_reg_root}\\11.0\\Common\\InstallRoot\", \"Path\") },\n                    };\n\n                    //只有x64系统才获取x64注册表值\n                    if (Environment.Is64BitOperatingSystem)\n                    {\n                        //2016及其以上版本\n                        office_installed_dir[OfficeArchi.Office_ODT_x64] = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, $@\"{office_reg_root}\\ClickToRun\", \"InstallPath\");\n\n                        //2013版\n                        office_installed_dir[OfficeArchi.Office_2013_x64] = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, $@\"{office_reg_root}\\15.0\\Common\\InstallRoot\", \"Path\");\n\n                        //2010版\n                        office_installed_dir[OfficeArchi.Office_2010_x64] = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, $@\"{office_reg_root}\\14.0\\Common\\InstallRoot\", \"Path\");\n\n                        //2007版\n                        office_installed_dir[OfficeArchi.Office_2007_x64] = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, $@\"{office_reg_root}\\12.0\\Common\\InstallRoot\", \"Path\");\n                    }\n\n                    return office_installed_dir;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return null;\n                }\n            }\n\n            internal static List<string> GetArchiDir()\n            {\n                try\n                {\n                    //先获取完整信息\n                    var office_installed_dir_full = GetArchiDirFull();\n\n                    //无目录返回 0 个\n                    if (office_installed_dir_full == null)\n                    {\n                        return new List<string>();\n                    }\n\n                    //遍历所有目录，验证有效性\n                    List<string> office_installed_dir = new List<string>();     //获取所有已知版本的安装路径//将完整信息中的路径作为List存储\n                    foreach (var now_dir in office_installed_dir_full)\n                    {\n                        //非空判断\n                        if (!string.IsNullOrEmpty(now_dir.Value))\n                        {\n                            if (Directory.Exists(now_dir.Value))\n                            {\n                                office_installed_dir.Add(now_dir.Value);\n                            }\n                        }\n                    }\n\n                    return office_installed_dir;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return new List<string>();\n                }\n            }\n\n            internal static List<string> LicenseInfo()\n            {\n                try\n                {\n                    string cmd_switch_cd = $\"pushd \\\"{Documents.SDKs.Activate}\\\"\";                  //切换至OSPP文件目录\n                    string cmd_installed_info = \"cscript ospp.vbs /dstatus\";                                //查看激活状态\n                    string detect_info = Com_ExeOS.Run.Cmd($\"({cmd_switch_cd})&({cmd_installed_info})\");     //查看所有版本激活情况\n\n                    //未安装key，直接返回 0 个\n                    if (detect_info.Contains(\"No installed product keys detected\"))\n                    {\n                        return new List<string>();\n                    }\n\n                    string[] info = detect_info.Split('\\n');\n                    List<string> key_list = new List<string>();\n                    if (info.Length == 0)\n                    {\n                        return new List<string>();\n                    }\n                    else\n                    {\n                        foreach (var now_line in info)\n                        {\n                            if (now_line.Contains(\"key:\"))\n                            {\n                                string now_key = Com_TextOS.GetCenterText(now_line, \"key:\", \"\").Replace(\" \", \"\").TrimEnd('\\r');     //需要移除尾部 \\r 的标记\n                                key_list.Add(now_key);\n                            }\n                        }\n                        return key_list;\n                    }\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return new List<string>();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_OfficeInstall.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_OfficeInstall.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing Microsoft.Win32;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Threading;\nusing static LKY_OfficeTools.Common.Com_ExeOS;\nusing static LKY_OfficeTools.Common.Com_SystemOS;\nusing static LKY_OfficeTools.Lib.Lib_AppCommand;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\nusing static LKY_OfficeTools.Lib.Lib_AppMessage;\nusing static LKY_OfficeTools.Lib.Lib_AppState;\nusing static LKY_OfficeTools.Lib.Lib_OfficeClean;\nusing static LKY_OfficeTools.Lib.Lib_OfficeInfo;\nusing static LKY_OfficeTools.Lib.Lib_OfficeInfo.OfficeLocalInfo;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_OfficeInstall\n    {\n        internal Lib_OfficeInstall()\n        {\n\n            try\n            {\n                //版本获取成功时，执行后续操作。\n                if (OfficeNetInfo.OfficeLatestVersion != null)\n                {\n                    //判断是否已经安装了当前版本\n                    InstallState install_state = GetOfficeState();\n                    if (install_state == InstallState.Correct)                //已安装最新版，无需下载\n                    {\n                        new Log($\"\\n      * 当前系统安装了最新 Office 版本，已跳过下载、安装流程。\", ConsoleColor.DarkMagenta);\n\n                        //开始激活\n                        Lib_OfficeActivate.Activating();\n                    }\n\n                    //当不存在 VersionToReport or 其版本与最新版不一致 or 产品ID不一致 or 安装位数与系统不一致时，需要下载新文件。\n                    else\n                    {\n                        //被动模式下，如果版本不正确，不会重新安装，因为用户可能已经卸载。\n                        if (Current_RunMode == RunMode.Passive)\n                        {\n                            new Log($\"\\n     × 当前系统未安装最新版本的 Office，激活停止！\", ConsoleColor.DarkRed);\n                            return;\n                        }\n\n                        //如果是主动模式，则下载并安装最新版\n                        else if (Current_RunMode == RunMode.Manual)\n                        {\n                            //开始下载，并获得下载结果\n                            int DownCode = Lib_OfficeDownload.StartDownload();\n\n                            //判断下载情况\n                            switch (DownCode)\n                            {\n                                //下载成功，开始安装\n                                case 1:\n                                    //冲突检查\n                                    if (ConflictCheck())\n                                    {\n                                        //通过进入安装，不通过直接返回false\n                                        if (StartInstall())\n                                        {\n                                            //安装成功，进入激活程序\n                                            Lib_OfficeActivate.Activating();\n                                        }\n                                        else\n                                        {\n                                            new Log($\"\\n     × 因 Office 安装失败，已跳过激活流程！\", ConsoleColor.DarkRed);\n                                            break;\n                                        }\n                                    }\n                                    else\n                                    {\n                                        new Log($\"\\n     × 无法完成 Office 冲突性检查，已跳过安装流程！\", ConsoleColor.DarkRed);\n                                        break;\n                                    }\n                                    break;\n                                case 0:\n                                    new Log($\"\\n     × 未能找到可用的 Office 安装文件，已跳过安装流程！\", ConsoleColor.DarkRed);\n                                    break;\n                                case -1:\n                                    //用户中止了下载\n                                    new Log($\"\\n     × 用户取消了下载 Office 安装文件，已跳过安装流程！\", ConsoleColor.DarkRed);\n                                    return;\n                            }\n                        }\n\n                        //其他模式跳出\n                        else\n                        {\n                            new Log($\"{Current_RunMode} 模式下，暂不支持下载、安装、激活操作。\");\n                            return;\n                        }\n                    }\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log($\"\\n     × Office 安装过程中发生异常，已跳过安装流程！\", ConsoleColor.DarkRed);\n                new Log(Ex.ToString());\n            }\n            finally\n            {\n                //全部完成后，判断是否成功\n                if (Current_StageType != ProcessStage.Finish_Success)\n                {\n                    //只要全部流程结束后，不是成功状态（并且没有中断情况），就设置为 失败 \n                    Current_StageType = ProcessStage.Finish_Fail;\n                }\n            }\n        }\n\n        internal static bool ConflictCheck()\n        {\n            try\n            {\n                new Log($\"\\n------> 正在检查 Office 安装环境 ...\", ConsoleColor.DarkCyan);\n\n                //--------------------------------------------- 先检查注册表 ---------------------------------------------\n                bool regdir_pass = false;      //默认是冲突的\n\n                var Current_Office_Dir = GetArchiDir();         //不能用 Full 函数判断是否 null，本字典始终有 key 的，所以恒不为 null\n                if (Current_Office_Dir != null && Current_Office_Dir.Count > 0)     //非空且为大于0时\n                {\n                    //注册表只有1个值，接下来判断这个值属于哪个架构\n                    if (Current_Office_Dir.Count == 1)\n                    {\n                        //判断仅有的1个office架构是不是odt架构（odt下面的 x32 x64 有1个不为空，则再判断是否符合 位数 和 发行ID）\n                        string odt_x32_reg = GetArchiDirFull()[OfficeArchi.Office_ODT_x32];\n                        string odt_x64_reg = GetArchiDirFull()[OfficeArchi.Office_ODT_x64];\n                        if (!string.IsNullOrEmpty(odt_x32_reg) || !string.IsNullOrEmpty(odt_x64_reg))\n                        {\n                            //64位系统额外增加位数判断\n                            if (Environment.Is64BitOperatingSystem)\n                            {\n                                if (!string.IsNullOrEmpty(odt_x32_reg))\n                                {\n                                    //x64系统，但是在注册表x32路径有值，冲突\n                                    regdir_pass = false;\n                                }\n                                else\n                                {\n                                    //x64系统，虽然在注册表x64路径有值了，但是还需判断是否 平台信息 一致\n                                    string platform = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, @\"SOFTWARE\\Microsoft\\Office\\ClickToRun\\Configuration\", \"Platform\");\n                                    if (string.IsNullOrWhiteSpace(platform) || platform == \"x86\")\n                                    {\n                                        regdir_pass = false;       //虽然出现在了与x64系统注册表匹配的路径，但是Office平台版本并非x64\n                                    }\n                                }\n                            }\n\n                            //odt位数与系统匹配，开始校验产品ID\n                            string Current_Office_ID = Register.Read.ValueBySystem(RegistryHive.LocalMachine, @\"SOFTWARE\\Microsoft\\Office\\ClickToRun\\Configuration\", \"ProductReleaseIds\");\n\n                            string Pop_Office_ID = \"2021Volume\";\n\n                            //ODT 只安装了1个版本，但 Current_Office_ID 为空，视为冲突\n                            if (string.IsNullOrEmpty(Current_Office_ID))\n                            {\n                                regdir_pass = false;\n                            }\n                            else\n                            {\n                                //替换支持的版本ID。如果用户还安装了其它架构，进行替换后，该值不是空\n                                var diff_products = Current_Office_ID\n                                    .Replace($\"ProjectPro{Pop_Office_ID}\", \"\")\n                                    .Replace($\"ProPlus{Pop_Office_ID}\", \"\")\n                                    .Replace($\"VisioPro{Pop_Office_ID}\", \"\")\n                                    .Replace(\",\", \"\");\n\n                                //判断有没有额外的版本ID\n                                if (string.IsNullOrWhiteSpace(Current_Office_ID))\n                                {\n                                    //为空，无额外的ID，是不冲突的\n                                    regdir_pass = true;\n                                }\n                                else\n                                {\n                                    //不为空，说明还安装了其他的版本，如：ProPlus2016Volume，则冲突\n                                    regdir_pass = false;\n                                }\n                            }\n                        }\n                        else\n                        {\n                            //不是odt架构，直接false\n                            regdir_pass = false;\n                        }\n                    }\n                    else\n                    {\n                        //如果注册表值大于1个，也就是安装了多个office，直接设置为冲突，设为false\n                        //如果用户的注册表存在两个ODT同版本不同位的情况（x32、x64都有），则也视为有冲突。\n                        regdir_pass = false;\n                    }\n                }\n                else\n                {\n                    //注册表没有值，或者 有值但目录不存在，无冲突，true\n                    regdir_pass = true;\n                }\n                //--------------------------------------------- 检查注册表 结束 ---------------------------------------------\n\n\n\n                //--------------------------------------------- 再检查许可证 ---------------------------------------------\n                bool license_pass = false;     //默认是冲突的\n\n                var installed_key = LicenseInfo();       //获取许可证列表\n                if (installed_key != null && installed_key.Count > 0)\n                {\n                    //注册表有值\n\n                    //获取当前的许可证信息\n                    string cmd_switch_cd = $\"pushd \\\"{AppPath.Documents.SDKs.Activate}\\\"\";                  //切换至OSPP文件目录\n                    string cmd_installed_info = \"cscript ospp.vbs /dstatus\";                                //查看激活状态\n                    string installed_license_info = Run.Cmd($\"({cmd_switch_cd})&({cmd_installed_info})\");     //查看所有版本激活情况\n\n                    //判断值的数量\n                    if (installed_key.Count == 1)\n                    {\n                        //判断安装的许可证是否是目标大版本\n                        if (installed_license_info.Contains(\"ProPlus2021VL\"))\n                        {\n                            //是目标大版本\n                            license_pass = true;\n                        }\n                        else\n                        {\n                            //不是本程序的大版本，直接false\n                            license_pass = false;\n                        }\n                    }\n                    else if (installed_key.Count == 2)\n                    {\n                        //检测到的2个版本是否是支持的3个大版本其中的2个版本。共有3种组合\n                        if (\n                            (installed_license_info.Contains(\"ProPlus2021VL\") && installed_license_info.Contains(\"ProjectPro2021VL\")) |\n                            (installed_license_info.Contains(\"ProPlus2021VL\") && installed_license_info.Contains(\"VisioPro2021VL\")) |\n                            (installed_license_info.Contains(\"ProjectPro2021VL\") && installed_license_info.Contains(\"VisioPro2021VL\"))\n                            )\n                        {\n                            //是目标大版本\n                            license_pass = true;\n                        }\n                        else\n                        {\n                            //不是本程序的大版本，直接false\n                            license_pass = false;\n                        }\n                    }\n                    else if (installed_key.Count == 3)\n                    {\n                        //安装3个许可证，必须是指定的三个版本，否则就是有别的许可证，属于冲突\n                        if (installed_license_info.Contains(\"ProPlus2021VL\")\n                            & installed_license_info.Contains(\"ProjectPro2021VL\")\n                            & installed_license_info.Contains(\"VisioPro2021VL\"))\n                        {\n                            //是目标大版本\n                            license_pass = true;\n                        }\n                        else\n                        {\n                            //不是本程序的大版本，直接false\n                            license_pass = false;\n                        }\n                    }\n                    else\n                    {\n                        //如果大于3个，也就是安装了多个office，直接设置为冲突，设为false\n                        license_pass = false;\n                    }\n                }\n                else\n                {\n                    //许可证没有值，无冲突，true\n                    license_pass = true;\n                }\n                //--------------------------------------------- 检查许可证 结束 ---------------------------------------------\n\n\n                //聚合判断是否有冲突，必须二者同时通过，才是不冲突。只要注册表或者许可证，有一个出现了false，就视为冲突\n                if (regdir_pass && license_pass)\n                {\n                    //不存在版本冲突时，直接开始安装\n                    new Log($\"     √ 已通过 Office 安装环境检查。\", ConsoleColor.DarkGreen);\n                    return true;\n                }\n                else\n                {\n                    new Log($\"     ★ 发现 {Current_Office_Dir.Count + installed_key.Count} 个冲突的 Office 版本，若要继续，必须先卸载旧版（卸载后可能需要重启系统）。\", ConsoleColor.Gray);\n\n                    //判断是否包含自动卸载标记\n                    if (!AppCommandFlag.HasFlag(ArgsFlag.Auto_Remove_Conflict_Office))\n                    {\n                        if (KeyMsg.Confirm(\"确认安装新版 Office 并卸载旧版本及其插件\"))\n                        {\n                            new Log($\"     √ 您已主动确认 卸载 Office 所有旧版本。\", ConsoleColor.DarkGreen);\n                            Thread.Sleep(500);     //基于体验，稍微停留下\n\n                            //获取卸载结果\n                            var result = RemoveAllOffice();\n\n                            //卸载后提示重启电脑\n                            if (KeyMsg.Choose(\"建议您“重启计算机”以确保卸载彻底。【注意】一经确认，系统将在 1分钟 后重启，请务必提前保存文件！\"))\n                            {\n                                //确认重启\n                                new Log($\"     √ 您已主动确认“重启计算机”。系统将在 1分钟 后重启，请及时保存文件。\", ConsoleColor.DarkGreen);\n\n                                //执行重启命令行（shutdown.exe -r -t 3600）\n                                Run.Cmd(\"shutdown.exe -r -t 60\");\n\n                                //重启打点&退出\n                                KeyMsg.Quit(128);\n                            }\n                            else\n                            {\n                                //跳过了重启选择\n                                new Log($\"      * 您已拒绝 重启计算机，若安装失败，您可在重启后重新运行本工具。\", ConsoleColor.DarkMagenta);\n                                Thread.Sleep(500);    //基于体验，稍微停留下\n                            }\n\n                            return result;\n                        }\n                        else\n                        {\n                            new Log($\"     × 您已拒绝 卸载 Office 其他版本，新版本无法安装！\", ConsoleColor.DarkRed);\n                            return false;\n                        }\n                    }\n                    else\n                    {\n                        //有自动卸载的标记，直接开始卸载\n                        new Log($\"      * 自动卸载冲突 Office 模式下，自动确认并开始 ...\", ConsoleColor.DarkMagenta);\n                        return RemoveAllOffice();\n                    }\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return false;\n            }\n        }\n\n        internal static bool StartInstall()\n        {\n            try\n            {\n                //定义ODT文件位置\n                string ODT_path_root = AppPath.Documents.SDKs.SDKs_Root + @\"\\ODT\";\n                string ODT_path_exe = ODT_path_root + @\"\\ODT.exe\";\n                string ODT_path_xml = ODT_path_root + @\"\\config.xml\";\n\n                //检查ODT文件是否存在\n                if (!File.Exists(ODT_path_exe) || !File.Exists(ODT_path_xml))\n                {\n                    new Log($\"     × 目录 {ODT_path_root} 下文件丢失！\", ConsoleColor.DarkRed);\n                    return false;\n                }\n\n                //修改新的xml信息\n                bool isNewInstallPath = Com_FileOS.XML.SetValue(ODT_path_xml, \"SourcePath\", AppPath.ExecuteDir);\n\n                //检查是否修改成功（安装目录）\n                if (!isNewInstallPath)\n                {\n                    new Log($\"     × 配置 Install 信息错误！\", ConsoleColor.DarkRed);\n                    return false;\n                }\n\n                bool isNewVersion = Com_FileOS.XML.SetValue(ODT_path_xml, \"Version\", OfficeNetInfo.OfficeLatestVersion.ToString());\n\n                //检查是否修改成功（版本号）\n                if (!isNewVersion)\n                {\n                    new Log($\"     × 配置 Version 信息错误！\", ConsoleColor.DarkRed);\n                    return false;\n                }\n\n                //获取系统位数\n                int sys_bit;\n                if (Environment.Is64BitOperatingSystem)\n                {\n                    sys_bit = 64;\n                }\n                else\n                {\n                    sys_bit = 32;\n                }\n                bool isNewBit = Com_FileOS.XML.SetValue(ODT_path_xml, \"OfficeClientEdition\", sys_bit.ToString());\n\n                //检查是否修改成功（位数）\n                if (!isNewBit)\n                {\n                    new Log($\"     × 配置 Edition 信息错误！\", ConsoleColor.DarkRed);\n                    return false;\n                }\n\n                var msg_tip = \"\\n     ★ 本工具默认安装 Word、PPT、Excel，并可选配更多组件：\";\n                var msg_index_first = \"\\n        \\tOutlook = 1\\tOneNote = 2\\tAccess = 3\";\n                var msg_index_second = \"\\n        \\tVisio = 4\\tProject = 5\\tPublisher = 6\";\n                var msg_index_third = \"\\n        \\tTeams = 7\\tOneDrive = 8\\tLync = 9\";\n                var msg_input = \"\\n        如安装：Outlook、OneNote、Visio，请输入：1,2,4 后回车，如不增加组件，请直接按回车键。\";\n                new Log(msg_tip + msg_index_first + msg_index_second + msg_index_third + msg_input, ConsoleColor.Gray);\n\n                Console.ForegroundColor = ConsoleColor.Gray;\n                Console.Write(\"\\n        请输入追加的组件序号（多个组件请用逗号隔开）：\");\n\n                //去除非法字符\n                var add_install = Console.ReadLine().Trim(' ').Trim(',').Trim('，').Trim('.').Trim('。')\n                    .Replace(\"，\", \",\").Replace(\",,\", \",\").Replace(\"，，\", \",\")\n                    .Replace(\".\", \",\").Replace(\"。\", \",\").Replace(\" \", \",\");\n\n                //读取配置全部内容\n                var config = File.ReadAllText(ODT_path_xml);\n\n                //默认不安装Visio、Project\n                bool install_visio = false;\n                bool install_project = false;\n\n                //非空判断\n                if (!string.IsNullOrWhiteSpace(add_install))\n                {\n                    //安装附加组件。当无分割符时，split后仍将得到一个元素个数为1的数组\n                    var add_install_list = add_install.Split(',');\n\n                    //遍历要安装的组件\n                    foreach (var now_add in add_install_list)\n                    {\n                        //当前元素非空判断\n                        if (string.IsNullOrWhiteSpace(now_add))\n                        {\n                            new Log($\"     >> 您输入的“{add_install}”存在无效字符，请注意检查！\", ConsoleColor.DarkYellow);\n                            return StartInstall();\n                        }\n\n                        //非法序号区间判断\n                        if (!int.TryParse(now_add, out int install_num) || install_num < 1 || install_num > 9)\n                        {\n                            new Log($\"     >> 您输入的“{now_add}”并非有效序号，如需安装多个组件请以逗号分隔！\", ConsoleColor.DarkYellow);\n                            return StartInstall();\n                        }\n\n                        //判断组件类型\n                        switch (install_num)\n                        {\n                            case 1:\n                                //Outlook\n                                config = config.Replace(\"<ExcludeApp ID=\\\"Outlook\\\" />\", \"\");\n                                break;\n                            case 2:\n                                //OneNote\n                                config = config.Replace(\"<ExcludeApp ID=\\\"OneNote\\\" />\", \"\");\n                                break;\n                            case 3:\n                                //Access\n                                config = config.Replace(\"<ExcludeApp ID=\\\"Access\\\" />\", \"\");\n                                break;\n                            case 4:\n                                //Visio\n                                install_visio = true;\n                                break;\n                            case 5:\n                                //Project\n                                install_project = true;\n                                break;\n                            case 6:\n                                //Publisher\n                                config = config.Replace(\"<ExcludeApp ID=\\\"Publisher\\\" />\", \"\");\n                                break;\n                            case 7:\n                                //Teams\n                                config = config.Replace(\"<ExcludeApp ID=\\\"Teams\\\" />\", \"\");\n                                break;\n                            case 8:\n                                //OneDrive\n                                config = config.Replace(\"<ExcludeApp ID=\\\"OneDrive\\\" />\", \"\");\n                                break;\n                            case 9:\n                                //Lync\n                                config = config.Replace(\"<ExcludeApp ID=\\\"Lync\\\" />\", \"\");\n                                break;\n                        }\n                    }\n\n                    new Log($\"     √ 检查通过，本工具将安装 Word、PPT、Excel，以及额外的 {add_install} 号组件。\", ConsoleColor.DarkGreen);\n                }\n                //不安装附加组件的情况\n                else\n                {\n                    new Log($\"     √ 未附加其他组件，本工具将只安装默认的 Word、PPT、Excel 三件套。\", ConsoleColor.DarkGreen);\n                }\n\n                //不安装Viso时，移除相关配置\n                if (!install_visio)\n                {\n                    var remove_info = Com_TextOS.GetCenterText(config, \"<Product ID=\\\"Visio\", \"</Product>\");\n                    remove_info = \"<Product ID=\\\"Visio\" + remove_info + \"</Product>\";\n                    config = config.Replace(remove_info, \"\");\n                }\n\n                //不安装Project时，移除相关配置\n                if (!install_project)\n                {\n                    var remove_info = Com_TextOS.GetCenterText(config, \"<Product ID=\\\"Project\", \"</Product>\");\n                    remove_info = \"<Product ID=\\\"Project\" + remove_info + \"</Product>\";\n                    config = config.Replace(remove_info, \"\");\n                }\n\n                //保存修改后的配置\n                File.WriteAllText(ODT_path_xml, config);\n\n                //开始安装\n                new Log($\"\\n------> 正在安装 Office v{OfficeNetInfo.OfficeLatestVersion} ...\", ConsoleColor.DarkCyan);\n\n                Lib_AppSdk.KillAllSdkProcess(KillExe.KillMode.Only_Force);\n\n                string install_args = $\"/configure \\\"{ODT_path_xml}\\\"\";     //配置命令行\n                var install_code = Run.Exe(ODT_path_exe, install_args);\n\n                //检查是否因配置不正确等导致，意外退出安装\n                if (install_code == -920921)\n                {\n                    new Log($\"     × Office v{OfficeNetInfo.OfficeLatestVersion} 安装意外结束！\", ConsoleColor.DarkRed);\n                    return false;\n                }\n\n                //无论是否成功，都增加一步结束进程\n                KillExe.ByExeName(\"OfficeClickToRun\", KillExe.KillMode.Only_Force, true);      //结束无关进程\n                KillExe.ByExeName(\"OfficeC2RClient\", KillExe.KillMode.Only_Force, true);       //结束无关进程\n\n                //检查安装是否成功\n                InstallState install_state = GetOfficeState();\n\n                //安装了最新版\n                if (install_state == InstallState.Correct)\n                {\n                    //安装成功\n                    new Log($\"     √ 已完成 Office v{OfficeNetInfo.OfficeLatestVersion} 安装。\", ConsoleColor.DarkGreen);\n                    return true;\n                }\n                else\n                {\n                    //安装存在问题\n                    string err_msg = $\"ODT Installing Exception, ExitCode: {install_code}\";\n                    if (install_code > 0)\n                    {\n                        //只解析错误码大于0的情况\n                        string err_string = string.Empty;\n                        if (ODT_Error.TryGetValue(((uint)install_code), out err_string))\n                        {\n                            err_msg += $\"{err_string}\";\n                        }\n                    }\n\n                    new Log(err_msg);         //回调错误码\n\n                    //未安装\n                    if (install_state == InstallState.None)\n                    {\n                        new Log($\"     × 安装失败，未在当前系统检测到任何 Office 版本！\", ConsoleColor.DarkRed);\n                        new Log(install_state);     //打点失败注册表记录\n                        return false;\n                    }\n\n                    //包含不同版本\n                    if (install_state == InstallState.Diff)\n                    {\n                        new Log($\"     × 已安装的 Office 版本与预期的 v{OfficeNetInfo.OfficeLatestVersion} 版本不符！\", ConsoleColor.DarkRed);\n                        new Log(install_state);     //打点失败注册表记录\n                        return false;\n                    }\n\n                    //包含多个版本\n                    if (install_state == InstallState.Multi)\n                    {\n                        //系统存在多个版本\n                        new Log($\"     × 安装异常，当前系统存在多个 Office 版本！\", ConsoleColor.DarkRed);\n                        new Log(install_state);     //打点失败注册表记录\n                        return false;\n                    }\n\n                    //其它未可知情况，视为失败\n                    return false;\n                }\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return false;\n            }\n        }\n\n        internal static Dictionary<uint, string> ODT_Error\n        {\n            get\n            {\n                Dictionary<uint, string> res = new Dictionary<uint, string>\n                {\n                    [0] = \"安装成功。\",\n                    [997] = \"安装正在进行中。\",\n                    [13] = \"无法验证下载的 Office 部署工具 (ODT) 的签名。\",\n                    [1460] = \"下载 ODT 超时。\",\n                    [1602] = \"用户已取消运行。\",\n                    [1603] = \"未通过任何预检检查。安装 2016 MSI 时尝试安装 SxS。\" +\n                    \"当前安装的 Office 与尝试安装的 Office 之间的位不匹配 (例如，在当前安装 64 位版本时尝试安装 32 位版本时。)\",\n                    [17000] = \"未能启动 C2RClient。\",\n                    [17001] = \"未能在 C2RClient 中排队安装方案。\",\n                    [17002] = \"未能完成该过程。可能的原因：\" +\n                    \"（1）用户已取消安装\" +\n                    \"（2）安装已由另一个安装取消\" +\n                    \"（3）安装期间磁盘空间不足\" +\n                    \"（4）未知语言 ID\",\n                    [17003] = \"另一个方案正在运行。\",\n                    [17004] = \"无法完成需要的清理。可能的原因：\" +\n                    \"（1）未知 SKU\" +\n                    \"（2）CDN 上不存在内容。例如，尝试安装不受支持的 LAP，例如 zh-sg\" +\n                    \"（3）内容不可用的 CDN 问题\" +\n                    \"（4）签名检查问题，例如 Office 内容的签名检查失败\" +\n                    \"（5）用户已取消\",\n                    [17005] = \"ERROR！SCENARIO CANCELLED AS PLANNED。\",\n                    [17006] = \"通过运行应用阻止更新。\",\n                    [17007] = \"客户端在“删除安装”方案中请求客户端清理。\",\n                    [17100] = \"C2RClient 命令行错误。\",\n                    [0x80004005] = \"ODT 不能用于安装批量许可证。\",\n                    [0x8000ffff] = \"尝试在计算机上没有 C2R Office 时卸载。\"\n                };\n                return res;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Lib/Lib_OfficeProcess.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : Lib_OfficeProcess.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Reflection;\nusing static LKY_OfficeTools.Common.Com_ExeOS.KillExe;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\n\nnamespace LKY_OfficeTools.Lib\n{\n    internal class Lib_OfficeProcess\n    {\n        internal static List<string> Process_List\n        {\n            get\n            {\n                try\n                {\n                    Stream office_processes_res = Assembly.GetExecutingAssembly().\n                    GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时，此值也需要调整 */\n                    + \".Resource.Office_Processes.list\");\n                    StreamReader office_processes_sr = new StreamReader(office_processes_res);\n                    string office_processes = office_processes_sr.ReadToEnd();\n                    if (!string.IsNullOrWhiteSpace(office_processes))\n                    {\n                        List<string> office_processes_list = new List<string>();\n                        string[] p_info = office_processes.Replace(\"\\r\", \"\").Split('\\n');      //分割出进程数组\n                        if (p_info != null && p_info.Length > 0)\n                        {\n                            foreach (var now_process in p_info)\n                            {\n                                office_processes_list.Add(now_process);\n                            }\n\n                            return office_processes_list;\n                        }\n                    }\n                    return null;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return null;\n                }\n            }\n        }\n\n        internal static List<string> GetRuningProcess()\n        {\n            try\n            {\n                List<string> runing_office_process = new List<string>();\n                foreach (var now_p in Process_List)\n                {\n                    if (Com_ExeOS.Info.IsRun(now_p))\n                    {\n                        runing_office_process.Add(now_p);\n                    }\n                }\n\n                return runing_office_process;\n            }\n            catch (Exception Ex)\n            {\n                new Log(Ex.ToString());\n                return null;\n            }\n        }\n\n        internal class KillOffice\n        {\n            internal static bool All()\n            {\n                try\n                {\n                    //轮询结束每个进程（等待其结束）\n                    foreach (var now_p in Process_List)\n                    {\n                        //new Log($\"     >> 等待 {now_p.ToLower()} 进程关闭中 ...\", ConsoleColor.DarkYellow);\n                        Com_ExeOS.KillExe.ByExeName(now_p, KillMode.Try_Friendly, true);\n                    }\n\n                    return true;\n                }\n                catch (Exception Ex)\n                {\n                    new Log(Ex.ToString());\n                    return false;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/OfficeTools.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : OfficeTools.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing LKY_OfficeTools.Common;\nusing LKY_OfficeTools.Lib;\nusing System;\nusing System.Text;\nusing static LKY_OfficeTools.Lib.Lib_AppClosing;\nusing static LKY_OfficeTools.Lib.Lib_AppCommand;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\nusing static LKY_OfficeTools.Lib.Lib_AppLog;\nusing static LKY_OfficeTools.Lib.Lib_AppMessage;\nusing static LKY_OfficeTools.Lib.Lib_AppState;\n\nnamespace LKY_OfficeTools\n{\n    internal class OfficeTools\n    {\n        static void Main(string[] args)\n        {\n            //命令行检测\n            new Lib_AppCommand(args);\n\n            //设定编码，解决英文系统乱码问题\n            Console.OutputEncoding = Encoding.GetEncoding(\"gbk\");       //必须在启动模式判断之后再进行本函数执行。否则服务模式将引发 1053 错误！\n\n            //中断检测\n            CloseWindow.SetConsoleCtrlHandler(CloseWindow.newDelegate, true);\n\n            //启动\n            Entry();\n        }\n\n        private static void Entry()\n        {\n            //欢迎话术\n            Console.Title = $\"{AppAttribute.AppName} v{AppAttribute.AppVersion}\";\n            new Log($\"{AppAttribute.AppName} [版本 {AppAttribute.AppVersion}]\\n\" +\n                $\"版权所有（C）LiuKaiyuan (Odysseus.Yuan)。保留所有权利。\\n\\n\" +\n                $\"探讨 {AppAttribute.AppName} 相关内容，可发送邮件至：OdysseusYuan@foxmail.com\", ConsoleColor.Gray);\n\n            //清理冗余信息\n            Log.Clean();\n\n            //数字签名证书检查\n            new Lib_AppSignCert();\n\n            //确认系统情况\n            if (int.Parse(Com_SystemOS.OSVersion.GetBuildNumber()) < 15063)\n            {\n                //小于 Win10 1703 的操作系统，激活存在失败问题\n                new Log($\"\\n     × 请将当前操作系统升级至 Windows 10 (1703) 或其以上版本，否则 Office 无法进行正版激活！\", ConsoleColor.DarkRed);\n\n                //退出提示\n                KeyMsg.Quit(-4);\n\n                return;\n            }\n\n            //确认联网情况\n            if (!Com_NetworkOS.Check.IsConnected)\n            {\n                new Log($\"\\n     × 请确保当前电脑可正常访问互联网！\", ConsoleColor.DarkRed);\n\n                //退出提示\n                KeyMsg.Quit(-5);\n\n                return;\n            }\n\n            //根据命令行判断是否等待用户，没有标记时，执行倒计时等待\n            if (!AppCommandFlag.HasFlag(ArgsFlag.None_Welcome_Confirm))\n            {\n                KeyMsg.DoByTime($\"部署\", 5);\n            }\n\n            //权限检查\n            Com_PrivilegeOS.PrivilegeAttention();\n\n            //SDK初始化\n            Lib_AppSdk.Initial();\n\n            //更新检查\n            Lib_AppUpdate.Check();\n\n            //继续\n            new Lib_OfficeInstall();\n\n            //部署成功时，提示是否配置为服务\n            if (Current_StageType == ProcessStage.Finish_Success)\n            {\n                //被强制使用“我的文档”目录时，往往因为权限问题才使用，此时禁用服务配置\n                if (!Must_Use_PersonalDir)\n                {\n                    //配置服务\n                    Lib_AppServiceConfig.Setup();\n                }\n\n                //结论\n                new Log($\"\\n     √ 您已成功完成 {AppAttribute.AppName} 所有流程，感谢您的使用。\", ConsoleColor.DarkGreen);\n            }\n            //部署失败，提示错误信息\n            else if (Current_StageType == ProcessStage.Finish_Fail)\n            {\n                //结论\n                new Log($\"\\n     × 当前部署存在失败环节，您可在稍后重试运行！\", ConsoleColor.DarkRed);\n            }\n\n            //退出提示\n            KeyMsg.Quit(0);\n\n        }\n    }\n}\n"
  },
  {
    "path": "LKY_OfficeTools/Properties/AssemblyInfo.cs",
    "content": "﻿/*\n *      [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc.\n *      \n *      FileName : AssemblyInfo.cs\n *      Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan)\n */\n\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing static LKY_OfficeTools.Lib.Lib_AppInfo;\n\n// 有关程序集的一般信息由以下\n// 控制。更改这些特性值可修改\n// 与程序集关联的信息。\n[assembly: AssemblyTitle(AppAttribute.AppName)]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyCompany(AppAttribute.Developer)]\n[assembly: AssemblyProduct(AppAttribute.AppName)]\n[assembly: AssemblyCopyright(\"Copyright (C) 2022 - 2024 LiuKaiyuan. All rights reserved.\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// 将 ComVisible 设置为 false 会使此程序集中的类型\n//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型\n//请将此类型的 ComVisible 特性设置为 true。\n[assembly: ComVisible(false)]\n\n// 如果此项目向 COM 公开，则下列 GUID 用于类型库的 ID\n[assembly: Guid(\"6d00f508-106a-42d1-be0f-048762434e69\")]\n\n// 程序集的版本信息由下列四个值组成: \n//\n//      主版本\n//      次版本\n//      生成号\n//      修订号\n//\n//可以指定所有这些值，也可以使用“生成号”和“修订号”的默认值\n//通过使用 \"*\"，如下所示:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(AppAttribute.AppVersion)]\n[assembly: AssemblyFileVersion(AppAttribute.AppVersion)]\n"
  },
  {
    "path": "LKY_OfficeTools/Properties/app.manifest",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <assemblyIdentity version=\"1.0.0.0\" name=\"MyApplication.app\" />\n  <trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v2\">\n    <security>\n      <requestedPrivileges xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n        <!-- UAC 清单选项\n             如果想要更改 Windows 用户帐户控制级别，请使用\n             以下节点之一替换 requestedExecutionLevel 节点。\n\n        <requestedExecutionLevel  level=\"asInvoker\" uiAccess=\"false\" />\n        <requestedExecutionLevel  level=\"requireAdministrator\" uiAccess=\"false\" />\n        <requestedExecutionLevel  level=\"highestAvailable\" uiAccess=\"false\" />\n\n            指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。\n            如果你的应用程序需要此虚拟化来实现向后兼容性，则移除此\n            元素。\n        -->\n        <requestedExecutionLevel level=\"requireAdministrator\" uiAccess=\"false\" />\n      </requestedPrivileges>\n      <applicationRequestMinimum>\n        <defaultAssemblyRequest permissionSetReference=\"Custom\" />\n        <PermissionSet class=\"System.Security.PermissionSet\" version=\"1\" ID=\"Custom\" SameSite=\"site\" />\n      </applicationRequestMinimum>\n    </security>\n  </trustInfo>\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的\n           Windows 版本的列表。取消评论适当的元素，\n           Windows 将自动选择最兼容的环境。 -->\n      <!-- Windows Vista -->\n      <!--<supportedOS Id=\"{e2011457-1546-43c5-a5fe-008deee3d3f0}\" />-->\n      <!-- Windows 7 -->\n      <!--<supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\" />-->\n      <!-- Windows 8 -->\n      <!--<supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\" />-->\n      <!-- Windows 8.1 -->\n      <!--<supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\" />-->\n      Windows 10\n      <supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\" />\n    </application>\n  </compatibility>\n  <!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行\n       自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI，无需\n       选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应\n       在其 app.config 中将 \"EnableWindowsFormsHighDpiAutoResizing\" 设置设置为 \"true\"。\n       \n       将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->\n  <!--\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true</dpiAware>\n      <longPathAware xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">true</longPathAware>\n    </windowsSettings>\n  </application>\n  -->\n  <!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->\n  <!--\n  <dependency>\n    <dependentAssembly>\n      <assemblyIdentity\n          type=\"win32\"\n          name=\"Microsoft.Windows.Common-Controls\"\n          version=\"6.0.0.0\"\n          processorArchitecture=\"*\"\n          publicKeyToken=\"6595b64144ccf1df\"\n          language=\"*\"\n        />\n    </dependentAssembly>\n  </dependency>\n  -->\n</assembly>"
  },
  {
    "path": "LKY_OfficeTools/Resource/Json/OfficeChannels.txt",
    "content": "[{\"name\":\"CurrentPreview\",\"displayName\":\"Current Channel (Preview)\",\"alternateNames\":[\"InsiderSlow\",\"FirstReleaseCurrent\",\"Insiders\"],\"baseUrl\":\"http://officecdn.microsoft.com/pr/64256afe-f5d9-4f86-8936-8840a6a4f5be\",\"updates\":[{\"version\":\"2205\",\"legacyVersion\":\"16.0.15225.20092\",\"build\":\"15225.20092\",\"pubTime\":\"2022-05-09T18:27:07.22Z\"},{\"version\":\"2204\",\"legacyVersion\":\"16.0.15128.20206\",\"build\":\"15128.20206\",\"pubTime\":\"2022-05-04T16:25:00.447Z\"}],\"latestUpdateVersion\":\"16.0.15225.20092\"},{\"name\":\"SemiAnnualPreview\",\"displayName\":\"Semi-Annual Enterprise Channel (Preview)\",\"alternateNames\":[\"FirstReleaseDeferred\",\"Targeted\"],\"baseUrl\":\"http://officecdn.microsoft.com/pr/b8f9b850-328d-4355-9145-c59439a0c4cf\",\"updates\":[{\"version\":\"2202\",\"legacyVersion\":\"16.0.14931.20392\",\"build\":\"14931.20392\",\"pubTime\":\"2022-05-10T17:06:07.17Z\"},{\"version\":\"2202\",\"legacyVersion\":\"16.0.14931.20274\",\"build\":\"14931.20274\",\"pubTime\":\"2022-04-12T08:40:50.223Z\"}],\"latestUpdateVersion\":\"16.0.14931.20392\"},{\"name\":\"SemiAnnual\",\"displayName\":\"Semi-Annual Enterprise Channel\",\"alternateNames\":[\"Deferred\",\"Broad\"],\"baseUrl\":\"http://officecdn.microsoft.com/pr/7ffbc6bf-bc32-4f92-8982-f9dd17fd3114\",\"updates\":[{\"version\":\"2108\",\"legacyVersion\":\"16.0.14326.20962\",\"build\":\"14326.20962\",\"pubTime\":\"2022-05-10T07:33:01.94Z\"},{\"version\":\"2108\",\"legacyVersion\":\"16.0.14326.20910\",\"build\":\"14326.20910\",\"pubTime\":\"2022-04-12T07:24:22.143Z\"},{\"version\":\"2108\",\"legacyVersion\":\"16.0.14326.20852\",\"build\":\"14326.20852\",\"pubTime\":\"2022-03-08T08:39:08.78Z\"},{\"version\":\"2108\",\"legacyVersion\":\"16.0.14326.20784\",\"build\":\"14326.20784\",\"pubTime\":\"2022-02-08T08:23:35.403Z\"},{\"version\":\"2108\",\"legacyVersion\":\"16.0.14326.20738\",\"build\":\"14326.20738\",\"pubTime\":\"2022-01-11T08:25:14.823Z\"},{\"version\":\"2102\",\"legacyVersion\":\"16.0.13801.21334\",\"build\":\"13801.21334\",\"pubTime\":\"2022-05-10T07:43:05.187Z\"},{\"version\":\"2102\",\"legacyVersion\":\"16.0.13801.21278\",\"build\":\"13801.21278\",\"pubTime\":\"2022-04-12T07:37:56.357Z\"},{\"version\":\"2102\",\"legacyVersion\":\"16.0.13801.21214\",\"build\":\"13801.21214\",\"pubTime\":\"2022-03-08T18:46:47.31Z\"},{\"version\":\"2102\",\"legacyVersion\":\"16.0.13801.21156\",\"build\":\"13801.21156\",\"pubTime\":\"2022-02-08T08:36:32.94Z\"},{\"version\":\"2102\",\"legacyVersion\":\"16.0.13801.21106\",\"build\":\"13801.21106\",\"pubTime\":\"2022-01-11T08:38:46.807Z\"},{\"version\":\"2102\",\"legacyVersion\":\"16.0.13801.21092\",\"build\":\"13801.21092\",\"pubTime\":\"2021-12-16T21:03:44.23Z\"},{\"version\":\"2102\",\"legacyVersion\":\"16.0.13801.21050\",\"build\":\"13801.21050\",\"pubTime\":\"2021-11-09T08:31:32.263Z\"},{\"version\":\"2102\",\"legacyVersion\":\"16.0.13801.21004\",\"build\":\"13801.21004\",\"pubTime\":\"2021-10-12T07:27:18.663Z\"},{\"version\":\"2102\",\"legacyVersion\":\"16.0.13801.20960\",\"build\":\"13801.20960\",\"pubTime\":\"2021-09-14T07:35:07.46Z\"},{\"version\":\"2102\",\"legacyVersion\":\"16.0.13801.20864\",\"build\":\"13801.20864\",\"pubTime\":\"2021-08-10T07:29:05.727Z\"},{\"version\":\"2102\",\"legacyVersion\":\"16.0.13801.20808\",\"build\":\"13801.20808\",\"pubTime\":\"2021-07-13T07:25:30.66Z\"}],\"latestUpdateVersion\":\"16.0.14326.20962\"},{\"name\":\"Current\",\"displayName\":\"Current Channel\",\"alternateNames\":[\"Current\",\"Monthly\"],\"baseUrl\":\"http://officecdn.microsoft.com/pr/492350f6-3a01-4f97-b9c0-c7c6ddf67d60\",\"updates\":[{\"version\":\"2204\",\"legacyVersion\":\"16.0.15128.20224\",\"build\":\"15128.20224\",\"pubTime\":\"2022-05-10T07:37:41.747Z\"},{\"version\":\"2204\",\"legacyVersion\":\"16.0.15128.20178\",\"build\":\"15128.20178\",\"pubTime\":\"2022-04-26T22:07:27.363Z\"}],\"latestUpdateVersion\":\"16.0.15128.20224\"},{\"name\":\"PerpetualVL2019\",\"displayName\":\"Office 2019 Perpetual Enterprise\",\"alternateNames\":[\"Perpetual2019\"],\"baseUrl\":\"http://officecdn.microsoft.com/pr/f2e724c1-748f-4b47-8fb8-8e0d210e9208\",\"updates\":[{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10386.20017\",\"build\":\"10386.20017\",\"pubTime\":\"2022-05-10T13:17:03.267Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10385.20027\",\"build\":\"10385.20027\",\"pubTime\":\"2022-04-12T13:18:24.81Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10384.20023\",\"build\":\"10384.20023\",\"pubTime\":\"2022-03-08T14:17:24.187Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10383.20027\",\"build\":\"10383.20027\",\"pubTime\":\"2022-02-08T14:17:19.753Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10382.20034\",\"build\":\"10382.20034\",\"pubTime\":\"2022-01-11T20:08:33.663Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10382.20010\",\"build\":\"10382.20010\",\"pubTime\":\"2021-12-20T18:57:20.533Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10380.20037\",\"build\":\"10380.20037\",\"pubTime\":\"2021-11-09T14:17:03.803Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10379.20043\",\"build\":\"10379.20043\",\"pubTime\":\"2021-10-12T13:30:53.57Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10378.20029\",\"build\":\"10378.20029\",\"pubTime\":\"2021-09-14T08:14:18.733Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10377.20023\",\"build\":\"10377.20023\",\"pubTime\":\"2021-08-10T13:19:17.46Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10376.20033\",\"build\":\"10376.20033\",\"pubTime\":\"2021-07-13T12:48:19Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10375.20036\",\"build\":\"10375.20036\",\"pubTime\":\"2021-06-08T12:48:30.98Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10374.20040\",\"build\":\"10374.20040\",\"pubTime\":\"2021-05-11T12:55:45.37Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10373.20050\",\"build\":\"10373.20050\",\"pubTime\":\"2021-04-13T13:19:21.94Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10372.20060\",\"build\":\"10372.20060\",\"pubTime\":\"2021-03-09T14:35:43.8Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10371.20060\",\"build\":\"10371.20060\",\"pubTime\":\"2021-02-09T14:34:38.107Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10370.20052\",\"build\":\"10370.20052\",\"pubTime\":\"2021-01-12T14:35:21.603Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10369.20032\",\"build\":\"10369.20032\",\"pubTime\":\"2020-12-08T15:34:15.573Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10368.20035\",\"build\":\"10368.20035\",\"pubTime\":\"2020-11-10T19:44:32.977Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10367.20048\",\"build\":\"10367.20048\",\"pubTime\":\"2020-10-13T16:05:46.547Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10366.20016\",\"build\":\"10366.20016\",\"pubTime\":\"2020-09-08T14:35:02.527Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10364.20059\",\"build\":\"10364.20059\",\"pubTime\":\"2020-08-11T14:34:44.503Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10363.20015\",\"build\":\"10363.20015\",\"pubTime\":\"2020-07-14T16:41:01.31Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10361.20002\",\"build\":\"10361.20002\",\"pubTime\":\"2020-06-09T16:08:24.84Z\"},{\"version\":\"LTSB2018\",\"legacyVersion\":\"16.0.10359.20023\",\"build\":\"10359.20023\",\"pubTime\":\"2020-05-12T14:41:12.863Z\"}],\"latestUpdateVersion\":\"16.0.10386.20017\"},{\"name\":\"PerpetualVL2021\",\"displayName\":\"Office 2021 Perpetual Enterprise\",\"alternateNames\":[\"Perpetual2021\"],\"baseUrl\":\"http://officecdn.microsoft.com/pr/5030841d-c919-4594-8d2d-84ae4f96e58e\",\"updates\":[{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20303\",\"build\":\"14332.20303\",\"pubTime\":\"2022-05-10T13:22:45.863Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20281\",\"build\":\"14332.20281\",\"pubTime\":\"2022-04-12T14:06:33.337Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20255\",\"build\":\"14332.20255\",\"pubTime\":\"2022-03-08T14:13:36.797Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20238\",\"build\":\"14332.20238\",\"pubTime\":\"2022-02-08T14:13:02.427Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20216\",\"build\":\"14332.20216\",\"pubTime\":\"2022-01-11T19:16:03.15Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20176\",\"build\":\"14332.20176\",\"pubTime\":\"2021-11-09T14:13:41.687Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20145\",\"build\":\"14332.20145\",\"pubTime\":\"2021-10-12T15:19:08.937Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20110\",\"build\":\"14332.20110\",\"pubTime\":\"2021-09-15T19:51:58.963Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20099\",\"build\":\"14332.20099\",\"pubTime\":\"2021-09-07T17:28:23.277Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20077\",\"build\":\"14332.20077\",\"pubTime\":\"2021-08-30T23:12:05.21Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20058\",\"build\":\"14332.20058\",\"pubTime\":\"2021-08-23T19:56:34.247Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20033\",\"build\":\"14332.20033\",\"pubTime\":\"2021-08-16T18:30:31.74Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20011\",\"build\":\"14332.20011\",\"pubTime\":\"2021-08-10T23:12:09.037Z\"},{\"version\":\"LTSB2021\",\"legacyVersion\":\"16.0.14332.20003\",\"build\":\"14332.20003\",\"pubTime\":\"2021-08-04T17:27:53.7Z\"}],\"latestUpdateVersion\":\"16.0.14332.20303\"},{\"name\":\"MonthlyEnterprise\",\"displayName\":\"Monthly Enterprise Channel\",\"alternateNames\":[\"MonthlyEnterprise\"],\"baseUrl\":\"http://officecdn.microsoft.com/pr/55336b82-a18d-4dd6-b5f6-9e5095c314a6\",\"updates\":[{\"version\":\"2203\",\"legacyVersion\":\"16.0.15028.20248\",\"build\":\"15028.20248\",\"pubTime\":\"2022-05-10T07:26:01.423Z\"},{\"version\":\"2202\",\"legacyVersion\":\"16.0.14931.20392\",\"build\":\"14931.20392\",\"pubTime\":\"2022-05-10T17:12:35.443Z\"}],\"latestUpdateVersion\":\"16.0.15028.20248\"}]"
  },
  {
    "path": "LKY_OfficeTools/Resource/Office_Processes.list",
    "content": "﻿EXCEL\nGROOVE\nINFOPATH\nlync\nMSACCESS\nmsoev\nmsotd\nMSOUC\nMSPUB\nMSQRY32\nMSTORE\nOcPubMgr\nOIS\nOLCFG\nONENOTE\nORGCHART\nOUTLOOK\nPOWERPNT\nSCANPST\nSETLANG\nTeams\nWINWORD"
  },
  {
    "path": "LKY_OfficeTools/Resource/SDK/SDK_Processes.list",
    "content": "﻿OSPPREARM\nlot_aria2c\nODT\ncleanospp_64\ncleanospp_86\nSaRACmd\nOfficeClickToRun\nOfficeC2RClient"
  },
  {
    "path": "LKY_OfficeTools.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.3.32929.385\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"LKY_OfficeTools\", \"LKY_OfficeTools\\LKY_OfficeTools.csproj\", \"{6D00F508-106A-42D1-BE0F-048762434E69}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{6D00F508-106A-42D1-BE0F-048762434E69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{6D00F508-106A-42D1-BE0F-048762434E69}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{6D00F508-106A-42D1-BE0F-048762434E69}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{6D00F508-106A-42D1-BE0F-048762434E69}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {BECB0D54-D9AD-42F5-9AAE-FD91AF555D72}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "README.md",
    "content": "﻿#\n\n## LKY Office Tools\n > 一键自动化 下载、安装、激活 Office 的利器。绿色、开源、安全、无毒。\n\n目前包含的功能：\n- 一键快速下载、安装、激活最新版 Microsoft Office 软件。\n- 用户可在安装 Word、PPT、Excel 的同时，根据软件提示，自助安装其它组件，包括：\nOutlook、OneNote、Access、Visio、Project、Publisher、Teams、OneDrive、Lync/Skype。\n- 工具可自动识别不同的操作系统架构，自动下载适配版本的 Office。\n- 本工具激活 Office 的方式为正版激活模式，不会篡改任何系统文件。\n- 当系统中存在多个冗余 Office 版本时，本工具在用户同意的情况下，可实现自动升级。\n\n## 使用方法\n- 下载形如：LKY_OfficeTools_v*.zip 的压缩包，最新版地址：https://github.com/OdysseusYuan/LKY_OfficeTools/releases/latest\n- 解压下载好的 zip 压缩包，运行解压后目录下的 LKY_OfficeTools.exe 文件即可完成部署。\n\n## 运行环境\n- 为保证安装、激活可以最大限度成功，请您尽可能在新装系统后（或从未安装过 Microsoft Office 软件的系统中）运行本工具。\n- 目前已经在 Windows 10(1703) 及以上版本进行了测试，均可在其 x86、x64 完美安装正版 Office 并激活。\n- 应 Office 官方要求，系统中只能部署唯一架构类型，故本工具部署时，会征求用户同意，由其自行决定是否卸载其它架构。\n- 2016年7月及其之前发布的 Windows 系统（即：Windows 10(1703) 之前的系统），因其架构过于陈旧，其已经不再支持安装最新的正版 Office。\n \n## 开源说明\n- 欢迎广大开发者、爱好者们 Fork、PR 本仓库。参考、引用、转发、二次开发本开源库时，请注明来源。\n- 本工具自动激活功能，仅用于给大家无偿的学习编程开发能力之用，如需商业办公使用，请从微软官网购买商业许可。\n- 任何自然人、企业、机构等，不得使用任何直接或间接的方式将其商业化，违犯成员需要为此承担全部法律责任。\n- 当前工具将持续进行迭代更新和维护，迭代过程中不排除在各种因素综合下，可能产生计算机运行的各种情况。\n\n## 版权保护\n- 软件已获多项著作权（含修改保护）：软著登字第10549507号、软著登字第10895737号、软著登字第10850084号、软著登字第11497426号、软著登字第11573194号等。\n\n## 关于\n- 如有建议、疑惑等，大家可以发邮件至 [OdysseusYuan@foxmail.com](mailto:OdysseusYuan@foxmail.com) 交流探讨。\n- © 2022 - 2024 LiuKaiyuan (Odysseus.Yuan). All Rights Reserved.\n"
  }
]