├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── README.md ├── README_cn.md ├── img ├── Confirmation01_cn.png ├── Confirmation01_en.png ├── FixPath01_cn.png ├── FixPath01_en.png ├── Main01_cn.png ├── Main01_en.png ├── Open01_cn.png ├── Open01_en.png ├── Open02_cn.png ├── Open02_en.png ├── RightClick01_cn.png ├── RightClick01_en.png ├── RightClick02_cn.png └── RightClick02_en.png └── src ├── DelApp.sln └── DelApp ├── App.config ├── DelApp.csproj ├── FormMain.Designer.cs ├── FormMain.cs ├── FormMain.resx ├── Internals ├── AppLanguageService.cs ├── DefaultIcons.cs ├── DisposableSingleton.cs ├── FileNDir.cs ├── FileUnlocker.cs ├── HandleInfo.cs ├── IAppLanguageProvider.cs ├── InternelDriveInfo.cs ├── NativeWin32 │ ├── ChangeFilterStruct.cs │ ├── FindFileHandle.cs │ ├── LUID.cs │ ├── NativeMethods.cs │ ├── RmStructs.cs │ ├── SHSTOCKICONINFO.cs │ ├── SingleTokenPrivilegeOn.cs │ ├── SysInfo.cs │ ├── WaitObjectResult.cs │ └── Win32FindData.cs ├── ObjPool.cs ├── PeReader.cs ├── PipeService.cs ├── ProcessModuleLite.cs ├── RemoteFunction.cs ├── RestartManagerHelper.cs ├── RightClickMenuHelper.cs ├── SoundPlayHelper.cs ├── UnmanagedBuffer.cs └── Utils.cs ├── Locals ├── AppLanguageProvider.cs ├── AppLanguageProviderChs.cs └── AppLanguageProviderEn.cs ├── OpenFileDialogLite.Designer.cs ├── OpenFileDialogLite.cs ├── OpenFileDialogLite.resx ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── app.manifest └── icon.ico /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd 364 | 365 | /ReferenceMaterial -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 differentrain 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Delapp 2 | 3 | [简体中文](./README_cn.md) 4 | 5 | A simple tool to delete files and folders in Windows. 6 | 7 | ## Requirement 8 | 9 | - Windows 7 or later 10 | - [.NET Framework 4.8 Runtime or later](https://dotnet.microsoft.com/zh-cn/download/dotnet-framework/net48) 11 | 12 | ## Getting Started 13 | 14 | Delapp is a single-file app, no installation required, just [download latest release version](https://github.com/differentrain/Delapp/releases/latest/download/Delapp.zip), and extract it into wherever you want. 15 | 16 | ![](./img/Main01_en.png) 17 | 18 | Drag and drop files or folders into the delete-list, and click `Fast delete` button or `Delete` button, a dialogue box will pop up on the screen. 19 | 20 | ![](./img/Confirmation01_en.png) 21 | 22 | Click `YES` , all the files and folders in delete-list will be deleted. The deleted items will **NOT** be moved to the recycle bin, so if you want to restore them, you must rely on other software. 23 | 24 | As a supplementary explanation, The objects which are Dragged and dropped on the icon of Delapp, will be add to the delete-list too. 25 | 26 | ## Right-click Menu 27 | 28 | ![](./img/RightClick01_en.png) 29 | 30 | Click `File` -- `Add to right-click menu` to add/remove Delapp into right-click context menu of explorer. 31 | 32 | Then you can add the files or folders by right-clicking the target objects and selete the `Delapp - add to delete-list` menu. 33 | 34 | ![](./img/RightClick02_en.png) 35 | 36 | Note that if you move Delapp to a new location, you should add right-click menu again. 37 | 38 | ## Invalid Pathes 39 | 40 | One of my motivations for writing this app is that some folders contains invalid chars can not be deleted easily. 41 | 42 | Open the Command Prompt, and try to create a folder end with `..` : 43 | 44 | ``` 45 | md test..\ 46 | ``` 47 | 48 | Then you get a folder named `test..`, which can not be deleted in general ways. 49 | 50 | Don't worry, this command could remove the tiresome folder: 51 | 52 | ``` 53 | rd test..\ 54 | ``` 55 | 56 | Or you can use Delapp. 57 | 58 | ![](./img/Open01_en.png) 59 | 60 | Click `File` -- `Open` menu, or press `Ctrl+O`, selete the files/folders you want to delete on right, then you can add them into delete-list. 61 | 62 | ![](./img/Open02_en.png) 63 | 64 | If the 'bad' folder contains files that you still needed, you can choose it in delete-list, and click `Edit` -- `Fix invalid path`, or press `Ctrl+R`, Delapp will fix the invalid path automatically. 65 | 66 | ![](./img/FixPath01_en.png) 67 | 68 | ## The Difference Between `Fast delete` and `Delete` 69 | 70 | Basically, `Fast delete` is faster than `Delete`. 71 | 72 | On most occasions, `Fast delete` is enough, but in some case, `Fast delete` is not satisfying. 73 | 74 | Delapp can remove occupied files - files opened by other programs, or executable files of running programs, and it's 'dll'. 75 | 76 | `Fast delete` powers by [Restart Manager](https://learn.microsoft.com/en-us/windows/win32/RstMgr/restart-manager-portal), which could find the programs or services that occupy the target files. 77 | 78 | Unfortunately, Restart Manager can not process the **folder** opened by other programs, although this is not a common case, but a more comprehensive way is still necessary : `Delete` is slower than `Fast delete`, but is checks all process to find file occupancies. 79 | 80 | Another potential problem of `Fast delete` is that it uses recursion to traversal folders. The maximum length of path is 32767 characters, so the value of maximum directory depth in theory may be very large. So if you got `StackoverflowException`, try `Delete` instead of `Fast delete`. 81 | 82 | ## Translations 83 | 84 | I would be grateful if you could help me to translate Delapp to your own language. 85 | 86 | You can pull a request to submit the translated version: 87 | 88 | - Create a new class derived from `DelApp.Locals.AppLanguageProvider` 89 | - Let `abstract string TwoLetterISOLanguageName` property returns the [ISO 639-1 two-letter or ISO 639-3 three-letter code](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.twoletterisolanguagename?view=netframework-4.8) 90 | - Let `abstract int LCID` property returns the [culture identifier](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.lcid?view=netframework-4.8) for the target language 91 | - Implement other members which returns relevant translated string. 92 | - Modify `StartApp()` method in `DelApp.Program` class, add this code : `YourLanguageProvider.Instance.Register();` 93 | 94 | Or you can also open a new issue directly to give me your translation. 95 | -------------------------------------------------------------------------------- /README_cn.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/README_cn.md -------------------------------------------------------------------------------- /img/Confirmation01_cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/Confirmation01_cn.png -------------------------------------------------------------------------------- /img/Confirmation01_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/Confirmation01_en.png -------------------------------------------------------------------------------- /img/FixPath01_cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/FixPath01_cn.png -------------------------------------------------------------------------------- /img/FixPath01_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/FixPath01_en.png -------------------------------------------------------------------------------- /img/Main01_cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/Main01_cn.png -------------------------------------------------------------------------------- /img/Main01_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/Main01_en.png -------------------------------------------------------------------------------- /img/Open01_cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/Open01_cn.png -------------------------------------------------------------------------------- /img/Open01_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/Open01_en.png -------------------------------------------------------------------------------- /img/Open02_cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/Open02_cn.png -------------------------------------------------------------------------------- /img/Open02_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/Open02_en.png -------------------------------------------------------------------------------- /img/RightClick01_cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/RightClick01_cn.png -------------------------------------------------------------------------------- /img/RightClick01_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/RightClick01_en.png -------------------------------------------------------------------------------- /img/RightClick02_cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/RightClick02_cn.png -------------------------------------------------------------------------------- /img/RightClick02_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/img/RightClick02_en.png -------------------------------------------------------------------------------- /src/DelApp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33424.131 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DelApp", "DelApp\DelApp.csproj", "{B7DD4552-F9AC-4487-866C-9C8BF686F196}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {B7DD4552-F9AC-4487-866C-9C8BF686F196}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {B7DD4552-F9AC-4487-866C-9C8BF686F196}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {B7DD4552-F9AC-4487-866C-9C8BF686F196}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {B7DD4552-F9AC-4487-866C-9C8BF686F196}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {D742A0BD-A83C-40C0-9B30-88055AC93165} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /src/DelApp/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/DelApp/DelApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {B7DD4552-F9AC-4487-866C-9C8BF686F196} 8 | WinExe 9 | Delapp 10 | Delapp 11 | v4.8 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | true 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | false 37 | true 38 | 39 | 40 | icon.ico 41 | 42 | 43 | app.manifest 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | Form 62 | 63 | 64 | FormMain.cs 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | Form 92 | 93 | 94 | OpenFileDialogLite.cs 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | FormMain.cs 107 | 108 | 109 | OpenFileDialogLite.cs 110 | 111 | 112 | ResXFileCodeGenerator 113 | Resources.Designer.cs 114 | Designer 115 | 116 | 117 | True 118 | Resources.resx 119 | True 120 | 121 | 122 | 123 | SettingsSingleFileGenerator 124 | Settings.Designer.cs 125 | 126 | 127 | True 128 | Settings.settings 129 | True 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /src/DelApp/FormMain.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace DelApp 2 | { 3 | partial class FormMain 4 | { 5 | /// 6 | /// 必需的设计器变量。 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// 清理所有正在使用的资源。 12 | /// 13 | /// 如果应释放托管资源,为 true;否则为 false。 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows 窗体设计器生成的代码 24 | 25 | /// 26 | /// 设计器支持所需的方法 - 不要修改 27 | /// 使用代码编辑器修改此方法的内容。 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormMain)); 32 | this.ToolStripMain = new System.Windows.Forms.ToolStrip(); 33 | this.ToolStripDropDownButtonFile = new System.Windows.Forms.ToolStripDropDownButton(); 34 | this.ToolStripMenuItemOpen = new System.Windows.Forms.ToolStripMenuItem(); 35 | this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); 36 | this.ToolStripMenuItemRightClickContextMenu = new System.Windows.Forms.ToolStripMenuItem(); 37 | this.ToolStripMenuItemSource = new System.Windows.Forms.ToolStripMenuItem(); 38 | this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); 39 | this.ToolStripMenuItemExitApp = new System.Windows.Forms.ToolStripMenuItem(); 40 | this.ToolStripDropDownButtonEdit = new System.Windows.Forms.ToolStripDropDownButton(); 41 | this.ToolStripMenuItemRemove = new System.Windows.Forms.ToolStripMenuItem(); 42 | this.ToolStripMenuItemClearList = new System.Windows.Forms.ToolStripMenuItem(); 43 | this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); 44 | this.ToolStripMenuItemFix = new System.Windows.Forms.ToolStripMenuItem(); 45 | this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator(); 46 | this.ToolStripButtonFastDelete = new System.Windows.Forms.ToolStripButton(); 47 | this.ToolStripButtonDelete = new System.Windows.Forms.ToolStripButton(); 48 | this.ListViewMain = new System.Windows.Forms.ListView(); 49 | this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); 50 | this.BackgroundWorkerMain = new System.ComponentModel.BackgroundWorker(); 51 | this.ToolStripMain.SuspendLayout(); 52 | this.SuspendLayout(); 53 | // 54 | // ToolStripMain 55 | // 56 | this.ToolStripMain.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; 57 | this.ToolStripMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 58 | this.ToolStripDropDownButtonFile, 59 | this.ToolStripDropDownButtonEdit, 60 | this.toolStripSeparator4, 61 | this.ToolStripButtonFastDelete, 62 | this.ToolStripButtonDelete}); 63 | this.ToolStripMain.Location = new System.Drawing.Point(0, 0); 64 | this.ToolStripMain.Name = "ToolStripMain"; 65 | this.ToolStripMain.Size = new System.Drawing.Size(433, 25); 66 | this.ToolStripMain.TabIndex = 0; 67 | // 68 | // ToolStripDropDownButtonFile 69 | // 70 | this.ToolStripDropDownButtonFile.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; 71 | this.ToolStripDropDownButtonFile.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { 72 | this.ToolStripMenuItemOpen, 73 | this.toolStripSeparator1, 74 | this.ToolStripMenuItemRightClickContextMenu, 75 | this.ToolStripMenuItemSource, 76 | this.toolStripSeparator3, 77 | this.ToolStripMenuItemExitApp}); 78 | this.ToolStripDropDownButtonFile.Image = ((System.Drawing.Image)(resources.GetObject("ToolStripDropDownButtonFile.Image"))); 79 | this.ToolStripDropDownButtonFile.ImageTransparentColor = System.Drawing.Color.Magenta; 80 | this.ToolStripDropDownButtonFile.Name = "ToolStripDropDownButtonFile"; 81 | this.ToolStripDropDownButtonFile.Size = new System.Drawing.Size(40, 22); 82 | this.ToolStripDropDownButtonFile.Text = "File"; 83 | // 84 | // ToolStripMenuItemOpen 85 | // 86 | this.ToolStripMenuItemOpen.Name = "ToolStripMenuItemOpen"; 87 | this.ToolStripMenuItemOpen.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); 88 | this.ToolStripMenuItemOpen.Size = new System.Drawing.Size(217, 22); 89 | this.ToolStripMenuItemOpen.Text = "Open..."; 90 | this.ToolStripMenuItemOpen.Click += new System.EventHandler(this.ToolStripMenuItemOpen_Click); 91 | // 92 | // toolStripSeparator1 93 | // 94 | this.toolStripSeparator1.Name = "toolStripSeparator1"; 95 | this.toolStripSeparator1.Size = new System.Drawing.Size(214, 6); 96 | // 97 | // ToolStripMenuItemRightClickContextMenu 98 | // 99 | this.ToolStripMenuItemRightClickContextMenu.CheckOnClick = true; 100 | this.ToolStripMenuItemRightClickContextMenu.Name = "ToolStripMenuItemRightClickContextMenu"; 101 | this.ToolStripMenuItemRightClickContextMenu.Size = new System.Drawing.Size(217, 22); 102 | this.ToolStripMenuItemRightClickContextMenu.Text = "Right click context menu"; 103 | // 104 | // ToolStripMenuItemSource 105 | // 106 | this.ToolStripMenuItemSource.Name = "ToolStripMenuItemSource"; 107 | this.ToolStripMenuItemSource.Size = new System.Drawing.Size(217, 22); 108 | this.ToolStripMenuItemSource.Text = "Source code"; 109 | this.ToolStripMenuItemSource.Click += new System.EventHandler(this.ToolStripMenuItemSource_Click); 110 | // 111 | // toolStripSeparator3 112 | // 113 | this.toolStripSeparator3.Name = "toolStripSeparator3"; 114 | this.toolStripSeparator3.Size = new System.Drawing.Size(214, 6); 115 | // 116 | // ToolStripMenuItemExitApp 117 | // 118 | this.ToolStripMenuItemExitApp.Name = "ToolStripMenuItemExitApp"; 119 | this.ToolStripMenuItemExitApp.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Alt | System.Windows.Forms.Keys.F4))); 120 | this.ToolStripMenuItemExitApp.Size = new System.Drawing.Size(217, 22); 121 | this.ToolStripMenuItemExitApp.Text = "Exit"; 122 | this.ToolStripMenuItemExitApp.Click += new System.EventHandler(this.ToolStripMenuItemExitApp_Click); 123 | // 124 | // ToolStripDropDownButtonEdit 125 | // 126 | this.ToolStripDropDownButtonEdit.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; 127 | this.ToolStripDropDownButtonEdit.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { 128 | this.ToolStripMenuItemRemove, 129 | this.ToolStripMenuItemClearList, 130 | this.toolStripSeparator2, 131 | this.ToolStripMenuItemFix}); 132 | this.ToolStripDropDownButtonEdit.Image = ((System.Drawing.Image)(resources.GetObject("ToolStripDropDownButtonEdit.Image"))); 133 | this.ToolStripDropDownButtonEdit.ImageTransparentColor = System.Drawing.Color.Magenta; 134 | this.ToolStripDropDownButtonEdit.Name = "ToolStripDropDownButtonEdit"; 135 | this.ToolStripDropDownButtonEdit.Size = new System.Drawing.Size(43, 22); 136 | this.ToolStripDropDownButtonEdit.Text = "Edit"; 137 | // 138 | // ToolStripMenuItemRemove 139 | // 140 | this.ToolStripMenuItemRemove.Enabled = false; 141 | this.ToolStripMenuItemRemove.Name = "ToolStripMenuItemRemove"; 142 | this.ToolStripMenuItemRemove.ShortcutKeys = System.Windows.Forms.Keys.Delete; 143 | this.ToolStripMenuItemRemove.Size = new System.Drawing.Size(220, 22); 144 | this.ToolStripMenuItemRemove.Text = "Remove from list"; 145 | this.ToolStripMenuItemRemove.Click += new System.EventHandler(this.ToolStripMenuItemRemove_Click); 146 | // 147 | // ToolStripMenuItemClearList 148 | // 149 | this.ToolStripMenuItemClearList.Enabled = false; 150 | this.ToolStripMenuItemClearList.Name = "ToolStripMenuItemClearList"; 151 | this.ToolStripMenuItemClearList.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Delete))); 152 | this.ToolStripMenuItemClearList.Size = new System.Drawing.Size(220, 22); 153 | this.ToolStripMenuItemClearList.Text = "Clear list"; 154 | this.ToolStripMenuItemClearList.Click += new System.EventHandler(this.ToolStripMenuItemClearList_Click); 155 | // 156 | // toolStripSeparator2 157 | // 158 | this.toolStripSeparator2.Name = "toolStripSeparator2"; 159 | this.toolStripSeparator2.Size = new System.Drawing.Size(217, 6); 160 | // 161 | // ToolStripMenuItemFix 162 | // 163 | this.ToolStripMenuItemFix.Enabled = false; 164 | this.ToolStripMenuItemFix.Name = "ToolStripMenuItemFix"; 165 | this.ToolStripMenuItemFix.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.R))); 166 | this.ToolStripMenuItemFix.Size = new System.Drawing.Size(220, 22); 167 | this.ToolStripMenuItemFix.Text = "Fix invalid path"; 168 | this.ToolStripMenuItemFix.Click += new System.EventHandler(this.ToolStripMenuItemFix_Click); 169 | // 170 | // toolStripSeparator4 171 | // 172 | this.toolStripSeparator4.Name = "toolStripSeparator4"; 173 | this.toolStripSeparator4.Size = new System.Drawing.Size(6, 25); 174 | // 175 | // ToolStripButtonFastDelete 176 | // 177 | this.ToolStripButtonFastDelete.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; 178 | this.ToolStripButtonFastDelete.Enabled = false; 179 | this.ToolStripButtonFastDelete.Image = ((System.Drawing.Image)(resources.GetObject("ToolStripButtonFastDelete.Image"))); 180 | this.ToolStripButtonFastDelete.ImageTransparentColor = System.Drawing.Color.Magenta; 181 | this.ToolStripButtonFastDelete.Name = "ToolStripButtonFastDelete"; 182 | this.ToolStripButtonFastDelete.Size = new System.Drawing.Size(76, 22); 183 | this.ToolStripButtonFastDelete.Text = "Fast Delete"; 184 | this.ToolStripButtonFastDelete.Click += new System.EventHandler(this.ToolStripButtonDelete_Click); 185 | // 186 | // ToolStripButtonDelete 187 | // 188 | this.ToolStripButtonDelete.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; 189 | this.ToolStripButtonDelete.Enabled = false; 190 | this.ToolStripButtonDelete.Image = ((System.Drawing.Image)(resources.GetObject("ToolStripButtonDelete.Image"))); 191 | this.ToolStripButtonDelete.ImageTransparentColor = System.Drawing.Color.Magenta; 192 | this.ToolStripButtonDelete.Name = "ToolStripButtonDelete"; 193 | this.ToolStripButtonDelete.Size = new System.Drawing.Size(49, 22); 194 | this.ToolStripButtonDelete.Text = "Delete"; 195 | this.ToolStripButtonDelete.Click += new System.EventHandler(this.ToolStripButtonDelete_Click); 196 | // 197 | // ListViewMain 198 | // 199 | this.ListViewMain.Alignment = System.Windows.Forms.ListViewAlignment.Left; 200 | this.ListViewMain.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 201 | this.ListViewMain.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { 202 | this.columnHeader1}); 203 | this.ListViewMain.Dock = System.Windows.Forms.DockStyle.Fill; 204 | this.ListViewMain.FullRowSelect = true; 205 | this.ListViewMain.GridLines = true; 206 | this.ListViewMain.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; 207 | this.ListViewMain.HideSelection = false; 208 | this.ListViewMain.Location = new System.Drawing.Point(0, 25); 209 | this.ListViewMain.Name = "ListViewMain"; 210 | this.ListViewMain.ShowGroups = false; 211 | this.ListViewMain.ShowItemToolTips = true; 212 | this.ListViewMain.Size = new System.Drawing.Size(433, 261); 213 | this.ListViewMain.TabIndex = 1; 214 | this.ListViewMain.UseCompatibleStateImageBehavior = false; 215 | this.ListViewMain.View = System.Windows.Forms.View.Details; 216 | this.ListViewMain.SelectedIndexChanged += new System.EventHandler(this.ListViewMain_SelectedIndexChanged); 217 | // 218 | // columnHeader1 219 | // 220 | this.columnHeader1.Text = "Delete list"; 221 | this.columnHeader1.Width = 10000; 222 | // 223 | // BackgroundWorkerMain 224 | // 225 | this.BackgroundWorkerMain.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BackgroundWorkerMain_DoWork); 226 | this.BackgroundWorkerMain.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.BackgroundWorkerMain_RunWorkerCompleted); 227 | // 228 | // FormMain 229 | // 230 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 231 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 232 | this.ClientSize = new System.Drawing.Size(433, 286); 233 | this.Controls.Add(this.ListViewMain); 234 | this.Controls.Add(this.ToolStripMain); 235 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 236 | this.Name = "FormMain"; 237 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 238 | this.Text = "Delapp"; 239 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormMain_FormClosing); 240 | this.ToolStripMain.ResumeLayout(false); 241 | this.ToolStripMain.PerformLayout(); 242 | this.ResumeLayout(false); 243 | this.PerformLayout(); 244 | 245 | } 246 | 247 | #endregion 248 | 249 | private System.Windows.Forms.ToolStrip ToolStripMain; 250 | private System.Windows.Forms.ToolStripDropDownButton ToolStripDropDownButtonFile; 251 | private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItemOpen; 252 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; 253 | private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItemExitApp; 254 | private System.Windows.Forms.ToolStripButton ToolStripButtonDelete; 255 | private System.Windows.Forms.ListView ListViewMain; 256 | private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItemRightClickContextMenu; 257 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; 258 | private System.ComponentModel.BackgroundWorker BackgroundWorkerMain; 259 | private System.Windows.Forms.ColumnHeader columnHeader1; 260 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator4; 261 | private System.Windows.Forms.ToolStripDropDownButton ToolStripDropDownButtonEdit; 262 | private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItemRemove; 263 | private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItemClearList; 264 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; 265 | private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItemFix; 266 | private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItemSource; 267 | private System.Windows.Forms.ToolStripButton ToolStripButtonFastDelete; 268 | } 269 | } 270 | 271 | -------------------------------------------------------------------------------- /src/DelApp/FormMain.cs: -------------------------------------------------------------------------------- 1 | using DelApp.Internals; 2 | using DelApp.Internals.Win32; 3 | using DelApp.Locals; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Data; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | using System.Windows.Forms; 11 | 12 | 13 | namespace DelApp 14 | { 15 | internal partial class FormMain : Form, IMessageFilter 16 | { 17 | private const uint WM_COPYGLOBALDATA = 0x0049; 18 | private const uint WM_COPYDATA = 0x004A; 19 | private const uint WM_DROPFILES = 0x0233; 20 | private const uint MSGFLT_ALLOW = 1; 21 | private const uint MSGFLT_RESET = 0; 22 | 23 | private readonly bool _canDrag; 24 | private readonly OpenFileDialogLite _openPathDialog = new OpenFileDialogLite(); 25 | 26 | public FormMain() 27 | { 28 | CheckForIllegalCrossThreadCalls = false; 29 | 30 | InitializeComponent(); 31 | 32 | _openPathDialog.Owner = this; 33 | 34 | PipeService.PathRecived += PipeService_PathRecived; 35 | 36 | ListViewMain.Columns[0].Width = Screen.GetWorkingArea(this).Width; 37 | ListViewMain.SmallImageList = _openPathDialog.ImageListMain; 38 | 39 | _canDrag = RegDragDropMsgFilter(ListViewMain); 40 | 41 | try 42 | { 43 | ToolStripMenuItemRightClickContextMenu.Checked = RightClickMenuHelper.HasRightClickMenu; 44 | } 45 | catch (Exception exc) 46 | { 47 | MessageBox.Show(exc.ToString()); 48 | } 49 | finally 50 | { 51 | ToolStripMenuItemRightClickContextMenu.CheckedChanged += ToolStripMenuItemRightClickContextMenu_CheckedChanged; 52 | } 53 | 54 | ListViewMain.BeginUpdate(); 55 | foreach (var item in Program.FilePathes) 56 | { 57 | OpenFileDialogLite.TryAddToList(ListViewMain, new FileNDir(item)); 58 | } 59 | ListViewMain.EndUpdate(); 60 | EnsureButtonForHoleList(); 61 | 62 | IAppLanguageProvider lp = AppLanguageService.LanguageProvider; 63 | 64 | ToolStripDropDownButtonFile.Text = lp.UI_File; 65 | ToolStripMenuItemOpen.Text = lp.UI_File_Open; 66 | ToolStripMenuItemRightClickContextMenu.Text = lp.UI_File_RightClickContextMenu; 67 | ToolStripMenuItemSource.Text = lp.UI_File_SourceCode; 68 | ToolStripMenuItemExitApp.Text = lp.UI_File_Exit; 69 | 70 | ToolStripDropDownButtonEdit.Text = lp.UI_Edit; 71 | ToolStripMenuItemRemove.Text = lp.UI_Edit_RemoveFromList; 72 | ToolStripMenuItemClearList.Text = lp.UI_Edit_ClearList; 73 | ToolStripMenuItemFix.Text = lp.UI_Edit_FixPath; 74 | 75 | ToolStripButtonFastDelete.Text = lp.UI_FastDelete; 76 | ToolStripButtonDelete.Text = lp.UI_Delete; 77 | 78 | } 79 | 80 | private void PipeService_PathRecived(object sender, EventArgs e) 81 | { 82 | if (ListViewMain.Enabled) 83 | AddPipeSendedFile(); 84 | } 85 | 86 | private void AddPipeSendedFile() 87 | { 88 | ListViewMain.BeginUpdate(); 89 | while (PipeService.PathQueue.TryDequeue(out FileNDir fd)) 90 | OpenFileDialogLite.TryAddToList(ListViewMain, fd); 91 | ListViewMain.EndUpdate(); 92 | EnsureButtonForHoleList(); 93 | } 94 | 95 | private void ToolStripMenuItemExitApp_Click(object sender, EventArgs e) 96 | { 97 | Close(); 98 | } 99 | 100 | public bool PreFilterMessage(ref Message m) 101 | { 102 | if (m.Msg == WM_DROPFILES) 103 | { 104 | ListViewMain.BeginUpdate(); 105 | foreach (FileNDir item in Utils.GetDrapDropFiles(m.WParam)) 106 | { 107 | OpenFileDialogLite.TryAddToList(ListViewMain, item); 108 | } 109 | ListViewMain.EndUpdate(); 110 | EnsureButtonForHoleList(); 111 | return true; 112 | } 113 | return false; 114 | } 115 | 116 | private void FormMain_FormClosing(object sender, FormClosingEventArgs e) 117 | { 118 | if (_canDrag) 119 | { 120 | RemoveDragDropMsgFilter(ListViewMain); 121 | } 122 | } 123 | 124 | private bool RegDragDropMsgFilter(Control control) 125 | { 126 | IntPtr handle = control.Handle; 127 | var status = new ChangeFilterStruct() { CbSize = 8 }; 128 | 129 | if (NativeMethods.ChangeWindowMessageFilterEx(handle, WM_DROPFILES, MSGFLT_ALLOW, in status) && 130 | NativeMethods.ChangeWindowMessageFilterEx(handle, WM_COPYGLOBALDATA, MSGFLT_ALLOW, in status) && 131 | NativeMethods.ChangeWindowMessageFilterEx(handle, WM_COPYDATA, MSGFLT_ALLOW, in status)) 132 | { 133 | NativeMethods.DragAcceptFiles(handle, true); 134 | Application.AddMessageFilter(this); 135 | return true; 136 | } 137 | return false; 138 | } 139 | 140 | private void RemoveDragDropMsgFilter(Control control) 141 | { 142 | IntPtr handle = control.Handle; 143 | NativeMethods.DragAcceptFiles(handle, false); 144 | var status = new ChangeFilterStruct() { CbSize = 8 }; 145 | NativeMethods.ChangeWindowMessageFilterEx(handle, WM_DROPFILES, MSGFLT_RESET, in status); 146 | NativeMethods.ChangeWindowMessageFilterEx(handle, WM_COPYGLOBALDATA, MSGFLT_RESET, in status); 147 | NativeMethods.ChangeWindowMessageFilterEx(handle, WM_COPYDATA, MSGFLT_RESET, in status); 148 | Application.RemoveMessageFilter(this); 149 | } 150 | 151 | 152 | 153 | private void ToolStripMenuItemRightClickContextMenu_CheckedChanged(object sender, EventArgs e) 154 | { 155 | ToolStripMenuItemRightClickContextMenu.CheckedChanged -= ToolStripMenuItemRightClickContextMenu_CheckedChanged; 156 | try 157 | { 158 | RightClickMenuHelper.HasRightClickMenu = ToolStripMenuItemRightClickContextMenu.Checked; 159 | ToolStripMenuItemRightClickContextMenu.Checked = RightClickMenuHelper.HasRightClickMenu; 160 | } 161 | catch (Exception exc) 162 | { 163 | MessageBox.Show(exc.ToString()); 164 | } 165 | finally 166 | { 167 | ToolStripMenuItemRightClickContextMenu.CheckedChanged += ToolStripMenuItemRightClickContextMenu_CheckedChanged; 168 | } 169 | } 170 | 171 | private void ListViewMain_SelectedIndexChanged(object sender, EventArgs e) 172 | { 173 | int selCount = ListViewMain.SelectedItems.Count; 174 | if (selCount == 0) 175 | { 176 | ToolStripMenuItemFix.Enabled = ToolStripMenuItemRemove.Enabled = false; 177 | } 178 | else 179 | { 180 | ToolStripMenuItemRemove.Enabled = true; 181 | ToolStripMenuItemFix.Enabled = selCount == 1 && (ListViewMain.SelectedItems[0].Tag as FileNDir).IsInvalidPath; 182 | } 183 | 184 | } 185 | 186 | private void ToolStripMenuItemRemove_Click(object sender, EventArgs e) 187 | { 188 | while (ListViewMain.SelectedItems.Count > 0) 189 | { 190 | ListViewMain.SelectedItems[0].Remove(); 191 | } 192 | EnsureButtonForHoleList(); 193 | } 194 | 195 | private void ToolStripMenuItemClearList_Click(object sender, EventArgs e) 196 | { 197 | ListViewMain.SelectedItems.Clear(); 198 | ListViewMain.Items.Clear(); 199 | EnsureButtonForHoleList(); 200 | } 201 | 202 | 203 | private void ToolStripMenuItemOpen_Click(object sender, EventArgs e) 204 | { 205 | if (_openPathDialog.ShowDialog() == DialogResult.OK) 206 | { 207 | int count = _openPathDialog.Pathes.Count; 208 | for (int i = 0; i < count; i++) 209 | { 210 | OpenFileDialogLite.TryAddToList(ListViewMain, _openPathDialog.Pathes[i]); 211 | } 212 | } 213 | EnsureButtonForHoleList(); 214 | } 215 | 216 | private void ToolStripMenuItemFix_Click(object sender, EventArgs e) 217 | { 218 | bool ret = (ListViewMain.SelectedItems[0].Tag as FileNDir).FixPath(out FileNDir newFile); 219 | 220 | if (ret) 221 | { 222 | ListViewMain.SelectedItems[0].Remove(); 223 | OpenFileDialogLite.TryAddToList(ListViewMain, newFile); 224 | } 225 | else 226 | { 227 | MessageBox.Show(AppLanguageService.LanguageProvider.Message_FailToFixPath, "Delapp"); 228 | } 229 | } 230 | 231 | private void ToolStripButtonDelete_Click(object sender, EventArgs e) 232 | { 233 | if (MessageBox.Show(AppLanguageService.LanguageProvider.Message_Confirmation, "Delapp", MessageBoxButtons.YesNo) == DialogResult.Yes) 234 | { 235 | ToolStripDropDownButtonFile.Enabled = false; 236 | ToolStripDropDownButtonEdit.Enabled = false; 237 | ListViewMain.Enabled = false; 238 | ToolStripButtonDelete.Enabled = false; 239 | ToolStripButtonFastDelete.Enabled = false; 240 | 241 | BackgroundWorkerMain.RunWorkerAsync( 242 | (sender as ToolStripButton).Name == "ToolStripButtonFastDelete"); 243 | } 244 | } 245 | 246 | private bool EnsureButtonForHoleList() 247 | { 248 | return ToolStripButtonFastDelete.Enabled = ToolStripButtonDelete.Enabled = ToolStripMenuItemClearList.Enabled = ListViewMain.Items.Count > 0; 249 | } 250 | 251 | private void BackgroundWorkerMain_DoWork(object sender, DoWorkEventArgs e) 252 | { 253 | if ((bool)e.Argument) 254 | FastDel(e); 255 | else 256 | NormalDel(e); 257 | } 258 | 259 | private void FastDel(DoWorkEventArgs e) 260 | { 261 | ListView.ListViewItemCollection items = ListViewMain.Items; 262 | List files = ObjPool.RentFDList(); 263 | List dirs = ObjPool.RentFDList(); 264 | FileNDir fd; 265 | ListViewMain.BeginUpdate(); 266 | for (int i = 0; i < items.Count; i++) 267 | { 268 | fd = items[i].Tag as FileNDir; 269 | if (fd.FastDelete(files, dirs)) 270 | items.RemoveAt(i--); 271 | } 272 | ListViewMain.EndUpdate(); 273 | if (files.Count > 0) 274 | { 275 | InternelDriveInfo.RefreshDriveCache(); 276 | 277 | int[] locker = RestartManagerHelper.Shared.GetHolderList(out _, files.Select(f => f.FullPath).ToArray()); 278 | FileUnlocker.UnlockModuleAndMemory(files.Select(fd1 => fd1.FullPath).ToDictionary(s => s, s => 0), locker); 279 | DeleteLockedFile(files, items); 280 | if (files.Count > 0) 281 | { 282 | FileUnlocker.UnlockHandle(files.Select(fd1 => fd1.FullPath).ToDictionary(s => s, s => 0), locker); 283 | DeleteLockedFile(files, items); 284 | } 285 | DeleteLockedFile(dirs, items); 286 | } 287 | e.Result = files.Count + dirs.Count; 288 | ObjPool.ReturnFDList(files); 289 | ObjPool.ReturnFDList(dirs); 290 | 291 | void DeleteLockedFile(List fl, ListView.ListViewItemCollection ob) 292 | { 293 | ListViewMain.BeginUpdate(); 294 | FileNDir fd1; 295 | FileNDir fd2; 296 | for (int i = 0; i < fl.Count; i++) 297 | { 298 | fd1 = fl[i]; 299 | if (fd1.FastDelete(null, null)) 300 | { 301 | fl.RemoveAt(i--); 302 | for (int j = 0; j < ob.Count; j++) 303 | { 304 | fd2 = ob[j].Tag as FileNDir; 305 | if (fd2.Equals(fd1)) 306 | { 307 | ob.RemoveAt(j); 308 | break; 309 | } 310 | } 311 | } 312 | } 313 | ListViewMain.EndUpdate(); 314 | } 315 | } 316 | 317 | 318 | 319 | private void NormalDel(DoWorkEventArgs e) 320 | { 321 | ListView.ListViewItemCollection items = ListViewMain.Items; 322 | List pathes = ObjPool.RentFDList(); 323 | FileNDir fd; 324 | ListViewMain.BeginUpdate(); 325 | for (int i = 0; i < items.Count; i++) 326 | { 327 | fd = items[i].Tag as FileNDir; 328 | if (fd.Delete(pathes)) 329 | items.RemoveAt(i--); 330 | } 331 | ListViewMain.EndUpdate(); 332 | if (pathes.Count > 0) 333 | { 334 | InternelDriveInfo.RefreshDriveCache(); 335 | FileUnlocker.UnlockModuleAndMemory(pathes.Select(pt => pt.FullPath).ToDictionary(s => s, s => 0)); 336 | DeleteLockedFile(pathes, items); 337 | if (pathes.Count > 0) 338 | { 339 | FileUnlocker.UnlockHandle(pathes.Select(pt => pt.FullPath).ToDictionary(s => s, s => 0)); 340 | DeleteLockedFile(pathes, items); 341 | } 342 | void DeleteLockedFile(List pl, ListView.ListViewItemCollection ob) 343 | { 344 | ListViewMain.BeginUpdate(); 345 | FileNDir fd1; 346 | FileNDir fd2; 347 | for (int i = 0; i < pl.Count; i++) 348 | { 349 | fd1 = pl[i]; 350 | if (fd1.Delete(null)) 351 | { 352 | pl.RemoveAt(i--); 353 | for (int j = 0; j < ob.Count; j++) 354 | { 355 | fd2 = ob[j].Tag as FileNDir; 356 | if (fd2.Equals(fd1)) 357 | { 358 | ob.RemoveAt(j); 359 | break; 360 | } 361 | } 362 | } 363 | } 364 | ListViewMain.EndUpdate(); 365 | } 366 | } 367 | e.Result = pathes.Count; 368 | ObjPool.ReturnFDList(pathes); 369 | } 370 | 371 | 372 | 373 | private void BackgroundWorkerMain_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 374 | { 375 | if ((int)e.Result != 0) 376 | { 377 | MessageBox.Show(AppLanguageService.LanguageProvider.Message_NotFullyCompleted, "Delapp"); 378 | } 379 | else 380 | SoundPlayHelper.Shared.TryPlayEmptyRecyclebin(true); 381 | 382 | ToolStripDropDownButtonFile.Enabled = true; 383 | ToolStripDropDownButtonEdit.Enabled = true; 384 | ListViewMain.Enabled = true; 385 | AddPipeSendedFile(); 386 | } 387 | 388 | private void ToolStripMenuItemSource_Click(object sender, EventArgs e) 389 | { 390 | Process.Start("https://github.com/differentrain/Delapp").Dispose(); 391 | } 392 | } 393 | } 394 | -------------------------------------------------------------------------------- /src/DelApp/Internals/AppLanguageService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | 4 | namespace DelApp.Locals 5 | { 6 | internal static class AppLanguageService 7 | { 8 | private static readonly Dictionary s_dictLanguageName; 9 | private static readonly Dictionary s_dictLCID; 10 | private static readonly IAppLanguageProvider s_defaultLan; 11 | 12 | private static IAppLanguageProvider s_provider; 13 | 14 | static AppLanguageService() 15 | { 16 | s_dictLanguageName = new Dictionary(); 17 | s_dictLCID = new Dictionary(); 18 | s_defaultLan = AppLanguageProviderEn.Instance; 19 | } 20 | 21 | public static IAppLanguageProvider LanguageProvider 22 | { 23 | get 24 | { 25 | if (s_provider == null) 26 | { 27 | CultureInfo lang = CultureInfo.CurrentCulture; 28 | s_provider = s_dictLCID.TryGetValue(lang.LCID, out IAppLanguageProvider p) || 29 | s_dictLanguageName.TryGetValue(lang.TwoLetterISOLanguageName, out p) ? 30 | p : 31 | s_defaultLan; 32 | } 33 | return s_provider; 34 | } 35 | 36 | } 37 | 38 | public static void RegisterLanguageProvider(IAppLanguageProvider lp) 39 | { 40 | if (!s_dictLanguageName.ContainsKey(lp.TwoLetterISOLanguageName)) 41 | s_dictLanguageName.Add(lp.TwoLetterISOLanguageName, lp); 42 | if (!s_dictLCID.ContainsKey(lp.LCID)) 43 | s_dictLCID.Add(lp.LCID, lp); 44 | } 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/DelApp/Internals/DefaultIcons.cs: -------------------------------------------------------------------------------- 1 | using DelApp.Internals.Win32; 2 | using System.Drawing; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace DelApp.Internals 6 | { 7 | internal static class DefaultIcons 8 | { 9 | 10 | private const uint SIID_FOLDER = 3; 11 | private const uint SIID_DOCNOASSOC = 0; 12 | private const uint SIID_FOLDERBACK = 75; 13 | private const uint SHGSI_ICON = 0x100; 14 | 15 | 16 | private const uint SHGSI_SMALLICON = 0x1; 17 | 18 | 19 | public static Icon BackSmall { get; } = GetStockIcon(SIID_FOLDERBACK, SHGSI_SMALLICON); 20 | public static Icon FileSmall { get; } = GetStockIcon(SIID_DOCNOASSOC, SHGSI_SMALLICON); 21 | public static Icon DirSmall { get; } = GetStockIcon(SIID_FOLDER, SHGSI_SMALLICON); 22 | 23 | public static Icon GetStockIcon(uint type, uint size) 24 | { 25 | var info = new SHSTOCKICONINFO(); 26 | info.cbSize = (uint)Marshal.SizeOf(info); 27 | 28 | NativeMethods.SHGetStockIconInfo(type, SHGSI_ICON | size, ref info); 29 | var icon = (Icon)Icon.FromHandle(info.hIcon).Clone(); // Get a copy that doesn't use the original handle 30 | NativeMethods.DestroyIcon(info.hIcon); // Clean up native icon to prevent resource leak 31 | 32 | return icon; 33 | } 34 | 35 | 36 | 37 | 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/DelApp/Internals/DisposableSingleton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DelApp.Internals 4 | { 5 | internal abstract class DisposableSingleton : IDisposable 6 | where TSelf : DisposableSingleton 7 | { 8 | private static TSelf _sharedInstance; 9 | private bool _disposedValue; 10 | 11 | // the inherited class must and has better has only a private constructor without arguments. 12 | public static TSelf Shared 13 | { 14 | get 15 | { 16 | if (_sharedInstance == null) 17 | _sharedInstance = (TSelf)Activator.CreateInstance(typeof(TSelf), true); 18 | return _sharedInstance; 19 | } 20 | } 21 | 22 | // Invoke when app exiting 23 | public static void ReleaseSharedInstance() 24 | { 25 | ((IDisposable)_sharedInstance)?.Dispose(); 26 | } 27 | 28 | 29 | protected abstract void DisposeManaged(); 30 | protected abstract void DisposeUnmanaged(); 31 | 32 | private void Dispose(bool disposing) 33 | { 34 | if (!_disposedValue) 35 | { 36 | if (disposing) 37 | { 38 | DisposeManaged(); 39 | } 40 | DisposeUnmanaged(); 41 | _disposedValue = true; 42 | } 43 | } 44 | 45 | ~DisposableSingleton() 46 | { 47 | Dispose(disposing: false); 48 | } 49 | 50 | // This is a singleton class. 51 | // So it is better to "hide" Dispose method, 52 | // to to remind developers that This method should only be invoked when app is closing, or never be invoked. 53 | void IDisposable.Dispose() 54 | { 55 | Dispose(disposing: true); 56 | GC.SuppressFinalize(this); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/DelApp/Internals/FileNDir.cs: -------------------------------------------------------------------------------- 1 | using DelApp.Internals.Win32; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Runtime.InteropServices; 6 | using System.Threading.Tasks; 7 | 8 | namespace DelApp.Internals 9 | { 10 | internal sealed class FileNDir : IEquatable 11 | { 12 | private const int ERROR_FILE_NOT_FOUND = 2; 13 | private const int ERROR_ACCESS_DENIED = 5; 14 | private const int ERROR_DIR_NOT_EMPTY = 145; 15 | private const int ERROR_SHARING_VIOLATION = 0x20; 16 | 17 | private const FileAttributes INVALID_FILE_ATTRIBUTES = (FileAttributes)(-1); 18 | private const string LongPathPrefix = @"\\?\"; 19 | private const string SearchSuffix = @"\*"; 20 | 21 | public FileNDir(string path) 22 | { 23 | FullPath = path; 24 | } 25 | 26 | public string FullPath { get; } 27 | 28 | public bool IsInvalidPath 29 | { 30 | get 31 | { 32 | unsafe 33 | { 34 | 35 | fixed (char* p = FullPath) 36 | { 37 | char ch = p[FullPath.Length - 1]; 38 | return ch == '.' || ch == ' '; 39 | } 40 | } 41 | } 42 | } 43 | 44 | public FileAttributes Attributes => GetFileAttributes(LongPathPrefix + FullPath); 45 | 46 | public bool Exists => Attributes != 0; 47 | 48 | public bool IsFile => (Attributes & FileAttributes.Directory) == 0; 49 | 50 | public bool IsDrive => InternelDriveInfo.IsDrive(FullPath); 51 | 52 | public FileNDir PreviewDir 53 | { 54 | get 55 | { 56 | if (InternelDriveInfo.IsDrive(FullPath)) 57 | return null; 58 | 59 | int length = FullPath.Length - 1; 60 | unsafe 61 | { 62 | fixed (char* p = FullPath) 63 | { 64 | while (p[length] != '\\') 65 | { 66 | --length; 67 | } 68 | if (p[length - 1] == ':') 69 | { 70 | ++length; 71 | } 72 | string dir = new string(p, 0, length); 73 | return new FileNDir(dir); 74 | } 75 | } 76 | } 77 | } 78 | 79 | public IEnumerable GetChilds() 80 | { 81 | FileAttributes attr = Attributes; 82 | if (attr == 0 || (attr & FileAttributes.Directory) == 0) 83 | yield break; 84 | foreach (var item in GetChildsCore(FullPath)) 85 | { 86 | yield return item; 87 | } 88 | } 89 | 90 | public bool Delete(List lockedList) 91 | { 92 | FileAttributes attr = Attributes; 93 | 94 | if (attr == 0) 95 | return true; 96 | else if ((attr & FileAttributes.Directory) != 0) 97 | return DeleteDirCore(lockedList); 98 | else 99 | return DeleteFileNDir(lockedList, NativeMethods.DeleteFileW); 100 | } 101 | 102 | public bool FastDelete(List fileList, List dirList) 103 | { 104 | FileAttributes attr = Attributes; 105 | if (attr == 0) 106 | return true; 107 | else if ((attr & FileAttributes.Directory) != 0) 108 | return DeleteDirCore2(fileList, dirList); 109 | else 110 | return DeleteFileNDir(fileList, NativeMethods.DeleteFileW); 111 | } 112 | 113 | public bool FixPath(out FileNDir newFile) 114 | { 115 | newFile = null; 116 | 117 | if (!Exists || !IsInvalidPath) 118 | return false; 119 | 120 | string npath = GetCorrectPath(FullPath); 121 | string lpath = LongPathPrefix + npath; 122 | int i = 2; 123 | 124 | while (GetFileAttributes(lpath) != 0) 125 | { 126 | lpath += i; 127 | npath += i; 128 | ++i; 129 | } 130 | 131 | bool ret = NativeMethods.MoveFileW(LongPathPrefix + FullPath, lpath); 132 | if (ret) 133 | { 134 | newFile = new FileNDir(npath); 135 | } 136 | return ret; 137 | string GetCorrectPath(string invalidPath) 138 | { 139 | unsafe 140 | { 141 | fixed (char* p = invalidPath) 142 | { 143 | int pos = invalidPath.Length - 1; 144 | while (p[pos] == '.' || 145 | p[pos] == ' ') 146 | --pos; 147 | 148 | var str = new string(p, 0, pos + 1); 149 | if (p[pos] == '\\') 150 | { 151 | return str + $"new_{(IsFile ? "file" : "directory")}"; 152 | } 153 | else 154 | { 155 | return str; 156 | } 157 | } 158 | } 159 | } 160 | } 161 | 162 | 163 | private bool DeleteDirCore(List lockedList) 164 | { 165 | List dirs = TraverseDirectory(lockedList); 166 | bool suc = true; 167 | int index = dirs.Count - 1; 168 | while (index >= 0) 169 | { 170 | if (!dirs[index--].DeleteFileNDir(lockedList, NativeMethods.RemoveDirectoryW)) 171 | suc = false; 172 | } 173 | ObjPool.ReturnFDList(dirs); 174 | return suc; 175 | 176 | List TraverseDirectory(List mlockedList) 177 | { 178 | List mdirs = ObjPool.RentFDList(); 179 | mdirs.Add(this); 180 | int i = 0; 181 | while (i < mdirs.Count) 182 | { 183 | foreach (var item in GetChildsCore(mdirs[i++].FullPath)) 184 | { 185 | if (item.IsFile) 186 | { 187 | item.DeleteFileNDir(mlockedList, NativeMethods.DeleteFileW); 188 | } 189 | else 190 | { 191 | mdirs.Add(item); 192 | } 193 | } 194 | } 195 | return mdirs; 196 | } 197 | } 198 | 199 | private bool DeleteDirCore2(List fileList, List dirList) 200 | { 201 | Parallel.ForEach(GetChilds(), fd => 202 | { 203 | fd.FastDelete(fileList, dirList); 204 | }); 205 | return DeleteFileNDir(dirList, NativeMethods.RemoveDirectoryW); 206 | } 207 | 208 | 209 | private bool DeleteFileNDir(List lockedList, Func func) 210 | { 211 | var path = LongPathPrefix + FullPath; 212 | var attr = GetFileAttributes(path); 213 | if (attr == INVALID_FILE_ATTRIBUTES) 214 | return true; 215 | if ((attr & FileAttributes.ReadOnly) != 0) 216 | NativeMethods.SetFileAttributesW(path, attr - 1); 217 | if (func(path)) 218 | return true; 219 | int erro = Marshal.GetLastWin32Error(); 220 | if (erro == ERROR_ACCESS_DENIED || erro == ERROR_SHARING_VIOLATION || erro == ERROR_DIR_NOT_EMPTY) 221 | lockedList?.Add(this); 222 | return false; 223 | } 224 | 225 | 226 | 227 | public static FileAttributes GetFileAttributes(string path) 228 | { 229 | FileAttributes ret = NativeMethods.GetFileAttributesW(path); 230 | return ret == INVALID_FILE_ATTRIBUTES ? 0 : ret; 231 | } 232 | 233 | public static string GetFullPath(string path) 234 | { 235 | if (path.StartsWith(LongPathPrefix) || InternelDriveInfo.IsDrive(path)) 236 | return null; 237 | 238 | path = LongPathPrefix + path; 239 | 240 | using (var handle = NativeMethods.CreateFileW( 241 | path, 242 | FileAccess.Read, 243 | FileShare.ReadWrite | FileShare.Delete, 244 | IntPtr.Zero, 245 | FileMode.Open, 246 | (FileAttributes)0x02000000, //FILE_FLAG_BACKUP_SEMANTICS 247 | IntPtr.Zero)) 248 | { 249 | if (handle.IsInvalid) 250 | return null; 251 | return Utils.GetPathByFileHandle(handle.DangerousGetHandle()); 252 | } 253 | } 254 | 255 | private static IEnumerable GetChildsCore(string path) 256 | { 257 | var sc = new SearchContent(); 258 | bool isDrive = InternelDriveInfo.IsDrive(path); 259 | if (isDrive) 260 | path = path.Substring(0, path.Length - 1); 261 | using (FindFileHandle handle = sc.BeginFind(LongPathPrefix + path + SearchSuffix)) 262 | { 263 | if (handle == null) 264 | yield break; 265 | 266 | if (!isDrive) 267 | sc.NextFind(handle); 268 | else 269 | yield return new FileNDir($@"{path}\{sc.LastFileName}"); 270 | 271 | while (sc.NextFind(handle)) 272 | { 273 | string lfn = sc.LastFileName; 274 | yield return new FileNDir($@"{path}\{lfn}"); 275 | } 276 | } 277 | } 278 | 279 | 280 | public bool Equals(FileNDir other) => FullPath == other.FullPath; 281 | 282 | public override bool Equals(object obj) => obj is FileNDir fd && Equals(fd); 283 | 284 | public override int GetHashCode() => FullPath.GetHashCode(); 285 | 286 | public override string ToString() => FullPath.ToString(); 287 | 288 | 289 | 290 | 291 | private struct SearchContent 292 | { 293 | private const int FINDFIRSTEX_INFO_BASIC = 1; 294 | private const int FINDFIRSTEX_SEARCH_NAME_MATCH = 0; 295 | private const int FINDFIRSTEX_LARGE_FETCH_ON_DISK_ENTRIES_ONLY = 2 | 4; 296 | 297 | private Win32FindData _findDate; 298 | 299 | public FileAttributes LastFileAttributs => _findDate.FileAttributes; 300 | 301 | public string LastFileName => _findDate.CFileName; 302 | 303 | public FindFileHandle BeginFind(string path) 304 | { 305 | FindFileHandle handle = NativeMethods.FindFirstFileExW( 306 | path, 307 | FINDFIRSTEX_INFO_BASIC, 308 | ref _findDate, 309 | FINDFIRSTEX_SEARCH_NAME_MATCH, 310 | IntPtr.Zero, 311 | FINDFIRSTEX_LARGE_FETCH_ON_DISK_ENTRIES_ONLY); 312 | 313 | if (handle.IsInvalid) 314 | { 315 | handle.Dispose(); 316 | return null; 317 | } 318 | return handle; 319 | } 320 | 321 | public bool NextFind(FindFileHandle findHandle) 322 | { 323 | return (NativeMethods.FindNextFileW(findHandle.DangerousGetHandle(), ref _findDate)); 324 | } 325 | } 326 | 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /src/DelApp/Internals/FileUnlocker.cs: -------------------------------------------------------------------------------- 1 | using DelApp.Internals.Win32; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace DelApp.Internals 9 | { 10 | internal static class FileUnlocker 11 | { 12 | private const int FILE_TYPE_DISK = 1; 13 | private const int DuplicateCloseSource = 0x00000001; 14 | 15 | private const int SystemHandleInformation = 0x0010; 16 | private const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004; 17 | private const uint Success = 0x00000000; 18 | 19 | private const int ALL_ACCESS = 0x001F0FFF; 20 | 21 | private const int LIST_MODULES_ALL = 0x03; 22 | 23 | private const int MemoryBasicInformation = 0; 24 | private const int MemorySectionName = 2; 25 | private const int MemInfoBufferSizeInt = 32767 * 2; 26 | private const int MEM_Commit = 0x1000; 27 | private const int MEM_Mapped = 0x40000; 28 | 29 | private const int DuplicateSameAccess = 0x00000002; 30 | 31 | private static readonly IntPtr s_stemMinAppAddress; 32 | private static readonly IntPtr s_systemMaxAppAddress; 33 | private static readonly IntPtr s_systemMaxApp32Address; 34 | 35 | private static readonly IntPtr s_memInfoBufferSizeIntPtr = new IntPtr(MemInfoBufferSizeInt); 36 | 37 | private static readonly int s_handleEntrySize = 8 + IntPtr.Size * 2; 38 | private static readonly int s_handleInfoSize = s_handleEntrySize + IntPtr.Size; 39 | 40 | static FileUnlocker() 41 | { 42 | var systemInfo = new SysInfo(); 43 | NativeMethods.GetSystemInfo(systemInfo); 44 | long maxAddr = systemInfo.MaximumApplicationAddress.ToInt64(); 45 | s_systemMaxApp32Address = new IntPtr(maxAddr & 0x7FFFFFFF); 46 | s_systemMaxAppAddress = (ulong)maxAddr <= uint.MaxValue ? s_systemMaxApp32Address : systemInfo.MaximumApplicationAddress; 47 | s_stemMinAppAddress = systemInfo.MinimumApplicationAddress; 48 | } 49 | 50 | 51 | public static void UnlockModuleAndMemory(Dictionary pathes) 52 | { 53 | using (var buffer = new UnmanagedBuffer(1024 * 1024)) 54 | { 55 | bool success; 56 | int realSize; 57 | while ((success = NativeMethods.K32EnumProcesses(buffer.Handle, buffer.Size, out realSize)) && buffer.Size <= realSize) 58 | { 59 | buffer.Resize(realSize << 1); 60 | } 61 | if (!success) 62 | return; 63 | unsafe 64 | { 65 | ReleaseModuleAndMemory(pathes, (int*)buffer.Handle.ToPointer(), realSize >> 2); 66 | } 67 | } 68 | } 69 | 70 | public static void UnlockHandle(Dictionary pathes) 71 | { 72 | 73 | ReleaseHandle(null, pathes); 74 | } 75 | 76 | public static void UnlockModuleAndMemory(Dictionary pathes, int[] locker) 77 | { 78 | unsafe 79 | { 80 | fixed (int* p = locker) 81 | ReleaseModuleAndMemory(pathes, p, locker.Length); 82 | } 83 | } 84 | 85 | public static void UnlockHandle(Dictionary pathes, int[] locker) 86 | { 87 | Dictionary handleLocker = locker.Where(p => p >= 0).ToDictionary(p => p, p => p); 88 | ReleaseHandle(handleLocker, pathes); 89 | } 90 | 91 | 92 | 93 | private unsafe static void ReleaseModuleAndMemory(Dictionary pathes, int* locker, int length) 94 | { 95 | Parallel.For(0, length, i => 96 | { 97 | IntPtr handle = NativeMethods.OpenProcess(ALL_ACCESS, false, locker[i]); 98 | if (handle == IntPtr.Zero || ReleaseModules(handle, pathes) || ReleaseMappedFile(handle, pathes)) 99 | locker[i] = -1; 100 | NativeMethods.CloseHandle(handle); 101 | 102 | }); 103 | } 104 | 105 | private static bool ReleaseModules(IntPtr handle, Dictionary pathes) 106 | { 107 | using (var buffer = new UnmanagedBuffer(4)) 108 | { 109 | bool success; 110 | while ((success = NativeMethods.K32EnumProcessModulesEx(handle, buffer.Handle, buffer.Size, out int newCb, LIST_MODULES_ALL)) && 111 | buffer.Size != newCb) 112 | { 113 | buffer.Resize(newCb); 114 | } 115 | 116 | if (!success) 117 | return true; 118 | 119 | IntPtr baseAddr = buffer.Read(0); 120 | 121 | // main module 122 | int found = IsFound(baseAddr, handle, pathes); 123 | 124 | if (found < 0) // can not get module 125 | return true; 126 | 127 | if (found != 0) // kill proc 128 | { 129 | NativeMethods.TerminateProcess(handle, -1); 130 | return true; 131 | } 132 | 133 | int moudleCount = buffer.Size / IntPtr.Size; 134 | 135 | for (int i = 1; i < moudleCount; i++) 136 | { 137 | baseAddr = buffer.Read(i * IntPtr.Size); 138 | found = IsFound(baseAddr, handle, pathes); 139 | if (found < 0) // can not get module 140 | return true; 141 | if (found != 0) // unmap dll 142 | NativeMethods.NtUnmapViewOfSection(handle, baseAddr); 143 | } 144 | return false; 145 | } 146 | 147 | int IsFound(IntPtr baseAddr, IntPtr phandle, Dictionary targets) 148 | { 149 | char[] charBuf = ObjPool.RentCharBuffer(); 150 | int clen = NativeMethods.K32GetMappedFileNameW(phandle, baseAddr, charBuf, charBuf.Length); 151 | if (clen == 0) 152 | { 153 | ObjPool.ReturnCharBuffer(charBuf); 154 | return -1; 155 | } 156 | string path = InternelDriveInfo.GetNtPathFromDosPath(charBuf, clen); 157 | ObjPool.ReturnCharBuffer(charBuf); 158 | return targets.ContainsKey(path) ? 1 : 0; 159 | } 160 | 161 | } 162 | 163 | private static bool ReleaseMappedFile(IntPtr handle, Dictionary pathes) 164 | { 165 | 166 | IntPtr start = s_stemMinAppAddress; 167 | long end = (!Utils.IsWow64Process(handle) ? s_systemMaxAppAddress : s_systemMaxApp32Address).ToInt64(); 168 | long regionSize; 169 | IntPtr baseAddress; 170 | string path; 171 | using (var buffer = new UnmanagedBuffer(MemInfoBufferSizeInt)) 172 | { 173 | while (start.ToInt64() < end) 174 | { 175 | //MEMORY_BASIC_INFORMATION 176 | if (NativeMethods.NtQueryVirtualMemory(handle, start, MemoryBasicInformation, buffer.Handle, s_memInfoBufferSizeIntPtr, out _) != Success) 177 | return true; 178 | baseAddress = buffer.Read(0); 179 | regionSize = buffer.ReadSizeT(IntPtr.Size * 3); 180 | 181 | if (buffer.Read(IntPtr.Size * 4) == MEM_Commit && 182 | buffer.Read(IntPtr.Size * 4 + 8) == MEM_Mapped && 183 | NativeMethods.NtQueryVirtualMemory(handle, baseAddress, MemorySectionName, buffer.Handle, s_memInfoBufferSizeIntPtr, out IntPtr len) == Success && 184 | // UNICODE_STRING 185 | !string.IsNullOrEmpty(path = InternelDriveInfo.GetNtPathFromDosPath(buffer.Handle + (IntPtr.Size << 1), (len.ToInt32() >> 1) - IntPtr.Size - 1)) && 186 | pathes.ContainsKey(path)) 187 | { 188 | NativeMethods.NtUnmapViewOfSection(handle, baseAddress); 189 | } 190 | start = new IntPtr(baseAddress.ToInt64() + regionSize); 191 | } 192 | return false; 193 | } 194 | } 195 | 196 | private static void ReleaseHandle(Dictionary handleLocker, Dictionary pathes) 197 | { 198 | using (var buffer = new UnmanagedBuffer(s_handleInfoSize)) 199 | { 200 | uint apiRet; 201 | while ((apiRet = NativeMethods.NtQuerySystemInformation( 202 | SystemHandleInformation, 203 | buffer.Handle, 204 | buffer.Size, 205 | out int realLength)) == STATUS_INFO_LENGTH_MISMATCH) 206 | { 207 | buffer.Resize(realLength); 208 | } 209 | if (apiRet != Success) 210 | return; 211 | long handleCount = buffer.ReadSizeT(0); 212 | 213 | Parallel.For(0L, handleCount, l => 214 | { 215 | int pid = buffer.Read((int)(IntPtr.Size + l * s_handleEntrySize)); 216 | 217 | if (handleLocker == null) 218 | { 219 | if (pid == Utils.AppProcessId) 220 | return; 221 | } 222 | else if (!handleLocker.ContainsKey(pid)) 223 | return; 224 | 225 | IntPtr handle = NativeMethods.OpenProcess(ALL_ACCESS, false, pid); 226 | if (handle == IntPtr.Zero) 227 | return; 228 | IntPtr objHandle = new IntPtr( 229 | buffer.Read((int)(l * s_handleEntrySize + IntPtr.Size + 6) 230 | )); 231 | // long access = buffer.ReadSizeT((int)(l * s_handleEntrySize + 8 + IntPtr.Size)); 232 | IntPtr mh; 233 | if ((mh = CopyHandle(handle, objHandle)) == IntPtr.Zero) 234 | return; 235 | string path; 236 | bool isTargetHandle = 237 | NativeMethods.GetFileType(mh) == FILE_TYPE_DISK && // is file or dir 238 | (path = Utils.GetPathByFileHandle(mh)) != null && 239 | pathes.ContainsKey(path); 240 | 241 | NativeMethods.CloseHandle(mh); 242 | 243 | if (isTargetHandle && 244 | (mh = CopyHandle(handle, objHandle, DuplicateCloseSource)) != IntPtr.Zero) 245 | NativeMethods.CloseHandle(mh); 246 | 247 | NativeMethods.CloseHandle(handle); 248 | 249 | IntPtr CopyHandle(IntPtr sph, IntPtr sh, int option = 0) 250 | { 251 | return NativeMethods.DuplicateHandle( 252 | sph, 253 | sh, 254 | Utils.AppProcessHandle, 255 | out IntPtr myHandle, 256 | 0, 257 | false, 258 | DuplicateSameAccess | option) ? myHandle : IntPtr.Zero; 259 | } 260 | }); 261 | } 262 | } 263 | 264 | 265 | 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/DelApp/Internals/HandleInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DelApp.Internals 4 | { 5 | internal sealed class HandleInfo 6 | { 7 | public readonly IntPtr Handle; 8 | public readonly long Access; 9 | 10 | private HandleInfo(IntPtr handle, long access) { Handle = handle; Access = access; } 11 | 12 | 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/DelApp/Internals/IAppLanguageProvider.cs: -------------------------------------------------------------------------------- 1 | namespace DelApp.Locals 2 | { 3 | internal interface IAppLanguageProvider 4 | { 5 | string TwoLetterISOLanguageName { get; } 6 | int LCID { get; } 7 | 8 | string UI_File { get; } 9 | string UI_File_Open { get; } 10 | string UI_File_RightClickContextMenu { get; } 11 | string UI_File_SourceCode { get; } 12 | string UI_File_Exit { get; } 13 | 14 | string UI_Edit { get; } 15 | string UI_Edit_RemoveFromList { get; } 16 | string UI_Edit_ClearList { get; } 17 | string UI_Edit_FixPath { get; } 18 | 19 | string UI_FastDelete { get; } 20 | string UI_Delete { get; } 21 | 22 | string UI_OpenDialog_Title { get; } 23 | string UI_OpenDialog_OKButton { get; } 24 | string UI_OpenDialog_CancelButton { get; } 25 | string UI_OpenDialog_FastAccess { get; } 26 | string UI_OpenDialog_FastAccess_Desktop { get; } 27 | string UI_OpenDialog_FastAccess_Document { get; } 28 | string UI_OpenDialog_Drives { get; } 29 | 30 | string Shell_RightClickContextText { get; } 31 | 32 | string Message_FailToFixPath { get; } 33 | string Message_Confirmation { get; } 34 | string Message_NotFullyCompleted { get; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/DelApp/Internals/InternelDriveInfo.cs: -------------------------------------------------------------------------------- 1 | using DelApp.Internals.Win32; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | namespace DelApp.Internals 7 | { 8 | internal static class InternelDriveInfo 9 | { 10 | private static readonly int s_pro_drive_count = Environment.GetLogicalDrives().Length << 1; 11 | private static readonly Dictionary s_driveCache = new Dictionary(s_pro_drive_count); 12 | private static readonly Dictionary s_driveDosNameMap = new Dictionary(s_pro_drive_count); 13 | 14 | public static bool IsDrive(string path) => s_driveCache.ContainsKey(path); 15 | 16 | public static void RefreshDriveCache() 17 | { 18 | s_driveCache.Clear(); 19 | s_driveDosNameMap.Clear(); 20 | foreach (string item in RefreshDriveCore()) 21 | { 22 | AddToCacheCore(item); 23 | } 24 | } 25 | 26 | 27 | public static IEnumerable RefreshDriveCacheAndReturnsDriveName() 28 | { 29 | 30 | s_driveCache.Clear(); 31 | s_driveDosNameMap.Clear(); 32 | 33 | foreach (string item in RefreshDriveCore()) 34 | { 35 | AddToCacheCore(item); 36 | yield return item; 37 | } 38 | } 39 | 40 | 41 | public static string GetNtPathFromDosPath(IntPtr dosPath, int charCount) 42 | { 43 | unsafe 44 | { 45 | var s = (char*)dosPath.ToPointer(); 46 | return GetNtPathFromDosPathCore(s, charCount); 47 | } 48 | 49 | } 50 | 51 | public static string GetNtPathFromDosPath(char[] dosPath, int charCount) 52 | { 53 | unsafe 54 | { 55 | fixed (char* s = dosPath) 56 | return GetNtPathFromDosPathCore(s, charCount); 57 | } 58 | } 59 | 60 | 61 | private static unsafe string GetNtPathFromDosPathCore(char* s, int charCount) 62 | { 63 | int length; 64 | int i = 0, j = 0; 65 | foreach (KeyValuePair item in s_driveDosNameMap) 66 | { 67 | length = item.Key.Length; 68 | if (length < charCount) 69 | { 70 | fixed (char* pDos = item.Key) 71 | { 72 | while (i < length) 73 | { 74 | if (pDos[i] != s[i]) 75 | { 76 | goto final; 77 | } 78 | ++i; 79 | } 80 | i -= item.Value.Length; 81 | fixed (char* pWin = item.Value) 82 | { 83 | while (i < length) 84 | { 85 | s[i++] = pWin[j++]; 86 | } 87 | } 88 | length = item.Key.Length - item.Value.Length; 89 | return new string(s, length, charCount - length); 90 | 91 | } 92 | } 93 | 94 | final: 95 | i = 0; 96 | j = 0; 97 | } 98 | return null; 99 | } 100 | 101 | 102 | 103 | private static void AddToCacheCore(string driveName) 104 | { 105 | s_driveCache.Add(driveName, null); 106 | string deviceName = driveName.Substring(0, driveName.Length - 1); 107 | var result = TryQueryDosDevice(deviceName); 108 | if (result != null) 109 | { 110 | s_driveDosNameMap.Add(result, deviceName); 111 | } 112 | 113 | string TryQueryDosDevice(string ntDeviceName) 114 | { 115 | char[] pathBuf = ObjPool.RentCharBuffer(); 116 | unsafe 117 | { 118 | fixed (char* p = pathBuf) 119 | { 120 | int len = NativeMethods.QueryDosDeviceW(ntDeviceName, p, pathBuf.Length); 121 | try 122 | { 123 | return len == 0 ? null : new string(p, 0, len - 2); 124 | } 125 | finally 126 | { 127 | ObjPool.ReturnCharBuffer(pathBuf); 128 | } 129 | } 130 | } 131 | 132 | } 133 | } 134 | 135 | private static IEnumerable RefreshDriveCore() 136 | { 137 | DriveInfo[] drives = DriveInfo.GetDrives(); 138 | int length = drives.Length; 139 | DriveInfo di; 140 | DriveType dt; 141 | for (int i = 0; i < length; i++) 142 | { 143 | di = drives[i]; 144 | dt = di.DriveType; 145 | if (di.IsReady && (dt == DriveType.Fixed || dt == DriveType.Removable)) 146 | { 147 | yield return di.Name; 148 | } 149 | } 150 | } 151 | 152 | 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/DelApp/Internals/NativeWin32/ChangeFilterStruct.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace DelApp.Internals.Win32 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | internal struct ChangeFilterStruct 7 | { 8 | public uint CbSize; 9 | public uint ExtStatus; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/DelApp/Internals/NativeWin32/FindFileHandle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | 3 | namespace DelApp.Internals.Win32 4 | { 5 | internal sealed class FindFileHandle : SafeHandleZeroOrMinusOneIsInvalid 6 | { 7 | private FindFileHandle() : base(true) 8 | { 9 | } 10 | 11 | protected override bool ReleaseHandle() 12 | { 13 | return NativeMethods.FindClose(handle); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/DelApp/Internals/NativeWin32/LUID.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace DelApp.Internals.Win32 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | internal struct LUID 7 | { 8 | public int LowPart; 9 | public int HighPart; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/DelApp/Internals/NativeWin32/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | using System.Security; 6 | 7 | namespace DelApp.Internals.Win32 8 | { 9 | [SuppressUnmanagedCodeSecurity] 10 | internal static class NativeMethods 11 | { 12 | //==================================== message ================================================ 13 | 14 | [DllImport("user32.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Winapi)] 15 | [return: MarshalAs(UnmanagedType.Bool)] 16 | public static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, uint message, uint action, in ChangeFilterStruct pChangeFilterStruct); 17 | 18 | //==================================== drag drop ================================================ 19 | 20 | [DllImport("shell32", ExactSpelling = true, CharSet = CharSet.Unicode)] 21 | public unsafe static extern int DragQueryFileW(IntPtr hDrop, uint iFile, char* pszFile, int cch); 22 | 23 | [DllImport("shell32.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Winapi)] 24 | public static extern void DragAcceptFiles(IntPtr hWnd, bool fAccept); 25 | 26 | [DllImport("shell32.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Winapi)] 27 | public static extern void DragFinish(IntPtr hDrop); 28 | 29 | //==================================== file ================================================ 30 | 31 | [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = false)] 32 | public static extern FindFileHandle FindFirstFileExW(string lpFileName, int fInfoLevelId, ref Win32FindData lpFindFileData, int fSearchOp, IntPtr lpSearchFilter, int dwAdditionalFlags); 33 | 34 | [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = false)] 35 | public static extern bool FindNextFileW(IntPtr hFindFile, ref Win32FindData lpFindFileData); 36 | 37 | [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false)] 38 | public static extern bool FindClose(IntPtr hFindFile); 39 | 40 | [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = false)] 41 | public static extern SafeFileHandle CreateFileW( 42 | string filename, 43 | FileAccess access, 44 | FileShare share, 45 | IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero 46 | FileMode creationDisposition, 47 | FileAttributes flagsAndAttributes, 48 | IntPtr templateFile); 49 | 50 | 51 | [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode)] 52 | public static extern FileAttributes GetFileAttributesW(string lpFileName); 53 | 54 | [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = false)] 55 | public static extern bool MoveFileW(string lpExistingFileName, string lpNewFileName); 56 | 57 | [DllImport("kernel32.dll", SetLastError = false)] 58 | public static extern int GetFileType(IntPtr hFile); 59 | 60 | [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] 61 | public static extern bool DeleteFileW(string lpPathName); 62 | 63 | [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] 64 | public static extern bool RemoveDirectoryW(string lpPathName); 65 | 66 | [DllImport("Kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)] 67 | public static extern int GetFinalPathNameByHandleW(IntPtr hFile, char[] lpszFilePath, int cchFilePath, uint dwFlags); 68 | 69 | [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = false)] 70 | public static extern bool SetFileAttributesW(string lpFileName, FileAttributes dwFileAttributes); 71 | 72 | //==================================== drives ================================================ 73 | 74 | [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] 75 | public unsafe static extern int QueryDosDeviceW(string lpDeviceName, char* lpTargetPath, int ucchMax); 76 | 77 | //==================================== icon info ================================================ 78 | 79 | [DllImport("shell32.dll")] 80 | public static extern int SHGetStockIconInfo(uint siid, uint uFlags, ref SHSTOCKICONINFO psii); 81 | 82 | [DllImport("user32.dll")] 83 | public static extern bool DestroyIcon(IntPtr handle); 84 | 85 | //==================================== query object ================================================ 86 | 87 | [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Winapi)] 88 | public static extern uint NtQuerySystemInformation(int infoClass, [Out] IntPtr info, int size, out int length); 89 | 90 | //[DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)] 91 | //public static extern uint NtQueryObject(IntPtr objectHandle, int informationClass, [Out] IntPtr informationPtr, int informationLength, out int Length); 92 | 93 | //==================================== environment ================================================ 94 | [DllImport("kernel32", ExactSpelling = true, SetLastError = false)] 95 | public static extern void GetSystemInfo(SysInfo lpSystemInfo); 96 | 97 | //==================================== process ================================================ 98 | [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false)] 99 | public static extern IntPtr OpenProcess(int processAccess, bool bInheritHandle, int processId); 100 | 101 | [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false)] 102 | public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, int dwDesiredAccess, bool bInheritHandle, int dwOptions); 103 | 104 | [DllImport("kernel32.dll", ExactSpelling = true)] 105 | public static extern IntPtr GetCurrentProcess(); 106 | 107 | [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)] 108 | public static extern uint NtQueryVirtualMemory(IntPtr hProcess, IntPtr lpAddress, int MemoryInformationClass, IntPtr lpBuffer, IntPtr dwLength, out IntPtr size); 109 | 110 | [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Winapi)] 111 | public static extern bool IsWow64Process(IntPtr hProcess, [Out] out bool Wow64Process); 112 | 113 | [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)] 114 | public static extern IntPtr GetModuleHandleW(string lpModuleName); 115 | 116 | [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false, CharSet = CharSet.Ansi, BestFitMapping = false, CallingConvention = CallingConvention.Winapi)] 117 | public static extern IntPtr GetProcAddress(IntPtr module, string proc); 118 | 119 | [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false)] 120 | public static extern bool CloseHandle(IntPtr hObject); 121 | 122 | [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false)] 123 | public static extern bool TerminateProcess(IntPtr processHandle, int exitCode); 124 | 125 | [DllImport("kernel32.dll", ExactSpelling = true, CallingConvention = CallingConvention.Winapi, SetLastError = false)] 126 | public static extern bool K32EnumProcesses(IntPtr buffer, int bufferSize, out int realSize); 127 | 128 | [DllImport("kernel32.dll", ExactSpelling = true, CallingConvention = CallingConvention.Winapi, SetLastError = false)] 129 | public static extern bool K32EnumProcessModulesEx( 130 | [In] IntPtr hProcess, 131 | [In, Out] IntPtr lphModule, 132 | [In] int cb, 133 | [Out] out int lpcbNeeded, 134 | [In] int dwFilterFlag); 135 | 136 | [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = false)] 137 | public static extern int K32GetMappedFileNameW([In] IntPtr hProcess, [In] IntPtr hModule, [In, Out] char[] lpFilename, [In] int nSize); 138 | 139 | [DllImport("kernel32.dll", ExactSpelling = true, CallingConvention = CallingConvention.Winapi, SetLastError = false)] 140 | public static extern int GetProcessId(IntPtr hProcess); 141 | 142 | [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Winapi)] 143 | public static extern uint NtUnmapViewOfSection(IntPtr hProcess, IntPtr baseAddress); 144 | 145 | 146 | [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = false)] 147 | public static extern bool OpenProcessToken(IntPtr processHandle, int desiredAccess, out IntPtr tokenHandle); 148 | [DllImport("advapi32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = false)] 149 | public static extern bool LookupPrivilegeValueW(string lpSystemName, string lpName, out LUID lpLuid); 150 | [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = false)] 151 | public static extern bool AdjustTokenPrivileges(IntPtr tokenHandle, bool disableAllPrivileges, SingleTokenPrivilegeOn NewState, int bufferLength, IntPtr previousState, IntPtr returnLength); 152 | 153 | 154 | // ========================================= thread =================================================== 155 | 156 | //[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Winapi)] 157 | //public static extern unsafe SafeWaitHandle CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, IntPtr dwStackSize, IntPtr lpStartAddress, void* lpParameter, int dwCreationFlags, out int threadID); 158 | 159 | //[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Winapi)] 160 | //public static extern WaitObjectResult WaitForSingleObject([In] IntPtr hHandle, [In] int dwMilliseconds); 161 | 162 | //[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Winapi)] 163 | //[return: MarshalAs(UnmanagedType.Bool)] 164 | //public static extern bool TerminateThread(IntPtr hThread, int dwExitCode); 165 | 166 | 167 | 168 | // =========================================restart manager =================================================== 169 | 170 | [DllImport("rstrtmgr.dll", ExactSpelling = true, CharSet = CharSet.Unicode)] 171 | public static extern int RmStartSession(out uint pSessionHandle, uint dwSessionFlags, char[] strSessionKey); 172 | [DllImport("rstrtmgr.dll", ExactSpelling = true, CharSet = CharSet.Unicode)] 173 | public static extern int RmRegisterResources(uint dwSessionHandle, int nFiles, string[] rgsFilenames, uint nApplications, RmUniqueProcess[] rgApplications, uint nServices, string[] rgsServiceNames); 174 | [DllImport("rstrtmgr.dll", ExactSpelling = true, CharSet = CharSet.Unicode)] 175 | public static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded, ref uint pnProcInfo, [Out] RmProcessInfo[] rgAffectedApps, out RmRebootReason lpdwRebootReasons); 176 | [DllImport("rstrtmgr.dll", ExactSpelling = true)] 177 | public static extern int RmEndSession(uint pSessionHandle); 178 | 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/DelApp/Internals/NativeWin32/RmStructs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DelApp.Internals.Win32 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | internal struct RmUniqueProcess 8 | { 9 | public int ProcessId; 10 | public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime; 11 | } 12 | 13 | internal enum RmAppType 14 | { 15 | RmUnknownApp = 0, 16 | RmMainWindow = 1, 17 | RmOtherWindow = 2, 18 | RmService = 3, 19 | RmExplorer = 4, 20 | RmConsole = 5, 21 | RmCritical = 1000 22 | } 23 | 24 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 25 | internal unsafe struct RmProcessInfo 26 | { 27 | public RmUniqueProcess Process; 28 | //CCH_RM_MAX_APP_NAME+1 29 | private fixed char ApplicationName[256]; 30 | //CCH_RM_MAX_SVC_NAME+1 31 | private fixed char ServiceShortName[64]; 32 | public RmAppType ApplicationType; 33 | public RmAppStatus AppStatus; 34 | public int TSSessionId; 35 | public bool bRestartable; 36 | public static int Size => Marshal.SizeOf(typeof(RmProcessInfo)); 37 | } 38 | [Flags] 39 | internal enum RmRebootReason 40 | { 41 | None = 0x0, 42 | PermissionDenied = 0x1, 43 | SessionMismatch = 0x2, 44 | CriticalProcess = 0x4, 45 | CriticalService = 0x8, 46 | DetectedSelf = 0x10 47 | } 48 | 49 | [Flags] 50 | internal enum RmAppStatus 51 | { 52 | RmStatusUnknown = 0x0, 53 | RmStatusRunning = 0x1, 54 | RmStatusStopped = 0x2, 55 | RmStatusStoppedOther = 0x4, 56 | RmStatusRestarted = 0x8, 57 | RmStatusErrorOnStop = 0x10, 58 | RmStatusErrorOnRestart = 0x20, 59 | RmStatusShutdownMasked = 0x40, 60 | RmStatusRestartMasked = 0x80 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/DelApp/Internals/NativeWin32/SHSTOCKICONINFO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DelApp.Internals.Win32 5 | { 6 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 7 | public unsafe struct SHSTOCKICONINFO 8 | { 9 | public uint cbSize; 10 | public IntPtr hIcon; 11 | public int iSysIconIndex; 12 | public int iIcon; 13 | public fixed char szPath[260]; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/DelApp/Internals/NativeWin32/SingleTokenPrivilegeOn.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace DelApp.Internals.Win32 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | internal class SingleTokenPrivilegeOn 7 | { 8 | public readonly int PrivilegeCount = 1; 9 | public LUID Luid; 10 | public readonly int Attributes = 2; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/DelApp/Internals/NativeWin32/SysInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DelApp.Internals.Win32 5 | { 6 | [StructLayout(LayoutKind.Sequential, Pack = 2)] 7 | internal sealed class SysInfo 8 | { 9 | private readonly uint _oemId; 10 | 11 | public readonly int PageSize; 12 | 13 | public readonly IntPtr MinimumApplicationAddress; 14 | 15 | public readonly IntPtr MaximumApplicationAddress; 16 | 17 | public readonly IntPtr ActiveProcessorMask; 18 | 19 | public readonly int NumberOfProcessors; 20 | 21 | public readonly int OrgProcessorType; 22 | 23 | public readonly int AllocationGranularity; 24 | 25 | public readonly short ProcessorLevel; 26 | 27 | public readonly short ProcessorRevision; 28 | 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/DelApp/Internals/NativeWin32/WaitObjectResult.cs: -------------------------------------------------------------------------------- 1 | namespace DelApp.Internals.Win32 2 | { 3 | 4 | internal enum WaitObjectResult : uint 5 | { 6 | WAIT_OBJECT_0 = 0x00000000, 7 | WAIT_TIMEOUT = 0x00000102, 8 | WAIT_FAILED = 0xFFFFFFFF, 9 | WAIT_ABANDONED = 0x00000080 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/DelApp/Internals/NativeWin32/Win32FindData.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DelApp.Internals.Win32 5 | { 6 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 7 | [BestFitMapping(false)] 8 | internal unsafe struct Win32FindData 9 | { 10 | public readonly FileAttributes FileAttributes; 11 | private readonly System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime; 12 | private readonly System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime; 13 | private readonly System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime; 14 | private readonly uint FileSizeHigh; 15 | private readonly uint FileSizeLow; 16 | private readonly uint dwReserved0; 17 | private readonly uint dwReserved1; 18 | private fixed char cFileName[260]; 19 | private fixed char _cAlternateFileName[14]; 20 | internal string CFileName { get { fixed (char* c = cFileName) return new string(c); } } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/DelApp/Internals/ObjPool.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Collections.Generic; 3 | 4 | namespace DelApp.Internals 5 | { 6 | internal static class ObjPool 7 | { 8 | public const int CharBufferSize = 32767; 9 | 10 | private static readonly ConcurrentBag s_chars_pool = new ConcurrentBag(); 11 | private static readonly ConcurrentBag> s_stringList_pool = new ConcurrentBag>(); 12 | 13 | public static char[] RentCharBuffer() => s_chars_pool.TryTake(out char[] buffer) ? buffer : new char[CharBufferSize]; 14 | public static void ReturnCharBuffer(char[] buffer) => s_chars_pool.Add(buffer); 15 | 16 | public static List RentFDList() => s_stringList_pool.TryTake(out List list) ? list : new List(512); 17 | public static void ReturnFDList(List list) 18 | { 19 | list.Clear(); 20 | s_stringList_pool.Add(list); 21 | } 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/DelApp/Internals/PeReader.cs: -------------------------------------------------------------------------------- 1 | //using System; 2 | //using System.Collections.Generic; 3 | //using System.IO; 4 | //using System.Linq; 5 | //using System.Runtime.InteropServices; 6 | //using System.Runtime.InteropServices.ComTypes; 7 | //using System.Text; 8 | //using System.Threading.Tasks; 9 | 10 | //namespace DelApp.Internals 11 | //{ 12 | // internal class PeReader : IDisposable 13 | // { 14 | // private const int BUFFER_SIZE = 4096; 15 | 16 | // private readonly byte[] _buf; 17 | 18 | // private Stream _stream; 19 | 20 | // public PeReader(string dllPath) 21 | // { 22 | // _stream = new FileStream(dllPath, FileMode.Open, FileAccess.Read, FileShare.Read); 23 | // _buf = new byte[BUFFER_SIZE]; 24 | // _stream.Read(_buf, 0, BUFFER_SIZE); 25 | // } 26 | 27 | // public T Read(long pos) 28 | // where T : unmanaged 29 | // { 30 | // int offset = GetBufferOffset(pos, Marshal.SizeOf()); 31 | // unsafe 32 | // { 33 | // fixed (byte* p = _buf) 34 | // return *(T*)(p + offset); 35 | // } 36 | // } 37 | 38 | // public string ReadStringAnsi(long pos) 39 | // { 40 | // the max length of function name is 4096 41 | // int offset = GetBufferOffset(pos, BUFFER_SIZE); 42 | // int i = offset; 43 | // unsafe 44 | // { 45 | // fixed (byte* p = _buf) 46 | // { 47 | // while (i < BUFFER_SIZE) 48 | // { 49 | // if (p[i] == 0) 50 | // { 51 | // return new string((sbyte*)p, offset, i - offset, Encoding.ASCII); 52 | // } 53 | // ++i; 54 | // } 55 | // } 56 | // throw new NotSupportedException(); 57 | // } 58 | // } 59 | 60 | // public bool EqualsStringAnsi(long pos, string str) 61 | // { 62 | // int length = str.Length; 63 | // int offset = GetBufferOffset(pos, length + 1); 64 | // unsafe 65 | // { 66 | // fixed (byte* ptrBuf = _buf) 67 | // fixed (char* ptrStr = str) 68 | // { 69 | // int i = 0; 70 | // byte* strInByte = ptrBuf + offset; 71 | // while (i < length) 72 | // { 73 | // if (strInByte[i] != ptrStr[i]) 74 | // return false; 75 | // ++i; 76 | // } 77 | // if (strInByte[i] != 0) 78 | // return false; 79 | // } 80 | // return true; 81 | // } 82 | // } 83 | 84 | 85 | // public int[] GetFunctionOffset(params string[] functionNames) 86 | // { 87 | // int addressOfNewExeHeader = Read(0x3C); 88 | // int optionalHeaderOffset = addressOfNewExeHeader + 0x04 + 0x14; 89 | // ushort magic = Read(optionalHeaderOffset); 90 | // int exportTableOffset = GetOptionalHeaderDataOffset(magic) + optionalHeaderOffset; 91 | // int exportTableRav = Read(exportTableOffset); 92 | // int numberOfSections = Read(addressOfNewExeHeader + 0x04 + 0x02); 93 | // int sectionOffset = optionalHeaderOffset + Read(addressOfNewExeHeader + 0x04 + 0x10); 94 | // if (!GetRavRelation( 95 | // numberOfSections, 96 | // sectionOffset, 97 | // rav: exportTableRav, 98 | // out int va, 99 | // out int pra, 100 | // out int foa)) 101 | // { 102 | // return Array.Empty(); 103 | // } 104 | // int numOfFunName = Read(foa + 0x18); 105 | // int addrOfFun = Read(foa + 0x1C); 106 | // int addrOfFunName = Read(foa + 0x20); 107 | // int orderOfFun = Read(foa + 0x24); 108 | // return SearchFuncs(numOfFunName, addrOfFunName, orderOfFun, addrOfFun, va, pra, functionNames); 109 | // } 110 | 111 | // protected virtual int GetOptionalHeaderDataOffset(ushort magic) 112 | // { 113 | // switch (magic) 114 | // { 115 | // case 0x10b: 116 | // return 0x60; 117 | // default: //0x20b 118 | // return 0x70; 119 | // } 120 | // } 121 | 122 | // private bool GetRavRelation(int numberOfSections, int sectionOffset, int rav, out int va, out int pra, out int foa) 123 | // { 124 | // pra = foa = 0; 125 | // sectionOffset += 0x0C; 126 | // int count = 0; 127 | // va = Read(sectionOffset); 128 | // int sra = Read(sectionOffset + 4); 129 | // while (count < numberOfSections && 130 | // (va + sra) < rav) 131 | // { 132 | // sectionOffset += 0x28; 133 | // va = Read(sectionOffset); 134 | // sra = Read(sectionOffset + 4); 135 | // ++count; 136 | // } 137 | // if (count == numberOfSections) 138 | // return false; 139 | // pra = Read(sectionOffset + 8); 140 | // foa = rav - va + pra; 141 | // return true; 142 | // } 143 | 144 | // private int[] SearchFuncs(int funcCount, int startFuncName, int startFuncOrder, int startFuncAddr, int va, int pra, params string[] functionNames) 145 | // { 146 | // int funcFoa; 147 | // int found = 0; 148 | // int offset = pra - va; 149 | // int searchLength = functionNames.Length; 150 | // int[] result = null; 151 | // startFuncName += offset; 152 | // startFuncOrder += offset; 153 | // startFuncAddr += offset; 154 | // for (int i = 0; i < funcCount; i++) 155 | // { 156 | // funcFoa = Read(startFuncName + (i << 2)) + offset; 157 | // for (int j = 0; j < searchLength; j++) 158 | // { 159 | // if (EqualsStringAnsi(funcFoa, functionNames[j])) 160 | // { 161 | // if (result == null) 162 | // result = new int[searchLength]; 163 | 164 | // var off = Read(startFuncOrder + (i << 1)); 165 | // result[j] = Read(startFuncAddr + (off << 2)); 166 | // ++found; 167 | // if (found == searchLength) 168 | // return result; 169 | // } 170 | // } 171 | // } 172 | // return Array.Empty(); 173 | // } 174 | 175 | // private int GetBufferOffset(long pos, int size) 176 | // { 177 | // long streamPos = _stream.Position; 178 | // if (pos + size > streamPos || 179 | // pos < streamPos - BUFFER_SIZE) 180 | // { 181 | // _stream.Seek(pos, SeekOrigin.Begin); 182 | // _stream.Read(_buf, 0, BUFFER_SIZE); 183 | // } 184 | // return (int)(pos - _stream.Position + BUFFER_SIZE); 185 | // } 186 | 187 | // protected virtual void Dispose(bool disposing) 188 | // { 189 | // if (_stream != null) 190 | // { 191 | // if (disposing) 192 | // _stream.Dispose(); 193 | // _stream = null; 194 | // } 195 | // } 196 | 197 | // public void Dispose() 198 | // { 199 | // Dispose(disposing: true); 200 | // GC.SuppressFinalize(this); 201 | // } 202 | // } 203 | //} 204 | -------------------------------------------------------------------------------- /src/DelApp/Internals/PipeService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.IO.Pipes; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Windows.Forms; 8 | 9 | namespace DelApp.Internals 10 | { 11 | internal static class PipeService 12 | { 13 | private const int DEFALUT_BYTES_BUFFER_SIZE = 32767 * 2; 14 | 15 | 16 | public static event EventHandler PathRecived; 17 | 18 | public static ConcurrentQueue PathQueue = new ConcurrentQueue(); 19 | 20 | 21 | public static bool CreateService() 22 | { 23 | return ThreadPool.QueueUserWorkItem(ServiceCoreAsync); 24 | } 25 | 26 | public static void SendPath(IEnumerable pathes) 27 | { 28 | using (var pipeClient = new NamedPipeClientStream(".", Utils.MyGuidString, PipeDirection.Out)) 29 | { 30 | var lenBuf = new byte[4]; 31 | var buffer = new byte[DEFALUT_BYTES_BUFFER_SIZE]; 32 | int len; 33 | try 34 | { 35 | pipeClient.Connect(); 36 | unsafe 37 | { 38 | fixed (byte* p = lenBuf) 39 | { 40 | foreach (var item in pathes) 41 | { 42 | len = Encoding.Unicode.GetBytes(item, 0, item.Length, buffer, 0); 43 | *(int*)p = len; 44 | pipeClient.Write(lenBuf, 0, 4); 45 | pipeClient.Write(buffer, 0, len); 46 | } 47 | } 48 | } 49 | pipeClient.WaitForPipeDrain(); 50 | } 51 | catch 52 | { 53 | } 54 | } 55 | } 56 | 57 | private static async void ServiceCoreAsync(object stateInfo) 58 | { 59 | using (var pipeServer = new NamedPipeServerStream(Utils.MyGuidString, PipeDirection.In)) 60 | { 61 | var lenBuf = new byte[4]; 62 | var buffer = new byte[DEFALUT_BYTES_BUFFER_SIZE]; 63 | int len; 64 | string path; 65 | while (true) 66 | { 67 | try 68 | { 69 | if (!pipeServer.IsConnected) 70 | await pipeServer.WaitForConnectionAsync().ConfigureAwait(false); 71 | 72 | unsafe 73 | { 74 | fixed (byte* p = lenBuf) 75 | { 76 | while (pipeServer.Read(lenBuf, 0, 4) == 4) 77 | { 78 | len = *(int*)p; 79 | if (pipeServer.Read(buffer, 0, len) == len) 80 | PathQueue.Enqueue(new FileNDir(Encoding.Unicode.GetString(buffer, 0, len))); 81 | } 82 | PathRecived?.Invoke(PathQueue, EventArgs.Empty); 83 | } 84 | } 85 | } 86 | catch (Exception ecx) 87 | { 88 | Utils.WriteErrorLog(ecx.Message); 89 | } 90 | finally 91 | { 92 | try 93 | { 94 | pipeServer.Disconnect(); 95 | } 96 | catch (Exception ecx2) 97 | { 98 | Utils.WriteErrorLog(ecx2.Message); 99 | } 100 | } 101 | } 102 | } 103 | } 104 | 105 | 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/DelApp/Internals/ProcessModuleLite.cs: -------------------------------------------------------------------------------- 1 | //using DelApp.Internals.Win32; 2 | //using Microsoft.Win32.SafeHandles; 3 | //using System; 4 | //using System.Collections.Generic; 5 | //using System.ComponentModel; 6 | //using System.Linq; 7 | //using System.Reflection; 8 | //using System.Text; 9 | //using System.Threading.Tasks; 10 | 11 | //namespace DelApp.Internals 12 | //{ 13 | // internal class ProcessModuleLite 14 | // { 15 | // private const int LIST_MODULES_ALL = 0x03; 16 | 17 | // private ProcessModuleLite(int index, IntPtr baseAddr, string path) 18 | // { 19 | // Index = index; 20 | // BaseAddr = baseAddr; 21 | // Path = path; 22 | // } 23 | // public readonly int Index; 24 | // public readonly IntPtr BaseAddr; 25 | // public readonly string Path; 26 | 27 | // public override string ToString() => Path; 28 | 29 | 30 | // public static IEnumerable GetModules(SafeProcessHandle handle) 31 | // { 32 | // IntPtr proc = handle.DangerousGetHandle(); 33 | // if (!NativeMethods.K32EnumProcessModulesEx(proc, null, 0, out int cbNeed, LIST_MODULES_ALL)) 34 | // { 35 | // handle.Dispose(); 36 | // yield break; 37 | // } 38 | // int moudleCount = cbNeed / IntPtr.Size; 39 | // var handleArray = new IntPtr[moudleCount]; 40 | // bool success; 41 | // while ((success = NativeMethods.K32EnumProcessModulesEx(proc, handleArray, cbNeed, out int newCb, LIST_MODULES_ALL)) && 42 | // cbNeed != newCb) 43 | // { 44 | // moudleCount = newCb / IntPtr.Size; 45 | // handleArray = new IntPtr[moudleCount]; 46 | // } 47 | // if (!success) 48 | // { 49 | // handle.Dispose(); 50 | // yield break; 51 | // } 52 | 53 | // char[] buffer = ObjPool.RentCharBuffer(); 54 | // IntPtr h; 55 | // int clen; 56 | // for (int i = 0; i < moudleCount; i++) 57 | // { 58 | // h = handleArray[i]; 59 | // clen = NativeMethods.K32GetMappedFileName(proc, h, buffer, buffer.Length); 60 | // if (clen == 0) 61 | // { 62 | // handle.Dispose(); 63 | // ObjPool.ReturnCharBuffer(buffer); 64 | // yield break; 65 | // } 66 | // yield return new ProcessModuleLite(i, h, InternelDriveInfo.GetNtPathFromDosPath(buffer, clen)); 67 | // } 68 | // ObjPool.ReturnCharBuffer(buffer); 69 | // } 70 | // } 71 | //} 72 | -------------------------------------------------------------------------------- /src/DelApp/Internals/RemoteFunction.cs: -------------------------------------------------------------------------------- 1 | //using DelApp.Internals.Win32; 2 | //using Microsoft.Win32.SafeHandles; 3 | //using System; 4 | //using System.CodeDom; 5 | //using System.Collections.Generic; 6 | //using System.Diagnostics; 7 | //using System.Drawing; 8 | //using System.IO; 9 | //using System.Linq; 10 | //using System.Runtime.InteropServices; 11 | //using System.Runtime.InteropServices.ComTypes; 12 | //using System.Security.Cryptography; 13 | //using System.Text; 14 | //using System.Threading.Tasks; 15 | 16 | //namespace DelApp.Internals 17 | //{ 18 | // internal static class RemoteFunction 19 | // { 20 | // public const int INFINITE_INT = -1; 21 | 22 | // private static readonly string s_ntdllPath32; 23 | 24 | // private static readonly IntPtr s_ldrUnloadDllAnyCpu; 25 | 26 | 27 | 28 | // static RemoteFunction() 29 | // { 30 | // s_ldrUnloadDllAnyCpu = NativeMethods.GetProcAddress(NativeMethods.GetModuleHandle("ntdll.dll"), "LdrUnloadDll"); 31 | // s_ntdllPath32 = FileNDir.GetFullPath(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86) + "\\ntdll.dll"); 32 | // if (Environment.Is64BitProcess) 33 | // { 34 | // using (var pe = new PeReader(s_ntdllPath32)) 35 | // { 36 | // int[] ints = pe.GetFunctionOffset("LdrUnloadDll"); 37 | // s_ldrUnloadDllOffset32 = ints.Length != 0 ? ints[0] : 0; 38 | // } 39 | // s_ntdllBaseAddress32 = IntPtr.Zero; 40 | // } 41 | // else 42 | // { 43 | // Any cpu 44 | // s_ldrUnloadDllOffset32 = -1; 45 | // s_ntdllBaseAddress32 = s_ldrUnloadDllAnyCpu; 46 | // } 47 | // } 48 | 49 | 50 | // public static async Task CallRemoteFunctionAsync(IntPtr proc, IntPtr func, TParam arg, int millisecondTimeout = INFINITE_INT) 51 | // where TParam : unmanaged 52 | // { 53 | // return await Task.FromResult(CallRemoteFunction(proc, func, arg, millisecondTimeout)).ConfigureAwait(false); 54 | // } 55 | 56 | // public static WaitObjectResult CallRemoteFunction(IntPtr proc, IntPtr func, TParam arg, int millisecondTimeout = INFINITE_INT) 57 | // where TParam : unmanaged 58 | // { 59 | // unsafe 60 | // { 61 | // using (SafeWaitHandle hHandle = NativeMethods.CreateRemoteThread( 62 | // proc, //process handle 63 | // IntPtr.Zero, 64 | // IntPtr.Zero, 65 | // func, //address 66 | // &arg, // pointer to param 67 | // 0, out _)) 68 | // { 69 | // if (hHandle.IsInvalid) 70 | // return WaitObjectResult.WAIT_FAILED; 71 | // WaitObjectResult waitResult = NativeMethods.WaitForSingleObject(hHandle.DangerousGetHandle(), millisecondTimeout); 72 | // if (waitResult == WaitObjectResult.WAIT_TIMEOUT || 73 | // waitResult == WaitObjectResult.WAIT_ABANDONED) 74 | // NativeMethods.TerminateThread(hHandle.DangerousGetHandle(), 0); 75 | // return waitResult; 76 | 77 | // } 78 | // } 79 | // } 80 | 81 | 82 | // } 83 | //} 84 | -------------------------------------------------------------------------------- /src/DelApp/Internals/RestartManagerHelper.cs: -------------------------------------------------------------------------------- 1 | using DelApp.Internals.Win32; 2 | using System; 3 | using System.Linq; 4 | 5 | namespace DelApp.Internals 6 | { 7 | //Unfortunately, RM DO NOT Supports folder. 8 | 9 | // The max count of the RestartManager's session is 64. 10 | internal sealed class RestartManagerHelper : DisposableSingleton 11 | { 12 | private const int ERROR_MORE_DATA = 234; 13 | 14 | private readonly uint _handle; 15 | 16 | private RestartManagerHelper() 17 | { 18 | if (NativeMethods.RmStartSession(out _handle, 0, Utils.MyGuidStringWithNullChar) != 0) 19 | _handle = 0; 20 | } 21 | 22 | public int[] GetHolderList(out RmRebootReason reason, params string[] fileNames) 23 | { 24 | reason = RmRebootReason.None; 25 | if (NativeMethods.RmRegisterResources( 26 | _handle, 27 | fileNames.Length, fileNames, 28 | 0, null, 29 | 0, null) != 0) 30 | return Array.Empty(); 31 | 32 | RmProcessInfo[] affectedApps = null; 33 | uint nCount = 0; 34 | int err; 35 | while ((err = NativeMethods.RmGetList(_handle, out var nlength, ref nCount, affectedApps, out reason)) == ERROR_MORE_DATA) 36 | { 37 | affectedApps = new RmProcessInfo[nlength]; 38 | nCount = (uint)affectedApps.Length; 39 | } 40 | return err == 0 ? affectedApps.Select(rmi => rmi.Process.ProcessId).ToArray() : Array.Empty(); 41 | } 42 | 43 | 44 | protected override void DisposeManaged() { } 45 | 46 | protected override void DisposeUnmanaged() 47 | { 48 | if (_handle != 0) 49 | NativeMethods.RmEndSession(_handle); 50 | } 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/DelApp/Internals/RightClickMenuHelper.cs: -------------------------------------------------------------------------------- 1 | using DelApp.Locals; 2 | using Microsoft.Win32; 3 | using System.Windows.Forms; 4 | 5 | namespace DelApp.Internals 6 | { 7 | internal static class RightClickMenuHelper 8 | { 9 | 10 | private static readonly string s_dir_subkey = $"Directory\\shell\\{Utils.MyGuidString}\\command"; 11 | private static readonly string s_file_subkey = $"*\\shell\\{Utils.MyGuidString}\\command"; 12 | private static readonly string s_open_command = $"{Application.ExecutablePath} \"%L\""; 13 | 14 | public static bool HasRightClickMenu 15 | { 16 | get => GetHasRightMenu(); 17 | set 18 | { 19 | if (value) 20 | AddRightClickMenu(); 21 | else 22 | RemoveRightClickMenu(); 23 | } 24 | } 25 | 26 | private static bool GetHasRightMenu() 27 | { 28 | using (RegistryKey regRoot0 = Registry.ClassesRoot.OpenSubKey("Directory\\shell", true), 29 | regRoot1 = Registry.ClassesRoot.OpenSubKey("*\\shell", true)) 30 | { 31 | if (!GetHasRightMenuCore(regRoot0)) 32 | return false; 33 | else if (!GetHasRightMenuCore(regRoot1)) 34 | { 35 | RemoveRightClickMenuCore(regRoot0); 36 | return false; 37 | } 38 | else 39 | return true; 40 | } 41 | } 42 | 43 | 44 | private static bool GetHasRightMenuCore(RegistryKey regRoot) 45 | { 46 | using (RegistryKey regshell = regRoot?.OpenSubKey(Utils.MyGuidString, true)) 47 | { 48 | if (regshell == null) 49 | return false; 50 | regshell.SetValue(null, AppLanguageService.LanguageProvider.Shell_RightClickContextText); 51 | regshell.SetValue("icon", Application.ExecutablePath); 52 | using (RegistryKey regCmd = regshell.CreateSubKey("command")) 53 | { 54 | if (regCmd == null) 55 | { 56 | regRoot.DeleteSubKeyTree(Utils.MyGuidString); 57 | return false; 58 | } 59 | regCmd.SetValue(null, s_open_command); 60 | } 61 | } 62 | return true; 63 | } 64 | 65 | // Do not need set value, since we always invokes HasRightClickMenu_get . 66 | private static void AddRightClickMenu() 67 | { 68 | using (RegistryKey regRoot = Registry.ClassesRoot.CreateSubKey(s_dir_subkey, true)) 69 | { 70 | } 71 | using (RegistryKey regRoot = Registry.ClassesRoot.CreateSubKey(s_file_subkey, true)) 72 | { 73 | } 74 | } 75 | 76 | private static void RemoveRightClickMenu() 77 | { 78 | using (RegistryKey regRoot0 = Registry.ClassesRoot.OpenSubKey("Directory\\shell", true), 79 | regRoot1 = Registry.ClassesRoot.OpenSubKey("*\\shell", true)) 80 | { 81 | RemoveRightClickMenuCore(regRoot0); 82 | RemoveRightClickMenuCore(regRoot1); 83 | } 84 | } 85 | 86 | private static void RemoveRightClickMenuCore(RegistryKey regRoot) 87 | { 88 | using (RegistryKey regshell = regRoot?.OpenSubKey(Utils.MyGuidString, true)) 89 | { 90 | if (regshell != null) 91 | regRoot.DeleteSubKeyTree(Utils.MyGuidString); 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/DelApp/Internals/SoundPlayHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System; 3 | using System.IO; 4 | using System.Media; 5 | 6 | 7 | namespace DelApp.Internals 8 | { 9 | // The large object heap threshold is 85K which is smaller than ths size of media file. 10 | // Singleton pattern can avoid repeatedly allocate memory. 11 | internal sealed class SoundPlayHelper : DisposableSingleton 12 | { 13 | private SoundPlayer _player; 14 | 15 | private SoundPlayHelper() 16 | { 17 | try 18 | { 19 | // returns null if not found. 20 | string soundPath = GetSoundPath(); 21 | 22 | if (soundPath != null) 23 | { 24 | _player = new SoundPlayer(soundPath); 25 | _player.Load(); 26 | } 27 | 28 | 29 | } 30 | catch (Exception ecx) 31 | { 32 | Utils.WriteErrorLog(ecx.Message); 33 | if (_player != null) 34 | { 35 | _player.Dispose(); 36 | _player = null; 37 | } 38 | } 39 | } 40 | 41 | 42 | public void TryPlayEmptyRecyclebin(bool isAsync) 43 | { 44 | if (_player != null) 45 | { 46 | try 47 | { 48 | if (isAsync) 49 | _player.Play(); 50 | else 51 | _player.PlaySync(); 52 | } 53 | catch (Exception ecx) 54 | { 55 | Utils.WriteErrorLog(ecx.Message); 56 | } 57 | } 58 | } 59 | 60 | 61 | protected override void DisposeManaged() => _player?.Dispose(); 62 | 63 | protected override void DisposeUnmanaged() => _player = null; 64 | 65 | 66 | // returns null if not found. 67 | private static string GetSoundPath() 68 | { 69 | // read path from registery 70 | using (RegistryKey reg = Registry.CurrentUser.OpenSubKey(@"AppEvents\Schemes\Apps\Explorer\EmptyRecycleBin\.Current", false)) 71 | { 72 | if (reg == null || !(reg.GetValue(null) is string path)) 73 | { 74 | // if not found, try "windows\media\Windows Recycle.wav" 75 | path = Environment.GetFolderPath(Environment.SpecialFolder.Windows); 76 | if (path == string.Empty) 77 | return null; 78 | path += "\\media\\Windows Recycle.wav"; 79 | return File.Exists(path) ? path : null; 80 | } 81 | return path; 82 | } 83 | } 84 | 85 | 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/DelApp/Internals/UnmanagedBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DelApp.Internals 5 | { 6 | internal struct UnmanagedBuffer : IDisposable 7 | { 8 | private IntPtr _handle; 9 | 10 | public UnmanagedBuffer(int size) 11 | { 12 | _handle = Marshal.AllocHGlobal(size); 13 | Size = size; 14 | } 15 | 16 | public IntPtr Handle => _handle; 17 | 18 | public int Size { get; private set; } 19 | 20 | public void Resize(int size) 21 | { 22 | Marshal.FreeHGlobal(_handle); 23 | _handle = Marshal.AllocHGlobal(size); 24 | Size = size; 25 | } 26 | 27 | public T Read(int offsetInBytes) 28 | where T : unmanaged 29 | { 30 | unsafe 31 | { 32 | var ptr = (byte*)_handle.ToPointer() + offsetInBytes; 33 | return *(T*)ptr; 34 | } 35 | } 36 | 37 | public long ReadSizeT(int offsetInBytes) 38 | { 39 | unsafe 40 | { 41 | var ptr = (byte*)_handle.ToPointer() + offsetInBytes; 42 | return (*(IntPtr*)ptr).ToInt64(); 43 | } 44 | } 45 | 46 | public void Dispose() 47 | { 48 | Marshal.FreeHGlobal(_handle); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/DelApp/Internals/Utils.cs: -------------------------------------------------------------------------------- 1 | using DelApp.Internals.Win32; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace DelApp.Internals 9 | { 10 | internal static class Utils 11 | { 12 | private const int TOKEN_ADJUST_PRIVILEGES = 0x20; 13 | 14 | public static readonly string MyGuidString = Marshal.GetTypeLibGuidForAssembly(Assembly.GetExecutingAssembly()).ToString(); 15 | public static readonly char[] MyGuidStringWithNullChar = ToCharArrayWithNullChar(MyGuidString); 16 | 17 | public static readonly IntPtr AppProcessHandle = NativeMethods.GetCurrentProcess(); 18 | public static readonly int AppProcessId = NativeMethods.GetProcessId(AppProcessHandle); 19 | 20 | public static bool IsWow64Process(IntPtr processHandle) => NativeMethods.IsWow64Process(processHandle, out bool isWow64) && isWow64; 21 | 22 | public static void EnablePrivileges() 23 | { 24 | if (NativeMethods.OpenProcessToken(AppProcessHandle, TOKEN_ADJUST_PRIVILEGES, out IntPtr hToken)) 25 | { 26 | var mtkp = new SingleTokenPrivilegeOn(); 27 | AddPrivileges(hToken, mtkp, "SeDebugPrivilege"); 28 | AddPrivileges(hToken, mtkp, "SeBackupPrivilege"); 29 | AddPrivileges(hToken, mtkp, "SeRestorePrivilege"); 30 | NativeMethods.CloseHandle(hToken); 31 | } 32 | void AddPrivileges(IntPtr token, SingleTokenPrivilegeOn tkp, string pn) 33 | { 34 | if (NativeMethods.LookupPrivilegeValueW(null, pn, out LUID luid)) 35 | { 36 | tkp.Luid = luid; 37 | NativeMethods.AdjustTokenPrivileges(token, false, tkp, 0, IntPtr.Zero, IntPtr.Zero); 38 | } 39 | } 40 | } 41 | 42 | public static string GetPathByFileHandle(IntPtr fileHandle) 43 | { 44 | char[] buffer = ObjPool.RentCharBuffer(); 45 | try 46 | { 47 | int len = NativeMethods.GetFinalPathNameByHandleW(fileHandle, buffer, buffer.Length - 1, 0); 48 | if (len == 0) 49 | return null; 50 | return new string(buffer, 4, len - 4); 51 | } 52 | finally 53 | { 54 | ObjPool.ReturnCharBuffer(buffer); 55 | } 56 | } 57 | 58 | 59 | 60 | 61 | public static IEnumerable GetDrapDropFiles(IntPtr hDrop) 62 | { 63 | 64 | int fileCount = GetDragFileCount(hDrop); 65 | if (fileCount <= 0) 66 | goto Final_Drag_Final; 67 | InternelDriveInfo.RefreshDriveCache(); 68 | for (uint i = 0; i < fileCount; i++) 69 | { 70 | var file = CreateFromDropItem(hDrop, i); 71 | if (!file.IsDrive && file.Exists) 72 | yield return CreateFromDropItem(hDrop, i); 73 | } 74 | Final_Drag_Final: 75 | NativeMethods.DragFinish(hDrop); 76 | 77 | int GetDragFileCount(IntPtr hd) 78 | { 79 | unsafe 80 | { 81 | return NativeMethods.DragQueryFileW(hd, uint.MaxValue, null, 0); 82 | } 83 | } 84 | 85 | FileNDir CreateFromDropItem(IntPtr hd, uint index) 86 | { 87 | char[] pathBuf = ObjPool.RentCharBuffer(); 88 | unsafe 89 | { 90 | fixed (char* p = pathBuf) 91 | { 92 | int len = NativeMethods.DragQueryFileW(hd, index, p, pathBuf.Length); 93 | try 94 | { 95 | return new FileNDir(new string(p, 0, len)); 96 | } 97 | finally 98 | { 99 | ObjPool.ReturnCharBuffer(pathBuf); 100 | } 101 | } 102 | } 103 | } 104 | } 105 | 106 | 107 | public static void WriteErrorLog(string info) 108 | { 109 | FileStream fs = null; 110 | StreamWriter sw = null; 111 | try 112 | { 113 | fs = File.Open("DelAppError.log", FileMode.Append); 114 | sw = new StreamWriter(fs); 115 | sw.Write(info); 116 | } 117 | catch { } 118 | finally 119 | { 120 | sw?.Dispose(); 121 | fs?.Dispose(); 122 | } 123 | } 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | private static char[] ToCharArrayWithNullChar(string str) 132 | { 133 | int length = str.Length; 134 | var chaArray = new char[length + 1]; 135 | str.CopyTo(0, chaArray, 0, length); 136 | return chaArray; 137 | } 138 | 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/DelApp/Locals/AppLanguageProvider.cs: -------------------------------------------------------------------------------- 1 | namespace DelApp.Locals 2 | { 3 | public abstract class AppLanguageProvider : IAppLanguageProvider 4 | where TSelf : AppLanguageProvider, new() 5 | { 6 | 7 | public static readonly TSelf Instance = new TSelf(); 8 | 9 | public abstract string TwoLetterISOLanguageName { get; } 10 | 11 | public abstract int LCID { get; } 12 | 13 | public abstract string UI_File { get; } 14 | public abstract string UI_File_Open { get; } 15 | public abstract string UI_File_RightClickContextMenu { get; } 16 | public abstract string UI_File_SourceCode { get; } 17 | public abstract string UI_File_Exit { get; } 18 | 19 | public abstract string UI_Edit { get; } 20 | public abstract string UI_Edit_RemoveFromList { get; } 21 | public abstract string UI_Edit_ClearList { get; } 22 | public abstract string UI_Edit_FixPath { get; } 23 | 24 | public abstract string UI_FastDelete { get; } 25 | public abstract string UI_Delete { get; } 26 | 27 | public abstract string UI_OpenDialog_Title { get; } 28 | public abstract string UI_OpenDialog_OKButton { get; } 29 | public abstract string UI_OpenDialog_CancelButton { get; } 30 | public abstract string UI_OpenDialog_FastAccess { get; } 31 | public abstract string UI_OpenDialog_FastAccess_Desktop { get; } 32 | public abstract string UI_OpenDialog_FastAccess_Document { get; } 33 | public abstract string UI_OpenDialog_Drives { get; } 34 | 35 | public abstract string Shell_RightClickContextText { get; } 36 | 37 | public abstract string Message_FailToFixPath { get; } 38 | public abstract string Message_Confirmation { get; } 39 | public abstract string Message_NotFullyCompleted { get; } 40 | 41 | public void Register() => AppLanguageService.RegisterLanguageProvider(Instance); 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/DelApp/Locals/AppLanguageProviderChs.cs: -------------------------------------------------------------------------------- 1 | namespace DelApp.Locals 2 | { 3 | internal sealed class AppLanguageProviderChs : AppLanguageProvider 4 | { 5 | public override string TwoLetterISOLanguageName => "zh"; 6 | 7 | public override int LCID => 2052; 8 | 9 | public override string UI_File => "文件"; 10 | public override string UI_File_Open => "打开..."; 11 | public override string UI_File_RightClickContextMenu => "添加到右键菜单"; 12 | public override string UI_File_SourceCode => "源代码"; 13 | public override string UI_File_Exit => "退出"; 14 | 15 | public override string UI_Edit => "编辑"; 16 | public override string UI_Edit_RemoveFromList => "移除项目"; 17 | public override string UI_Edit_ClearList => "清空列表"; 18 | public override string UI_Edit_FixPath => "修复错误路径"; 19 | 20 | public override string UI_FastDelete => "快速删除"; 21 | public override string UI_Delete => "删除"; 22 | 23 | public override string UI_OpenDialog_Title => "打开..."; 24 | public override string UI_OpenDialog_OKButton => "确定"; 25 | public override string UI_OpenDialog_CancelButton => "取消"; 26 | public override string UI_OpenDialog_FastAccess => "快速访问"; 27 | public override string UI_OpenDialog_FastAccess_Desktop => "桌面"; 28 | public override string UI_OpenDialog_FastAccess_Document => "我的文档"; 29 | public override string UI_OpenDialog_Drives => "驱动器"; 30 | 31 | public override string Shell_RightClickContextText => "加入 Delapp 删除列表"; 32 | 33 | public override string Message_FailToFixPath => "修复路径失败。"; 34 | public override string Message_Confirmation => "我知道我在干啥。"; 35 | public override string Message_NotFullyCompleted => "只完成了部分的删除任务。"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/DelApp/Locals/AppLanguageProviderEn.cs: -------------------------------------------------------------------------------- 1 | namespace DelApp.Locals 2 | { 3 | internal sealed class AppLanguageProviderEn : AppLanguageProvider 4 | { 5 | public override string TwoLetterISOLanguageName => "en"; 6 | 7 | public override int LCID => 9; 8 | 9 | public override string UI_File => "File"; 10 | public override string UI_File_Open => "Open..."; 11 | public override string UI_File_RightClickContextMenu => "Add to right-click menu"; 12 | public override string UI_File_SourceCode => "Source code"; 13 | public override string UI_File_Exit => "Exit"; 14 | 15 | public override string UI_Edit => "Edit"; 16 | public override string UI_Edit_RemoveFromList => "Remove from list"; 17 | public override string UI_Edit_ClearList => "Clear list"; 18 | public override string UI_Edit_FixPath => "Fix invalid path"; 19 | 20 | public override string UI_FastDelete => "Fast delete"; 21 | public override string UI_Delete => "Delete"; 22 | 23 | public override string UI_OpenDialog_Title => "Open..."; 24 | public override string UI_OpenDialog_OKButton => "OK"; 25 | public override string UI_OpenDialog_CancelButton => "Cancel"; 26 | public override string UI_OpenDialog_FastAccess => "Fast Access"; 27 | public override string UI_OpenDialog_FastAccess_Desktop => "Desktop"; 28 | public override string UI_OpenDialog_FastAccess_Document => "My Documents"; 29 | public override string UI_OpenDialog_Drives => "Drives"; 30 | 31 | public override string Shell_RightClickContextText => "Delapp - Add to delete-list"; 32 | 33 | public override string Message_FailToFixPath => "Fail to fix path."; 34 | public override string Message_Confirmation => "I know what I want to do."; 35 | public override string Message_NotFullyCompleted => "Only part of delete tasks were completed."; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/DelApp/OpenFileDialogLite.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace DelApp 2 | { 3 | partial class OpenFileDialogLite 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.components = new System.ComponentModel.Container(); 32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(OpenFileDialogLite)); 33 | this.ButtonOK = new System.Windows.Forms.Button(); 34 | this.ButtonCancel = new System.Windows.Forms.Button(); 35 | this.ListViewMain = new System.Windows.Forms.ListView(); 36 | this.ColumnHeaderBack = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); 37 | this.ImageListMain = new System.Windows.Forms.ImageList(this.components); 38 | this.TreeViewMain = new System.Windows.Forms.TreeView(); 39 | this.SuspendLayout(); 40 | // 41 | // ButtonOK 42 | // 43 | this.ButtonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 44 | this.ButtonOK.DialogResult = System.Windows.Forms.DialogResult.OK; 45 | this.ButtonOK.Location = new System.Drawing.Point(329, 275); 46 | this.ButtonOK.Name = "ButtonOK"; 47 | this.ButtonOK.Size = new System.Drawing.Size(75, 23); 48 | this.ButtonOK.TabIndex = 0; 49 | this.ButtonOK.TabStop = false; 50 | this.ButtonOK.Text = "OK"; 51 | this.ButtonOK.UseVisualStyleBackColor = true; 52 | this.ButtonOK.Click += new System.EventHandler(this.ButtonOK_Click); 53 | // 54 | // ButtonCancel 55 | // 56 | this.ButtonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 57 | this.ButtonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; 58 | this.ButtonCancel.Location = new System.Drawing.Point(12, 275); 59 | this.ButtonCancel.Name = "ButtonCancel"; 60 | this.ButtonCancel.Size = new System.Drawing.Size(75, 23); 61 | this.ButtonCancel.TabIndex = 3; 62 | this.ButtonCancel.Text = "Cancel"; 63 | this.ButtonCancel.UseVisualStyleBackColor = true; 64 | // 65 | // ListViewMain 66 | // 67 | this.ListViewMain.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 68 | | System.Windows.Forms.AnchorStyles.Left) 69 | | System.Windows.Forms.AnchorStyles.Right))); 70 | this.ListViewMain.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { 71 | this.ColumnHeaderBack}); 72 | this.ListViewMain.FullRowSelect = true; 73 | this.ListViewMain.HideSelection = false; 74 | this.ListViewMain.Location = new System.Drawing.Point(143, 12); 75 | this.ListViewMain.Name = "ListViewMain"; 76 | this.ListViewMain.Size = new System.Drawing.Size(261, 253); 77 | this.ListViewMain.TabIndex = 4; 78 | this.ListViewMain.UseCompatibleStateImageBehavior = false; 79 | this.ListViewMain.View = System.Windows.Forms.View.Details; 80 | this.ListViewMain.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.ListViewMain_ColumnClick); 81 | this.ListViewMain.SelectedIndexChanged += new System.EventHandler(this.ListViewMain_SelectedIndexChanged); 82 | this.ListViewMain.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.ListViewMain_MouseDoubleClick); 83 | // 84 | // ColumnHeaderBack 85 | // 86 | this.ColumnHeaderBack.Text = "↑"; 87 | // 88 | // ImageListMain 89 | // 90 | this.ImageListMain.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit; 91 | this.ImageListMain.ImageSize = new System.Drawing.Size(16, 16); 92 | this.ImageListMain.TransparentColor = System.Drawing.Color.Transparent; 93 | // 94 | // TreeViewMain 95 | // 96 | this.TreeViewMain.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 97 | | System.Windows.Forms.AnchorStyles.Left))); 98 | this.TreeViewMain.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 99 | this.TreeViewMain.FullRowSelect = true; 100 | this.TreeViewMain.HideSelection = false; 101 | this.TreeViewMain.Location = new System.Drawing.Point(12, 12); 102 | this.TreeViewMain.Name = "TreeViewMain"; 103 | this.TreeViewMain.Size = new System.Drawing.Size(125, 253); 104 | this.TreeViewMain.TabIndex = 5; 105 | this.TreeViewMain.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.TreeViewMain_NodeMouseClick); 106 | // 107 | // OpenFileDialogLite 108 | // 109 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 110 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 111 | this.CancelButton = this.ButtonCancel; 112 | this.ClientSize = new System.Drawing.Size(411, 304); 113 | this.Controls.Add(this.TreeViewMain); 114 | this.Controls.Add(this.ListViewMain); 115 | this.Controls.Add(this.ButtonCancel); 116 | this.Controls.Add(this.ButtonOK); 117 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 118 | this.MaximizeBox = false; 119 | this.MinimizeBox = false; 120 | this.MinimumSize = new System.Drawing.Size(323, 213); 121 | this.Name = "OpenFileDialogLite"; 122 | this.ShowInTaskbar = false; 123 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 124 | this.Text = "Open..."; 125 | this.Load += new System.EventHandler(this.OpenFileDialogLite_Load); 126 | this.ResumeLayout(false); 127 | 128 | } 129 | 130 | #endregion 131 | private System.Windows.Forms.Button ButtonOK; 132 | private System.Windows.Forms.Button ButtonCancel; 133 | private System.Windows.Forms.ListView ListViewMain; 134 | public System.Windows.Forms.ImageList ImageListMain; 135 | private System.Windows.Forms.TreeView TreeViewMain; 136 | private System.Windows.Forms.ColumnHeader ColumnHeaderBack; 137 | } 138 | } -------------------------------------------------------------------------------- /src/DelApp/OpenFileDialogLite.cs: -------------------------------------------------------------------------------- 1 | using DelApp.Internals; 2 | using DelApp.Locals; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Drawing; 7 | using System.Net.NetworkInformation; 8 | using System.Windows.Forms; 9 | 10 | 11 | 12 | 13 | namespace DelApp 14 | { 15 | internal partial class OpenFileDialogLite : Form 16 | { 17 | private const int FILE_ICON_INDEX = 0; 18 | private const int DIR_ICON_INDEX = 1; 19 | 20 | private static readonly string s_mpath = Application.ExecutablePath; 21 | 22 | private readonly FileNDir _defaultDir; 23 | private FileNDir _currentDir; 24 | 25 | public OpenFileDialogLite() 26 | { 27 | InitializeComponent(); 28 | 29 | 30 | IAppLanguageProvider lp = AppLanguageService.LanguageProvider; 31 | Text = lp.UI_OpenDialog_Title; 32 | ButtonOK.Text = lp.UI_OpenDialog_OKButton; 33 | ButtonCancel.Text = lp.UI_OpenDialog_CancelButton; 34 | 35 | ListViewMain.ListViewItemSorter = new ListViewComparer(); 36 | ImageListMain.Images.Add("file", FromIconToBitmap(DefaultIcons.FileSmall)); 37 | ImageListMain.Images.Add("dir", FromIconToBitmap(DefaultIcons.DirSmall)); 38 | ListViewMain.SmallImageList = ImageListMain; 39 | ListViewMain.Columns[0].Width = Screen.PrimaryScreen.WorkingArea.Width; 40 | TreeViewMain.BeginUpdate(); 41 | var rootFastAccess = new TreeNode(lp.UI_OpenDialog_FastAccess); 42 | _defaultDir = AddEnvFolder(rootFastAccess, Environment.SpecialFolder.DesktopDirectory, lp.UI_OpenDialog_FastAccess_Desktop); 43 | AddEnvFolder(rootFastAccess, Environment.SpecialFolder.MyDocuments, lp.UI_OpenDialog_FastAccess_Document); 44 | TreeViewMain.Nodes.Add(rootFastAccess); 45 | TreeViewMain.Nodes.Add(lp.UI_OpenDialog_Drives); 46 | TreeViewMain.EndUpdate(); 47 | 48 | } 49 | 50 | public List Pathes { get; } = new List(); 51 | 52 | 53 | 54 | 55 | internal static void TryAddToList(ListView listview, FileNDir fd, bool check = true) 56 | { 57 | int itemIconIdx; 58 | if (fd.IsFile) 59 | { 60 | itemIconIdx = FILE_ICON_INDEX; 61 | if (check && (s_mpath == fd.FullPath || ListViewContainsFile(listview, fd.FullPath))) 62 | return; 63 | } 64 | else 65 | { 66 | itemIconIdx = DIR_ICON_INDEX; 67 | if (check && (s_mpath.StartsWith(fd.FullPath) || ListViewContainsDir(listview, fd.FullPath))) 68 | return; 69 | } 70 | 71 | var listItem = new ListViewItem(fd.FullPath, itemIconIdx) 72 | { 73 | Tag = fd, 74 | ToolTipText = fd.FullPath 75 | }; 76 | listItem.SubItems.Add(fd.FullPath); 77 | listview.Items.Add(listItem); 78 | 79 | } 80 | 81 | 82 | private static bool ListViewContainsFile(ListView listview, string path) 83 | { 84 | ListView.ListViewItemCollection listViewItemCollection = listview.Items; 85 | ListViewItem item; 86 | FileNDir file; 87 | for (int i = 0; i < listViewItemCollection.Count; i++) 88 | { 89 | item = listViewItemCollection[i]; 90 | file = item.Tag as FileNDir; 91 | if (file.IsFile) 92 | { 93 | if (item.Text == path) 94 | return true; 95 | } 96 | else if (path.StartsWith(item.Text)) 97 | return true; 98 | } 99 | return false; 100 | } 101 | 102 | private static bool ListViewContainsDir(ListView listview, string path) 103 | { 104 | ListView.ListViewItemCollection listViewItemCollection = listview.Items; 105 | ListViewItem item; 106 | FileNDir file; 107 | 108 | int length = listViewItemCollection.Count; 109 | for (int i = 0; i < length; i++) 110 | { 111 | item = listViewItemCollection[i]; 112 | file = item.Tag as FileNDir; 113 | if (file.IsFile) 114 | { 115 | if (item.Text.StartsWith(path)) 116 | { 117 | listview.Items.RemoveAt(i); 118 | --i; 119 | --length; 120 | } 121 | 122 | } 123 | else 124 | { 125 | if (path.Length >= item.Text.Length) 126 | { 127 | if (path.StartsWith(item.Text)) 128 | return true; 129 | } 130 | else if (item.Text.StartsWith(path)) 131 | { 132 | listview.Items.RemoveAt(i); 133 | --i; 134 | --length; 135 | } 136 | } 137 | } 138 | return false; 139 | } 140 | 141 | 142 | private void ListViewMain_SelectedIndexChanged(object sender, EventArgs e) 143 | { 144 | int count = ListViewMain.SelectedIndices.Count; 145 | ButtonOK.Enabled = count != 0; 146 | } 147 | 148 | private void ListViewMain_MouseDoubleClick(object sender, MouseEventArgs e) 149 | { 150 | ListViewHitTestInfo info = ListViewMain.HitTest(e.X, e.Y); 151 | ListViewItem item = info.Item; 152 | 153 | if (item != null && item.Tag is FileNDir file && !file.IsFile) 154 | { 155 | _currentDir = file; 156 | RefreshListView(); 157 | } 158 | 159 | } 160 | 161 | private void ButtonOK_Click(object sender, EventArgs e) 162 | { 163 | this.DialogResult = DialogResult.OK; 164 | Pathes.Clear(); 165 | 166 | var items = ListViewMain.SelectedItems; 167 | var length = items.Count; 168 | FileNDir file; 169 | for (int i = 0; i < length; i++) 170 | { 171 | file = (items[i].Tag as FileNDir); 172 | if (file.Exists) 173 | Pathes.Add(file); 174 | } 175 | this.Close(); 176 | } 177 | 178 | private void ListViewMain_ColumnClick(object sender, ColumnClickEventArgs e) 179 | { 180 | if (ListViewMain.Columns[0].Tag is FileNDir file) 181 | { 182 | _currentDir = file; 183 | RefreshListView(); 184 | } 185 | } 186 | 187 | private void TreeViewMain_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) 188 | { 189 | var selNode = e.Node; 190 | if (selNode.Tag is FileNDir file) 191 | { 192 | _currentDir = file; 193 | RefreshListView(); 194 | } 195 | } 196 | 197 | 198 | private void OpenFileDialogLite_Load(object sender, EventArgs e) 199 | { 200 | TreeViewMain.BeginUpdate(); 201 | TreeNode root = TreeViewMain.Nodes[1]; 202 | root.Nodes.Clear(); 203 | foreach (string item in InternelDriveInfo.RefreshDriveCacheAndReturnsDriveName()) 204 | { 205 | AddNode(root, item, item); 206 | } 207 | TreeViewMain.ExpandAll(); 208 | TreeViewMain.EndUpdate(); 209 | 210 | RefreshListView(); 211 | } 212 | 213 | 214 | private void RefreshListView() 215 | { 216 | if (_currentDir == null) 217 | { 218 | _currentDir = _defaultDir; 219 | } 220 | else 221 | { 222 | if (!_currentDir.Exists) 223 | _currentDir = _defaultDir; 224 | } 225 | 226 | ListViewMain.BeginUpdate(); 227 | ListViewMain.SelectedItems.Clear(); 228 | ListViewMain.Items.Clear(); 229 | ListViewMain.Columns[0].Tag = _currentDir.PreviewDir; 230 | foreach (FileNDir item in _currentDir.GetChilds()) 231 | { 232 | TryAddToList(ListViewMain, item, false); 233 | } 234 | 235 | ListViewMain.EndUpdate(); 236 | } 237 | 238 | private FileNDir AddNode(TreeNode node, string path, string text) 239 | { 240 | var file = new FileNDir(path); 241 | node.Nodes.Add(new TreeNode(text) 242 | { 243 | Tag = file 244 | }); 245 | return file; 246 | } 247 | 248 | private FileNDir AddEnvFolder(TreeNode node, Environment.SpecialFolder folder, string text) 249 | { 250 | try 251 | { 252 | string path = Environment.GetFolderPath(folder); 253 | return path == string.Empty ? null : AddNode(node, path, text); 254 | } 255 | catch (Exception ecx) 256 | { 257 | Utils.WriteErrorLog(ecx.Message); 258 | return null; 259 | } 260 | 261 | } 262 | 263 | private static Bitmap FromIconToBitmap(Icon icon) 264 | { 265 | Bitmap bmp = new Bitmap(icon.Width, icon.Height); 266 | using (Graphics gp = Graphics.FromImage(bmp)) 267 | { 268 | gp.Clear(Color.Transparent); 269 | gp.DrawIcon(icon, new Rectangle(0, 0, icon.Width, icon.Height)); 270 | } 271 | return bmp; 272 | } 273 | 274 | 275 | private sealed class ListViewComparer : IComparer 276 | { 277 | public int Compare(object x, object y) 278 | { 279 | var a = (x as ListViewItem); 280 | var b = (y as ListViewItem); 281 | int ret = -(a.ImageIndex.CompareTo(b.ImageIndex)); 282 | return ret == 0 ? 283 | a.Name.CompareTo(b.Name) : 284 | ret; 285 | } 286 | } 287 | 288 | 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /src/DelApp/Program.cs: -------------------------------------------------------------------------------- 1 | using DelApp.Internals; 2 | using DelApp.Locals; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading; 6 | using System.Windows.Forms; 7 | 8 | namespace DelApp 9 | { 10 | internal static class Program 11 | { 12 | 13 | public static IEnumerable FilePathes; 14 | 15 | /// 16 | /// 应用程序的主入口点。 17 | /// 18 | [STAThread] 19 | static void Main() 20 | { 21 | AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 22 | 23 | 24 | 25 | 26 | using (var mutex = new Mutex(true, Utils.MyGuidString, out bool createdNew)) 27 | { 28 | if (createdNew) 29 | { 30 | FilePathes = GetPathes(); 31 | if (PipeService.CreateService()) 32 | StartApp(); 33 | } 34 | else 35 | { 36 | PipeService.SendPath(GetPathes()); 37 | } 38 | } 39 | } 40 | 41 | 42 | static void StartApp() 43 | { 44 | // Todo : add new language providers here. 45 | AppLanguageProviderChs.Instance.Register(); 46 | 47 | Utils.EnablePrivileges(); 48 | 49 | Application.EnableVisualStyles(); 50 | Application.SetCompatibleTextRenderingDefault(false); 51 | Application.Run(new FormMain()); 52 | RestartManagerHelper.ReleaseSharedInstance(); 53 | SoundPlayHelper.ReleaseSharedInstance(); 54 | } 55 | 56 | static IEnumerable GetPathes() 57 | { 58 | string[] args = Environment.GetCommandLineArgs(); 59 | if (args.Length == 1) 60 | yield break; 61 | string path; 62 | InternelDriveInfo.RefreshDriveCache(); 63 | for (int i = 1; i < args.Length; i++) 64 | { 65 | path = FileNDir.GetFullPath(args[i]); 66 | if (path != null) 67 | yield return path; 68 | } 69 | } 70 | 71 | static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) => Utils.WriteErrorLog(e.ExceptionObject.ToString()); 72 | 73 | 74 | 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/DelApp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // 有关程序集的一般信息由以下 5 | // 控制。更改这些特性值可修改 6 | // 与程序集关联的信息。 7 | [assembly: AssemblyTitle("DelApp")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("DelApp")] 12 | [assembly: AssemblyCopyright("Copyright © 2023")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // 将 ComVisible 设置为 false 会使此程序集中的类型 17 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 18 | //请将此类型的 ComVisible 特性设置为 true。 19 | [assembly: ComVisible(false)] 20 | 21 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 22 | [assembly: Guid("b7dd4552-f9ac-4487-866c-9c8bf686f196")] 23 | 24 | // 程序集的版本信息由下列四个值组成: 25 | // 26 | // 主版本 27 | // 次版本 28 | // 生成号 29 | // 修订号 30 | // 31 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 32 | //通过使用 "*",如下所示: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.2.0")] 35 | [assembly: AssemblyFileVersion("1.0.2.0")] 36 | -------------------------------------------------------------------------------- /src/DelApp/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Delapp.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Delapp.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/DelApp/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /src/DelApp/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Delapp.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 29 | public bool ClearListEnabled { 30 | get { 31 | return ((bool)(this["ClearListEnabled"])); 32 | } 33 | set { 34 | this["ClearListEnabled"] = value; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/DelApp/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | False 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/DelApp/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | true 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/DelApp/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/differentrain/Delapp/c0d1d34a9a6327fb0326938493ca32162d78c9e3/src/DelApp/icon.ico --------------------------------------------------------------------------------