Repository: OdysseusYuan/LKY_OfficeTools Branch: master Commit: 6f9a1bd47191 Files: 47 Total size: 272.3 KB Directory structure: gitextract_16dtn_90/ ├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── LKY_OfficeTools/ │ ├── App.config │ ├── Common/ │ │ ├── Com_ExeOS.cs │ │ ├── Com_FileOS.cs │ │ ├── Com_InstallerOS.cs │ │ ├── Com_NetworkOS.cs │ │ ├── Com_PrivilegeOS.cs │ │ ├── Com_ServiceOS.cs │ │ ├── Com_SystemOS.cs │ │ ├── Com_TextOS.cs │ │ ├── Com_Timer.cs │ │ └── Com_WebOS.cs │ ├── LKY_OfficeTools.csproj │ ├── Lib/ │ │ ├── Lib_AppClosing.cs │ │ ├── Lib_AppCommand.cs │ │ ├── Lib_AppInfo.cs │ │ ├── Lib_AppLog.cs │ │ ├── Lib_AppMessage.cs │ │ ├── Lib_AppSdk.cs │ │ ├── Lib_AppServiceConfig.cs │ │ ├── Lib_AppServiceHub.Designer.cs │ │ ├── Lib_AppServiceHub.cs │ │ ├── Lib_AppSignCert.cs │ │ ├── Lib_AppState.cs │ │ ├── Lib_AppUpdate.cs │ │ ├── Lib_Aria2c.cs │ │ ├── Lib_OfficeActivate.cs │ │ ├── Lib_OfficeClean.cs │ │ ├── Lib_OfficeDownload.cs │ │ ├── Lib_OfficeInfo.cs │ │ ├── Lib_OfficeInstall.cs │ │ └── Lib_OfficeProcess.cs │ ├── OfficeTools.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ └── app.manifest │ └── Resource/ │ ├── Json/ │ │ └── OfficeChannels.txt │ ├── Office_Processes.list │ ├── PublisherCert.cer │ └── SDK/ │ ├── Activate.lotp │ ├── Aria2c.lotp │ ├── ODT.lotp │ ├── SDK_Processes.list │ └── SaRA.lotp ├── LKY_OfficeTools.sln └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Mono auto generated files mono_crash.* # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Oo]ut/ [Ll]og/ [Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUnit *.VisualState.xml TestResult.xml nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # ASP.NET Scaffolding ScaffoldingReadMe.txt # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Coverlet is a free, cross platform Code Coverage Tool coverage*.json coverage*.xml coverage*.info # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # NuGet Symbol Packages *.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx *.appxbundle *.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !?*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser *- [Bb]ackup.rdl *- [Bb]ackup ([0-9]).rdl *- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # CodeRush personal settings .cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Local History for Visual Studio .localhistory/ # BeatPulse healthcheck temp database healthchecksdb # Backup folder for Package Reference Convert tool in Visual Studio 2017 MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ # Fody - auto-generated XML schema FodyWeavers.xsd /LKY_OfficeTools/Resource/Json/LKY_OfficeTools_AppInfo.json ================================================ FILE: LICENSE.txt ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: LKY_OfficeTools/App.config ================================================ ================================================ FILE: LKY_OfficeTools/Common/Com_ExeOS.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Com_ExeOS.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using System.Collections.Generic; using System.Diagnostics; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Common { internal class Com_ExeOS { internal class Run { internal static int Exe(string file_path, string args) { try { Process p = new Process(); var result = Process(file_path, args, out p, true); //默认等待完成 //是否执行了 if (!result) { throw new Exception($"执行 {file_path} 异常!"); } return p.ExitCode; } catch (Exception Ex) { new Log(Ex.ToString()); return -920921; } } internal static bool Process(string file_path, string args, out Process ProcessInfo, bool WaitForExit) { try { Console.ForegroundColor = ConsoleColor.Gray; ProcessInfo = new Process(); ProcessInfo.StartInfo.FileName = file_path; //需要启动的程序名 ProcessInfo.StartInfo.Arguments = args; //启动参数 //是否使用操作系统shell启动 ProcessInfo.StartInfo.UseShellExecute = false; //启动 ProcessInfo.Start(); //接收返回值 //p.StandardInput.AutoFlush = true; //获取输出信息 //string strOuput = p.StandardOutput.ReadToEnd(); //等待程序执行完退出进程 if (WaitForExit) { ProcessInfo.WaitForExit(); } return true; } catch (Exception Ex) { ProcessInfo = null; new Log(Ex.ToString()); return false; } } internal static string Cmd(string args) { try { Console.ForegroundColor = ConsoleColor.Gray; Process p = new Process(); //设置要启动的应用程序 p.StartInfo.FileName = "cmd.exe"; //是否使用操作系统shell启动 p.StartInfo.UseShellExecute = false; // 接受来自调用程序的输入信息 p.StartInfo.RedirectStandardInput = true; //输出信息 p.StartInfo.RedirectStandardOutput = true; // 输出错误 p.StartInfo.RedirectStandardError = true; //不显示程序窗口 p.StartInfo.CreateNoWindow = true; //启动程序 p.Start(); //向cmd窗口发送输入信息 p.StandardInput.WriteLine(args + "&exit"); p.StandardInput.AutoFlush = true; //获取输出信息 string strOuput = p.StandardOutput.ReadToEnd(); //等待程序执行完退出进程 p.WaitForExit(); p.Close(); return strOuput; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } } internal class KillExe { internal enum KillMode { Try_Friendly = 1, Only_Friendly = 2, Only_Force = 4, } internal static bool ByExeName(string exe_name, KillMode kill_mode, bool isWait) { try { //先判断是否存在进程 if (Info.IsRun(exe_name)) { Process[] p = Process.GetProcessesByName(exe_name); foreach (Process now_p in p) { ByProcessID(now_p.Id, kill_mode, isWait); } return true; } else { //不存在时,直接返回 true return true; } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool ByProcessID(int exe_id, KillMode kill_mode, bool isWait) { try { //先判断是否存在进程 if (Info.IsRun(exe_id)) { //进程存在,获取对象 Process now_p = Process.GetProcessById(exe_id); switch (kill_mode) { case KillMode.Try_Friendly: { //先尝试友好关闭 if (!now_p.CloseMainWindow()) { //有好关闭失败时,启用强制结束 now_p.Kill(); } break; } case KillMode.Only_Friendly: { //只友好关闭 now_p.CloseMainWindow(); break; } case KillMode.Only_Force: { //只强制结束 now_p.Kill(); break; } } //判断是否等待进程结束 if (isWait) { //等待进程被结束 now_p.WaitForExit(); } return true; //如果不是等待结束进程,中途未出现catch时,也返回true } else { //不存在时,直接返回 true return true; } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } internal class Info { internal static bool IsRun(string exe_name) { try { Process[] p = Process.GetProcesses(); foreach (Process now_p in p) { if (now_p.ProcessName.Equals(exe_name, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool IsRun(int exe_id) { try { Process[] p = Process.GetProcesses(); foreach (Process now_p in p) { if (now_p.Id == exe_id) { return true; } } return false; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static List GetProcessByTitle(string window_title, bool need_equal = false) { try { Process[] process_list = Process.GetProcesses(); List result = new List(); foreach (var now_p in process_list) { if (need_equal) { //严格相等 if (now_p.MainWindowTitle == window_title) { result.Add(now_p); } } else { //包含即可 if (now_p.MainWindowTitle.Contains(window_title)) { result.Add(now_p); } } } return result; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } } } } ================================================ FILE: LKY_OfficeTools/Common/Com_FileOS.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Com_FileOS.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Text; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Common { internal class Com_FileOS { internal class XML { internal static bool SetValue(string xml_path, string Key_str, string new_Value) { try { //读取文件 string xml_content = File.ReadAllText(xml_path, Encoding.UTF8); //获得当前值 string current_value = Com_TextOS.GetCenterText(xml_content, $"{Key_str}=\"", "\""); //替换值 string xml_new_content = xml_content.Replace(current_value, new_Value); //判断是否替换成功 if (string.IsNullOrEmpty(xml_new_content)) { //new Log("替换失败!"); return false; } //写入文件 File.WriteAllText(xml_path, xml_new_content, Encoding.UTF8); return true; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } internal class ScanFiles { internal List FilesList = new List(); internal void GetFilesByExtension(string dirPath, string fileType = "*", bool isRoot = false) { if (Directory.Exists(dirPath)) //目录存在 { DirectoryInfo folder = new DirectoryInfo(dirPath); //获取当前目录下的文件名 foreach (FileInfo file in folder.GetFiles()) { //如果没有限制文件后缀名,或者满足了特定后缀名,开始写入List if (fileType == "*" || file.Extension == (fileType)) { //扫描到的文件添加到列表中 FilesList.Add(file.FullName); } } //如果是根目录先排除掉 回收站目录 if (isRoot) { foreach (DirectoryInfo dir in folder.GetDirectories()) { if (dir.FullName.Contains("$RECYCLE.BIN") || dir.FullName.Contains("System Volume Information")) { //Console.ForegroundColor = ConsoleColor.Gray; //new Log("跳过: " + dir.FullName); } else { //new Log("----->: " + dir.FullName); GetFilesByExtension(dir.FullName, fileType); } } } else { //遍历下一个子目录 foreach (DirectoryInfo subFolders in folder.GetDirectories()) { //new Log(subFolders.FullName); GetFilesByExtension(subFolders.FullName, fileType); } } } else { /*Console.ForegroundColor = ConsoleColor.Gray; new Log("不存在: " + dirPath);*/ } } } internal class Covert { /* - - - - - - - - - - - - - - - - - - - - - - - - * Stream 和 byte[] 之间的转换 * - - - - - - - - - - - - - - - - - - - - - - - */ internal static byte[] StreamToBytes(Stream stream) { byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, bytes.Length); // 设置当前流的位置为流的开始 stream.Seek(0, SeekOrigin.Begin); return bytes; } } internal class Write { internal static bool TextToFile(string file_path, string content, bool is_append = true) { try { // 创建文件所在目录 Directory.CreateDirectory(new FileInfo(file_path).DirectoryName); //非追加模式,并且已经存在目标文件,则先删除原有文件 if (!is_append && File.Exists(file_path)) { File.Delete(file_path); } var fs = new FileStream(file_path, FileMode.OpenOrCreate, FileAccess.Write); var sw = new StreamWriter(fs); sw.BaseStream.Seek(0, SeekOrigin.End); sw.WriteLine(content); sw.Flush(); sw.Close(); fs.Close(); return true; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool FromStream(Stream stream, string to_path) { try { byte[] Save = Covert.StreamToBytes(stream); //创建文件所在目录 Directory.CreateDirectory(new FileInfo(to_path).DirectoryName); FileStream fsObj = new FileStream(to_path, FileMode.Create); fsObj.Write(Save, 0, Save.Length); fsObj.Close(); return true; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } internal class Info { internal static string GetHash(string file_path) { try { //文件不存在,返回null if (!File.Exists(file_path)) { return null; } //为了防止文件被占用,无法校验,这里先拷贝一份副本,校验副本的值 string file_copied = DateTime.Now.ToFileTime().ToString() + ".hash"; File.Copy(file_path, file_copied, true); if (!File.Exists(file_copied)) { return null; //副本文件拷贝失败,返回null } //var hash = SHA256.Create(); //var hash = MD5.Create(); var hash = SHA1.Create(); var stream = new FileStream(file_copied, FileMode.Open); byte[] hashByte = hash.ComputeHash(stream); stream.Close(); //校验完成,删除副本 if (File.Exists(file_copied)) { File.Delete(file_copied); } return BitConverter.ToString(hashByte).Replace("-", ""); } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } } } } ================================================ FILE: LKY_OfficeTools/Common/Com_InstallerOS.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Com_InstallerOS.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using WindowsInstaller; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Common { internal class Com_InstallerOS { internal enum MsiInfoType { ProductName, ProductCode, ProductVersion } internal static string GetProductInfo(string msi_path, MsiInfoType msi_info) { try { Type oType = Type.GetTypeFromProgID("WindowsInstaller.Installer"); if (oType == null) { return null; } Installer inst = Activator.CreateInstance(oType) as Installer; if (inst == null) { return null; } Database DB = inst.OpenDatabase(msi_path, MsiOpenDatabaseMode.msiOpenDatabaseModeReadOnly); if (DB == null) { return null; } //生成要查询的内容 string str = $" SELECT * FROM Property WHERE Property = '{msi_info}' "; View thisView = DB.OpenView(str); if (thisView == null) { return null; } thisView.Execute(); Record thisRecord = thisView.Fetch(); if (thisRecord == null) { return null; } string result = thisRecord.get_StringData(2); return result; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } } } ================================================ FILE: LKY_OfficeTools/Common/Com_NetworkOS.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Com_NetworkOS.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Common { internal class Com_NetworkOS { internal class Check { //导入判断网络是否连接的 .dll [DllImport("wininet.dll", EntryPoint = "InternetGetConnectedState")] //判断网络状况的方法,返回值true为连接,false为未连接 private extern static bool InternetGetConnectedState(out int conState, int reder); internal static bool IsConnected { get { return InternetGetConnectedState(out int n, 0); } } } } } ================================================ FILE: LKY_OfficeTools/Common/Com_PrivilegeOS.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Com_PrivilegeOS.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using System.IO; using System.Security.Principal; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppLog; using static LKY_OfficeTools.Lib.Lib_AppMessage; using static LKY_OfficeTools.Lib.Lib_AppState; namespace LKY_OfficeTools.Common { internal class Com_PrivilegeOS { internal static bool IsRunByAdmin() { WindowsIdentity identity = WindowsIdentity.GetCurrent(); WindowsPrincipal principal = new WindowsPrincipal(identity); return principal.IsInRole(WindowsBuiltInRole.Administrator); } internal static bool CanWriteProgramDataDir() { try { string time_sn = $"{DateTime.UtcNow.Hour}{DateTime.UtcNow.Minute}{DateTime.UtcNow.Second}{DateTime.UtcNow.Millisecond}"; string test_file_dir = AppPath.Documents.Temp + $"\\{time_sn}"; string test_file_path = test_file_dir + "\\time_sn.tmp"; var isSuccess = Com_FileOS.Write.TextToFile(test_file_path, "test", false); //删除测试的文件和目录 if (Directory.Exists(test_file_dir)) { Directory.Delete(test_file_dir, true); } return isSuccess; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static void PrivilegeAttention() { new Log($"\n------> 正在进行 {AppAttribute.AppName} 权限检查 ...", ConsoleColor.DarkCyan); //提权检验,非提权,激活会出问题 if (!IsRunByAdmin()) { new Log($" × 权限错误,请以管理员身份运行此文件!", ConsoleColor.DarkRed); Current_StageType = ProcessStage.Finish_Fail; //设置为失败模式 //退出提示 KeyMsg.Quit(-1); } //ProgramData目录权限检查 if (!CanWriteProgramDataDir()) { //ProgramData目录缺少权限,已改用“我的文档”目录。服务维护流程已伴随关闭! Must_Use_PersonalDir = true; new Log($" * 已采用备用权限策略!", ConsoleColor.DarkMagenta); } new Log($" √ 已通过 {AppAttribute.AppName} 权限检查。", ConsoleColor.DarkGreen); } } } ================================================ FILE: LKY_OfficeTools/Common/Com_ServiceOS.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Com_ServiceOS.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using System.ServiceProcess; using System.Threading; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Common { internal class Com_ServiceOS { internal class Query { internal static int RunState(string serv_name) { try { using (var control = new ServiceController(serv_name)) { return (int)control.Status; } } catch (Exception Ex) { new Log(Ex.ToString()); return 0; } } internal static bool IsCreated(string serv_name) { try { if (GetService(serv_name) != null) { //服务不为空,服务存在 return true; } else { //服务为空,服务不存在 return false; } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static ServiceController GetService(string serv_name) { try { ServiceController service_info = null; //即将获得的服务对象 ServiceController[] services_list = ServiceController.GetServices(); //获得所有服务 foreach (var now_service in services_list) //遍历搜索服务 { if (now_service.ServiceName.ToLower() == serv_name.ToLower()) //全部取小写,防止判断麻烦 { service_info = now_service; break; } } return service_info; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } internal static bool CompareBinPath(string serv_name, string compare_path) { try { //服务未创建,不相等 if (!IsCreated(serv_name)) { return false; } string cmd_query = $"sc qc {serv_name}"; string query_result = Com_ExeOS.Run.Cmd(cmd_query); //返回值为空,不相等 if (string.IsNullOrEmpty(query_result)) { return false; } if (query_result.Replace(@"\\", @"\").Contains(compare_path.Replace(@"\\", @"\"))) //替换两个斜杠,为单斜杠之后在比对 { //包含指定路径(含命令行) return true; } else { //不包含指定路径(含命令行) return false; } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool CompareDescription(string serv_name, string compare_description) { try { //服务未创建,不相等 if (!IsCreated(serv_name)) { return false; } string cmd_query = $"sc qdescription {serv_name}"; string query_result = Com_ExeOS.Run.Cmd(cmd_query); //返回值为空,不相等 if (string.IsNullOrEmpty(query_result)) { return false; } if (query_result.Contains(compare_description)) { //包含描述 return true; } else { //不包含描述 return false; } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } internal class Action { internal static bool Start(string serv_name) { try { //未创建服务,不启动! if (!Query.IsCreated(serv_name)) { throw new Exception($"启动服务 {serv_name} 时失败。未找到该服务!"); } //已安装服务,开始启动 using (var control = new ServiceController(serv_name)) { //没有处于运行状态的服务,才运行。 if (control.Status != ServiceControllerStatus.Running) { control.Start(); } //判断状态,是否是开启了 int wait_time = 0; //等待时间总计 while (wait_time <= (10 * 1000)) //最多等待10秒 { //获取服务状态 if (Query.RunState(serv_name) == (int)ServiceControllerStatus.Running) { //已经处于运行状态 return true; } else { //不是运行状态 Thread.Sleep(1000); //延迟 1s 后,再度查询 wait_time += 1000; //追加等待时间 continue; } } //如果在轮询期间,没有返回true,那么最终返回false return false; } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool Stop(string serv_name) { try { //未创建服务,不能停止! if (!Query.IsCreated(serv_name)) { throw new Exception($"停止服务 {serv_name} 时失败。未找到该服务!"); } //已安装服务,开始停止 using (var control = new ServiceController(serv_name)) { //只要不是停止状态,就发送停止指令 if (control.Status != ServiceControllerStatus.Stopped) { control.Stop(); } //判断状态,是否是停止了 int wait_time = 0; //等待时间总计 while (wait_time <= (10 * 1000)) //最多等待10秒 { //获取服务状态 if (Query.RunState(serv_name) == (int)ServiceControllerStatus.Stopped) { //已经处于停止状态 return true; } else { //不是停止状态 Thread.Sleep(1000); //延迟 1s 后,再度查询 wait_time += 1000; //追加等待时间 continue; } } //如果在轮询期间,没有返回true,那么最终返回false return false; } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool Restart(string serv_name) { try { //未创建服务,不能停止! if (!Query.IsCreated(serv_name)) { throw new Exception($"重启服务 {serv_name} 时失败。未找到该服务!"); } //已安装服务,开始重启 if (Stop(serv_name)) { if (Start(serv_name)) { return true; //当且仅当停止成功,开启成功,返回true。除此之外,均为false } } return false; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } internal class Config { internal static bool Create(string serv_name, string serv_runpath, string serv_displayname, string serv_description = null) { try { if (Query.IsCreated(serv_name)) { //已经创建服务,直接返回真 return true; } else { //未安装,开始安装 string cmd_install = $"sc create \"{serv_name}\" binPath=\"{serv_runpath}\" start=auto DisplayName=\"{serv_displayname}\""; var install_result = Com_ExeOS.Run.Cmd(cmd_install); //非空判断,若返回值为空,则为假 if (string.IsNullOrEmpty(install_result)) { throw new Exception($"创建服务 {serv_name} 时,返回值为空!"); } //判断是否安装成功 if (install_result.Contains("成功") || install_result.ToLower().Contains("success")) { //描述信息不为空,配置描述信息 if (!string.IsNullOrEmpty(serv_description)) { Modify.Description(serv_name, serv_description); } //无论修改描述信息是否成功,均返回成功 return true; } else { return false; } } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool Delete(string serv_name) { if (Query.IsCreated(serv_name)) { //已经创建服务,才能删除 //先停止服务 if (!Action.Stop(serv_name)) { throw new Exception($"服务 {serv_name} 因无法停止,导致卸载失败!"); } string cmd_del = $"sc delete \"{serv_name}\""; var del_result = Com_ExeOS.Run.Cmd(cmd_del); //非空判断,若返回值为空,则为假 if (string.IsNullOrEmpty(del_result)) { throw new Exception($"删除服务 {serv_name} 时,返回值为空!"); } //判断是否删除成功 if (del_result.Contains("成功") || del_result.ToLower().Contains("success")) { return true; } else { throw new Exception($"删除服务 {serv_name} 失败!"); } } else { throw new Exception($"没有找到名称为 {serv_name} 的服务,无法删除该服务!"); } } internal class Modify { internal static bool BinPath(string serv_name, string serv_binpath) { try { if (Query.IsCreated(serv_name)) { //已经创建服务,才进行修改 string cmd_modify_binpath = $"sc config \"{serv_name}\" binPath=\"{serv_binpath}\""; var modify_result = Com_ExeOS.Run.Cmd(cmd_modify_binpath); //非空判断,若返回值为空,则为假 if (string.IsNullOrEmpty(modify_result)) { throw new Exception($"执行修改 {serv_name} binPath 参数时,返回值为空!"); } //判断是否修改 binPath 成功 if (modify_result.Contains("成功") || modify_result.ToLower().Contains("success")) { return true; } else { throw new Exception($"修改服务 {serv_name} 的 binPath 信息为 {serv_binpath} 失败!"); } } else { throw new Exception($"尝试修改服务 {serv_name} 的 binPath 信息为 {serv_binpath},但该服务未安装!"); } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool DisplayName(string serv_name, string serv_displayname) { try { if (Query.IsCreated(serv_name)) { //已经创建服务,才进行修改 string cmd_modify_displayname = $"sc config \"{serv_name}\" DisplayName=\"{serv_displayname}\""; var modify_result = Com_ExeOS.Run.Cmd(cmd_modify_displayname); //非空判断,若返回值为空,则为假 if (string.IsNullOrEmpty(modify_result)) { throw new Exception($"执行修改 {serv_name} DisplayName 参数时,返回值为空!"); } //判断是否修改 DisplayName 成功 if (modify_result.Contains("成功") || modify_result.ToLower().Contains("success")) { return true; } else { throw new Exception($"修改服务 {serv_name} 的 DisplayName 信息为 {serv_displayname} 失败!"); } } else { throw new Exception($"尝试修改服务 {serv_name} 的 DisplayName 信息为 {serv_displayname},但该服务未安装!"); } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool Description(string serv_name, string serv_description) { try { if (Query.IsCreated(serv_name)) { //已经创建服务,才进行修改 string cmd_modify_desc = $"sc description \"{serv_name}\" \"{serv_description}\""; var modify_result = Com_ExeOS.Run.Cmd(cmd_modify_desc); //非空判断,若返回值为空,则为假 if (string.IsNullOrEmpty(modify_result)) { throw new Exception($"执行修改 {serv_name} 描述信息时,返回值为空!"); } //判断是否修改描述成功 if (modify_result.Contains("成功") || modify_result.ToLower().Contains("success")) { return true; } else { throw new Exception($"修改服务 {serv_name} 的描述信息为 {serv_description} 失败!"); } } else { throw new Exception($"尝试修改服务 {serv_name} 的描述信息为 {serv_description},但该服务未安装!"); } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } } } } ================================================ FILE: LKY_OfficeTools/Common/Com_SystemOS.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Com_SystemOS.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using Microsoft.Win32; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Common { internal class Com_SystemOS { internal class OSVersion { internal enum OSType { LowVersion, WinXP, WinVista, Win7, //支持.NET 4.8 Win8_1, //支持.NET 4.8 Win10, //1607开始,支持.NET 4.8 Win11, UnKnow } internal static readonly IDictionary WinPublishType = new Dictionary { //win10版本号 { "10240", "1507" }, { "10586", "1511" }, { "14393", "1607" }, //.NET 4.8从此版本开始支持 { "15063", "1703" }, { "16299", "1709" }, { "17134", "1803" }, { "17763", "1809" }, { "18362", "1903" }, { "18363", "1909" }, { "19041", "2004" }, { "19042", "20H2" }, { "19043", "21H1" }, { "19044", "21H2" }, { "19045", "22H2" }, //win11版本号 { "22000", "21H2" }, { "22621", "22H2" }, //{ "1111111111111111", "LTSB" }, //{ "1111111111111111", "LTSC" }, //{ "1111111111111111", "ARM" }, }; internal static OSType GetPublishType() { try { Version ver = Environment.OSVersion.Version; if (ver.Major < 5) { return OSType.LowVersion; } else if (ver.Major == 5 && ver.Minor == 1) { return OSType.WinXP; } else if (ver.Major == 6 && ver.Minor == 0) { return OSType.WinVista; } else if (ver.Major == 6 && ver.Minor == 1) { return OSType.Win7; } else if (ver.Major == 6 && ver.Minor == 2) { return OSType.Win8_1; } else if (ver.Major == 10 && ver.Minor == 0) //正确获取win10版本号,需要在exe里面加入app.manifest { //检查注册表,因为win10和11的主版本号都为10,只能用buildID来判断了 string curr_ver = Register.Read.ValueBySystem(RegistryHive.LocalMachine, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentBuild"); if (!string.IsNullOrEmpty(curr_ver) && int.Parse(curr_ver) < 22000) //Win11目前内部版本号 { return OSType.Win10; } else { return OSType.Win11; } } else { return OSType.UnKnow; } } catch (Exception Ex) { new Log(Ex.ToString()); return OSType.UnKnow; } } internal static string GetBuildNumber(bool isCoreVersion = true) { try { //检查注册表 string curr_mode = Register.Read.ValueBySystem(RegistryHive.LocalMachine, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentBuild"); //为空返回未知 if (string.IsNullOrEmpty(curr_mode)) { return "unknow"; } //判断返回的内容 if (isCoreVersion) //返回内部版本号 { return curr_mode; } else //返回发行版本 { return WinPublishType[curr_mode]; } } catch (Exception Ex) { new Log(Ex.ToString()); return "error!"; } } } internal class Register { internal class Read { internal static string Value(RegistryHive reg_root, RegistryView reg_view, string path, string key) { try { RegistryKey HK_Root = RegistryKey.OpenBaseKey(reg_root, reg_view); RegistryKey path_reg = HK_Root.OpenSubKey(path); //先获取路径 if (path_reg == null) { //找不到注册表路径 return null; } else { object value = path_reg.GetValue(key); if (value != null) //必须先判断不为null,否则会抛出异常 { //一切正常 return value.ToString(); } else { //Key不存在或值为空 return null; } } } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } internal static string ValueBySystem(RegistryHive reg_root, string path, string key) { try { if (Environment.Is64BitOperatingSystem) { //x64系统,访问x64注册表 return Value(reg_root, RegistryView.Registry64, path, key); } else { //x32系统,访问x32注册表 return Value(reg_root, RegistryView.Registry32, path, key); } } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } internal static List AllValues(RegistryHive reg_root, string path, string key) { try { List result = new List(); //获取x32的结构值 string value_x32 = Value(reg_root, RegistryView.Registry32, path, key); if (!string.IsNullOrWhiteSpace(value_x32)) { result.Add(value_x32); } //获取x64的结构值(仅在当前计算机为 x64 系统时,才获取) if (Environment.Is64BitOperatingSystem) { string value_x64 = Value(reg_root, RegistryView.Registry64, path, key); if (!string.IsNullOrWhiteSpace(value_x64)) { result.Add(value_x64); } } return result; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } } internal static bool ExistItem(RegistryHive reg_root, RegistryView reg_view, string item_path) { try { RegistryKey reg = RegistryKey.OpenBaseKey(reg_root, reg_view); var result = reg.OpenSubKey(item_path); if (result != null) { return true; } result.Close(); return false; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool DeleteItem(RegistryHive reg_root, RegistryView reg_view, string path, string item) { try { RegistryKey HK_Root = RegistryKey.OpenBaseKey(reg_root, reg_view); RegistryKey path_reg = HK_Root.OpenSubKey(path, true); //先获取路径,启动可写模式 //找不到注册表路径,默认已删除,返回true if (path_reg == null) { return true; } path_reg.DeleteSubKeyTree(item, false); return true; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool ExportReg(string reg_path, string save_to) { try { //先删除已经存在的文件 if (File.Exists(save_to)) { File.Delete(save_to); } //开始导出 Directory.CreateDirectory(new FileInfo(save_to).DirectoryName); //先创建文件所在目录 Process.Start("regedit", $" /E \"{save_to}\" \"{reg_path}\"").WaitForExit(); if (File.Exists(save_to)) { return true; } else { return false; } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } } } ================================================ FILE: LKY_OfficeTools/Common/Com_TextOS.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Com_TextOS.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Common { internal class Com_TextOS { internal static string GetCenterText(string str, string strLeft, string strRight) { try { if (str == null || str.Length == 0) return ""; if (strLeft != "") { int indexLeft = str.IndexOf(strLeft);//左边字符串位置 if (indexLeft < 0) return ""; indexLeft = indexLeft + strLeft.Length;//左边字符串长度 if (strRight != "") { int indexRight = str.IndexOf(strRight, indexLeft);//右边字符串位置 if (indexRight < 0) return ""; return str.Substring(indexLeft, indexRight - indexLeft);//indexRight - indexLeft是取中间字符串长度 } else return str.Substring(indexLeft, str.Length - indexLeft);//取字符串右边 } else { //取字符串左边 int indexRight = str.IndexOf(strRight); if (indexRight <= 0) return ""; else return str.Substring(0, indexRight); } } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } internal static int GetStringTimes(string str, string scan_str) { try { int index = 0; int count = 0; while ((index = str.IndexOf(scan_str, index)) != -1) { count++; index += scan_str.Length; } return count; } catch (Exception Ex) { new Log(Ex.ToString()); return 0; } } } } ================================================ FILE: LKY_OfficeTools/Common/Com_Timer.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Com_Timer.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using System.Threading; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Common { internal class Com_Timer { internal class Countdown_Timer { internal int Remaining_Time { get; set; } internal bool isRun { get; set; } internal void Start(int total_time) { try { //此处不放入线程,否则使用isRun判断是否在运行时,会导致isRun状态无法判断 Remaining_Time = total_time; isRun = true; Thread time_t = new Thread(() => { Update(); //迭代完成后,自动停止运行 isRun = false; }); //time_t.SetApartmentState(ApartmentState.STA); time_t.Start(); } catch (Exception Ex) { new Log(Ex.ToString()); return; } } void Update() { try { //倒计时不为0,且给出运行指令 if (Remaining_Time > 0 & isRun) { Thread.Sleep(1000); Remaining_Time--; Update(); //轮询继续 } } catch (Exception Ex) { new Log(Ex.ToString()); return; } } internal void Pause() { try { isRun = false; } catch (Exception Ex) { new Log(Ex.ToString()); return; } } internal void Continue() { try { if (Remaining_Time != 0) { isRun = true; } } catch (Exception Ex) { new Log(Ex.ToString()); return; } } internal void Stop() { try { Pause(); Remaining_Time = 0; } catch (Exception Ex) { new Log(Ex.ToString()); return; } } } } } ================================================ FILE: LKY_OfficeTools/Common/Com_WebOS.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Com_WebOS.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using System.Net; using System.Text; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Common { internal class Com_WebOS { internal static string Visit_WebClient(string url, Encoding encoding = null) { try { if (encoding == null) { encoding = Encoding.UTF8; } using (WebClient WC = new WebClient()) { 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"); WC.Credentials = CredentialCache.DefaultCredentials;//获取或设置用于向Internet资源的请求进行身份验证的网络凭据 Byte[] pageData = WC.DownloadData(url); //从指定网站下载数据 return encoding.GetString(pageData); } } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } } } ================================================ FILE: LKY_OfficeTools/LKY_OfficeTools.csproj ================================================  Debug AnyCPU {6D00F508-106A-42D1-BE0F-048762434E69} Exe LKY_OfficeTools LKY_OfficeTools v4.7.2 512 true true AnyCPU true full false bin\Debug\ DEBUG;TRACE prompt 4 false AnyCPU pdbonly true bin\Release\ TRACE prompt 4 false x86 bin\x86\Debug\ x86 bin\x86\Release\ logo.ico Custom false Properties\app.manifest Component Lib_AppServiceHub.cs {000C1092-0000-0000-C000-000000000046} 1 0 1033 tlbimp False True ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppClosing.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_AppClosing.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using System.Runtime.InteropServices; using static LKY_OfficeTools.Common.Com_ExeOS.KillExe; using static LKY_OfficeTools.Lib.Lib_AppLog; using static LKY_OfficeTools.Lib.Lib_AppState; namespace LKY_OfficeTools.Lib { internal class Lib_AppClosing { internal class CloseWindow { internal delegate bool ControlCtrlDelegate(int CtrlType); [DllImport("kernel32.dll")] internal static extern bool SetConsoleCtrlHandler(ControlCtrlDelegate HandlerRoutine, bool Add); internal static ControlCtrlDelegate newDelegate = new ControlCtrlDelegate(HandlerRoutine); internal static bool HandlerRoutine(int CtrlType) { //只要程序不是已完成(无论成功与否),手动关闭,就会显示文字并打点 if (Current_StageType != ProcessStage.Finish_Fail && Current_StageType != ProcessStage.Finish_Success) { new Log($"\n × 正在尝试 取消部署,请稍候 ...", ConsoleColor.DarkRed); Current_StageType = ProcessStage.Interrupt; //设置中断状态。非完成情况下,关闭,属于 中断部署 状态,此处用于停止 下载 office 进程 Console.ForegroundColor = ConsoleColor.Gray; //重置颜色,如果第一次失败,颜色还是可以正常的 //结束残存进程 Lib_AppSdk.KillAllSdkProcess(KillMode.Try_Friendly); /*Pointing(ProcessStage.Interrupt); 暂停中断打点 */ //中断 点位。下载时触发该逻辑,打点会失败。 } else { //完成状态时,清理文件夹 Lib_AppSdk.Clean(); //清理SDK目录 } return false; } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppCommand.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_AppCommand.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using static LKY_OfficeTools.Lib.Lib_AppLog; using static LKY_OfficeTools.Lib.Lib_AppState; namespace LKY_OfficeTools.Lib { internal class Lib_AppCommand { internal static ArgsFlag AppCommandFlag { get; set; } internal Lib_AppCommand(string[] args) { try { //非空判断 if (args != null && args.Length > 0) { foreach (var now_arg in args) { //单独arg非空判断 if (!string.IsNullOrWhiteSpace(now_arg)) { //对于 /passive 模式,自动跳过所有需要确认的步骤 if (now_arg.Contains("/passive")) { SkipAllConfirm(); Current_RunMode = RunMode.Passive; //设置为被动模式 break; } //服务模式。该模式 与 passive 是互斥的。 else if (now_arg.Contains("/service")) { Current_RunMode = RunMode.Service; //设置为服务模式 //以服务模式运行 Lib_AppServiceConfig.Start(); //在Hub类库设置中,service模式将被添加passive标记。 //找到服务模式,不再执行后续的 Environment.Exit(-100); } //非服务、非被动模式 else { Current_RunMode = RunMode.Manual; //设置为手动模式 if (now_arg.ToLower().Contains("/none_welcome_confirm")) { AppCommandFlag |= ArgsFlag.None_Welcome_Confirm; } if (now_arg.ToLower().Contains("/ignore_manual_update_msg")) { AppCommandFlag |= ArgsFlag.Ignore_Manual_Update_Msg; } if (now_arg.ToLower().Contains("/auto_remove_conflict_office")) { AppCommandFlag |= ArgsFlag.Auto_Remove_Conflict_Office; } if (now_arg.ToLower().Contains("/none_finish_presskey")) { AppCommandFlag |= ArgsFlag.None_Finish_PressKey; } continue; } } } } } catch (Exception Ex) { new Log(Ex.ToString()); return; } } [Flags] internal enum ArgsFlag { None_Welcome_Confirm = 2, Ignore_Manual_Update_Msg = 4, Auto_Remove_Conflict_Office = 8, None_Finish_PressKey = 16, } private static bool SkipAllConfirm() { try { AppCommandFlag |= ArgsFlag.None_Welcome_Confirm | ArgsFlag.Auto_Remove_Conflict_Office | ArgsFlag.Ignore_Manual_Update_Msg | ArgsFlag.None_Finish_PressKey; return true; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppInfo.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_AppInfo.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using System; using System.Diagnostics; using System.IO; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Lib { internal class Lib_AppInfo { internal class AppAttribute { internal const string AppName = "LKY Office Tools"; internal const string AppName_Short = "LOT"; internal static readonly string ServiceName = ServiceDisplayName.Replace(" ", ""); internal const string ServiceDisplayName = AppName + " Service"; internal const string AppFilename = "LKY_OfficeTools.exe"; internal const string AppVersion = "1.3.0.223"; internal const string Developer = "LiuKaiyuan"; } internal class AppJson { private static string AppJsonInfo = null; internal static string Info { get { try { //内部变量非空时,直接返回其值 if (!string.IsNullOrEmpty(AppJsonInfo)) { return AppJsonInfo; } #if (!DEBUG) //release模式地址 string json_url = $"https://gitee.com/OdysseusYuan/LKY_OfficeTools/releases/download/AppInfo/LKY_OfficeTools_AppInfo.json"; #else //debug模式地址 string json_url = $"https://gitee.com/OdysseusYuan/LOT_OnlyTest/releases/download/AppInfo_Test/test.json"; #endif //获取 Json 信息 string result = Com_WebOS.Visit_WebClient(json_url); AppJsonInfo = result; return AppJsonInfo; } catch (Exception Ex) { new Log(Ex.ToString()); //new Log($" × 获取 AppJson 信息失败!", ConsoleColor.DarkRed); new Log($"获取 AppJson 信息失败!"); return null; } } } } internal class AppDevelop { internal const string NameSpace_Top = "LKY_OfficeTools"; } internal class AppPath { internal static string ExecuteDir { get { //使用 BaseDirectory 获取路径时,结尾会多一个 \ 符号,为了替换之,先在结果处增加一个斜杠,变成 \\ 结尾,然后替换掉这个 双斜杠 return (AppDomain.CurrentDomain.BaseDirectory + @"\").Replace(@"\\", @""); } } internal static string Executer { get { return new FileInfo(Process.GetCurrentProcess().MainModule.FileName).FullName; } } internal class Documents { internal static string Documents_Root { get { if (Lib_AppState.Must_Use_PersonalDir) { return $"{Environment.GetFolderPath(Environment.SpecialFolder.Personal)}\\{AppAttribute.AppName}"; //我的文档 } else { return $"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}\\{AppAttribute.AppName}"; //ProgramData } } } internal class Update { internal static string Update_Root { get { return $"{Documents_Root}\\Update"; } } internal static string UpdateTrash { get { if (Path.GetPathRoot(Executer) == Path.GetPathRoot(Documents_Root)) { //相同盘符,使用默认回收站目录 return $"{Update_Root}\\Trash"; } else { //不同盘符,使用子目录 return $"{ExecuteDir}\\Trash"; } } } } internal class Services { internal static string Services_Root { get { return $"{Documents_Root}\\Services"; } } internal static string ServicesTrash { get { return $"{Services_Root}\\Trash"; } } internal static string ServiceAutorun { get { return $"{Services_Root}\\Autorun"; } } internal static string ServiceAutorun_Exe { get { return $"{ServiceAutorun}\\{AppAttribute.AppFilename}"; } } internal static string PassiveProcessInfo { get { return $"{Services_Root}\\PassiveProcess.info"; } } } internal static string Logs { get { return $"{Documents_Root}\\Logs"; } } internal static string Temp { get { return $"{Documents_Root}\\Temp"; } } internal class SDKs { internal static string SDKs_Root { get { return $"{Documents_Root}\\SDKs"; } } internal static string Activate { get { return $"{SDKs_Root}\\Activate"; } } internal static string Activate_OSPP { get { return $"{Activate}\\OSPP.VBS"; } } } } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppLog.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_AppLog.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using System; using System.IO; using static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath; using static LKY_OfficeTools.Lib.Lib_OfficeInfo.OfficeLocalInfo; namespace LKY_OfficeTools.Lib { internal class Lib_AppLog { internal class Log { internal static string log_filepath = null; internal static string reg_install_error { get; set; } internal static string log_info { get; set; } /* internal static List error_screen_path = new List(); */ internal enum Output_Type { Display, Write, Display_Write } internal Log(string str, ConsoleColor str_color, Output_Type output_type = Output_Type.Display_Write) { try { //判断是否需要显示文字 if (output_type == Output_Type.Display || output_type == Output_Type.Display_Write) { Console.ForegroundColor = str_color; Console.WriteLine(str); //输出后恢复颜色 Console.ForegroundColor = ConsoleColor.Gray; } //需要输出日志文件时,进行判断 if (output_type == Output_Type.Write || output_type == Output_Type.Display_Write) { string datatime_format = DateTime.Now.ToString("s").Replace("T", "_").Replace(":", "-"); //服务模式,写出到日志文件 if (Lib_AppState.Current_RunMode == Lib_AppState.RunMode.Service) { //为空时,创建日志路径 if (string.IsNullOrEmpty(log_filepath)) { string file_name = "service_" + datatime_format + ".log"; log_filepath = $"{Documents.Logs}\\{file_name}"; } //目录不存在时创建目录 Directory.CreateDirectory(new FileInfo(log_filepath).DirectoryName); //文件不存在时创建&写入 File.AppendAllText(log_filepath, $"{datatime_format}, {str}\n"); } //将日志记录在内存中 string now_log = $"{datatime_format}, {str.Replace("\n", "")}"; if (str.Contains("×")) { now_log = $"{now_log}"; //有错误标红、加粗 } else if (str.Contains("Exception")) { now_log = $"{now_log}"; //抛出异常用橙红色 } log_info += now_log + "
"; /* //出现错误时,增加附加信息 if (str.Contains("×")) { string err_filename = datatime_format + ".png"; err_filename = $"{Lib_AppInfo.Path.Dir_Log}\\{err_filename}"; if (Com_SystemOS.Screen.CaptureToSave(err_filename)) { error_screen_path.Add(err_filename); } } */ } } catch { return; } } internal Log(string err_str) { try { //整合格式 string msg = $"---------- [Error Log: BEGIN] ----------\n{err_str}\n---------- [END] ----------"; //非服务模式,替换换行符 if (Lib_AppState.Current_RunMode != Lib_AppState.RunMode.Service) { msg = msg.Replace("\n", "
"); } //输出日志 new Log(msg, ConsoleColor.Gray, Output_Type.Write); } catch { return; } } internal Log(InstallState install_error) { try { //安装出错后,会记录系统目前office注册表情况 //导出注册表 Office 信息 string office_reg_path = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office"; //依据不同的安装错误生成不同的注册表文件名 string reg_filename = "error"; //包含未安装标记 if (install_error == InstallState.None) { reg_filename += "_none"; } //包含不同版本标记 if (install_error == InstallState.Diff) { reg_filename += "_diff"; } //包含多版本标记 if (install_error == InstallState.Multi) { reg_filename += "_multi"; } //包含安装正确标记 if (install_error == InstallState.Correct) { reg_filename += "_correct"; } //合成最终注册表路径 reg_install_error = Documents.Logs + $@"\{reg_filename}.reg"; //生成注册表信息 Com_SystemOS.Register.ExportReg(office_reg_path, reg_install_error); return; } catch (Exception Ex) { new Log(Ex.ToString()); return; } } internal static bool Clean() { try { /* //清理日志 if (log_filepath != null) { try { File.Delete(log_filepath); } catch (Exception Ex) { new Log(Ex.ToString()); } } */ /* //清理错误截屏 if (error_screen_path != null) { foreach (var now_file in error_screen_path) { try { File.Delete(now_file); } catch (Exception Ex) { new Log(Ex.ToString()); } } } */ //清理整个Log文件夹 if (Directory.Exists(Documents.Logs)) { try { Directory.Delete(Documents.Logs, true); } catch (Exception Ex) { new Log(Ex.ToString()); } } return true; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppMessage.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_AppMessage.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using System.Threading; using static LKY_OfficeTools.Common.Com_Timer; using static LKY_OfficeTools.Lib.Lib_AppCommand; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Lib { internal class Lib_AppMessage { internal class KeyMsg { internal static void Quit(int exit_code) { //清理SDK缓存 Lib_AppSdk.Clean(); //退出机制 if (!AppCommandFlag.HasFlag(ArgsFlag.None_Finish_PressKey)) { //不包含“结束无需确认”命令行,需要人工按键结束 Console.ForegroundColor = ConsoleColor.Gray; Console.Write("\n请按 任意键 退出 ..."); Console.ReadKey(); Environment.Exit(exit_code); } } internal static bool Confirm(string msg_str = null) { Console.ForegroundColor = ConsoleColor.Gray; //判断是否为空 if (string.IsNullOrWhiteSpace(msg_str)) { //msg为空,直接展示回车键继续,并且前面不空格 msg_str = $"\n请按 回车键(Enter)继续 ..."; } else { //msg不为空,一般在运行过程中的确认,有空格,并且增加逗号 msg_str = $" {msg_str},请按 回车键(Enter)继续 ..."; } Console.Write(msg_str); //提示信息 new Log(msg_str, ConsoleColor.Gray, Log.Output_Type.Write); //写入日志 if (Console.ReadKey().Key == ConsoleKey.Enter) { Console.WriteLine(); //增加一个空白行 return true; } else { Console.WriteLine(); //增加一个空白行 return false; } } internal static void DoByTime(string msg_str, int countdown_time) { Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(); //插入一个空白行 //设置一个倒计时组件 Countdown_Timer timer = new Countdown_Timer(); timer.Start(countdown_time); //循环输出 while (timer.isRun) { string msg = $"{msg_str}将在 {timer.Remaining_Time} 秒内开始 ..."; new Log(msg, ConsoleColor.Gray, Log.Output_Type.Write); //写入日志 Thread.Sleep(100); //输出消息倒计时 Console.Write($"\r{msg}"); } //倒计时结束后,告知开始 Console.Write($"\r{msg_str}启动 ... "); //完成等待 Console.WriteLine(); //增加一个空白行 return; } internal static bool Choose(string todo_thing) { new Log($"\n ★ {todo_thing}", ConsoleColor.Gray); Console.ForegroundColor = ConsoleColor.Gray; string msg = $" 按 回车键(Enter)确认执行上述操作,按 其它键 跳过此环节 ..."; Console.Write(msg); new Log(msg, ConsoleColor.Gray, Log.Output_Type.Write); //写入日志 if (Console.ReadKey().Key == ConsoleKey.Enter) { Console.WriteLine(); //增加一个空白行 return true; } else { Console.WriteLine(); //增加一个空白行 return false; } } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppSdk.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_AppSdk.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Threading; using static LKY_OfficeTools.Common.Com_ExeOS; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath; using static LKY_OfficeTools.Lib.Lib_AppLog; using static LKY_OfficeTools.Lib.Lib_AppMessage; using static LKY_OfficeTools.Lib.Lib_AppState; namespace LKY_OfficeTools.Lib { internal class Lib_AppSdk { enum SdkPackage { Activate, Aria2c, ODT, SaRA, } private static Dictionary SdkPackageDic { get { try { //初始字典 Dictionary res_dic = new Dictionary(); var asm = Assembly.GetExecutingAssembly(); res_dic[SdkPackage.Activate] = asm.GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时,此值也需要调整 */ + $".Resource.SDK.{SdkPackage.Activate}.lotp"); res_dic[SdkPackage.Aria2c] = asm.GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时,此值也需要调整 */ + $".Resource.SDK.{SdkPackage.Aria2c}.lotp"); res_dic[SdkPackage.ODT] = asm.GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时,此值也需要调整 */ + $".Resource.SDK.{SdkPackage.ODT}.lotp"); res_dic[SdkPackage.SaRA] = asm.GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时,此值也需要调整 */ + $".Resource.SDK.{SdkPackage.SaRA}.lotp"); return res_dic; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } } private static Dictionary SdkPkgPath { get { try { //初始字典 Dictionary extra_dic = new Dictionary(); extra_dic[SdkPackage.Activate] = Documents.SDKs.SDKs_Root + $"\\{SdkPackage.Activate}.lotp"; extra_dic[SdkPackage.Aria2c] = Documents.SDKs.SDKs_Root + $"\\{SdkPackage.Aria2c}.lotp"; extra_dic[SdkPackage.ODT] = Documents.SDKs.SDKs_Root + $"\\{SdkPackage.ODT}.lotp"; extra_dic[SdkPackage.SaRA] = Documents.SDKs.SDKs_Root + $"\\{SdkPackage.SaRA}.lotp"; return extra_dic; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } } internal static bool Initial() { try { new Log($"\n------> 正在配置 {AppAttribute.AppName} 基础组件 ...", ConsoleColor.DarkCyan); Thread.Sleep(1000); //短暂间隔,提升下体验 new Log($" >> 此过程会持续些许时间,这取决于您的电脑硬件配置,请耐心等待 ...", ConsoleColor.DarkYellow); //初始化前先清理SDK目录,防止因为文件已经存在,引发解压的catch Clean(); //释放文件 if (SdkPackageDic == null) { throw new Exception("读取 SDK 内存资源失败!"); } foreach (var now_pkg in SdkPackageDic) { string pkg_path = SdkPkgPath[now_pkg.Key]; bool isToDisk = Com_FileOS.Write.FromStream(now_pkg.Value, pkg_path); if (!isToDisk) { //写出异常,抛出 throw new IOException($"无法写出 SDK 文件 {pkg_path} 到硬盘!"); } //无异常,解压包 ZipFile.ExtractToDirectory(pkg_path, Documents.SDKs.SDKs_Root + $@"\{now_pkg.Key}"); } new Log($" √ 已完成 {AppAttribute.AppName} 组件配置。", ConsoleColor.DarkGreen); return true; } catch (IOException IO_Ex) { new Log(IO_Ex.ToString()); //读写出现意外 new Log($" × 配置 {AppAttribute.AppName} 基础组件失败。请确保您的系统盘具备足够的可写空间!", ConsoleColor.DarkRed); //清理SDK缓存 Clean(); Current_StageType = ProcessStage.Finish_Fail; //设置为失败模式 //退出提示 KeyMsg.Quit(-2); return false; } catch (UnauthorizedAccessException Au_Ex) { new Log(Au_Ex.ToString()); //不具备读写权限 new Log($" × 配置 {AppAttribute.AppName} 基础组件失败。请确保您具备对 {Documents.SDKs.SDKs_Root} 目录的写入权限!", ConsoleColor.DarkRed); //清理SDK缓存 Clean(); Current_StageType = ProcessStage.Finish_Fail; //设置为失败模式 //退出提示 KeyMsg.Quit(-2); return false; } catch (Exception Ex) { new Log(Ex.ToString()); //其它未知问题 new Log($" × 配置 {AppAttribute.AppName} 基础组件失败,无法继续。请重新下载本软件或联系开发者!", ConsoleColor.DarkRed); //清理SDK缓存 Clean(); Current_StageType = ProcessStage.Finish_Fail; //设置为失败模式 //退出提示 KeyMsg.Quit(-10); return false; } finally { //清理 SDK pkg文件 var extra_sdk_list = SdkPkgPath.Values.ToList(); foreach (var now_path in extra_sdk_list) { if (File.Exists(now_path)) { try { File.Delete(now_path); } catch (Exception Ex) { new Log(Ex.ToString()); new Log($"Exception: 清理 SDK 的 {now_path} 文件失败!"); } } } } } internal static bool Clean() { try { //目录不存在时,自动返回为真 if (!Directory.Exists(Documents.SDKs.SDKs_Root)) { return true; } Directory.Delete(Documents.SDKs.SDKs_Root, true); return true; } catch (Exception Ex) { new Log(Ex.ToString()); new Log($"清理 SDK 目录失败!"); return false; } } internal static List Process_List { get { try { Stream sdk_processes_res = Assembly.GetExecutingAssembly(). GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时,此值也需要调整 */ + ".Resource.SDK.SDK_Processes.list"); StreamReader sdk_processes_sr = new StreamReader(sdk_processes_res); string sdk_processes = sdk_processes_sr.ReadToEnd(); if (!string.IsNullOrWhiteSpace(sdk_processes)) { List sdk_processes_list = new List(); string[] p_info = sdk_processes.Replace("\r", "").Split('\n'); //分割出进程数组 if (p_info != null && p_info.Length > 0) { foreach (var now_process in p_info) { sdk_processes_list.Add(now_process); } return sdk_processes_list; } } return null; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } } internal static bool KillAllSdkProcess(KillExe.KillMode mode) { try { //轮询结束每个进程(不等待) foreach (var now_p in Process_List) { KillExe.ByExeName(now_p, mode, false); } return true; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppServiceConfig.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_AppServiceConfig.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using System; using System.IO; using System.ServiceProcess; using static LKY_OfficeTools.Common.Com_ServiceOS; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppLog; using static LKY_OfficeTools.Lib.Lib_AppMessage; using static LKY_OfficeTools.Lib.Lib_AppState; namespace LKY_OfficeTools.Lib { internal class Lib_AppServiceConfig { internal static void Setup() { try { //无论手动、被动模式,只要安装了服务,就自动更新之 if (Com_ServiceOS.Query.IsCreated(AppAttribute.ServiceName)) { AddOrUpdate(); } else { //未安装过。根据模式不同,给出判断 //手动模式,提示添加 if (Current_RunMode == RunMode.Manual) { //让用户选择是否添加自检服务 if (KeyMsg.Choose("为保证 Office 始终处于最新正版,即将添加 Office 自动更新/正版激活 服务。")) { AddOrUpdate(); } else { new Log($" × 您已拒绝添加 Office 自动更新/正版激活 服务,若要重新添加,请再次运行本软件!", ConsoleColor.DarkRed); return; } } //被动模式,自动安装服务信息 else if (Current_RunMode == RunMode.Passive) { AddOrUpdate(); } } } catch (Exception Ex) { new Log(Ex.ToString()); return; } } enum Add_Result { Add_Success, Add_Fail, Add_Error, Update_Success, Update_Fail, Update_Error, Unknow, } static Add_Result AddOrUpdate() { try { //构建服务基本信息 string serv_name = AppAttribute.ServiceName; string serv_execmd = AppPath.Documents.Services.ServiceAutorun_Exe + " /service"; string serv_displayname = AppAttribute.ServiceDisplayName; string serv_desc = "用于检测、下载、安装和激活正版 Office 的重要服务。如果此服务被禁用,这台计算机的用户将无法获取 Office 更新,也无法使其处于最新正版激活状态。"; //判断是否安装 if (Com_ServiceOS.Query.IsCreated(serv_name)) { //已安装服务 new Log($"\n------> 正在更新 {AppAttribute.ServiceDisplayName} 服务 ...", ConsoleColor.DarkCyan); //校验其属性是否一致 //修改 exe 运行信息 if (!Com_ServiceOS.Query.CompareBinPath(serv_name, serv_execmd)) { Com_ServiceOS.Config.Modify.BinPath(serv_name, serv_execmd); } //修改 DisplayName var service_info = Com_ServiceOS.Query.GetService(serv_name); //无需判断是否为空,IsCreated() 已经判断过 if (service_info.DisplayName != serv_displayname) { //DisplayName 不同时,修改之 Com_ServiceOS.Config.Modify.DisplayName(serv_name, serv_displayname); } //修改 描述信息 if (!Com_ServiceOS.Query.CompareDescription(serv_name, serv_desc)) { Com_ServiceOS.Config.Modify.Description(serv_name, serv_desc); } //信息修改完成后,需要更新服务对应的文件 string serv_filepath = AppPath.Documents.Services.ServiceAutorun_Exe; if (File.Exists(serv_filepath)) { //运行的文件和服务目录下的文件 哈希值 一致时,跳过替换旧版本,否则要升级替换旧版本(常用于更新场景下) if (Com_FileOS.Info.GetHash(AppPath.Executer) == Com_FileOS.Info.GetHash(serv_filepath)) { //哈希值一致,说明,服务文件和当前运行的文件是相同的,无需替换 new Log($" √ 无需升级服务,已刷新 {AppAttribute.ServiceDisplayName} 信息。", ConsoleColor.DarkGreen); return Add_Result.Update_Success; } //文件哈希值不一样,开始替换升级 /* 替换逻辑 * 1、如果运行路径 = 服务路径,【因为服务配置发生在更新之后,相等时,文件已经是最新的,达到了预期(更新服务文件)的目的。不做任何操作】 * 2、运行路径 != 服务路径,【移动旧文件到trash目录,拷贝运行路径文件到服务路径】 */ if (AppPath.Executer != serv_filepath) //用户在非服务目录下运行 { //move方式,解决服务启动状态中,文件无法被替换的问题。(停止服务会导致自身进程被结束) string dest_filepath = AppPath.Documents.Services.ServicesTrash + $"\\{DateTime.Now.ToFileTime()}.old"; Directory.CreateDirectory(Path.GetDirectoryName(dest_filepath)); //创建计划移动到的目录 File.Move(serv_filepath, dest_filepath); //复制自身文件到服务目录下 File.Copy(AppPath.Executer, serv_filepath, true); } } else { //如果服务安装了,但文件丢失了,此处将自身文件 copy 到服务专用目录 Directory.CreateDirectory(Path.GetDirectoryName(serv_filepath)); //先创建服务目录,否则copy文件会异常 File.Copy(AppPath.Executer, serv_filepath, true); } new Log($" √ 已更新 {AppAttribute.ServiceDisplayName} 服务。", ConsoleColor.DarkGreen); return Add_Result.Update_Success; } else { //未安装服务时,添加服务 new Log($"\n------> 正在安装 Office 自动更新/正版激活 服务 ...", ConsoleColor.DarkCyan); var create_result = Com_ServiceOS.Config.Create(serv_name, serv_execmd, serv_displayname, serv_desc); //判断是否成功安装 if (create_result) { Directory.CreateDirectory(Path.GetDirectoryName(AppPath.Documents.Services.ServiceAutorun_Exe)); //先创建服务目录,否则copy文件会异常 File.Copy(AppPath.Executer, AppPath.Documents.Services.ServiceAutorun_Exe, true); //将当前文件复制到服务专用文件夹 new Log($" √ 已安装 Office 自动更新/正版激活 服务。", ConsoleColor.DarkGreen); return Add_Result.Add_Success; } else { new Log($" × Office 自动更新/正版激活 服务安装失败!若要添加,请重新运行本软件。", ConsoleColor.DarkRed); return Add_Result.Add_Fail; } } } catch (Exception Ex) { new Log(Ex.ToString()); new Log($" × 因特殊原因,服务配置失败!如有问题,可联系开发者。", ConsoleColor.DarkRed); return Add_Result.Unknow; } } internal static bool Start() { try { //服务没有被创建时,不运行 if (!Com_ServiceOS.Query.IsCreated(AppAttribute.ServiceName)) { return false; } //启动服务 ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new Lib_AppServiceHub() }; ServiceBase.Run(ServicesToRun); return true; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool Stop() { try { //服务存在时,展示停止文字提示 if (Com_ServiceOS.Query.IsCreated(AppAttribute.ServiceName)) { new Log($"\n------> 正在停止 {AppAttribute.ServiceDisplayName} 服务 ...", ConsoleColor.DarkCyan); //停止服务 if (Com_ServiceOS.Action.Stop(AppAttribute.ServiceName)) { new Log($" √ 已停止 {AppAttribute.ServiceDisplayName} 服务。", ConsoleColor.DarkGreen); return true; } else { new Log($" × 无法停止 {AppAttribute.ServiceDisplayName} 服务。", ConsoleColor.DarkGreen); return false; } } else { //服务不存在,直接返回真 return true; } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static void RestartSelf() { try { //自身服务名称 string serv_name = AppAttribute.ServiceName; //未创建服务,不能停止! if (!Query.IsCreated(serv_name)) { throw new Exception($"重启服务 {serv_name} 时失败。未找到该服务!"); } //已安装服务,开始重启 string cmd = $"(net stop {serv_name})&(net start {serv_name})"; string result = Com_ExeOS.Run.Cmd(cmd); } catch (Exception Ex) { new Log(Ex.ToString()); return; } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppServiceHub.Designer.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_AppService.Designer.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ namespace LKY_OfficeTools.Lib { partial class Lib_AppServiceHub { private System.ComponentModel.IContainer components = null; protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region 组件设计器生成的代码 private void InitializeComponent() { components = new System.ComponentModel.Container(); this.ServiceName = "Lib_AppService"; } #endregion } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppServiceHub.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_AppService.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using System; using System.Diagnostics; using System.IO; using System.ServiceProcess; using static LKY_OfficeTools.Common.Com_FileOS; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Lib { partial class Lib_AppServiceHub : ServiceBase { public Lib_AppServiceHub() { InitializeComponent(); } protected override void OnStart(string[] args) { try { //每次启动服务时,自动删除 Services Trash 目录中的 .old 文件 ScanFiles oldFiles = new ScanFiles(); oldFiles.GetFilesByExtension(AppPath.Documents.Services.ServicesTrash, ".old"); if (oldFiles.FilesList != null && oldFiles.FilesList.Count > 0) { foreach (var now_file in oldFiles.FilesList) { //使用 try catch 模式。以防异常。 try { File.Delete(now_file); } catch { } } } //以无人值守的模式,隐式的运行本程序。 Process process_info = new Process(); Com_ExeOS.Run.Process(AppPath.Executer, "/passive", out process_info, false); //异步运行 //保存进程信息 Directory.CreateDirectory(AppPath.Documents.Services.Services_Root); string info_file = AppPath.Documents.Services.PassiveProcessInfo; File.WriteAllText(info_file, process_info.Id.ToString()); } catch (Exception Ex) { new Log(Ex.ToString()); return; } } protected override void OnStop() { try { //服务停止时,如果有正在运行的 passive 进程,立即结束。 string info_path = AppPath.Documents.Services.PassiveProcessInfo; if (File.Exists(info_path)) { string info = File.ReadAllText(info_path); if (!string.IsNullOrWhiteSpace(info)) { Com_ExeOS.KillExe.ByProcessID(int.Parse(info), Com_ExeOS.KillExe.KillMode.Try_Friendly, true); //尝试友好的结束进程 } File.Delete(info_path); } } catch (Exception Ex) { new Log(Ex.ToString()); return; } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppSignCert.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_AppSignCert.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using System; using System.IO; using System.Reflection; using System.Security.Cryptography.X509Certificates; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Lib { internal class Lib_AppSignCert { internal Lib_AppSignCert() { try { if (!AlreadyImported("12EA025393C6D19347EFB7C71313A9DD")) { string cer_filename = "PublisherCert.cer"; string cer_path = Documents.Temp + $"\\{cer_filename}"; //cer文件不存在时,写出到运行目录 if (!File.Exists(cer_path)) { Assembly assm = Assembly.GetExecutingAssembly(); Stream istr = assm.GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时,词值也需要调整 */ + $".Resource.{cer_filename}"); Com_FileOS.Write.FromStream(istr, cer_path); } //导入证书 ImportCert(cer_path); } } catch (Exception Ex) { new Log(Ex.ToString()); return; } } internal static bool AlreadyImported(string serial_number) { try { X509Store store2 = new X509Store(StoreName.Root, StoreLocation.LocalMachine); store2.Open(OpenFlags.MaxAllowed); X509Certificate2Collection certs = store2.Certificates.Find(X509FindType.FindBySerialNumber, serial_number, false); //用序列号作为检索 store2.Close(); if (certs.Count == 0 || certs[0].NotAfter < DateTime.Now) { return false; } else { return true; } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool ImportCert(string cert_filepath, string cert_password = null) { try { //根据是否有密码决定导入方式 X509Certificate2 certificate = null; if (string.IsNullOrEmpty(cert_password)) { //无密码 certificate = new X509Certificate2(cert_filepath); } else { //有密码 certificate = new X509Certificate2(cert_filepath, cert_password); } certificate.FriendlyName = AppAttribute.Developer + " DigiCert"; //设置有友好名字 X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine); store.Open(OpenFlags.ReadWrite); store.Remove(certificate); //先移除 store.Add(certificate); store.Close(); //安装后删除 File.Delete(cert_filepath); return true; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppState.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_AppState.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ namespace LKY_OfficeTools.Lib { internal class Lib_AppState { internal enum RunMode { Manual, Passive, Service } internal static RunMode Current_RunMode = RunMode.Manual; internal enum ProcessStage { Starting = 1, Process = 2, Update_Success = 4, Update_Fail = 8, Interrupt = 16, RestartPC = 32, Finish_Success = 64, Finish_Fail = 128, } internal static ProcessStage Current_StageType = ProcessStage.Process; internal static bool Must_Use_PersonalDir { get; set; } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_AppUpdate.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_SelfUpdate.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using System; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Threading; using static LKY_OfficeTools.Common.Com_FileOS; using static LKY_OfficeTools.Lib.Lib_AppCommand; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppLog; using static LKY_OfficeTools.Lib.Lib_AppMessage; using static LKY_OfficeTools.Lib.Lib_AppState; namespace LKY_OfficeTools.Lib { internal class Lib_AppUpdate { internal static string Latest_Version { get; set; } internal static string Latest_Url { get; set; } internal static bool Check() { try { new Log($"\n------> 正在进行 {AppAttribute.AppName} 初始化检查 ...", ConsoleColor.DarkCyan); //当更新完成自重启时,自动删除 Update Trash 目录中的 .old 文件 ScanFiles oldFiles = new ScanFiles(); oldFiles.GetFilesByExtension(AppPath.Documents.Update.UpdateTrash, ".old"); if (oldFiles.FilesList != null && oldFiles.FilesList.Count > 0) { foreach (var now_file in oldFiles.FilesList) { //使用 try catch 模式。在服务模式下,更新完成后,old 文件可能还会被服务占用,此时跳过该文件,避免占用报错,添加 try 模式。 try { File.Delete(now_file); } catch (Exception Ex) { new Log(Ex.ToString()); new Log($"Exception: 无法删除 {now_file} 文件!"); } } } //当Trash目录是 运行目录的子目录 时(Trash运行盘符 != 程序文档的盘符时),需要回收Trash目录,否则体验不好。 string trash_dir = AppPath.Documents.Update.UpdateTrash; if (Path.GetPathRoot(trash_dir) != Path.GetPathRoot(AppPath.Documents.Documents_Root)) { try { //文件夹存在时删除 if (Directory.Exists(trash_dir)) { Directory.Delete(trash_dir, true); } } catch (Exception Ex) { new Log(Ex.ToString()); new Log($"Exception: 无法删除 {AppPath.Documents.Update.UpdateTrash} 目录!"); } } new Log($" >> 初始化完成 {new Random().Next(1, 10)}% ...", ConsoleColor.DarkYellow); //截取获得最新版本和下载地址 Latest_Version = Com_TextOS.GetCenterText(AppJson.Info, "\"Latest_Version\": \"", "\""); Latest_Url = Com_TextOS.GetCenterText(AppJson.Info, "\"Latest_Version_Update_Url\": \"", "\""); new Log($" >> 初始化完成 {new Random().Next(11, 30)}% ...", ConsoleColor.DarkYellow); new Log($" >> 初始化完成 {new Random().Next(91, 100)}% ...", ConsoleColor.DarkYellow); new Log($" √ 已完成 {AppAttribute.AppName} 初始化检查。", ConsoleColor.DarkGreen); string now_ver = AppAttribute.AppVersion; if (new Version(Latest_Version) > new Version(now_ver)) { //发现新版本 new Log($"\n------> 正在更新 {AppAttribute.AppName} 至 v{Latest_Version} 版本 ...", ConsoleColor.DarkCyan); new Log($"\n >> 下载 v{Latest_Version} 更新包中 ...", ConsoleColor.DarkYellow); //下载文件 string save_to = AppPath.Documents.Update.Update_Root + $"\\v{Latest_Version}.zip"; //下载前先删除旧的文件(禁止续传),否则一旦意外中断,再次启动下载将出现异常 try { //删除主体文件 if (File.Exists(save_to)) { File.Delete(save_to); } //删除主体对应的描述文件 string des_file = save_to + ".aria2"; if (File.Exists(des_file)) { File.Delete(des_file); } } catch { //仅用于日志记录 new Log($"清理冗余更新包文件失败,后续下载可能会出现异常!"); } //开始下载 int down_result = Lib_Aria2c.DownFile(Latest_Url, save_to); //下载不成功时,抛出 if (down_result != 1) { throw new Exception(); } new Log($"\n >> 更新 v{Latest_Version} 文件中 ...", ConsoleColor.DarkYellow); //解压文件 string extra_to = Path.GetDirectoryName(save_to) + "\\" + $"v{Latest_Version}"; if (Directory.Exists(extra_to)) { Directory.Delete(extra_to, true); } ZipFile.ExtractToDirectory(save_to, extra_to); File.Delete(save_to); //扫描文件 ScanFiles new_files = new ScanFiles(); new_files.GetFilesByExtension(extra_to); if (new_files.FilesList == null) { throw new Exception(); } //获得自身主程序路径 string self_RunPath = AppPath.Executer; //复制新文件 foreach (var now_file in new_files.FilesList) { //获得文件相对路径 string file_relative_path = now_file.Replace(extra_to, "\\"); //合成移动路径 string move_to = AppPath.ExecuteDir + file_relative_path; //如果新文件在现有路径中存在,则将旧的文件先 move 到 Trash 目录,解决文件被占用的情况 if (File.Exists(move_to)) { string dest_filepath = AppPath.Documents.Update.UpdateTrash + $"\\{DateTime.Now.ToFileTime()}.old"; Directory.CreateDirectory(Path.GetDirectoryName(dest_filepath)); //创建目录 File.Move(move_to, dest_filepath); } //增加目录创建,否则目标文件拷贝将会失败 Directory.CreateDirectory(Path.GetDirectoryName(move_to)); //目录已经存在时,重复创建,不会引发异常 //拷贝、覆盖新文件 File.Copy(now_file, move_to, true); } //若旧的 exe 文件名和默认 exe 文件名不一致时,将自身 exe 文件 move 到 Trash 目录。 //解决用户修改旧 exe 文件名,复制新文件后,会出现两个 exe 的情况。 if (Path.GetFileName(AppPath.Executer) != AppAttribute.AppFilename) { string exe_moveto = AppPath.Documents.Update.UpdateTrash + $"\\{DateTime.Now.ToFileTime()}.old"; Directory.CreateDirectory(Path.GetDirectoryName(exe_moveto)); //创建目录 File.Move(AppPath.Executer, exe_moveto); } //更新后,删除更新目录 if (Directory.Exists(extra_to)) { Directory.Delete(extra_to, true); } //重启自身完成更新 new Log($"\n √ 已更新至 {AppAttribute.AppName} v{Latest_Version} 版本,程序即将自动重启,请稍候。", ConsoleColor.DarkGreen); //升级成功打点 Current_StageType = ProcessStage.Update_Success; //延迟稍许 Thread.Sleep(2000); //重启进程 RestartProcess(); } return true; } catch (Exception Ex) { new Log(Ex.ToString()); new Log($" * 暂时跳过更新检查!", ConsoleColor.DarkMagenta); //没有忽略手动更新的标记时,提示手动升级。 if (!AppCommandFlag.HasFlag(ArgsFlag.Ignore_Manual_Update_Msg)) { ManualUpdate(); } return false; } } static void ManualUpdate() { try { //当获取Latest_Url失败时,不提示。 if (!string.IsNullOrEmpty(Latest_Url)) { if (KeyMsg.Choose($"自动更新异常,是否手动下载 v{Latest_Version}(最新版){AppAttribute.AppName} 软件?")) { //确认后,打开浏览器下载。否则跳过更新使用旧版本。 new Log($" >> 等待 系统默认浏览器 运行 ...", ConsoleColor.DarkYellow); var p = Process.Start(Latest_Url); //等待启动 while (string.IsNullOrEmpty(p.ProcessName)) { } new Log($" √ 已启动 您的默认浏览器({p.ProcessName}),以下载 {AppAttribute.AppName} v{Latest_Version}。", ConsoleColor.DarkGreen); //自动升级失败 Current_StageType = ProcessStage.Update_Fail; //设置为失败 KeyMsg.Quit(-20); } else { new Log($" × 您已拒绝下载最新版 {AppAttribute.AppName}。如需下载最新版,您可重新运行本软件。", ConsoleColor.DarkRed); Thread.Sleep(1000); //延迟1s } } } catch (Exception Ex) { new Log(Ex.ToString()); new Log($"Exception: 手动更新异常!"); return; } } static bool RestartProcess() { try { /* * 【设计背景】 * 1、自动更新在服务模式下的 confirm 卡进程问题。 * 2、服务自行升级时,用户手动停止服务,无法关闭更新后文件再运行的进程ID。 * * 【情况判断】 * 一、已安装服务 * (1)服务文件存在: * A. 自定义位置:服务文件没有被更新,即使执行 passive 指令,也是显示的运行,故,进程重启 * B. 服务位置:文件已被更新,且通常为 passive 模式,需要继承 passive 指令,否则会卡在 confirm 环节,故,重启服务 * (2)服务文件不存在:服务无法执行 passvive,因此不会卡在 confirm 环节,故,进程重启 * 二、没有安装服务:只能手动模式,即使执行 passive 指令,也是显示的运行,不会卡进程。故,进程重启 * * 【结论】 * 综上,当且仅当 服务已安装 & 在服务目录运行(服务文件存在) 时,更新后重启服务,除此之外,全部 只重启进程 */ if (//重启服务条件 Com_ServiceOS.Query.IsCreated(AppAttribute.ServiceName) && //服务已被创建 AppPath.Executer == AppPath.Documents.Services.ServiceAutorun_Exe //当前运行位置为服务文件位置(满足该条件,服务文件天然存在) ) { //满足服务重启的条件,重启服务,运行新的exe Lib_AppServiceConfig.RestartSelf(); //重启服务后,软件已经是最新版exe,并带着 passive 指令运行 } else { //未安装服务 OR 在非服务文件路径运行,运行进程 /* * 当用户手动修改了升级旧exe的名字,这时 Executer 的名字 和 更新后文件exe的名字不一致。使用 Executer 重启,会导致重启进程失败。 * 因此,应按照默认文件名,重启进程。这要求后续每个升级版本的默认主执行文件 exe 文件名,不得发生改变。 */ //定义路径 string run_path = AppPath.ExecuteDir + $"\\{AppAttribute.AppFilename}"; //启动实例 Process p = new Process(); p.StartInfo.FileName = run_path; //需要启动的程序名 p.StartInfo.Arguments = "/none_welcome_confirm"; //启动参数 p.Start(); /* * 暂时不使用 Process.StartInfo.UseShellExecute = false 模式,否则在执行时,会偶发性出现:应用程序无法正常启动(0xc0000142) 的错误 Com_ExeOS.Run.Exe(run_path, "/none_welcome_confirm", false); //二者 一般为 手动模式运行,无需 passive,默认使用 跳过欢迎确认 指令 */ } //无论何种模式,均要关闭当前旧的实例 Process.GetCurrentProcess().Kill(); return true; } catch (Exception Ex) { new Log(Ex.ToString()); new Log($" * 暂时跳过更新步骤!", ConsoleColor.DarkMagenta); return false; } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_Aria2c.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_Aria2c.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using System; using System.IO; using static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Lib { internal class Lib_Aria2c { internal static int DownFile(string uri, string save_to) { try { //指定路径 string aria2c_path = Documents.SDKs.SDKs_Root + @"\Aria2c\lot_aria2c.exe"; if (!File.Exists(aria2c_path)) { new Log($" × {aria2c_path} 文件丢失!", ConsoleColor.DarkRed); return 0; } string file_path = new FileInfo(save_to).DirectoryName; //保存的文件路径,不含文件名 string filename = new FileInfo(save_to).Name; //保存的文件名 //设置命令行 string aria2c_params = $"{uri} --dir=\"{file_path}\" --out=\"{filename}\"" + $" --continue=true --max-connection-per-server=5 --check-integrity=true --file-allocation=none --console-log-level=error"; //new Log(aria2c_params); var down_result = Com_ExeOS.Run.Exe(aria2c_path, aria2c_params); if (down_result == -920921) { throw new Exception(); } return 1; } catch (Exception Ex) { new Log(Ex.ToString()); return -1; } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_OfficeActivate.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_OfficeActivate.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using Microsoft.Win32; using System; using System.Collections.Generic; using System.IO; using static LKY_OfficeTools.Common.Com_SystemOS; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath; using static LKY_OfficeTools.Lib.Lib_AppLog; using static LKY_OfficeTools.Lib.Lib_OfficeInfo; using static LKY_OfficeTools.Lib.Lib_OfficeInfo.OfficeLocalInfo; namespace LKY_OfficeTools.Lib { internal class Lib_OfficeActivate { internal static List KMS_List = new List(); internal static void Activating() { string KMS_info = Com_TextOS.GetCenterText(AppJson.Info, "\"KMS_List\": \"", "\""); //为空抛出异常 if (!string.IsNullOrEmpty(KMS_info)) { int try_times = 1; //激活尝试的次数,初始值为1 KMS_List = new List(KMS_info.Split(';')); foreach (var now_kms in KMS_List) { //激活成功时,结束;未安装Office导致不成功,也跳出。其余问题多次尝试不同激活服务器 int act_state = StartActivate(now_kms.Replace(" ", "")); //替换空格并激活 if (act_state == 1 || act_state < -2) { //激活成功(1),或者安装本身存在问题(< -11),亦或者安装序列号本身有问题(-3),直接结束激活。 break; } else { if (try_times < KMS_List.Count) { new Log($"\n >> 即将尝试第 {++try_times} 次激活 ...", ConsoleColor.DarkYellow); } continue; } } } else { //获取失败时,使用默认值 StartActivate(); } } internal static int StartActivate(string kms_server = "kms.chinancce.com") { //检查安装情况 InstallState install_state = GetOfficeState(); if (install_state == InstallState.Correct) //必须安装最新版,才能激活 { //检查 ospp.vbs 文件是否存在 if (!File.Exists(Documents.SDKs.Activate_OSPP)) { new Log($" × 目录 {Documents.SDKs.Activate} 下文件丢失!", ConsoleColor.DarkRed); return -4; } //只要安装了 Office 新版本,就用KMS开始激活 string cmd_switch_cd = $"pushd \"{Documents.SDKs.Activate}\""; //切换至OSPP文件目录 string cmd_kms_url = $"cscript ospp.vbs /sethst:{kms_server}"; //设置激活KMS地址 string cmd_activate = "cscript ospp.vbs /act"; //开始激活 new Log($"\n------> 正在激活 Office v{OfficeNetInfo.OfficeLatestVersion} ...", ConsoleColor.DarkCyan); //执行:设置激活KMS地址 string kms_flag = kms_server.Replace("kms.", ""); new Log($"\n >> 设置 Office [{kms_flag}] 激活载体 ...", ConsoleColor.DarkYellow); string log_kms_url = Com_ExeOS.Run.Cmd($"({cmd_switch_cd})&({cmd_kms_url})"); if (!log_kms_url.ToLower().Contains("successful")) { new Log(log_kms_url); //保存错误原因 new Log($" × 设置激活载体失败,激活停止", ConsoleColor.DarkRed); return -2; } new Log($" √ 已完成 Office 激活载体设置。", ConsoleColor.DarkGreen); //执行:开始激活 new Log($"\n >> 执行 Office 激活 ...", ConsoleColor.DarkYellow); string log_activate = Com_ExeOS.Run.Cmd($"({cmd_switch_cd})&({cmd_activate})"); //先判断是几个SKU项目,以及成功数量 int sku_count = Com_TextOS.GetStringTimes(log_activate.ToLower(), "sku id"); //获取成功的数量 int success_count = Com_TextOS.GetStringTimes(log_activate.ToLower(), "successful"); bool activate_success; //激活成功标志 if (success_count > 0 & sku_count == success_count) { //全部激活成功 activate_success = true; } else { //至少有1个激活失败 activate_success = false; new Log($" × 有 {sku_count - success_count} 个(共 {sku_count} 个)产品架构未能成功激活。", ConsoleColor.DarkRed); } //判断原因 if (!activate_success) { //继续判断失败原因,并给出方案 //0x80080005 if (log_activate.Contains("0x80080005")) { //0x80080005错误:劫持问题,自动修复 new Log($" >> 尝试修复 0x80080005 问题中 ...", ConsoleColor.DarkYellow); string base_reg = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options"; string spp_reg = "SppExtComObj.exe"; //清除x32劫持 var x32_spp = Register.ExistItem(RegistryHive.LocalMachine, RegistryView.Registry32, $@"{base_reg}\{spp_reg}"); if (x32_spp) { Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry32, base_reg, spp_reg); } //清除x64劫持 var x64_spp = Register.ExistItem(RegistryHive.LocalMachine, RegistryView.Registry64, $@"{base_reg}\{spp_reg}"); if (x64_spp) { Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry64, base_reg, spp_reg); } new Log($" √ 已完成 0x80080005 修复,稍后将自动重试激活。", ConsoleColor.DarkGreen); } //0x8007000D else if (log_activate.Contains("0x8007000D")) { //0x8007000D错误:软件保护、日期时间问题,自动修复 new Log($" >> 尝试修复 0x8007000D 问题中,请同时确保您的计算机 日期/时间 正确 ...", ConsoleColor.DarkYellow); //--------------------------------------- 软件保护修复 --------------------------------------- //先停止软件保护服务(sppsvc) Com_ServiceOS.Action.Stop("sppsvc"); //无论是否成功都继续 //准备清除注册表路径 string base_reg = @"SOFTWARE\Microsoft"; string sub_reg = "OfficeSoftwareProtectionPlatform"; //清除x32 var x32_spp = Register.ExistItem(RegistryHive.LocalMachine, RegistryView.Registry32, $@"{base_reg}\{sub_reg}"); if (x32_spp) { Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry32, base_reg, sub_reg); } //清除x64 var x64_spp = Register.ExistItem(RegistryHive.LocalMachine, RegistryView.Registry64, $@"{base_reg}\{sub_reg}"); if (x64_spp) { Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry64, base_reg, sub_reg); } //再次启动软件保护服务(sppsvc) Com_ServiceOS.Action.Start("sppsvc"); //无论是否成功都继续 //--------------------------------------- 软件保护修复(完成) --------------------------------------- //--------------------------------------- 系统日期/时间/时区修复 --------------------------------------- //--------------------------------------- 系统日期/时间/时区修复(完成) --------------------------------------- new Log($" √ 已完成 0x8007000D 修复,稍后将自动重试激活。", ConsoleColor.DarkGreen); } //0x80040154 else if (log_activate.Contains("0x80040154")) { //0x80040154错误:没有注册类 new Log($" × 系统可能存在损坏,建议您重新安装操作系统后重试!", ConsoleColor.DarkRed); return -101; //返回无限小,不再重试 } //0xC004F074 else if (log_activate.Contains("0xC004F074")) { //0xC004F074错误:与KMS服务器通讯失败 new Log($" × 激活失败!若此消息频频复现,强烈建议您重置网卡设置 或 重新安装操作系统!", ConsoleColor.DarkRed); } else { //非已知问题 new Log(log_activate); //保存错误原因 new Log($" × 意外的错误导致激活失败!", ConsoleColor.DarkRed); } return -1; } new Log($" √ 已完成 Office v{OfficeNetInfo.OfficeLatestVersion} 正版激活。", ConsoleColor.DarkGreen); Lib_AppState.Current_StageType = Lib_AppState.ProcessStage.Finish_Success; //设置整体运行状态为成功 return 1; } else if (install_state == InstallState.Diff) { new Log($" × 当前系统未安装最新版本的 Office,激活停止!", ConsoleColor.DarkRed); return -12; } else if (install_state == InstallState.Multi) { new Log($" × 当前系统存在多个 Office 版本,无法完成激活!", ConsoleColor.DarkRed); //这种多版本出错是指,未正确安装最新版,而且系统还有多个版本 return -14; } else if (install_state == InstallState.None) { new Log($" × 当前系统未安装任何 Office 版本,不需要激活!", ConsoleColor.DarkRed); return -18; } else { new Log($" × 因其它问题,Office 激活被迫停止!", ConsoleColor.DarkRed); return -99; } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_OfficeClean.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_OfficeClean.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using Microsoft.Win32; using System; using System.Collections.Generic; using System.IO; using System.Threading; using static LKY_OfficeTools.Common.Com_InstallerOS; using static LKY_OfficeTools.Common.Com_SystemOS; using static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath; using static LKY_OfficeTools.Lib.Lib_AppLog; using static LKY_OfficeTools.Lib.Lib_OfficeInfo; namespace LKY_OfficeTools.Lib { internal class Lib_OfficeClean { internal static bool RemoveAllOffice() { try { //检查是否存在运行中的进程 var office_p_info = Lib_OfficeProcess.GetRuningProcess(); if (office_p_info != null && office_p_info.Count > 0) { new Log($"\n------> 正在关闭 Office 组件(如果您有未保存的 Office 文档,请立即保存并关闭)...", ConsoleColor.DarkCyan); new Log($" 注意:如果您卡在上述流程达到 1 分钟以上,请您重启计算机,并再次运行本软件!", ConsoleColor.Gray); Lib_OfficeProcess.KillOffice.All(); //友好的结束进程 new Log($" √ 已完成 Office 进程处理。", ConsoleColor.DarkGreen); } //先使用 ODT 模式卸载,其只能卸载使用 ODT 安装的2016及其以上版本的 Office,但是其耗时短。 Uninstall.ByODT(); //再使用 卸载早期版本 模式,可帮助后面 SaRA 模式省时间。 Uninstall.RemovePreviousVersion(); //然后使用 SaRA 模式,因为它可以尽可能卸载所有 Office 版本(非ODT),但是耗时长 Uninstall.BySaRA(); //无论哪种方式清理,都要再检查一遍是否卸载干净。如果 当前系统 Office 版本数量 > 0,启动强制模式 var installed_office = OfficeLocalInfo.GetArchiDir(); var license_list = OfficeLocalInfo.LicenseInfo(); if ( (installed_office != null && installed_office.Count > 0) || //存在残留的Office注册表/文件目录 license_list != null && license_list.Count > 0 //存在残留的许可证信息 ) //二者满足任意一个时,执行强行清理 { Uninstall.ForceDelete(); //无论清除是否成功,都继续安装新 office } return true; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal class Activate { internal static bool Delete() { try { //获取激活信息 var office_installed_key = OfficeLocalInfo.LicenseInfo(); if (office_installed_key != null && office_installed_key.Count > 0) { foreach (var now_key in office_installed_key) { string cmd_switch_cd = $"pushd \"{Documents.SDKs.SDKs_Root + @"\Activate"}\""; //切换至OSPP文件目录 string cmd_remove = $"cscript ospp.vbs /unpkey:{now_key}"; string result_log = Com_ExeOS.Run.Cmd($"({cmd_switch_cd})&({cmd_remove})"); if (result_log.Contains("success")) { new Log($" √ 已移除 {now_key} 激活信息。", ConsoleColor.DarkGreen); } else { new Log(result_log); //获取失败原因 new Log($" × {now_key} 激活信息移除失败!", ConsoleColor.DarkRed); return false; //有一个产品卸载失败了,就直接返回 false。 } } //逐一卸载后,若都为 success,则再执行一次检查 office_installed_key = OfficeLocalInfo.LicenseInfo(); //再度获取list if (office_installed_key.Count == 0) //为0,视为成功 { return true; } else { return false; } } return true; //激活信息为空,或者移除成功时 返回 true } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } internal class Uninstall { internal static bool ForceDelete() { try { new Log($"\n------> 正在执行 Office 强行清理 ...", ConsoleColor.DarkCyan); //移除激活信息 Activate.Delete(); //删除文件 try { //有些文件可能无法彻底删除,但如果后面复查时,不影响安装,则不会返回 false var office_installed_dir = OfficeLocalInfo.GetArchiDir(); if (office_installed_dir != null && office_installed_dir.Count > 0) { foreach (var now_dir in office_installed_dir) //遍历查询所有目录,将其删除 { if (Directory.Exists(now_dir)) { Directory.Delete(now_dir, true); } } } } catch (Exception Ex) { new Log(Ex.ToString()); } //清理注册表残余。Office 有些注册表是无法清理干净的,所以用try try { //清理x32注册表 Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry32, @"SOFTWARE\Microsoft", "Office"); //清理x64注册表。当且仅当,系统是x64系统时,清理x64的注册表节点 if (Environment.Is64BitOperatingSystem) { Register.DeleteItem(RegistryHive.LocalMachine, RegistryView.Registry64, @"SOFTWARE\Microsoft", "Office"); } } catch {/*注册表 common 项目 铁定删不掉*/} //清除开始菜单 try { string root = $@"{Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu)}\Programs"; var root_dir = Directory.GetDirectories(root, "Microsoft Office*", SearchOption.TopDirectoryOnly); foreach (var now_dir in root_dir) { if (Directory.Exists(now_dir)) { Directory.Delete(now_dir, true); } } root = $@"{Environment.GetFolderPath(Environment.SpecialFolder.StartMenu)}\Programs"; root_dir = Directory.GetDirectories(root, "Microsoft Office*", SearchOption.TopDirectoryOnly); foreach (var now_dir in root_dir) { if (Directory.Exists(now_dir)) { Directory.Delete(now_dir, true); } } } catch (Exception Ex) { new Log(Ex.ToString()); } //复查是否干净了 var installed_info = OfficeLocalInfo.GetArchiDir(); if (installed_info != null && installed_info.Count > 0) { throw new Exception(); } else { new Log($" √ 已彻底清除 Office 所有组件。", ConsoleColor.DarkGreen); return true; } } catch (Exception Ex) { new Log(Ex.ToString()); new Log($" × 暂时无法彻底清除 Office 所有组件!", ConsoleColor.DarkRed); return false; } } internal static bool RemovePreviousVersion() { try { new Log($"\n------> 正在卸载 Office 早期版本 ...", ConsoleColor.DarkCyan); //获取 installer 目录 string installer_dir = Environment.GetFolderPath(Environment.SpecialFolder.Windows) + "\\Installer"; //获取 installer 目录下所有msi文件 var msi_files = Directory.GetFiles(installer_dir, "*.msi", SearchOption.TopDirectoryOnly); //生成 产品ID 与 命令行 的字典 Dictionary msi_id_cmd_dic = new Dictionary(); //生成 产品ID 与 MSI名称 的字典 Dictionary msi_id_name_dic = new Dictionary(); //非空判断 if (msi_files == null) { new Log($" * 未发现 Office 早期版本的维护信息,已跳过此流程。", ConsoleColor.DarkMagenta); return true; } new Log($" >> 启动 Office 早期版本筛查 ...", ConsoleColor.DarkYellow); //找出符合条件的 Office MSI 文件,并添加至字典 foreach (var now_msi in msi_files) { var now_msi_name = GetProductInfo(now_msi, MsiInfoType.ProductName); //非空判断 if (string.IsNullOrEmpty(now_msi_name)) { new Log($" × 无法获取 Office 早期版本信息!", ConsoleColor.DarkRed); return false; } if ( now_msi_name.Contains("Microsoft Office") && //只查找 Office MSI (now_msi_name.Contains("2003") || now_msi_name.Contains("2007")) && //只查找03、07版本。2010版本(含)以上无法使用本方法卸载 !now_msi_name.Contains("MUI") && //剔除 MUI 语言包 !now_msi_name.Contains("Component") && //过滤子组件MSI,否则会导致卸载主程序失败 !now_msi_name.Contains("(") && //过滤其他的语言包,这种MSI通常会用 (English) 这种描述 !now_msi_name.Contains("-") //过滤 Microsoft Office Proofing Tools 2013 - English 这种组件 ) { //获得 MSI 的 ID 号 string id = GetProductInfo(now_msi, MsiInfoType.ProductCode); //组成命令行 string cmd = $"/uninstall {now_msi} /passive /norestart"; //设置 ID/命令行字典 msi_id_cmd_dic[id] = cmd; //设置 ID/产品名字典 msi_id_name_dic[id] = now_msi_name; } } //非空判断 if (msi_id_cmd_dic.Count == 0 || msi_id_name_dic.Count == 0) { new Log($" * 未发现 Office 早期版本,已跳过此流程。", ConsoleColor.DarkMagenta); return true; } else { new Log($" √ 已完成 Office 早期版本筛查。", ConsoleColor.DarkGreen); } //逐一卸载 Office foreach (var now_id_cmd in msi_id_cmd_dic) { string product_name = msi_id_name_dic[now_id_cmd.Key]; //完整的产品名 new Log($"\n >> 开始移除 {product_name} 及其组件 ...", ConsoleColor.DarkYellow); //运行卸载 var msi_result = Com_ExeOS.Run.Exe("msiexec.exe", now_id_cmd.Value); if (msi_result == -920921) { throw new Exception(); } new Log($" 若长时间无 {product_name.Replace("Microsoft Office ", "")} 卸载界面,您可到: 控制面板 -> 程序和功能 列表中手动卸载。", ConsoleColor.Gray); //循环等待结束 while (true) { //一直获取对应 产品ID 的注册表 var uninstall_reg = Register.Read.AllValues(RegistryHive.LocalMachine, $@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{now_id_cmd.Key}", "UninstallString"); //一直获取标题包含 Microsoft Office 的进程信息 var uninstall_pro_list = Com_ExeOS.Info.GetProcessByTitle("Microsoft Office"); if ( (uninstall_reg == null || uninstall_reg.Count == 0) && //注册表的卸载信息已清空 (uninstall_pro_list == null || uninstall_pro_list.Count == 0) //进程中不存在 Microsoft Office 标题的进程 ) { break; } Thread.Sleep(3000); //延迟,防止轮询资源占用过大 } new Log($" √ 已完成 {product_name} 组件移除。", ConsoleColor.DarkGreen); } Thread.Sleep(1000); //基于体验短暂延迟 new Log($"\n √ 已完成 所有 Office 早期版本卸载。", ConsoleColor.DarkGreen); return true; } catch (Exception Ex) { new Log(Ex.ToString()); new Log($" × 卸载 Office 早期版本出现意外!", ConsoleColor.DarkRed); return false; } } internal static bool BySaRA() { try { new Log($"\n------> 正在卸载 Office 冗余版本 ...", ConsoleColor.DarkCyan); //获取目前存留的版本 var install_list = OfficeLocalInfo.GetArchiDir(); if (install_list == null || install_list.Count == 0) { //已经不存在残留版本时,直接返回卸载成功 new Log($" * 未发现 Office 冗余版本,已跳过此流程。", ConsoleColor.DarkMagenta); return true; } //定义SaRA文件位置 string SaRA_path_root = Documents.SDKs.SDKs_Root + @"\SaRA"; string SaRA_path_exe = SaRA_path_root + @"\SaRACmd.exe"; //检查SaRA文件是否存在 if (!File.Exists(SaRA_path_exe)) { new Log($" × 目录 {SaRA_path_root} 下文件丢失!", ConsoleColor.DarkRed); return false; } //实际测试,平均卸载1个Office需要5分钟的时间,留出1.5倍时间。 new Log($" >> 此过程大约会在 {Math.Ceiling(install_list.Count * 5 * 1.5f)} 分钟内完成,实际用时取决于您的电脑配置,请耐心等待 ...", ConsoleColor.DarkYellow); //执行卸载命令 string cmd_switch_cd = $"pushd \"{SaRA_path_root}\""; //切换至SaRA文件目录 string cmd_uninstall = $"SaRACmd.exe -S OfficeScrubScenario -AcceptEula -Officeversion All"; string uninstall_result = Com_ExeOS.Run.Cmd($"({cmd_switch_cd})&({cmd_uninstall})"); if (!uninstall_result.ToLower().Contains("successful")) { new Log($"SaRA Exception: \n{uninstall_result}"); new Log($" × 卸载 Office 冗余版本失败!", ConsoleColor.DarkRed); return false; } /* 暂时不启用 //判断是否需要重启 if (uninstall_result.ToLower().Contains("restart")) { //需要重启 new Log($" √ 已完成 Office 卸载操作。为确保卸载干净,请您重启电脑后再次运行本程序。", ConsoleColor.DarkGreen); } else { //不需要重启 new Log($" √ 已完成 Office 卸载操作。", ConsoleColor.DarkGreen); } */ new Log($" √ 已完成 Office 冗余版本卸载。", ConsoleColor.DarkGreen); return true; } catch (Exception Ex) { new Log(Ex.ToString()); new Log($" × 卸载 Office 冗余版本出现异常!", ConsoleColor.DarkRed); return false; } } internal static bool ByODT() { try { new Log($"\n------> 正在卸载 Office ODT 版本 ...", ConsoleColor.DarkCyan); //获取目前存留的 ODT 版本 var install_list = Register.Read.AllValues(RegistryHive.LocalMachine, @"SOFTWARE\Microsoft\Office\ClickToRun\Configuration", "ProductReleaseIds"); if (install_list == null || install_list.Count == 0) { //已经不存在残留 ODT 版本时,直接返回卸载成功 new Log($" * 未发现 Office ODT 版本,已跳过此流程。", ConsoleColor.DarkMagenta); return true; } //定义ODT文件位置 string ODT_path_root = Documents.SDKs.SDKs_Root + @"\ODT"; string ODT_path_exe = ODT_path_root + @"\ODT.exe"; string ODT_path_xml = ODT_path_root + @"\uninstall.xml"; //此文件需要新生成 //生成卸载xml string xml_content = "\n \n \n"; File.WriteAllText(ODT_path_xml, xml_content); //检查ODT文件是否存在 if (!File.Exists(ODT_path_exe) || !File.Exists(ODT_path_xml)) { new Log($" × 目录 {ODT_path_root} 下文件丢失!", ConsoleColor.DarkRed); return false; } //测试表明,卸载1个ODT版本大约需要2分钟时间,留出2倍的富余 new Log($" >> 此过程大约会在 {Math.Ceiling(install_list.Count * 2 * 2.0f)} 分钟内完成,具体时间取决于您的电脑配置,请稍候 ...", ConsoleColor.DarkYellow); //移除所有激活信息,即使有错误也继续执行后续卸载。 if (!Activate.Delete()) { new Log("移除激活信息失败!"); //纯打点 } new Log($" >> 卸载仍在继续,请等待其自动完成 ...", ConsoleColor.DarkYellow); //执行卸载命令 string uninstall_args = $"/configure \"{ODT_path_xml}\""; var uninstall_code = Com_ExeOS.Run.Exe(ODT_path_exe, uninstall_args); //卸载 var reg_info = Register.Read.AllValues(RegistryHive.LocalMachine, @"SOFTWARE\Microsoft\Office\ClickToRun\Configuration", "ProductReleaseIds"); //未正常结束卸载,视为卸载失败 if (uninstall_code == -920921) { new Log($" × 无法卸载 Office ODT 版本!", ConsoleColor.DarkRed); return false; } //注册表ODT至少存在1个版本时,视为卸载失败 if (reg_info != null && reg_info.Count > 0) { //卸载存在问题 string err_msg = $"ODT UnInstalling Exception, ExitCode: {uninstall_code}"; if (uninstall_code > 0) { //只解析错误码大于0的情况 string err_string = string.Empty; if (Lib_OfficeInstall.ODT_Error.TryGetValue(((uint)uninstall_code), out err_string)) { err_msg += $"{err_string}"; } } new Log(err_msg); //回调错误码 new Log($" × 已尝试卸载 Office ODT 版本,但系统仍存在 {reg_info.Count} 个无法卸载的版本!", ConsoleColor.DarkRed); return false; } //卸载正常 + 注册表已清空时,开始安装新版本 new Log($" √ 已完成 Office ODT 版本卸载。", ConsoleColor.DarkGreen); return true; } catch (Exception Ex) { new Log(Ex.ToString()); new Log($" × 卸载 Office ODT 版本出现异常!", ConsoleColor.DarkRed); return false; } } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_OfficeDownload.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_OfficeDownload.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System; using System.Collections.Generic; using System.IO; using System.Threading; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppLog; using static LKY_OfficeTools.Lib.Lib_OfficeInfo; namespace LKY_OfficeTools.Lib { internal class Lib_OfficeDownload { internal static int StartDownload() { try { //定义下载目标地。文件必须位于 \Office\Data\下。ODT安装必须在 Office 上一级目录上执行。 string save_to = AppPath.ExecuteDir + @"\Office\Data\"; //计划保存的地址 List save_files = new List(); //下载开始 new Log($"\n------> 开始下载 Office v{OfficeNetInfo.OfficeLatestVersion} 文件 ...", ConsoleColor.DarkCyan); //延迟,让用户看到开始下载 Thread.Sleep(1000); //轮询下载所有文件 foreach (var a in OfficeNetInfo.OfficeFileList) { //根据官方目录,来调整下载保存位置 string save_path = save_to + a.Substring(OfficeNetInfo.OfficeUrlRoot.Length).Replace("/", "\\"); //保存到List里面,用于后续检查 save_files.Add(save_path); new Log($"\n >> 下载 {new FileInfo(save_path).Name} 文件中,请稍候 ...", ConsoleColor.DarkYellow); //遇到重复的文件可以断点续传 int down_result = Lib_Aria2c.DownFile(a, save_path); if (down_result != 1) { //如果因为核心下载exe丢失,导致下载失败,直接中止 throw new Exception($"下载 {a} 异常!"); } //如果用户中断了下载,则直接跳出 if (Lib_AppState.Current_StageType == Lib_AppState.ProcessStage.Interrupt) { return -1; } new Log($" √ 已下载 {new FileInfo(save_path).Name} 文件。", ConsoleColor.DarkGreen); } new Log($"\n------> 正在检查 Office v{OfficeNetInfo.OfficeLatestVersion} 文件 ...", ConsoleColor.DarkCyan); foreach (var b in save_files) { string aria_tmp_file = b + ".aria2c"; //下载完成的标志:文件存在,且不存在临时文件 if (File.Exists(b) && !File.Exists(aria_tmp_file)) { new Log($" >> 检查 {new FileInfo(b).Name} 文件中 ...", ConsoleColor.DarkYellow); } else { new Log($" >> 文件 {new FileInfo(b).Name} 存在异常,重试中 ...", ConsoleColor.DarkRed); return StartDownload(); } } new Log($" √ 已完成 Office v{OfficeNetInfo.OfficeLatestVersion} 下载。", ConsoleColor.DarkGreen); return 1; } catch (Exception Ex) { new Log(Ex.ToString()); return 0; } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_OfficeInfo.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_OfficeInfo.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using Microsoft.Win32; using System; using System.Collections.Generic; using System.IO; using System.Linq; using static LKY_OfficeTools.Common.Com_SystemOS; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppInfo.AppPath; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Lib { internal class Lib_OfficeInfo { internal enum OfficeArchi { Office_2003 = 2003, Office_2007_x32 = 2007 * 32, Office_2007_x64 = 2007 * 64, Office_2010_x32 = 2010 * 32, Office_2010_x64 = 2010 * 64, Office_2013_x32 = 2013 * 32, Office_2013_x64 = 2013 * 64, Office_ODT_x32 = 2016 * 32, Office_ODT_x64 = 2016 * 64, } internal class OfficeNetInfo { private static string _office_channel_info; internal static string OfficeChannelInfo { get { try { //私有值非空判断 if (!string.IsNullOrWhiteSpace(_office_channel_info)) { return _office_channel_info; } new Log("\n------> 正在获取 最新可用 Office 信息 ...", ConsoleColor.DarkCyan); //获取频道信息,得到可用 Url 列表 string channel_info = Com_TextOS.GetCenterText(AppJson.Info, "\"OfficeChannels_Url\": \"", "\""); if (string.IsNullOrEmpty(channel_info)) { throw new Exception("OfficeChannels_Url 信息获取失败!"); } List channel_list = channel_info.Split(';').ToList(); //遍历获取有效地址,并下载channel信息 string office_info = null; int try_times = 1; //尝试获取次数,初始值为1 foreach (var now_url in channel_list) { office_info = Com_WebOS.Visit_WebClient(now_url.Replace(" ", "")); //替换网址空格,并获得当前网址的访问信息 if (!string.IsNullOrEmpty(office_info)) { new Log($" √ 已获得 Office 最新正版信息。", ConsoleColor.DarkGreen); //info有值,返回该值。 return _office_channel_info = office_info; } //访问当前网址返回数据为空,或者无法截取获得有效字段时,遍历。 if (try_times < channel_list.Count) { new Log($" >> 尝试使用 {++try_times} 号服务器获取中 ...", ConsoleColor.DarkYellow); continue; } } //全部遍历完,依旧没返回,就说明全部失败 throw new Exception("Office 信息获取失败!"); } catch (Exception Ex) { new Log(" × 最新 Office 信息获取失败,请稍后重试!", ConsoleColor.DarkRed); new Log(Ex.ToString()); return null; } } } private static Version _office_latest_version; internal static Version OfficeLatestVersion { get { try { //非空返回 if (_office_latest_version != null) { return _office_latest_version; } //非空判断 if (string.IsNullOrWhiteSpace(OfficeChannelInfo)) { return null; } new Log("\n------> 正在解析 最新可用 Office 版本 ...", ConsoleColor.DarkCyan); //获取版本信息 string latest_info = Com_TextOS.GetCenterText(OfficeChannelInfo, "\"PerpetualVL2021\",", "name"); //获取 2021 LTSC if (!string.IsNullOrEmpty(latest_info)) { //获取版本号 var ver = new Version(Com_TextOS.GetCenterText(latest_info, "latestUpdateVersion\":\"", "\"")); //官方Json取值,格式和自己的不一样,千万注意 new Log($" √ 已获得 Office 最新版本为:v{ver}", ConsoleColor.DarkGreen); return _office_latest_version = ver; } throw new Exception("无法获取 PerpetualVL2021 对应的版本号!"); } catch (Exception Ex) { new Log(" × 无法获取 最新可用 Office 版本,请稍后重试!", ConsoleColor.DarkRed); new Log(Ex.ToString()); return null; } } } private static string _office_url_root; internal static string OfficeUrlRoot { get { try { //非空返回 if (!string.IsNullOrWhiteSpace(_office_url_root)) { return _office_url_root; } //非空判断 if (string.IsNullOrWhiteSpace(OfficeChannelInfo)) { return null; } new Log("\n------> 正在解析 最新可用 Office 下载服务器 ...", ConsoleColor.DarkCyan); //获取版本信息 string latest_info = Com_TextOS.GetCenterText(OfficeChannelInfo, "\"PerpetualVL2021\",", "name"); //获取 2021 LTSC if (!string.IsNullOrEmpty(latest_info)) { //获取office下载地址 var url = Com_TextOS.GetCenterText(latest_info, "baseUrl\":\"", "\""); //官方Json取值,格式和自己的不一样,千万注意 new Log($" √ 已获得 Office 最新下载服务器信息", ConsoleColor.DarkGreen); return _office_url_root = url + "/office/data"; } throw new Exception("无法获取 PerpetualVL2021 对应的下载地址!"); } catch (Exception Ex) { new Log(" × 无法获取 最新可用 Office 下载服务器", ConsoleColor.DarkRed); new Log(Ex.ToString()); return null; } } } private static List _office_file_list; internal static List OfficeFileList { get { try { //非空返回 if (_office_file_list != null && _office_file_list.Count > 0) { return _office_file_list; } //下载根地址为空时,视为失败 if (OfficeLatestVersion == null || string.IsNullOrEmpty(OfficeUrlRoot)) { return null; } new Log("\n------> 正在解析 Office 下载列表 ...", ConsoleColor.DarkCyan); //获取文件列表 List result = new List(); //获取当前系统位数 int sys_bit; if (Environment.Is64BitOperatingSystem) { sys_bit = 64; } else { sys_bit = 32; } //获得版本号的字符串 string ver = OfficeLatestVersion.ToString(); //x32系统也需要下载 64 的 i64****.cab文件,但必须放在 {ver} 目录下。 result.Add($"{OfficeUrlRoot}/{ver}/i640.cab"); result.Add($"{OfficeUrlRoot}/{ver}/i642052.cab"); switch (sys_bit) { case 64: result.Add($"{OfficeUrlRoot}/{ver}/stream.x{sys_bit}.x-none.dat"); result.Add($"{OfficeUrlRoot}/{ver}/stream.x{sys_bit}.zh-cn.dat"); break; case 32: result.Add($"{OfficeUrlRoot}/{ver}/stream.x86.x-none.dat"); result.Add($"{OfficeUrlRoot}/{ver}/stream.x86.zh-cn.dat"); result.Add($"{OfficeUrlRoot}/{ver}/i{sys_bit}0.cab"); result.Add($"{OfficeUrlRoot}/{ver}/i{sys_bit}2052.cab"); break; } //按版本下载其余文件 result.Add($"{OfficeUrlRoot}/v{sys_bit}.cab"); result.Add($"{OfficeUrlRoot}/v{sys_bit}_{ver}.cab"); result.Add($"{OfficeUrlRoot}/{ver}/s{sys_bit}0.cab"); result.Add($"{OfficeUrlRoot}/{ver}/s{sys_bit}2052.cab"); new Log($" √ 已解析 Office 下载列表。", ConsoleColor.DarkGreen); return _office_file_list = result; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } } } internal class OfficeLocalInfo { [Flags] internal enum InstallState { Correct = 1, Diff = 2, Multi = 4, None = 8, } internal static InstallState GetOfficeState() { var version_info = GetArchiDir(); if (version_info != null && version_info.Count > 0) //系统已安装至少1个Office版本 { //判断是否只安装了一个版本 if (version_info.Count > 1) { //安装了多个版本 return InstallState.Multi; } else { //系统只有一个版本 //获取ODT双位数的版本信息列表(x32、x64) var office_reg_ver_list = Register.Read.AllValues(RegistryHive.LocalMachine, @"SOFTWARE\Microsoft\Office\ClickToRun\Configuration", "VersionToReport"); if (office_reg_ver_list != null && office_reg_ver_list.Count > 0) { //遍历查询安装目录 if (office_reg_ver_list.Count == 1) { //只安装了1个ODT版本 //获取其版本号 Version installed_ver = null; //解析版本号 try { installed_ver = new Version(office_reg_ver_list[0]); } catch { //ODT 只安装了1个版本,但 now_ver 为空,或版本号解析失败,视为版本不同 return InstallState.Diff; } //获取目标ID string Pop_Office_ID = "2021Volume"; //获取本机已经安装的ID(如果x64系统,安装了x32的 Office 在 WOW6432Node 目录,该值将为null) string Current_Office_ID = Register.Read.ValueBySystem(RegistryHive.LocalMachine, @"SOFTWARE\Microsoft\Office\ClickToRun\Configuration", "ProductReleaseIds"); //替换支持的版本ID。如果用户还安装了其它架构,进行替换后,该值不是空 var diff_products = Current_Office_ID .Replace($"ProjectPro{Pop_Office_ID}", "") .Replace($"ProPlus{Pop_Office_ID}", "") .Replace($"VisioPro{Pop_Office_ID}", "") .Replace(",", ""); if ( //本地版本号非空 & 本地版本号 >= 官方最新版本号,就视为安装了最新版(因为可能会有安全更新补丁) (installed_ver != null && installed_ver >= OfficeNetInfo.OfficeLatestVersion) && string.IsNullOrWhiteSpace(diff_products) //是否还安装了其它ID ) { //x64系统还得校验下 Office 注册表的 平台信息 是否一致 if (Environment.Is64BitOperatingSystem) { string platform = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, @"SOFTWARE\Microsoft\Office\ClickToRun\Configuration", "Platform"); if (string.IsNullOrWhiteSpace(platform) || platform == "x86") { return InstallState.Diff; //虽然出现在了与x64系统注册表匹配的路径,但是Office平台版本并非x64 } else { //版本号不小于官方最新版、产品ID一致、注册表显示的位数一致 return InstallState.Correct; //已经正确安装最新版 } } else { //x32系统,直接满足了 return InstallState.Correct; //已经正确安装最新版 } } else { //ODT 只安装了1个版本,但版本号小于预期版本号,或者还安装了其它版本, //如:ProPlus2016Volume,视为版本不同 return InstallState.Diff; } } else { //存在多个ODT版本(如:version信息为1,但odt的path信息有2个),不管版本是否相同,都设置 Multi 标记 return InstallState.Multi; } } else { //安装了1个Office版本,但ODT版本安装数量为0,则版本不同 return InstallState.Diff; } } } else { //未安装任何 return InstallState.None; } } internal static Dictionary GetArchiDirFull() { try { //注册表根路径 string office_reg_root = "SOFTWARE\\Microsoft\\Office"; //获取所有版本 office 的安装目录 //先获取 x32 的,并填充 null 到 x64 Dictionary office_installed_dir = new Dictionary { //2016及其以上版本,通过odt安装的office {OfficeArchi.Office_ODT_x64, null}, {OfficeArchi.Office_ODT_x32, Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry32, $@"{office_reg_root}\ClickToRun", "InstallPath") }, //2013版 {OfficeArchi.Office_2013_x64, null}, {OfficeArchi.Office_2013_x32, Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry32, $@"{office_reg_root}\15.0\Common\InstallRoot", "Path" ) }, //2010版 {OfficeArchi.Office_2010_x64, null}, {OfficeArchi.Office_2010_x32, Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry32, $@"{office_reg_root}\14.0\Common\InstallRoot", "Path") }, //2007版 {OfficeArchi.Office_2007_x64, null}, {OfficeArchi.Office_2007_x32, Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry32, $@"{office_reg_root}\12.0\Common\InstallRoot", "Path") }, //2003版 {OfficeArchi.Office_2003, Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry32, $@"{office_reg_root}\11.0\Common\InstallRoot", "Path") }, }; //只有x64系统才获取x64注册表值 if (Environment.Is64BitOperatingSystem) { //2016及其以上版本 office_installed_dir[OfficeArchi.Office_ODT_x64] = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, $@"{office_reg_root}\ClickToRun", "InstallPath"); //2013版 office_installed_dir[OfficeArchi.Office_2013_x64] = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, $@"{office_reg_root}\15.0\Common\InstallRoot", "Path"); //2010版 office_installed_dir[OfficeArchi.Office_2010_x64] = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, $@"{office_reg_root}\14.0\Common\InstallRoot", "Path"); //2007版 office_installed_dir[OfficeArchi.Office_2007_x64] = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, $@"{office_reg_root}\12.0\Common\InstallRoot", "Path"); } return office_installed_dir; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } internal static List GetArchiDir() { try { //先获取完整信息 var office_installed_dir_full = GetArchiDirFull(); //无目录返回 0 个 if (office_installed_dir_full == null) { return new List(); } //遍历所有目录,验证有效性 List office_installed_dir = new List(); //获取所有已知版本的安装路径//将完整信息中的路径作为List存储 foreach (var now_dir in office_installed_dir_full) { //非空判断 if (!string.IsNullOrEmpty(now_dir.Value)) { if (Directory.Exists(now_dir.Value)) { office_installed_dir.Add(now_dir.Value); } } } return office_installed_dir; } catch (Exception Ex) { new Log(Ex.ToString()); return new List(); } } internal static List LicenseInfo() { try { string cmd_switch_cd = $"pushd \"{Documents.SDKs.Activate}\""; //切换至OSPP文件目录 string cmd_installed_info = "cscript ospp.vbs /dstatus"; //查看激活状态 string detect_info = Com_ExeOS.Run.Cmd($"({cmd_switch_cd})&({cmd_installed_info})"); //查看所有版本激活情况 //未安装key,直接返回 0 个 if (detect_info.Contains("No installed product keys detected")) { return new List(); } string[] info = detect_info.Split('\n'); List key_list = new List(); if (info.Length == 0) { return new List(); } else { foreach (var now_line in info) { if (now_line.Contains("key:")) { string now_key = Com_TextOS.GetCenterText(now_line, "key:", "").Replace(" ", "").TrimEnd('\r'); //需要移除尾部 \r 的标记 key_list.Add(now_key); } } return key_list; } } catch (Exception Ex) { new Log(Ex.ToString()); return new List(); } } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_OfficeInstall.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_OfficeInstall.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using Microsoft.Win32; using System; using System.Collections.Generic; using System.IO; using System.Threading; using static LKY_OfficeTools.Common.Com_ExeOS; using static LKY_OfficeTools.Common.Com_SystemOS; using static LKY_OfficeTools.Lib.Lib_AppCommand; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppLog; using static LKY_OfficeTools.Lib.Lib_AppMessage; using static LKY_OfficeTools.Lib.Lib_AppState; using static LKY_OfficeTools.Lib.Lib_OfficeClean; using static LKY_OfficeTools.Lib.Lib_OfficeInfo; using static LKY_OfficeTools.Lib.Lib_OfficeInfo.OfficeLocalInfo; namespace LKY_OfficeTools.Lib { internal class Lib_OfficeInstall { internal Lib_OfficeInstall() { try { //版本获取成功时,执行后续操作。 if (OfficeNetInfo.OfficeLatestVersion != null) { //判断是否已经安装了当前版本 InstallState install_state = GetOfficeState(); if (install_state == InstallState.Correct) //已安装最新版,无需下载 { new Log($"\n * 当前系统安装了最新 Office 版本,已跳过下载、安装流程。", ConsoleColor.DarkMagenta); //开始激活 Lib_OfficeActivate.Activating(); } //当不存在 VersionToReport or 其版本与最新版不一致 or 产品ID不一致 or 安装位数与系统不一致时,需要下载新文件。 else { //被动模式下,如果版本不正确,不会重新安装,因为用户可能已经卸载。 if (Current_RunMode == RunMode.Passive) { new Log($"\n × 当前系统未安装最新版本的 Office,激活停止!", ConsoleColor.DarkRed); return; } //如果是主动模式,则下载并安装最新版 else if (Current_RunMode == RunMode.Manual) { //开始下载,并获得下载结果 int DownCode = Lib_OfficeDownload.StartDownload(); //判断下载情况 switch (DownCode) { //下载成功,开始安装 case 1: //冲突检查 if (ConflictCheck()) { //通过进入安装,不通过直接返回false if (StartInstall()) { //安装成功,进入激活程序 Lib_OfficeActivate.Activating(); } else { new Log($"\n × 因 Office 安装失败,已跳过激活流程!", ConsoleColor.DarkRed); break; } } else { new Log($"\n × 无法完成 Office 冲突性检查,已跳过安装流程!", ConsoleColor.DarkRed); break; } break; case 0: new Log($"\n × 未能找到可用的 Office 安装文件,已跳过安装流程!", ConsoleColor.DarkRed); break; case -1: //用户中止了下载 new Log($"\n × 用户取消了下载 Office 安装文件,已跳过安装流程!", ConsoleColor.DarkRed); return; } } //其他模式跳出 else { new Log($"{Current_RunMode} 模式下,暂不支持下载、安装、激活操作。"); return; } } } } catch (Exception Ex) { new Log($"\n × Office 安装过程中发生异常,已跳过安装流程!", ConsoleColor.DarkRed); new Log(Ex.ToString()); } finally { //全部完成后,判断是否成功 if (Current_StageType != ProcessStage.Finish_Success) { //只要全部流程结束后,不是成功状态(并且没有中断情况),就设置为 失败 Current_StageType = ProcessStage.Finish_Fail; } } } internal static bool ConflictCheck() { try { new Log($"\n------> 正在检查 Office 安装环境 ...", ConsoleColor.DarkCyan); //--------------------------------------------- 先检查注册表 --------------------------------------------- bool regdir_pass = false; //默认是冲突的 var Current_Office_Dir = GetArchiDir(); //不能用 Full 函数判断是否 null,本字典始终有 key 的,所以恒不为 null if (Current_Office_Dir != null && Current_Office_Dir.Count > 0) //非空且为大于0时 { //注册表只有1个值,接下来判断这个值属于哪个架构 if (Current_Office_Dir.Count == 1) { //判断仅有的1个office架构是不是odt架构(odt下面的 x32 x64 有1个不为空,则再判断是否符合 位数 和 发行ID) string odt_x32_reg = GetArchiDirFull()[OfficeArchi.Office_ODT_x32]; string odt_x64_reg = GetArchiDirFull()[OfficeArchi.Office_ODT_x64]; if (!string.IsNullOrEmpty(odt_x32_reg) || !string.IsNullOrEmpty(odt_x64_reg)) { //64位系统额外增加位数判断 if (Environment.Is64BitOperatingSystem) { if (!string.IsNullOrEmpty(odt_x32_reg)) { //x64系统,但是在注册表x32路径有值,冲突 regdir_pass = false; } else { //x64系统,虽然在注册表x64路径有值了,但是还需判断是否 平台信息 一致 string platform = Register.Read.Value(RegistryHive.LocalMachine, RegistryView.Registry64, @"SOFTWARE\Microsoft\Office\ClickToRun\Configuration", "Platform"); if (string.IsNullOrWhiteSpace(platform) || platform == "x86") { regdir_pass = false; //虽然出现在了与x64系统注册表匹配的路径,但是Office平台版本并非x64 } } } //odt位数与系统匹配,开始校验产品ID string Current_Office_ID = Register.Read.ValueBySystem(RegistryHive.LocalMachine, @"SOFTWARE\Microsoft\Office\ClickToRun\Configuration", "ProductReleaseIds"); string Pop_Office_ID = "2021Volume"; //ODT 只安装了1个版本,但 Current_Office_ID 为空,视为冲突 if (string.IsNullOrEmpty(Current_Office_ID)) { regdir_pass = false; } else { //替换支持的版本ID。如果用户还安装了其它架构,进行替换后,该值不是空 var diff_products = Current_Office_ID .Replace($"ProjectPro{Pop_Office_ID}", "") .Replace($"ProPlus{Pop_Office_ID}", "") .Replace($"VisioPro{Pop_Office_ID}", "") .Replace(",", ""); //判断有没有额外的版本ID if (string.IsNullOrWhiteSpace(Current_Office_ID)) { //为空,无额外的ID,是不冲突的 regdir_pass = true; } else { //不为空,说明还安装了其他的版本,如:ProPlus2016Volume,则冲突 regdir_pass = false; } } } else { //不是odt架构,直接false regdir_pass = false; } } else { //如果注册表值大于1个,也就是安装了多个office,直接设置为冲突,设为false //如果用户的注册表存在两个ODT同版本不同位的情况(x32、x64都有),则也视为有冲突。 regdir_pass = false; } } else { //注册表没有值,或者 有值但目录不存在,无冲突,true regdir_pass = true; } //--------------------------------------------- 检查注册表 结束 --------------------------------------------- //--------------------------------------------- 再检查许可证 --------------------------------------------- bool license_pass = false; //默认是冲突的 var installed_key = LicenseInfo(); //获取许可证列表 if (installed_key != null && installed_key.Count > 0) { //注册表有值 //获取当前的许可证信息 string cmd_switch_cd = $"pushd \"{AppPath.Documents.SDKs.Activate}\""; //切换至OSPP文件目录 string cmd_installed_info = "cscript ospp.vbs /dstatus"; //查看激活状态 string installed_license_info = Run.Cmd($"({cmd_switch_cd})&({cmd_installed_info})"); //查看所有版本激活情况 //判断值的数量 if (installed_key.Count == 1) { //判断安装的许可证是否是目标大版本 if (installed_license_info.Contains("ProPlus2021VL")) { //是目标大版本 license_pass = true; } else { //不是本程序的大版本,直接false license_pass = false; } } else if (installed_key.Count == 2) { //检测到的2个版本是否是支持的3个大版本其中的2个版本。共有3种组合 if ( (installed_license_info.Contains("ProPlus2021VL") && installed_license_info.Contains("ProjectPro2021VL")) | (installed_license_info.Contains("ProPlus2021VL") && installed_license_info.Contains("VisioPro2021VL")) | (installed_license_info.Contains("ProjectPro2021VL") && installed_license_info.Contains("VisioPro2021VL")) ) { //是目标大版本 license_pass = true; } else { //不是本程序的大版本,直接false license_pass = false; } } else if (installed_key.Count == 3) { //安装3个许可证,必须是指定的三个版本,否则就是有别的许可证,属于冲突 if (installed_license_info.Contains("ProPlus2021VL") & installed_license_info.Contains("ProjectPro2021VL") & installed_license_info.Contains("VisioPro2021VL")) { //是目标大版本 license_pass = true; } else { //不是本程序的大版本,直接false license_pass = false; } } else { //如果大于3个,也就是安装了多个office,直接设置为冲突,设为false license_pass = false; } } else { //许可证没有值,无冲突,true license_pass = true; } //--------------------------------------------- 检查许可证 结束 --------------------------------------------- //聚合判断是否有冲突,必须二者同时通过,才是不冲突。只要注册表或者许可证,有一个出现了false,就视为冲突 if (regdir_pass && license_pass) { //不存在版本冲突时,直接开始安装 new Log($" √ 已通过 Office 安装环境检查。", ConsoleColor.DarkGreen); return true; } else { new Log($" ★ 发现 {Current_Office_Dir.Count + installed_key.Count} 个冲突的 Office 版本,若要继续,必须先卸载旧版(卸载后可能需要重启系统)。", ConsoleColor.Gray); //判断是否包含自动卸载标记 if (!AppCommandFlag.HasFlag(ArgsFlag.Auto_Remove_Conflict_Office)) { if (KeyMsg.Confirm("确认安装新版 Office 并卸载旧版本及其插件")) { new Log($" √ 您已主动确认 卸载 Office 所有旧版本。", ConsoleColor.DarkGreen); Thread.Sleep(500); //基于体验,稍微停留下 //获取卸载结果 var result = RemoveAllOffice(); //卸载后提示重启电脑 if (KeyMsg.Choose("建议您“重启计算机”以确保卸载彻底。【注意】一经确认,系统将在 1分钟 后重启,请务必提前保存文件!")) { //确认重启 new Log($" √ 您已主动确认“重启计算机”。系统将在 1分钟 后重启,请及时保存文件。", ConsoleColor.DarkGreen); //执行重启命令行(shutdown.exe -r -t 3600) Run.Cmd("shutdown.exe -r -t 60"); //重启打点&退出 KeyMsg.Quit(128); } else { //跳过了重启选择 new Log($" * 您已拒绝 重启计算机,若安装失败,您可在重启后重新运行本工具。", ConsoleColor.DarkMagenta); Thread.Sleep(500); //基于体验,稍微停留下 } return result; } else { new Log($" × 您已拒绝 卸载 Office 其他版本,新版本无法安装!", ConsoleColor.DarkRed); return false; } } else { //有自动卸载的标记,直接开始卸载 new Log($" * 自动卸载冲突 Office 模式下,自动确认并开始 ...", ConsoleColor.DarkMagenta); return RemoveAllOffice(); } } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static bool StartInstall() { try { //定义ODT文件位置 string ODT_path_root = AppPath.Documents.SDKs.SDKs_Root + @"\ODT"; string ODT_path_exe = ODT_path_root + @"\ODT.exe"; string ODT_path_xml = ODT_path_root + @"\config.xml"; //检查ODT文件是否存在 if (!File.Exists(ODT_path_exe) || !File.Exists(ODT_path_xml)) { new Log($" × 目录 {ODT_path_root} 下文件丢失!", ConsoleColor.DarkRed); return false; } //修改新的xml信息 bool isNewInstallPath = Com_FileOS.XML.SetValue(ODT_path_xml, "SourcePath", AppPath.ExecuteDir); //检查是否修改成功(安装目录) if (!isNewInstallPath) { new Log($" × 配置 Install 信息错误!", ConsoleColor.DarkRed); return false; } bool isNewVersion = Com_FileOS.XML.SetValue(ODT_path_xml, "Version", OfficeNetInfo.OfficeLatestVersion.ToString()); //检查是否修改成功(版本号) if (!isNewVersion) { new Log($" × 配置 Version 信息错误!", ConsoleColor.DarkRed); return false; } //获取系统位数 int sys_bit; if (Environment.Is64BitOperatingSystem) { sys_bit = 64; } else { sys_bit = 32; } bool isNewBit = Com_FileOS.XML.SetValue(ODT_path_xml, "OfficeClientEdition", sys_bit.ToString()); //检查是否修改成功(位数) if (!isNewBit) { new Log($" × 配置 Edition 信息错误!", ConsoleColor.DarkRed); return false; } var msg_tip = "\n ★ 本工具默认安装 Word、PPT、Excel,并可选配更多组件:"; var msg_index_first = "\n \tOutlook = 1\tOneNote = 2\tAccess = 3"; var msg_index_second = "\n \tVisio = 4\tProject = 5\tPublisher = 6"; var msg_index_third = "\n \tTeams = 7\tOneDrive = 8\tLync = 9"; var msg_input = "\n 如安装:Outlook、OneNote、Visio,请输入:1,2,4 后回车,如不增加组件,请直接按回车键。"; new Log(msg_tip + msg_index_first + msg_index_second + msg_index_third + msg_input, ConsoleColor.Gray); Console.ForegroundColor = ConsoleColor.Gray; Console.Write("\n 请输入追加的组件序号(多个组件请用逗号隔开):"); //去除非法字符 var add_install = Console.ReadLine().Trim(' ').Trim(',').Trim(',').Trim('.').Trim('。') .Replace(",", ",").Replace(",,", ",").Replace(",,", ",") .Replace(".", ",").Replace("。", ",").Replace(" ", ","); //读取配置全部内容 var config = File.ReadAllText(ODT_path_xml); //默认不安装Visio、Project bool install_visio = false; bool install_project = false; //非空判断 if (!string.IsNullOrWhiteSpace(add_install)) { //安装附加组件。当无分割符时,split后仍将得到一个元素个数为1的数组 var add_install_list = add_install.Split(','); //遍历要安装的组件 foreach (var now_add in add_install_list) { //当前元素非空判断 if (string.IsNullOrWhiteSpace(now_add)) { new Log($" >> 您输入的“{add_install}”存在无效字符,请注意检查!", ConsoleColor.DarkYellow); return StartInstall(); } //非法序号区间判断 if (!int.TryParse(now_add, out int install_num) || install_num < 1 || install_num > 9) { new Log($" >> 您输入的“{now_add}”并非有效序号,如需安装多个组件请以逗号分隔!", ConsoleColor.DarkYellow); return StartInstall(); } //判断组件类型 switch (install_num) { case 1: //Outlook config = config.Replace("", ""); break; case 2: //OneNote config = config.Replace("", ""); break; case 3: //Access config = config.Replace("", ""); break; case 4: //Visio install_visio = true; break; case 5: //Project install_project = true; break; case 6: //Publisher config = config.Replace("", ""); break; case 7: //Teams config = config.Replace("", ""); break; case 8: //OneDrive config = config.Replace("", ""); break; case 9: //Lync config = config.Replace("", ""); break; } } new Log($" √ 检查通过,本工具将安装 Word、PPT、Excel,以及额外的 {add_install} 号组件。", ConsoleColor.DarkGreen); } //不安装附加组件的情况 else { new Log($" √ 未附加其他组件,本工具将只安装默认的 Word、PPT、Excel 三件套。", ConsoleColor.DarkGreen); } //不安装Viso时,移除相关配置 if (!install_visio) { var remove_info = Com_TextOS.GetCenterText(config, ""); remove_info = ""; config = config.Replace(remove_info, ""); } //不安装Project时,移除相关配置 if (!install_project) { var remove_info = Com_TextOS.GetCenterText(config, ""); remove_info = ""; config = config.Replace(remove_info, ""); } //保存修改后的配置 File.WriteAllText(ODT_path_xml, config); //开始安装 new Log($"\n------> 正在安装 Office v{OfficeNetInfo.OfficeLatestVersion} ...", ConsoleColor.DarkCyan); Lib_AppSdk.KillAllSdkProcess(KillExe.KillMode.Only_Force); string install_args = $"/configure \"{ODT_path_xml}\""; //配置命令行 var install_code = Run.Exe(ODT_path_exe, install_args); //检查是否因配置不正确等导致,意外退出安装 if (install_code == -920921) { new Log($" × Office v{OfficeNetInfo.OfficeLatestVersion} 安装意外结束!", ConsoleColor.DarkRed); return false; } //无论是否成功,都增加一步结束进程 KillExe.ByExeName("OfficeClickToRun", KillExe.KillMode.Only_Force, true); //结束无关进程 KillExe.ByExeName("OfficeC2RClient", KillExe.KillMode.Only_Force, true); //结束无关进程 //检查安装是否成功 InstallState install_state = GetOfficeState(); //安装了最新版 if (install_state == InstallState.Correct) { //安装成功 new Log($" √ 已完成 Office v{OfficeNetInfo.OfficeLatestVersion} 安装。", ConsoleColor.DarkGreen); return true; } else { //安装存在问题 string err_msg = $"ODT Installing Exception, ExitCode: {install_code}"; if (install_code > 0) { //只解析错误码大于0的情况 string err_string = string.Empty; if (ODT_Error.TryGetValue(((uint)install_code), out err_string)) { err_msg += $"{err_string}"; } } new Log(err_msg); //回调错误码 //未安装 if (install_state == InstallState.None) { new Log($" × 安装失败,未在当前系统检测到任何 Office 版本!", ConsoleColor.DarkRed); new Log(install_state); //打点失败注册表记录 return false; } //包含不同版本 if (install_state == InstallState.Diff) { new Log($" × 已安装的 Office 版本与预期的 v{OfficeNetInfo.OfficeLatestVersion} 版本不符!", ConsoleColor.DarkRed); new Log(install_state); //打点失败注册表记录 return false; } //包含多个版本 if (install_state == InstallState.Multi) { //系统存在多个版本 new Log($" × 安装异常,当前系统存在多个 Office 版本!", ConsoleColor.DarkRed); new Log(install_state); //打点失败注册表记录 return false; } //其它未可知情况,视为失败 return false; } } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } internal static Dictionary ODT_Error { get { Dictionary res = new Dictionary { [0] = "安装成功。", [997] = "安装正在进行中。", [13] = "无法验证下载的 Office 部署工具 (ODT) 的签名。", [1460] = "下载 ODT 超时。", [1602] = "用户已取消运行。", [1603] = "未通过任何预检检查。安装 2016 MSI 时尝试安装 SxS。" + "当前安装的 Office 与尝试安装的 Office 之间的位不匹配 (例如,在当前安装 64 位版本时尝试安装 32 位版本时。)", [17000] = "未能启动 C2RClient。", [17001] = "未能在 C2RClient 中排队安装方案。", [17002] = "未能完成该过程。可能的原因:" + "(1)用户已取消安装" + "(2)安装已由另一个安装取消" + "(3)安装期间磁盘空间不足" + "(4)未知语言 ID", [17003] = "另一个方案正在运行。", [17004] = "无法完成需要的清理。可能的原因:" + "(1)未知 SKU" + "(2)CDN 上不存在内容。例如,尝试安装不受支持的 LAP,例如 zh-sg" + "(3)内容不可用的 CDN 问题" + "(4)签名检查问题,例如 Office 内容的签名检查失败" + "(5)用户已取消", [17005] = "ERROR!SCENARIO CANCELLED AS PLANNED。", [17006] = "通过运行应用阻止更新。", [17007] = "客户端在“删除安装”方案中请求客户端清理。", [17100] = "C2RClient 命令行错误。", [0x80004005] = "ODT 不能用于安装批量许可证。", [0x8000ffff] = "尝试在计算机上没有 C2R Office 时卸载。" }; return res; } } } } ================================================ FILE: LKY_OfficeTools/Lib/Lib_OfficeProcess.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : Lib_OfficeProcess.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using System; using System.Collections.Generic; using System.IO; using System.Reflection; using static LKY_OfficeTools.Common.Com_ExeOS.KillExe; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppLog; namespace LKY_OfficeTools.Lib { internal class Lib_OfficeProcess { internal static List Process_List { get { try { Stream office_processes_res = Assembly.GetExecutingAssembly(). GetManifestResourceStream(AppDevelop.NameSpace_Top /* 当命名空间发生改变时,此值也需要调整 */ + ".Resource.Office_Processes.list"); StreamReader office_processes_sr = new StreamReader(office_processes_res); string office_processes = office_processes_sr.ReadToEnd(); if (!string.IsNullOrWhiteSpace(office_processes)) { List office_processes_list = new List(); string[] p_info = office_processes.Replace("\r", "").Split('\n'); //分割出进程数组 if (p_info != null && p_info.Length > 0) { foreach (var now_process in p_info) { office_processes_list.Add(now_process); } return office_processes_list; } } return null; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } } internal static List GetRuningProcess() { try { List runing_office_process = new List(); foreach (var now_p in Process_List) { if (Com_ExeOS.Info.IsRun(now_p)) { runing_office_process.Add(now_p); } } return runing_office_process; } catch (Exception Ex) { new Log(Ex.ToString()); return null; } } internal class KillOffice { internal static bool All() { try { //轮询结束每个进程(等待其结束) foreach (var now_p in Process_List) { //new Log($" >> 等待 {now_p.ToLower()} 进程关闭中 ...", ConsoleColor.DarkYellow); Com_ExeOS.KillExe.ByExeName(now_p, KillMode.Try_Friendly, true); } return true; } catch (Exception Ex) { new Log(Ex.ToString()); return false; } } } } } ================================================ FILE: LKY_OfficeTools/OfficeTools.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : OfficeTools.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using LKY_OfficeTools.Common; using LKY_OfficeTools.Lib; using System; using System.Text; using static LKY_OfficeTools.Lib.Lib_AppClosing; using static LKY_OfficeTools.Lib.Lib_AppCommand; using static LKY_OfficeTools.Lib.Lib_AppInfo; using static LKY_OfficeTools.Lib.Lib_AppLog; using static LKY_OfficeTools.Lib.Lib_AppMessage; using static LKY_OfficeTools.Lib.Lib_AppState; namespace LKY_OfficeTools { internal class OfficeTools { static void Main(string[] args) { //命令行检测 new Lib_AppCommand(args); //设定编码,解决英文系统乱码问题 Console.OutputEncoding = Encoding.GetEncoding("gbk"); //必须在启动模式判断之后再进行本函数执行。否则服务模式将引发 1053 错误! //中断检测 CloseWindow.SetConsoleCtrlHandler(CloseWindow.newDelegate, true); //启动 Entry(); } private static void Entry() { //欢迎话术 Console.Title = $"{AppAttribute.AppName} v{AppAttribute.AppVersion}"; new Log($"{AppAttribute.AppName} [版本 {AppAttribute.AppVersion}]\n" + $"版权所有(C)LiuKaiyuan (Odysseus.Yuan)。保留所有权利。\n\n" + $"探讨 {AppAttribute.AppName} 相关内容,可发送邮件至:OdysseusYuan@foxmail.com", ConsoleColor.Gray); //清理冗余信息 Log.Clean(); //数字签名证书检查 new Lib_AppSignCert(); //确认系统情况 if (int.Parse(Com_SystemOS.OSVersion.GetBuildNumber()) < 15063) { //小于 Win10 1703 的操作系统,激活存在失败问题 new Log($"\n × 请将当前操作系统升级至 Windows 10 (1703) 或其以上版本,否则 Office 无法进行正版激活!", ConsoleColor.DarkRed); //退出提示 KeyMsg.Quit(-4); return; } //确认联网情况 if (!Com_NetworkOS.Check.IsConnected) { new Log($"\n × 请确保当前电脑可正常访问互联网!", ConsoleColor.DarkRed); //退出提示 KeyMsg.Quit(-5); return; } //根据命令行判断是否等待用户,没有标记时,执行倒计时等待 if (!AppCommandFlag.HasFlag(ArgsFlag.None_Welcome_Confirm)) { KeyMsg.DoByTime($"部署", 5); } //权限检查 Com_PrivilegeOS.PrivilegeAttention(); //SDK初始化 Lib_AppSdk.Initial(); //更新检查 Lib_AppUpdate.Check(); //继续 new Lib_OfficeInstall(); //部署成功时,提示是否配置为服务 if (Current_StageType == ProcessStage.Finish_Success) { //被强制使用“我的文档”目录时,往往因为权限问题才使用,此时禁用服务配置 if (!Must_Use_PersonalDir) { //配置服务 Lib_AppServiceConfig.Setup(); } //结论 new Log($"\n √ 您已成功完成 {AppAttribute.AppName} 所有流程,感谢您的使用。", ConsoleColor.DarkGreen); } //部署失败,提示错误信息 else if (Current_StageType == ProcessStage.Finish_Fail) { //结论 new Log($"\n × 当前部署存在失败环节,您可在稍后重试运行!", ConsoleColor.DarkRed); } //退出提示 KeyMsg.Quit(0); } } } ================================================ FILE: LKY_OfficeTools/Properties/AssemblyInfo.cs ================================================ /* * [LKY Office Tools] Copyright (C) 2022 - 2024 LiuKaiyuan Inc. * * FileName : AssemblyInfo.cs * Developer: OdysseusYuan@foxmail.com (Odysseus.Yuan) */ using System.Reflection; using System.Runtime.InteropServices; using static LKY_OfficeTools.Lib.Lib_AppInfo; // 有关程序集的一般信息由以下 // 控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle(AppAttribute.AppName)] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(AppAttribute.Developer)] [assembly: AssemblyProduct(AppAttribute.AppName)] [assembly: AssemblyCopyright("Copyright (C) 2022 - 2024 LiuKaiyuan. All rights reserved.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // 将 ComVisible 设置为 false 会使此程序集中的类型 //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 //请将此类型的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID [assembly: Guid("6d00f508-106a-42d1-be0f-048762434e69")] // 程序集的版本信息由下列四个值组成: // // 主版本 // 次版本 // 生成号 // 修订号 // //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion(AppAttribute.AppVersion)] [assembly: AssemblyFileVersion(AppAttribute.AppVersion)] ================================================ FILE: LKY_OfficeTools/Properties/app.manifest ================================================  Windows 10 ================================================ FILE: LKY_OfficeTools/Resource/Json/OfficeChannels.txt ================================================ [{"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"}] ================================================ FILE: LKY_OfficeTools/Resource/Office_Processes.list ================================================ EXCEL GROOVE INFOPATH lync MSACCESS msoev msotd MSOUC MSPUB MSQRY32 MSTORE OcPubMgr OIS OLCFG ONENOTE ORGCHART OUTLOOK POWERPNT SCANPST SETLANG Teams WINWORD ================================================ FILE: LKY_OfficeTools/Resource/SDK/SDK_Processes.list ================================================ OSPPREARM lot_aria2c ODT cleanospp_64 cleanospp_86 SaRACmd OfficeClickToRun OfficeC2RClient ================================================ FILE: LKY_OfficeTools.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LKY_OfficeTools", "LKY_OfficeTools\LKY_OfficeTools.csproj", "{6D00F508-106A-42D1-BE0F-048762434E69}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {6D00F508-106A-42D1-BE0F-048762434E69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6D00F508-106A-42D1-BE0F-048762434E69}.Debug|Any CPU.Build.0 = Debug|Any CPU {6D00F508-106A-42D1-BE0F-048762434E69}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D00F508-106A-42D1-BE0F-048762434E69}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BECB0D54-D9AD-42F5-9AAE-FD91AF555D72} EndGlobalSection EndGlobal ================================================ FILE: README.md ================================================ # ## LKY Office Tools > 一键自动化 下载、安装、激活 Office 的利器。绿色、开源、安全、无毒。 目前包含的功能: - 一键快速下载、安装、激活最新版 Microsoft Office 软件。 - 用户可在安装 Word、PPT、Excel 的同时,根据软件提示,自助安装其它组件,包括: Outlook、OneNote、Access、Visio、Project、Publisher、Teams、OneDrive、Lync/Skype。 - 工具可自动识别不同的操作系统架构,自动下载适配版本的 Office。 - 本工具激活 Office 的方式为正版激活模式,不会篡改任何系统文件。 - 当系统中存在多个冗余 Office 版本时,本工具在用户同意的情况下,可实现自动升级。 ## 使用方法 - 下载形如:LKY_OfficeTools_v*.zip 的压缩包,最新版地址:https://github.com/OdysseusYuan/LKY_OfficeTools/releases/latest - 解压下载好的 zip 压缩包,运行解压后目录下的 LKY_OfficeTools.exe 文件即可完成部署。 ## 运行环境 - 为保证安装、激活可以最大限度成功,请您尽可能在新装系统后(或从未安装过 Microsoft Office 软件的系统中)运行本工具。 - 目前已经在 Windows 10(1703) 及以上版本进行了测试,均可在其 x86、x64 完美安装正版 Office 并激活。 - 应 Office 官方要求,系统中只能部署唯一架构类型,故本工具部署时,会征求用户同意,由其自行决定是否卸载其它架构。 - 2016年7月及其之前发布的 Windows 系统(即:Windows 10(1703) 之前的系统),因其架构过于陈旧,其已经不再支持安装最新的正版 Office。 ## 开源说明 - 欢迎广大开发者、爱好者们 Fork、PR 本仓库。参考、引用、转发、二次开发本开源库时,请注明来源。 - 本工具自动激活功能,仅用于给大家无偿的学习编程开发能力之用,如需商业办公使用,请从微软官网购买商业许可。 - 任何自然人、企业、机构等,不得使用任何直接或间接的方式将其商业化,违犯成员需要为此承担全部法律责任。 - 当前工具将持续进行迭代更新和维护,迭代过程中不排除在各种因素综合下,可能产生计算机运行的各种情况。 ## 版权保护 - 软件已获多项著作权(含修改保护):软著登字第10549507号、软著登字第10895737号、软著登字第10850084号、软著登字第11497426号、软著登字第11573194号等。 ## 关于 - 如有建议、疑惑等,大家可以发邮件至 [OdysseusYuan@foxmail.com](mailto:OdysseusYuan@foxmail.com) 交流探讨。 - © 2022 - 2024 LiuKaiyuan (Odysseus.Yuan). All Rights Reserved.