├── .gitignore
├── LICENSE
├── PRIVACY
├── README.md
├── assets
├── img
│ └── easyWSL-Logo.png
└── vid
│ ├── configureDistro.mp4
│ ├── getDistro.mp4
│ ├── manageDistro.mp4
│ ├── settingsPage.mp4
│ └── snapshots.mp4
├── easyWSL.sln
├── easyWSL
├── AboutDialog.xaml
├── AboutDialog.xaml.cs
├── App.xaml
├── App.xaml.cs
├── Assets
│ ├── BadgeLogo.scale-100.png
│ ├── BadgeLogo.scale-125.png
│ ├── BadgeLogo.scale-150.png
│ ├── BadgeLogo.scale-200.png
│ ├── BadgeLogo.scale-400.png
│ ├── LargeTile.scale-100.png
│ ├── LargeTile.scale-125.png
│ ├── LargeTile.scale-150.png
│ ├── LargeTile.scale-200.png
│ ├── LargeTile.scale-400.png
│ ├── LockScreenLogo.scale-200.png
│ ├── SmallTile.scale-100.png
│ ├── SmallTile.scale-125.png
│ ├── SmallTile.scale-150.png
│ ├── SmallTile.scale-200.png
│ ├── SmallTile.scale-400.png
│ ├── SplashScreen.scale-100.png
│ ├── SplashScreen.scale-125.png
│ ├── SplashScreen.scale-150.png
│ ├── SplashScreen.scale-200.png
│ ├── SplashScreen.scale-400.png
│ ├── Square150x150Logo.scale-100.png
│ ├── Square150x150Logo.scale-125.png
│ ├── Square150x150Logo.scale-150.png
│ ├── Square150x150Logo.scale-200.png
│ ├── Square150x150Logo.scale-400.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-16.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-24.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-256.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-32.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-48.png
│ ├── Square44x44Logo.altform-unplated_targetsize-16.png
│ ├── Square44x44Logo.altform-unplated_targetsize-256.png
│ ├── Square44x44Logo.altform-unplated_targetsize-32.png
│ ├── Square44x44Logo.altform-unplated_targetsize-48.png
│ ├── Square44x44Logo.scale-100.png
│ ├── Square44x44Logo.scale-125.png
│ ├── Square44x44Logo.scale-150.png
│ ├── Square44x44Logo.scale-200.png
│ ├── Square44x44Logo.scale-400.png
│ ├── Square44x44Logo.targetsize-16.png
│ ├── Square44x44Logo.targetsize-24.png
│ ├── Square44x44Logo.targetsize-24_altform-unplated.png
│ ├── Square44x44Logo.targetsize-256.png
│ ├── Square44x44Logo.targetsize-32.png
│ ├── Square44x44Logo.targetsize-48.png
│ ├── StoreLogo.backup.png
│ ├── StoreLogo.scale-100.png
│ ├── StoreLogo.scale-125.png
│ ├── StoreLogo.scale-150.png
│ ├── StoreLogo.scale-200.png
│ ├── StoreLogo.scale-400.png
│ ├── Wide310x150Logo.scale-100.png
│ ├── Wide310x150Logo.scale-125.png
│ ├── Wide310x150Logo.scale-150.png
│ ├── Wide310x150Logo.scale-200.png
│ └── Wide310x150Logo.scale-400.png
├── ManageDistrosPage.xaml
├── ManageDistrosPage.xaml.cs
├── ManageSnapshotsPage.xaml
├── ManageSnapshotsPage.xaml.cs
├── MoreInfoDialog.xaml
├── MoreInfoDialog.xaml.cs
├── NavigationRoot_Window.xaml
├── NavigationRoot_Window.xaml.cs
├── Package.appxmanifest
├── PlatformHelpers.cs
├── Properties
│ └── launchSettings.json
├── RegisterNewDistro_Page.xaml
├── RegisterNewDistro_Page.xaml.cs
├── SettingsPage.xaml
├── SettingsPage.xaml.cs
├── WelcomeWindow.xaml
├── WelcomeWindow.xaml.cs
├── WslSdk.cs
├── app.manifest
├── dep
│ ├── WindowsHelloBridge.exe
│ ├── bsdtar.exe
│ ├── bzip2.dll
│ ├── libarchive2.dll
│ ├── pam_wsl_hello.so
│ └── zlib1.dll
├── easyWSL.csproj
└── easyWSL.ico
├── easyWSLcmd
├── PlatformHelpers.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
└── easyWSLcmd.csproj
└── easyWslLib
├── DockerDownloader.cs
├── Helpers.cs
├── IPlatformHelpers.cs
└── easyWslLib.csproj
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | bin/
3 | obj/
4 | easyWSL/easyWSL.csproj.user
5 | easyWSL/Package.StoreAssociation.xml
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | # easyWSL
2 |
3 | Copyright (c) 2020 RedCode-Labs and Jakub Wróbel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
11 | # bsdtar
12 |
13 | All of the C source code and documentation in this package is subject
14 | to the following:
15 |
16 | Copyright (c) 2003-2006 Tim Kientzle
17 | All rights reserved.
18 |
19 | Redistribution and use in source and binary forms, with or without
20 | modification, are permitted provided that the following conditions
21 | are met:
22 | 1. Redistributions of source code must retain the above copyright
23 | notice, this list of conditions and the following disclaimer
24 | in this position and unchanged.
25 | 2. Redistributions in binary form must reproduce the above copyright
26 | notice, this list of conditions and the following disclaimer in the
27 | documentation and/or other materials provided with the distribution.
28 |
29 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
30 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
31 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
32 | IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
33 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
34 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
38 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 |
40 | # WSL-Hello-sudo
41 |
42 | Copyright 2017 Takaya Saeki
43 |
44 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
45 |
46 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
47 |
48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
49 |
--------------------------------------------------------------------------------
/PRIVACY:
--------------------------------------------------------------------------------
1 | We don't collect any data.
2 | Your data is yours, not ours.
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
easyWSL
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Create WSL distros based on Docker Images.
10 |
11 |
12 | > Made with ❤ by @redcode-labs team.
13 |
14 | ## What does this project do?
15 |
16 | easyWSL makes it way easier to use the wonders of WSL functionality on Windows 10 and 11 systems. Thanks to our efforts, easyWSL grants you an access to use most (almost all) system images from Docker Hub as WSL distros. Our C# sorcery allows us to use Docker Hub API in order to get .tar or .tar.gz images from there. After getting an image, single or multi-layered, we turn it into single image (multi-layered Docker image case) which we easily import as WSL distro.
17 |
18 | https://user-images.githubusercontent.com/40501814/161391038-10bf9360-429d-4a47-887f-63b35fae90cd.mp4
19 |
20 | ## Features
21 |
22 | In our latest release, we've added even more features than ever before. All of them wrapped around GUI app using a beauty of WinUI framework.
23 |
24 | We've managed to add management features to WSL. With a single click, you can unregister, open .vhdx location as well as see the filesystem of the given distro.
25 |
26 | https://user-images.githubusercontent.com/40501814/161391082-101aeaa4-48e1-4cc6-bac3-9bc251efe469.mp4
27 |
28 | While this sounds so good so far, we've also made a separate 'Settings' page, to manage your WSL in general. There you can easily adjust things like memory, the number of cores or the swap size assigned with WSL VM. You can also point to different swap file, custom Linux kernel and provide it with custom command line arguments. The given page has also a number of switches, with which you can manage things like localhost forwarding, page reporing, GUI applications (WSLg) support, debug console, nested virtualization. Don't worry if you get lost - you can always just 'Revert to defaults'.
29 |
30 | We've added a functionality to register currently or previously used WSL distros using their .vhdx file. Going further down the rabbit hole, we've turned it into more advanced feature - easyWSL can now make snapshots, which can easily be used as a backups/restore points for your distros.
31 |
32 | https://user-images.githubusercontent.com/40501814/161391108-42a4e891-99da-4d36-a49d-3960dff28410.mp4
33 |
34 | We've added several experimental features as well. One of them, is creating a new user at the time of installing a distro. You now can set the username and password, and, if you want to, use experimental integration of [WSL-Hello-sudo](https://github.com/nullpo-head/WSL-Hello-sudo).
35 |
36 | Furthermore, you can use an experimental feature to install development environments during the install process. This includes environments such as Python, Node.js, C/C++ and more. Not to mention, that it works cross-distro and more can be added along the way.
37 |
38 | https://user-images.githubusercontent.com/40501814/161391121-e76dc012-b819-434b-acb2-0d2696862911.mp4
39 |
40 | ## How to get it?
41 |
42 | Just go to our Microsoft Store page, which you can find [here](https://www.microsoft.com/store/apps/9NHBTMKS47RB).
43 | Get it, install, and voilà!
44 |
45 | ## Building on your own
46 |
47 | ### Prerequisites
48 |
49 | * Windows 10 1607 or later (for WSL1) or Windows 10 1903 (18362) or later
50 | * Note: you might want to check instructions on how to enable WSL [here](https://docs.microsoft.com/en-us/windows/wsl/install-manual)
51 | * [Developer Mode enabled](https://docs.microsoft.com/windows/uwp/get-started/enable-your-device-for-development)
52 | * [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) (recommended by Microsoft)
53 | * The following workloads:
54 | * .NET Desktop Development
55 | * Individual components:
56 | * Windows 10 SDK (10.0.19041.0)
57 | * The following extensions:
58 | * [System.Text.Json](https://www.nuget.org/packages/System.Text.Json/5.0.2?_src=template)
59 |
60 | More detailed info, including building on older releases of Visual Studio, can be found
61 | [here](https://docs.microsoft.com/en-us/windows/apps/windows-app-sdk/set-up-your-development-environment?tabs=vs-2022-17-1-a%2Cvs-2022-17-1-b)
62 |
63 | (Upon opening the repo in Visual Studio, it will prompt you to install any missing workloads and features.)
64 |
65 | ### Building
66 |
67 | We currently only build using the solution; command line methods of building a VS solution should work as well.
68 |
69 | ## Future plans
70 |
71 | ### CLI
72 |
73 | We are looking forward to make a CLI compatible with all of our latest features, in order to make our tool usable in scripts.
74 |
75 | If you're looking into using easyWSL as a CLI, the only current option is to use the older version (easyWSL 1.2), which you can get from the release page [here](https://github.com/redcode-labs/easyWSL/releases/tag/1.2).
76 |
--------------------------------------------------------------------------------
/assets/img/easyWSL-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/assets/img/easyWSL-Logo.png
--------------------------------------------------------------------------------
/assets/vid/configureDistro.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/assets/vid/configureDistro.mp4
--------------------------------------------------------------------------------
/assets/vid/getDistro.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/assets/vid/getDistro.mp4
--------------------------------------------------------------------------------
/assets/vid/manageDistro.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/assets/vid/manageDistro.mp4
--------------------------------------------------------------------------------
/assets/vid/settingsPage.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/assets/vid/settingsPage.mp4
--------------------------------------------------------------------------------
/assets/vid/snapshots.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/assets/vid/snapshots.mp4
--------------------------------------------------------------------------------
/easyWSL.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.32112.339
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "easyWSL", "easyWSL\easyWSL.csproj", "{B6C34736-A384-44BB-A43B-2F54EC32E175}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "easyWslCmd", "easyWSLcmd\easyWslCmd.csproj", "{350F963D-AD6E-4883-8405-F99D9770A9BB}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "easyWslLib", "easyWslLib\easyWslLib.csproj", "{206BBF4A-1D8B-4CD4-9519-2633D5AB2562}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Debug|arm64 = Debug|arm64
16 | Debug|x64 = Debug|x64
17 | Debug|x86 = Debug|x86
18 | Release|Any CPU = Release|Any CPU
19 | Release|arm64 = Release|arm64
20 | Release|x64 = Release|x64
21 | Release|x86 = Release|x86
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|Any CPU.ActiveCfg = Debug|x64
25 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|Any CPU.Build.0 = Debug|x64
26 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|Any CPU.Deploy.0 = Debug|x64
27 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|arm64.ActiveCfg = Debug|arm64
28 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|arm64.Build.0 = Debug|arm64
29 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|arm64.Deploy.0 = Debug|arm64
30 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|x64.ActiveCfg = Debug|x64
31 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|x64.Build.0 = Debug|x64
32 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|x64.Deploy.0 = Debug|x64
33 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|x86.ActiveCfg = Debug|x86
34 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|x86.Build.0 = Debug|x86
35 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Debug|x86.Deploy.0 = Debug|x86
36 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|Any CPU.ActiveCfg = Release|x64
37 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|Any CPU.Build.0 = Release|x64
38 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|Any CPU.Deploy.0 = Release|x64
39 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|arm64.ActiveCfg = Release|arm64
40 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|arm64.Build.0 = Release|arm64
41 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|arm64.Deploy.0 = Release|arm64
42 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|x64.ActiveCfg = Release|x64
43 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|x64.Build.0 = Release|x64
44 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|x64.Deploy.0 = Release|x64
45 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|x86.ActiveCfg = Release|x86
46 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|x86.Build.0 = Release|x86
47 | {B6C34736-A384-44BB-A43B-2F54EC32E175}.Release|x86.Deploy.0 = Release|x86
48 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Debug|arm64.ActiveCfg = Debug|Any CPU
51 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Debug|arm64.Build.0 = Debug|Any CPU
52 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Debug|x64.ActiveCfg = Debug|Any CPU
53 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Debug|x64.Build.0 = Debug|Any CPU
54 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Debug|x86.ActiveCfg = Debug|Any CPU
55 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Debug|x86.Build.0 = Debug|Any CPU
56 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
57 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Release|Any CPU.Build.0 = Release|Any CPU
58 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Release|arm64.ActiveCfg = Release|Any CPU
59 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Release|arm64.Build.0 = Release|Any CPU
60 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Release|x64.ActiveCfg = Release|Any CPU
61 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Release|x64.Build.0 = Release|Any CPU
62 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Release|x86.ActiveCfg = Release|Any CPU
63 | {350F963D-AD6E-4883-8405-F99D9770A9BB}.Release|x86.Build.0 = Release|Any CPU
64 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
65 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Debug|Any CPU.Build.0 = Debug|Any CPU
66 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Debug|arm64.ActiveCfg = Debug|Any CPU
67 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Debug|arm64.Build.0 = Debug|Any CPU
68 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Debug|x64.ActiveCfg = Debug|Any CPU
69 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Debug|x64.Build.0 = Debug|Any CPU
70 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Debug|x86.ActiveCfg = Debug|Any CPU
71 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Debug|x86.Build.0 = Debug|Any CPU
72 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Release|Any CPU.ActiveCfg = Release|Any CPU
73 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Release|Any CPU.Build.0 = Release|Any CPU
74 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Release|arm64.ActiveCfg = Release|Any CPU
75 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Release|arm64.Build.0 = Release|Any CPU
76 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Release|x64.ActiveCfg = Release|Any CPU
77 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Release|x64.Build.0 = Release|Any CPU
78 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Release|x86.ActiveCfg = Release|Any CPU
79 | {206BBF4A-1D8B-4CD4-9519-2633D5AB2562}.Release|x86.Build.0 = Release|Any CPU
80 | EndGlobalSection
81 | GlobalSection(SolutionProperties) = preSolution
82 | HideSolutionNode = FALSE
83 | EndGlobalSection
84 | GlobalSection(ExtensibilityGlobals) = postSolution
85 | SolutionGuid = {831ABB30-ED12-4897-8B12-322E2CB11E55}
86 | EndGlobalSection
87 | EndGlobal
88 |
--------------------------------------------------------------------------------
/easyWSL/AboutDialog.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 | EasyWSL
12 |
13 | WSL made easy. Create Linux distributions, manage them and make snapshots on Windows 10/11.
14 | Made by
15 | Red Code Labs
16 |
17 |
18 |
19 | Resources used
20 |
21 | libarchive
22 |
23 |
24 |
25 | WSL Hello sudo
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/easyWSL/AboutDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml.Controls;
2 |
3 | namespace easyWSL
4 | {
5 | public sealed partial class AboutDialog : Page
6 | {
7 | public AboutDialog()
8 | {
9 | this.InitializeComponent();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/easyWSL/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Black
14 | Black
15 | White
16 | White
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/easyWSL/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using System;
3 | using PInvoke;
4 | using Windows.Web.Http;
5 | using System.Reflection;
6 |
7 | namespace easyWSL
8 | {
9 | public partial class App : Application
10 | {
11 | public App()
12 | {
13 | this.InitializeComponent();
14 | }
15 |
16 | IntPtr m_windowhandle;
17 | public IntPtr MainWindowWindowHandle { get { return m_windowhandle; } }
18 |
19 | protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
20 | {
21 | var isWSLInstalled = WslSdk.CheckIfWSLInstalled();
22 | if (!isWSLInstalled)
23 | {
24 | m_window = new WelcomeWindow();
25 | }
26 | else
27 | {
28 | m_window = new NavigationRoot_Window();
29 | }
30 |
31 | m_window.Activate();
32 | m_window.Title = "easyWSL";
33 | m_windowhandle = User32.GetActiveWindow();
34 | User32.ShowWindow(m_windowhandle, User32.WindowShowStyle.SW_MAXIMIZE);
35 | }
36 |
37 | private Window m_window;
38 |
39 | public static readonly HttpClient httpClient = new();
40 |
41 | public static string executableLocation = Assembly.GetExecutingAssembly().Location.Substring(0, Assembly.GetExecutingAssembly().Location.LastIndexOf(@"\"));
42 | public static Windows.Storage.StorageFolder tmpDirectory = Windows.Storage.ApplicationData.Current.TemporaryFolder;
43 |
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/easyWSL/Assets/BadgeLogo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/BadgeLogo.scale-100.png
--------------------------------------------------------------------------------
/easyWSL/Assets/BadgeLogo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/BadgeLogo.scale-125.png
--------------------------------------------------------------------------------
/easyWSL/Assets/BadgeLogo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/BadgeLogo.scale-150.png
--------------------------------------------------------------------------------
/easyWSL/Assets/BadgeLogo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/BadgeLogo.scale-200.png
--------------------------------------------------------------------------------
/easyWSL/Assets/BadgeLogo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/BadgeLogo.scale-400.png
--------------------------------------------------------------------------------
/easyWSL/Assets/LargeTile.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/LargeTile.scale-100.png
--------------------------------------------------------------------------------
/easyWSL/Assets/LargeTile.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/LargeTile.scale-125.png
--------------------------------------------------------------------------------
/easyWSL/Assets/LargeTile.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/LargeTile.scale-150.png
--------------------------------------------------------------------------------
/easyWSL/Assets/LargeTile.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/LargeTile.scale-200.png
--------------------------------------------------------------------------------
/easyWSL/Assets/LargeTile.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/LargeTile.scale-400.png
--------------------------------------------------------------------------------
/easyWSL/Assets/LockScreenLogo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/LockScreenLogo.scale-200.png
--------------------------------------------------------------------------------
/easyWSL/Assets/SmallTile.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/SmallTile.scale-100.png
--------------------------------------------------------------------------------
/easyWSL/Assets/SmallTile.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/SmallTile.scale-125.png
--------------------------------------------------------------------------------
/easyWSL/Assets/SmallTile.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/SmallTile.scale-150.png
--------------------------------------------------------------------------------
/easyWSL/Assets/SmallTile.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/SmallTile.scale-200.png
--------------------------------------------------------------------------------
/easyWSL/Assets/SmallTile.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/SmallTile.scale-400.png
--------------------------------------------------------------------------------
/easyWSL/Assets/SplashScreen.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/SplashScreen.scale-100.png
--------------------------------------------------------------------------------
/easyWSL/Assets/SplashScreen.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/SplashScreen.scale-125.png
--------------------------------------------------------------------------------
/easyWSL/Assets/SplashScreen.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/SplashScreen.scale-150.png
--------------------------------------------------------------------------------
/easyWSL/Assets/SplashScreen.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/SplashScreen.scale-200.png
--------------------------------------------------------------------------------
/easyWSL/Assets/SplashScreen.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/SplashScreen.scale-400.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square150x150Logo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square150x150Logo.scale-100.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square150x150Logo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square150x150Logo.scale-125.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square150x150Logo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square150x150Logo.scale-150.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square150x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square150x150Logo.scale-200.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square150x150Logo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square150x150Logo.scale-400.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.altform-unplated_targetsize-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.altform-unplated_targetsize-16.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.altform-unplated_targetsize-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.altform-unplated_targetsize-256.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.altform-unplated_targetsize-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.altform-unplated_targetsize-32.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.altform-unplated_targetsize-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.altform-unplated_targetsize-48.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.scale-100.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.scale-125.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.scale-150.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.scale-200.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.scale-400.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.targetsize-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.targetsize-16.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.targetsize-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.targetsize-24.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.targetsize-24_altform-unplated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.targetsize-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.targetsize-256.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.targetsize-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.targetsize-32.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Square44x44Logo.targetsize-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Square44x44Logo.targetsize-48.png
--------------------------------------------------------------------------------
/easyWSL/Assets/StoreLogo.backup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/StoreLogo.backup.png
--------------------------------------------------------------------------------
/easyWSL/Assets/StoreLogo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/StoreLogo.scale-100.png
--------------------------------------------------------------------------------
/easyWSL/Assets/StoreLogo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/StoreLogo.scale-125.png
--------------------------------------------------------------------------------
/easyWSL/Assets/StoreLogo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/StoreLogo.scale-150.png
--------------------------------------------------------------------------------
/easyWSL/Assets/StoreLogo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/StoreLogo.scale-200.png
--------------------------------------------------------------------------------
/easyWSL/Assets/StoreLogo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/StoreLogo.scale-400.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Wide310x150Logo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Wide310x150Logo.scale-100.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Wide310x150Logo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Wide310x150Logo.scale-125.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Wide310x150Logo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Wide310x150Logo.scale-150.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Wide310x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Wide310x150Logo.scale-200.png
--------------------------------------------------------------------------------
/easyWSL/Assets/Wide310x150Logo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/Assets/Wide310x150Logo.scale-400.png
--------------------------------------------------------------------------------
/easyWSL/ManageDistrosPage.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | All your data stored on this distribution will be gone.
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/easyWSL/ManageDistrosPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using System;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Threading.Tasks;
7 | using easyWslLib;
8 |
9 | namespace easyWSL
10 | {
11 | public sealed partial class ManageDistrosPage : Page
12 | {
13 | public class EditedDistro
14 | {
15 | public string name { get; set; }
16 | public string version { get; set; }
17 | public string path { get; set; }
18 | }
19 |
20 | private Windows.Storage.StorageFolder storageDirectory = Windows.Storage.ApplicationData.Current.LocalFolder;
21 |
22 |
23 | public ManageDistrosPage()
24 | {
25 | InitializeComponent();
26 | RefreshInstalledDistros();
27 | }
28 |
29 | private WslSdk wslSdk = new();
30 | private Helpers helpers = new();
31 | private ManageSnapshotsPage manageSnapshotsPage = new();
32 | public string selectedDistroName;
33 |
34 | private async Task RefreshInstalledDistros()
35 | {
36 | await WslSdk.GetInstalledDistributions();
37 | distrosListView.ItemsSource = null;
38 | distrosListView.Items.Clear();
39 | distrosListView.ItemsSource = WslSdk.InstalledDistros.Values;
40 | if(distrosListView.Items.Count != 0)
41 | {
42 | distrosListView.SelectedIndex = 0;
43 | }
44 |
45 | }
46 | private void distrosListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
47 | {
48 | if (e.AddedItems.Count == 1)
49 | {
50 | selectedDistroName = distrosListView.SelectedValue.ToString();
51 | }
52 | }
53 | private async void removeDistroButton_Click(object sender, RoutedEventArgs e)
54 | {
55 | confirmDistroRemovalDialog.Title = $"Are you sure you want to remove {selectedDistroName}?";
56 | confirmDistroRemovalDialog.XamlRoot = removeDistroButton.XamlRoot;
57 | ContentDialogResult dialogResult = await confirmDistroRemovalDialog.ShowAsync();
58 | if (dialogResult == ContentDialogResult.Primary)
59 | {
60 | await helpers.ExecuteProcessAsynch("wsl.exe", $"--unregister {selectedDistroName}");
61 | string distroStoragePath = Path.Combine(storageDirectory.Path, selectedDistroName);
62 | if(Directory.Exists(distroStoragePath))
63 | {
64 | Directory.Delete(distroStoragePath, true);
65 | }
66 | await RefreshInstalledDistros();
67 | }
68 | }
69 |
70 | private async void moreInfoButton_Click(object sender, RoutedEventArgs e)
71 | {
72 | var name = selectedDistroName;
73 | var version = WslSdk.InstalledDistros[name].version;
74 | var path = WslSdk.InstalledDistros[name].path;
75 |
76 | var dialogContent = new MoreInfoDialog(name, version, path);
77 |
78 | ContentDialog getMoreInfoDialog = new ContentDialog();
79 | getMoreInfoDialog.XamlRoot = moreInfoButton.XamlRoot;
80 | getMoreInfoDialog.Title = "More info";
81 | getMoreInfoDialog.CloseButtonText = "Close";
82 | getMoreInfoDialog.Content = dialogContent;
83 |
84 |
85 | var result = await getMoreInfoDialog.ShowAsync();
86 |
87 | }
88 |
89 | private async void setDefaultDistroButton_Click(object sender, RoutedEventArgs e)
90 | {
91 | await helpers.ExecuteProcessAsynch("wsl.exe", $"-s {selectedDistroName}");
92 | }
93 | private void openFilesystemButton_Click(object sender, RoutedEventArgs e)
94 | {
95 | var path = Path.Combine(@"\\wsl$", selectedDistroName);
96 | Process.Start("explorer.exe", path);
97 | }
98 |
99 | private async void startDistroButton_Click(object sender, RoutedEventArgs e)
100 | {
101 | helpers.StartWSLDistroAsync(selectedDistroName);
102 | }
103 |
104 | private async void stopDistroButton_Click(object sender, RoutedEventArgs e)
105 | {
106 | await helpers.ExecuteProcessAsynch("wsl.exe", $"-t {selectedDistroName}");
107 | }
108 |
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/easyWSL/ManageSnapshotsPage.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/easyWSL/ManageSnapshotsPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Threading.Tasks;
9 | using easyWslLib;
10 |
11 |
12 | namespace easyWSL
13 | {
14 | public sealed partial class ManageSnapshotsPage : Page
15 | {
16 | private WslSdk wslSdk = new();
17 | private Windows.Storage.StorageFolder storageDirectory = Windows.Storage.ApplicationData.Current.LocalFolder;
18 |
19 | private Helpers helpers = new();
20 |
21 | private List snapshotsList = new();
22 |
23 | public string nameToRegister { get; set; }
24 |
25 | public ManageSnapshotsPage()
26 | {
27 | InitializeComponent();
28 | RefreshDistros();
29 | }
30 |
31 | public async Task RefreshDistros()
32 | {
33 | await WslSdk.GetInstalledDistributions();
34 | var distrosList = WslSdk.InstalledDistros.Values.Select(o => o.name);
35 | distrosComboBox.ItemsSource = distrosList;
36 |
37 | FillSnapshotsListView();
38 | }
39 |
40 | private void distrosComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
41 | {
42 | FillSnapshotsListView();
43 | }
44 |
45 | private async void addSnapshotButton_Click(object sender, RoutedEventArgs e)
46 | {
47 | var snapshotsStoragePath = Path.Combine(storageDirectory.Path, "snapshots");
48 | var distroName = distrosComboBox.SelectedItem as string;
49 | ShowProgressBar($"Creating snapshot of {distroName}");
50 |
51 | Guid fileUUID = Guid.NewGuid();
52 | var snapshotName = fileUUID.ToString();
53 |
54 | var snapshotPath = Path.Combine(snapshotsStoragePath, distroName, $"{snapshotName}.tar.gz");
55 | await helpers.ExecuteProcessAsynch("wsl.exe", $"--export {distroName} {snapshotPath}");
56 |
57 | HideProgressBar();
58 |
59 | ContentDialog succedDialog = new ContentDialog();
60 | succedDialog.Title = "Succesfuly created snapshot";
61 | succedDialog.CloseButtonText = "Cancel";
62 | succedDialog.DefaultButton = ContentDialogButton.Close;
63 | FillSnapshotsListView();
64 | }
65 |
66 | private void removeSnapshotButton_Click(object sender, RoutedEventArgs e)
67 | {
68 | int index = snapshotsListView.SelectedIndex;
69 | var snapshotPath = snapshotsList[index];
70 |
71 | if (File.Exists(snapshotPath))
72 | {
73 | File.Delete(snapshotPath);
74 | }
75 | string distro = distrosComboBox.SelectedValue as string;
76 | string path = Path.Combine(storageDirectory.Path, "snapshots", distro);
77 | if (Directory.Exists(path))
78 | {
79 | int filesInDir = Directory.EnumerateFiles(path).Count();
80 | if (filesInDir == 0)
81 | {
82 | Directory.Delete(path, true);
83 | }
84 |
85 | }
86 | FillSnapshotsListView();
87 | }
88 |
89 | private async void FillSnapshotsListView()
90 | {
91 | await WslSdk.GetInstalledDistributions();
92 | var distrosList = WslSdk.InstalledDistros.Values.Select(o => o.name);
93 |
94 | var snapshotsStoragePath = Path.Combine(storageDirectory.Path, "snapshots");
95 | try
96 | {
97 | Directory.CreateDirectory(snapshotsStoragePath);
98 | }
99 | catch (Exception e)
100 | {
101 | await showErrorModal();
102 | }
103 | foreach (string name in distrosList)
104 | {
105 | Directory.CreateDirectory(Path.Combine(snapshotsStoragePath, name));
106 | }
107 |
108 | var distro = distrosComboBox.SelectedValue as string;
109 | if (distro == null)
110 | {
111 | return;
112 | }
113 |
114 | var snapshotsPath = Path.Combine(storageDirectory.Path, "snapshots", distro);
115 | snapshotsList = Directory.EnumerateFiles(snapshotsPath, "*.tar.gz").ToList();
116 |
117 | List listviewLabels = new();
118 |
119 | foreach (var snapshot in snapshotsList)
120 | {
121 | listviewLabels.Add(File.GetCreationTime(snapshot).ToString());
122 | }
123 |
124 | snapshotsListView.ItemsSource = null;
125 | snapshotsListView.Items.Clear();
126 | snapshotsListView.ItemsSource = listviewLabels;
127 | snapshotsListView.SelectedIndex = 0;
128 |
129 | if (snapshotsList.Count == 0)
130 | {
131 | snapshotsTitle.Text = $"There are no snapshots of {distro} distribution";
132 | }
133 | else
134 | {
135 | snapshotsTitle.Text = $"Snapshots of {distro} distribution";
136 | }
137 | }
138 |
139 | private void ShowProgressBar(string text)
140 | {
141 | snapshottingStatusTextBlock.Text = text;
142 | snapshottingStatusTextBlock.Visibility = Visibility.Visible;
143 | snapshottingProgressBar.Visibility = Visibility.Visible;
144 | }
145 |
146 | private void HideProgressBar()
147 | {
148 | snapshottingProgressBar.Visibility = Visibility.Collapsed;
149 | snapshottingStatusTextBlock.Visibility = Visibility.Collapsed;
150 | }
151 |
152 | private async void registerDistroFromSnapshotButton_Click(object sender, RoutedEventArgs e)
153 | {
154 | TextBox distroNameTextBox = new TextBox();
155 | distroNameTextBox.Header = "Name";
156 |
157 | ContentDialog dialog = new ContentDialog();
158 | dialog.XamlRoot = registerDistroFromSnapshotButton.XamlRoot;
159 | dialog.Title = "Register from snapshot";
160 | dialog.PrimaryButtonText = "Register";
161 | dialog.CloseButtonText = "Cancel";
162 | dialog.DefaultButton = ContentDialogButton.Primary;
163 | dialog.Content = distroNameTextBox;
164 |
165 |
166 |
167 | var result = await dialog.ShowAsync();
168 | if (result == ContentDialogResult.Primary)
169 | {
170 | string name = distroNameTextBox.Text;
171 |
172 | if (name == "" || name.Contains(" "))
173 | {
174 | await showErrorModal();
175 | return;
176 | }
177 | else
178 | {
179 | int index = snapshotsListView.SelectedIndex;
180 | var snapshotPath = snapshotsList[index];
181 | await RegisterDistro(name, snapshotPath);
182 | }
183 | }
184 | }
185 |
186 | private async Task RegisterDistro(string name, string path)
187 | {
188 | ShowProgressBar($"Registering the {name} distribution");
189 | var distroStoragePath = Path.Combine(storageDirectory.Path, name);
190 | Directory.CreateDirectory(distroStoragePath);
191 | await helpers.ExecuteProcessAsynch("wsl.exe", $"--import {name} {distroStoragePath} {path}");
192 |
193 | HideProgressBar();
194 |
195 | ContentDialog registerDistroDialog = new ContentDialog();
196 | registerDistroDialog.XamlRoot = registerDistroFromSnapshotButton.XamlRoot;
197 | registerDistroDialog.Title = $"{name} has been registered";
198 | registerDistroDialog.SecondaryButtonText = "Close";
199 | registerDistroDialog.PrimaryButtonText = "Run distribution";
200 | registerDistroDialog.DefaultButton = ContentDialogButton.Primary;
201 |
202 | var result = await registerDistroDialog.ShowAsync();
203 |
204 | if (result == ContentDialogResult.Primary)
205 | {
206 | helpers.StartWSLDistroAsync(name);
207 | }
208 | }
209 |
210 | private void openSnapshotsButton_Click(object sender, RoutedEventArgs e)
211 | {
212 | string distro = distrosComboBox.SelectedValue as string;
213 | string path = Path.Combine(storageDirectory.Path, "snapshots", distro);
214 | Process.Start("explorer.exe", path);
215 | }
216 | private async Task showErrorModal()
217 | {
218 | ContentDialog errorDialog = new ContentDialog();
219 | errorDialog.XamlRoot = registerDistroFromSnapshotButton.XamlRoot;
220 | errorDialog.Title = "Error";
221 | errorDialog.CloseButtonText = "Cancel";
222 | errorDialog.DefaultButton = ContentDialogButton.Close;
223 | errorDialog.Content = "There were problems with registering your distribution.";
224 | await errorDialog.ShowAsync();
225 | }
226 | }
227 | }
228 |
229 |
--------------------------------------------------------------------------------
/easyWSL/MoreInfoDialog.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 | Version
14 |
15 |
16 |
17 |
18 |
19 | Open VHD location
20 |
21 |
22 |
--------------------------------------------------------------------------------
/easyWSL/MoreInfoDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using Microsoft.UI.Xaml.Controls.Primitives;
4 | using Microsoft.UI.Xaml.Data;
5 | using Microsoft.UI.Xaml.Input;
6 | using Microsoft.UI.Xaml.Media;
7 | using Microsoft.UI.Xaml.Navigation;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Diagnostics;
11 | using System.IO;
12 | using System.Linq;
13 | using System.Runtime.InteropServices.WindowsRuntime;
14 | using Windows.Foundation;
15 | using Windows.Foundation.Collections;
16 |
17 |
18 | namespace easyWSL
19 | {
20 |
21 | public sealed partial class MoreInfoDialog : Page
22 | {
23 | private ManageDistrosPage manageDistrosPage = new();
24 |
25 | public string name = "";
26 | public string version = "";
27 | public string path = "";
28 |
29 | public MoreInfoDialog(string name, string version, string path)
30 | {
31 | this.InitializeComponent();
32 |
33 | nameTextBox.Text = name;
34 | versionTextBlock.Text = version;
35 | pathTextBox.Text = path;
36 | }
37 |
38 | private void openVHDLocationButton_Click(object sender, RoutedEventArgs e)
39 | {
40 | Process.Start("explorer.exe", pathTextBox.Text);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/easyWSL/NavigationRoot_Window.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | EasyWSL
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/easyWSL/NavigationRoot_Window.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Microsoft.UI.Xaml;
3 | using Microsoft.UI.Xaml.Controls;
4 |
5 | namespace easyWSL
6 | {
7 | public sealed partial class NavigationRoot_Window : Window
8 | {
9 | public NavigationRoot_Window()
10 | {
11 | this.InitializeComponent();
12 | ExtendsContentIntoTitleBar = true;
13 | SetTitleBar(TitleBar);
14 | }
15 | private void mainNavigation_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
16 | {
17 | if (args.IsSettingsInvoked)
18 | {
19 | rootFrame.Navigate(typeof(SettingsPage));
20 | }
21 | else
22 | {
23 | var item = sender.MenuItems.OfType().First(x => (string)x.Content == (string)args.InvokedItem);
24 | NavView_Navigate(item as NavigationViewItem);
25 | }
26 | }
27 |
28 | private void NavView_Navigate(NavigationViewItem item)
29 | {
30 | switch (item.Name)
31 | {
32 | case "manageDistributionsButton":
33 | rootFrame.Navigate(typeof(ManageDistrosPage));
34 | break;
35 |
36 | case "addNewDistributionButton":
37 | rootFrame.Navigate(typeof(RegisterNewDistro_Page));
38 | break;
39 |
40 | case "manageSnapshots":
41 | rootFrame.Navigate(typeof(ManageSnapshotsPage));
42 | break;
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/easyWSL/Package.appxmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
15 |
16 |
17 | easyWSL
18 | Red Code Labs
19 | Assets\StoreLogo.png
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
35 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/easyWSL/PlatformHelpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net.Http.Headers;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using easyWslLib;
9 | using Windows.Storage.Streams;
10 | using Windows.Storage;
11 | using Windows.Web.Http;
12 |
13 | namespace easyWSL
14 | {
15 | internal class PlatformHelpers: IPlatformHelpers
16 | {
17 | private readonly Action _httpProgressCallback;
18 | public PlatformHelpers(string tarCommand, Action httpProgressCallback)
19 | {
20 | TarCommand = tarCommand;
21 | this._httpProgressCallback = httpProgressCallback;
22 | }
23 | public async Task CopyFileAsync(string sourcePath, string destinationPath)
24 | {
25 | string destinationFolderPath = sourcePath.Substring(0, sourcePath.LastIndexOf(@"\"));
26 | string destinationFileName = Path.GetFileName(destinationPath);
27 |
28 | StorageFile sourceFile = await StorageFile.GetFileFromPathAsync(sourcePath);
29 | StorageFolder destinationFolder = await StorageFolder.GetFolderFromPathAsync(destinationFolderPath);
30 | StorageFile destinationFile = await destinationFolder.CreateFileAsync(destinationFileName);
31 |
32 | IInputStream inputStream = await sourceFile.OpenAsync(FileAccessMode.Read);
33 | IOutputStream outputStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite);
34 |
35 | await RandomAccessStream.CopyAndCloseAsync(inputStream, outputStream);
36 | inputStream.Dispose();
37 | outputStream.Dispose();
38 | }
39 |
40 | public async Task DownloadFileAsync(Uri uri, IEnumerable> headers, FileInfo destinationPath)
41 | {
42 | HttpRequestHeaders _headers;
43 |
44 | var httpRequestMessage = new HttpRequestMessage
45 | {
46 | Method = HttpMethod.Get,
47 | RequestUri = uri,
48 | };
49 | foreach (var header in headers)
50 | {
51 | httpRequestMessage.Headers.Add(header.Key, header.Value);
52 | }
53 | Progress progressCallback = new Progress(_httpProgressCallback);
54 | HttpResponseMessage response = await App.httpClient.SendRequestAsync(httpRequestMessage).AsTask(progressCallback);
55 |
56 | StorageFolder downloadFolder = await StorageFolder.GetFolderFromPathAsync(destinationPath.DirectoryName);
57 | StorageFile downloadFile = await downloadFolder.CreateFileAsync(destinationPath.Name);
58 |
59 | IInputStream inputStream = await response.Content.ReadAsInputStreamAsync();
60 | IOutputStream outputStream = await downloadFile.OpenAsync(FileAccessMode.ReadWrite);
61 | await RandomAccessStream.CopyAndCloseAsync(inputStream, outputStream);
62 |
63 |
64 | inputStream.Dispose();
65 | outputStream.Dispose();
66 | }
67 |
68 | public string TarCommand { get; }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/easyWSL/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "easyWSL (Package)": {
4 | "commandName": "MsixPackage"
5 | },
6 | "easyWSL (Unpackaged)": {
7 | "commandName": "Project"
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/easyWSL/RegisterNewDistro_Page.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Distribution details
26 |
27 |
28 | Supported distro list
29 | Docker Hub
30 | Local hard drive
31 |
32 |
33 | Browse
34 |
35 |
36 |
37 | Supported image formats
38 | image:tag
39 | profile/image
40 | profile/image:tag
41 |
42 |
43 | Select a linux rootfs in *.tar or *.tar.bz format.
44 |
45 |
46 |
47 |
48 | Configure user account
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Set up development environment
64 | (Experimental)
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/easyWSL/RegisterNewDistro_Page.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using Microsoft.UI.Xaml;
5 | using Microsoft.UI.Xaml.Controls;
6 | using System.Runtime.InteropServices;
7 | using Windows.Storage.Pickers;
8 | using WinRT;
9 | using System.Diagnostics;
10 | using System.Threading.Tasks;
11 | using easyWslLib;
12 |
13 | namespace easyWSL
14 | {
15 | public sealed partial class RegisterNewDistro_Page : Page
16 | {
17 |
18 | private string distroTarballPath;
19 | private string distroSource;
20 | private Helpers helpers = new();
21 | private Windows.Storage.StorageFolder storageDirectory = Windows.Storage.ApplicationData.Current.LocalFolder;
22 |
23 | private Dictionary distrosSources = new()
24 | {
25 | { "Ubuntu 20.04", "ubuntu:20.04"},
26 | { "Ubuntu 21.10", "ubuntu:21.10" },
27 | { "Debian Stable", "debian:stable" },
28 | { "Debian Testing", "debian:testing" },
29 | { "Debian Unstable", "debian:unstable"},
30 | { "Fedora 34", "fedora:34" },
31 | { "Fedora 35", "fedora:35" },
32 | { "Arch Linux", "archlinux:base-devel" },
33 | { "CentOS 7", "centos:7" },
34 | { "Kali Linux", "kalilinux/kali-rolling" },
35 | { "Parrot Security", "parrotsec/security" },
36 | { "Alpine Linux", "alpine:latest" }
37 | };
38 |
39 |
40 | public RegisterNewDistro_Page()
41 | {
42 | this.InitializeComponent();
43 | SupportedDistroListbox.ItemsSource = distrosSources.Keys;
44 | SupportedDistroListbox.SelectedIndex = 0;
45 |
46 | }
47 | private async void ChooseDistroTarball(object sender, RoutedEventArgs e)
48 | {
49 | var filePicker = new FileOpenPicker();
50 |
51 | IntPtr hwnd = (App.Current as App).MainWindowWindowHandle;
52 |
53 | var initializeWithWindow = filePicker.As();
54 | initializeWithWindow.Initialize(hwnd);
55 |
56 | filePicker.FileTypeFilter.Add(".tar");
57 | filePicker.FileTypeFilter.Add(".tar.bz");
58 |
59 | var folder = await filePicker.PickSingleFileAsync();
60 | distroTarballPath = folder != null ? folder.Path : string.Empty;
61 | }
62 |
63 | [ComImport]
64 | [Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")]
65 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
66 | private interface IInitializeWithWindow
67 | {
68 | void Initialize(IntPtr hwnd);
69 | }
70 | [ComImport]
71 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
72 | [Guid("EECDBF0E-BAE9-4CB6-A68E-9598E1CB57BB")]
73 | internal interface IWindowNative
74 | {
75 | IntPtr WindowHandle { get; }
76 | }
77 | private async void registerDistroProceedButton_Click(object sender, RoutedEventArgs e)
78 | {
79 | DockerDownloader dockerDownloader = new(App.tmpDirectory.Path,
80 | new PlatformHelpers(Path.Combine(App.executableLocation, "dep", "bsdtar.exe"), HttpProgressCallback));
81 |
82 |
83 | string image;
84 | var distroName = distroNameTextBox.Text;
85 | string userName = "";
86 | string password = "";
87 |
88 |
89 | if (distroName == "" || distroName.Contains(" "))
90 | {
91 | await showErrorModal();
92 | return;
93 | }
94 | if (newUserSwitch.IsOn == true && distroSource == "Supported distro list")
95 | {
96 | userName = userNameTextBox.Text;
97 | if (userName == "" || userName.Contains(" "))
98 | {
99 | await showErrorModal();
100 | return;
101 | }
102 | string passwd1 = password1TextBox.Password.ToString();
103 | string passwd2 = password2TextBox.Password.ToString();
104 | if (passwd1 != passwd2)
105 | {
106 | ContentDialog wrongPasswordDialog = new ContentDialog();
107 | wrongPasswordDialog.XamlRoot = registerDistroProceedButton.XamlRoot;
108 | wrongPasswordDialog.Title = "Passwords are not identical";
109 | wrongPasswordDialog.CloseButtonText = "Cancel";
110 | wrongPasswordDialog.DefaultButton = ContentDialogButton.Close;
111 | var result = await wrongPasswordDialog.ShowAsync();
112 | return;
113 | }
114 | else
115 | {
116 | password = passwd1;
117 | }
118 | }
119 |
120 |
121 | switch (distroSource)
122 | {
123 | case "Supported distro list":
124 | image = distrosSources[SupportedDistroListbox.SelectedItem as string];
125 | registeringStatusTextBlock.Text = $"Downloading the {distroName} distribution";
126 | registerDistroProgressBar.IsIndeterminate = false;
127 | registeringStatusTextBlock.Visibility = Visibility.Visible;
128 | registerDistroProgressBar.Visibility = Visibility.Visible;
129 |
130 | try
131 | {
132 | await dockerDownloader.DownloadImage(image);
133 | }
134 | catch (DockerDownloader.DockerException)
135 | {
136 | registeringStatusTextBlock.Visibility = Visibility.Collapsed;
137 | registerDistroProgressBar.Visibility = Visibility.Collapsed;
138 | await showErrorModal();
139 | return;
140 | }
141 |
142 | registerDistroProgressBar.Visibility = Visibility.Collapsed;
143 | registeringStatusTextBlock.Visibility = Visibility.Collapsed;
144 |
145 | await dockerDownloader.CombineLayers();
146 | await RegisterDistro(distroName, Path.Combine(App.tmpDirectory.Path, "install.tar.bz"));
147 | break;
148 | case "Docker Hub":
149 | image = dockerImageTextBox.Text;
150 | registeringStatusTextBlock.Text = $"Downloading the {distroName} distribution";
151 | registerDistroProgressBar.IsIndeterminate = false;
152 | registeringStatusTextBlock.Visibility = Visibility.Visible;
153 | registerDistroProgressBar.Visibility = Visibility.Visible;
154 |
155 | try
156 | {
157 | await dockerDownloader.DownloadImage(image);
158 | }
159 | catch (DockerDownloader.DockerException)
160 | {
161 | registeringStatusTextBlock.Visibility = Visibility.Collapsed;
162 | registerDistroProgressBar.Visibility = Visibility.Collapsed;
163 | await showErrorModal();
164 | return;
165 | }
166 |
167 | registerDistroProgressBar.Visibility = Visibility.Collapsed;
168 | registeringStatusTextBlock.Visibility = Visibility.Collapsed;
169 |
170 | await dockerDownloader.CombineLayers();
171 | await RegisterDistro(distroName, Path.Combine(App.tmpDirectory.Path, "install.tar.bz"));
172 | break;
173 | case "Local hard drive":
174 | if(File.Exists(distroTarballPath))
175 | {
176 | await RegisterDistro(distroName, distroTarballPath);
177 |
178 | }
179 | else
180 | {
181 | await showErrorModal();
182 | return;
183 | }
184 |
185 | break;
186 | }
187 |
188 | string postInstallCommand = String.Join(
189 | "pwconv; ",
190 | "grpconv; ",
191 | "chmod 0744 /etc/shadow; ",
192 | "chmod 0744 /etc/gshadow; ",
193 | "chown -R root:root /bin/su; ",
194 | "chmod 755 /bin/su; ",
195 | "chmod u+s /bin/su; ",
196 | "touch /etc/fstab; "
197 | );
198 | await helpers.ExecuteCommandInWSLAsync(distroName, postInstallCommand);
199 |
200 | if(distroSource == "Supported distro list")
201 | {
202 | ShowProgressBar($"Configuring the {distroName} distrbution");
203 | string configureCommand = String.Join(
204 | // apt
205 | "if command -v apt; then apt-get -y update; apt-get -y install sudo bash curl; fi; ",
206 | // pacman
207 | "if command -v pacman; then pacman -Syu --noconfirm; pacman -S --noconfirm sudo bash curl; fi; ",
208 | // apk
209 | "if command -v apk; then apk update; apk add sudo shadow bash curl; fi; ",
210 | // dnf
211 | "if command -v dnf; then dnf -y update; dnf -y install sudo bash passwd curl; fi; "
212 | );
213 | await helpers.ExecuteCommandInWSLAsync(distroName, configureCommand);
214 |
215 | if (newUserSwitch.IsOn == true)
216 | {
217 |
218 |
219 | await helpers.ExecuteCommandInWSLAsync(distroName, $"useradd -m {userName} -s $(type -p bash)");
220 | await helpers.ExecuteCommandInWSLAsync(distroName, $"echo '{userName}:{password}' | chpasswd");
221 |
222 | if (isAdminSwitch.IsOn == true)
223 | {
224 | await helpers.ExecuteCommandInWSLAsync(distroName, $"echo '{userName} ALL=(ALL:ALL) ALL' >> /etc/sudoers; ");
225 | }
226 |
227 | await helpers.ExecuteCommandInWSLAsync(distroName, $"echo '[user]' >> /etc/wsl.conf");
228 | await helpers.ExecuteCommandInWSLAsync(distroName, $"echo 'default={userName}' >> /etc/wsl.conf");
229 |
230 | if (winHelloSwitch.IsOn == true)
231 | {
232 | string pamPath = Path.Combine(App.executableLocation, @"dep\pam_wsl_hello.so");
233 | string authPath = Path.Combine(App.executableLocation, @"dep\WindowsHelloBridge.exe");
234 | string command = String.Concat(
235 |
236 | "if [ -d \"/lib/x86_64-linux-gnu/security/\" ]; then ",
237 | $"cp $(wslpath \"{pamPath}\") /lib/x86_64-linux-gnu/security/; ",
238 | "chown root:root /lib/x86_64-linux-gnu/security/pam_wsl_hello.so; ",
239 | "chmod 644 /lib/x86_64-linux-gnu/security/pam_wsl_hello.so; ",
240 | "chmod +x /lib/x86_64-linux-gnu/security/pam_wsl_hello.so; ",
241 | "fi; ",
242 |
243 | "if [ -d \"/lib64/security/\" ]; then ",
244 | $"cp -f $(wslpath \"{pamPath}\") /lib64/security/; ",
245 | "chown root:root /lib64/security/pam_wsl_hello.so; ",
246 | "chmod 644 /lib64/security/pam_wsl_hello.so; ",
247 | "chmod +x /lib64/security/pam_wsl_hello.so; ",
248 | "fi; ",
249 |
250 | "if [ -d \"/usr/lib/security\" ]; then ",
251 | $"cp $(wslpath \"{pamPath}\") /lib/x86_64-linux-gnu/security/; ",
252 | "chown root:root /lib/x86_64-linux-gnu/security/pam_wsl_hello.so; ",
253 | "chmod 644 /lib/x86_64-linux-gnu/security/pam_wsl_hello.so; ",
254 | "chmod +x /lib/x86_64-linux-gnu/security/pam_wsl_hello.so; ",
255 | "fi; ",
256 |
257 | "if [ -d \"/lib/security\" ]; then ",
258 | $"cp $(wslpath \"{pamPath}\") /lib/security/; ",
259 | "chown root:root /lib/security/pam_wsl_hello.so; ",
260 | "chmod 644 /lib/security/pam_wsl_hello.so; ",
261 | "chmod +x /lib/security/pam_wsl_hello.so; ",
262 | "fi; ",
263 | "sed -i \"1a\\\\auth sufficient pam_wsl_hello.so\" /etc/pam.d/sudo; ",
264 | "mkdir -p /etc/pam_wsl_hello/; ",
265 | $"echo \"authenticator_path = \\\"$(wslpath \"{authPath}\")\\\"\" >> /etc/pam_wsl_hello/config; ",
266 | "echo \"win_mnt = \\\"/mnt/c\\\"\" >> /etc/pam_wsl_hello/config; ",
267 | $"su - {userName} -c '$(wslpath \"{authPath}\") creator \"pam_wsl_hello_{userName}\"'; ",
268 | "mkdir -p /etc/pam_wsl_hello/public_keys; ",
269 | $"cp /home/{userName}/pam_wsl_hello_{userName}.pem /etc/pam_wsl_hello/public_keys/; ",
270 | $"rm -f /home/{userName}/pam_wsl_hello_{userName}.pem; "
271 | );
272 | Trace.WriteLine($"cmd: {command}");
273 | await helpers.ExecuteCommandInWSLAsync(distroName, command);
274 | }
275 | }
276 |
277 | if (pythonCheckbox.IsChecked == true)
278 | {
279 | string cmd = String.Concat(
280 | // apt
281 | "if command -v apt; then apt -y install python3 python3-pip python3-dev; fi; ",
282 | // pacman
283 | "if command -v pacman; then pacman -S --noconfirm python; fi; ",
284 | // apk
285 | "if command -v apk; then apk add python3 py3-pip; fi; ",
286 | // dnf
287 | "if command -v dnf; then dnf -y install python; fi; "
288 | );
289 | await helpers.ExecuteCommandInWSLAsync(distroName, cmd);
290 | }
291 |
292 | if (nodeCheckbox.IsChecked == true)
293 | {
294 | string cmd = String.Concat(
295 | // apt
296 | "if command -v apt; then curl -fsSL https://deb.nodesource.com/setup_17.x | bash -; apt -y install nodejs; fi; ",
297 | // pacman
298 | "if command -v pacman; then pacman -S --noconfirm nodejs npm; fi; ",
299 | // apk
300 | "if command -v apk; then apk add nodejs npm; fi; ",
301 | // dnf
302 | "if command -v dnf; then dnf -y install nodejs npm; fi; "
303 | );
304 | await helpers.ExecuteCommandInWSLAsync(distroName, cmd);
305 | }
306 |
307 | if (goCheckbox.IsChecked == true)
308 | {
309 | string cmd = String.Concat(
310 | // apt
311 | "if command -v apt; then apt -y install golang-go; mkdir -p $HOME/go; echo 'export GOPATH=$HOME/go' >> $HOME/.bashrc; source $HOME/.bashrc; fi; ",
312 | // pacman
313 | "if command -v pacman; then pacman -Syu --noconfirm; pacman -S --noconfirm go; mkdir -p $HOME/go; echo 'export GOPATH=$HOME/go' >> .bashrc; source .bashrc; fi; ",
314 | // apk
315 | "if command -v apk; then apk update; apk add go; fi; ",
316 | // dnf
317 | "if command -v dnf; then dnf -y update; dnf -y install golang; fi; "
318 | );
319 | await helpers.ExecuteCommandInWSLAsync(distroName, cmd);
320 | }
321 |
322 | if (cppCheckbox.IsChecked == true)
323 | {
324 | string cmd = String.Concat(
325 | // apt
326 | "if command -v apt; then apt -y install gcc clang g++ ; fi; ",
327 | // pacman
328 | "if command -v pacman; then pacman -S --noconfirm gcc clang; fi; ",
329 | // apk
330 | "if command -v apk; then apk add build-base; fi; ",
331 | // dnf
332 | "if command -v dnf; then dnf -y install gcc clang gcc-c++; fi; "
333 | );
334 | await helpers.ExecuteCommandInWSLAsync(distroName, cmd);
335 | }
336 |
337 | if (haskellCheckbox.IsChecked == true)
338 | {
339 | string cmd = String.Concat(
340 | // apt
341 | "if command -v apt; then apt -y install haskell-platform; fi; ",
342 | // pacman
343 | "if command -v pacman; then pacman -S --noconfirm ghc stack cabal-install; fi; ",
344 | // apk
345 | //"if command -v apk; then apk update; apk add sudo shadow bash; fi; ",
346 | // dnf
347 | "if command -v dnf; then dnf -y install stack ghc haskell-platform cabal-install; fi; "
348 | );
349 | await helpers.ExecuteCommandInWSLAsync(distroName, cmd);
350 | }
351 |
352 | if (javaCheckbox.IsChecked == true)
353 | {
354 | string cmd = String.Concat(
355 | // apt
356 | "if command -v apt; then apt -y install default-jre default-jdk; fi; ",
357 | // pacman
358 | "if command -v pacman; then pacman -S --noconfirm jre-openjdk jdk-openjdk; fi; ",
359 | // apk
360 | "if command -v apk; then apk add openjdk11; fi; ",
361 | // dnf
362 | "if command -v dnf; then dnf -y install java-11-openjdk.x86_64 java-11-openjdk-devel.x86-64; fi; "
363 | );
364 | await helpers.ExecuteCommandInWSLAsync(distroName, cmd);
365 | }
366 |
367 | await helpers.ExecuteProcessAsynch("wsl.exe", $"-t {distroName}");
368 |
369 | HideProgressBar();
370 |
371 | }
372 |
373 |
374 | await ShowRegisteredModal(distroName);
375 | }
376 |
377 | public async Task ShowRegisteredModal(string name)
378 | {
379 | ContentDialog registerDistroDialog = new ContentDialog();
380 | registerDistroDialog.XamlRoot = registerDistroProceedButton.XamlRoot;
381 | registerDistroDialog.Title = $"{name} has been registered";
382 | registerDistroDialog.SecondaryButtonText = "Close";
383 | registerDistroDialog.PrimaryButtonText = "Run distribution";
384 | registerDistroDialog.DefaultButton = ContentDialogButton.Primary;
385 |
386 | var result = await registerDistroDialog.ShowAsync();
387 |
388 | if (result == ContentDialogResult.Secondary)
389 | {
390 | this.Frame.Navigate(typeof(ManageDistrosPage));
391 | }
392 | else if (result == ContentDialogResult.Primary)
393 | {
394 | helpers.StartWSLDistroAsync(name);
395 | this.Frame.Navigate(typeof(ManageDistrosPage));
396 | }
397 |
398 | }
399 |
400 | public void HttpProgressCallback(Windows.Web.Http.HttpProgress progress)
401 | {
402 | if (progress.TotalBytesToReceive == null) return;
403 |
404 | registerDistroProgressBar.Minimum = 0;
405 | registerDistroProgressBar.Maximum = (double)progress.TotalBytesToReceive;
406 | registerDistroProgressBar.Value = progress.BytesReceived;
407 | }
408 |
409 | public async Task RegisterDistro(string distroName, string tarballPath)
410 | {
411 | ShowProgressBar($"Registering the {distroName} distribution");
412 |
413 | var distroStoragePath = Path.Combine(storageDirectory.Path, distroName);
414 | Directory.CreateDirectory(distroStoragePath);
415 |
416 | await helpers.ExecuteProcessAsynch("wsl.exe", $"--import {distroName} {distroStoragePath} {tarballPath}");
417 |
418 | HideProgressBar();
419 | }
420 |
421 | private void distroSourceComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
422 | {
423 | distroSource = e.AddedItems[0].ToString();
424 | switch (distroSource)
425 | {
426 | case "Supported distro list":
427 | if (SupportedDistroListbox != null)
428 | {
429 | SupportedDistroListbox.Visibility = Visibility.Visible;
430 | SupportedDistroListbox.SelectedIndex = 0;
431 | tarballFileChooserButton.Visibility = Visibility.Collapsed;
432 | dockerImageTextBox.Visibility = Visibility.Collapsed;
433 | dockerImageDescription.Visibility = Visibility.Collapsed;
434 | tarballDescription.Visibility = Visibility.Collapsed;
435 | }
436 | if (createUserStackPanel != null)
437 | {
438 | createUserStackPanel.Visibility = Visibility.Visible;
439 | }
440 | break;
441 | case "Docker Hub":
442 | dockerImageTextBox.Visibility = Visibility.Visible;
443 | SupportedDistroListbox.Visibility = Visibility.Collapsed;
444 | tarballFileChooserButton.Visibility = Visibility.Collapsed;
445 | createUserStackPanel.Visibility = Visibility.Collapsed;
446 | dockerImageDescription.Visibility = Visibility.Visible;
447 | tarballDescription.Visibility = Visibility.Collapsed;
448 | break;
449 | case "Local hard drive":
450 | tarballFileChooserButton.Visibility = Visibility.Visible;
451 | SupportedDistroListbox.Visibility = Visibility.Collapsed;
452 | dockerImageTextBox.Visibility = Visibility.Collapsed;
453 | createUserStackPanel.Visibility = Visibility.Collapsed;
454 | dockerImageDescription.Visibility = Visibility.Collapsed;
455 | tarballDescription.Visibility = Visibility.Visible;
456 | break;
457 | }
458 | }
459 |
460 | private void newUserSwitch_Toggled(object sender, RoutedEventArgs e)
461 | {
462 | var isEnabled = newUserSwitch.IsOn;
463 | if (isEnabled)
464 | {
465 | userNameTextBox.Visibility = Visibility.Visible;
466 | passwordTextBoxGrid.Visibility = Visibility.Visible;
467 | isAdminSwitch.Visibility = Visibility.Visible;
468 | winHelloSwitch.Visibility = Visibility.Visible;
469 | }
470 | else
471 | {
472 | userNameTextBox.Visibility = Visibility.Collapsed;
473 | passwordTextBoxGrid.Visibility = Visibility.Collapsed;
474 | isAdminSwitch.Visibility = Visibility.Collapsed;
475 | winHelloSwitch.Visibility = Visibility.Collapsed;
476 | }
477 | }
478 | private void ShowProgressBar(string text)
479 | {
480 | registeringStatusTextBlock.Text = text;
481 | registeringStatusTextBlock.Visibility = Visibility.Visible;
482 | registerDistroProgressBar.Visibility = Visibility.Visible;
483 | registerDistroProgressBar.IsIndeterminate = true;
484 | }
485 |
486 | private void HideProgressBar()
487 | {
488 | registerDistroProgressBar.IsIndeterminate = false;
489 | registerDistroProgressBar.Visibility = Visibility.Collapsed;
490 | registeringStatusTextBlock.Visibility = Visibility.Collapsed;
491 | }
492 |
493 | private void cancelButton_Click(object sender, RoutedEventArgs e)
494 | {
495 | this.Frame.Navigate(typeof(ManageDistrosPage));
496 | }
497 |
498 | private async Task showErrorModal()
499 | {
500 | ContentDialog errorDialog = new ContentDialog();
501 | errorDialog.XamlRoot = registerDistroProceedButton.XamlRoot;
502 | errorDialog.Title = "Error";
503 | errorDialog.CloseButtonText = "Cancel";
504 | errorDialog.DefaultButton = ContentDialogButton.Close;
505 | errorDialog.Content = "There were problems with registering your distribution.";
506 | await errorDialog.ShowAsync();
507 | }
508 | }
509 |
510 |
511 | }
512 |
--------------------------------------------------------------------------------
/easyWSL/SettingsPage.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
14 |
15 | Settings
16 |
17 |
18 | Memory
19 | How much memory to assign to the WSL 2 VM.
20 |
21 |
22 | GB
23 |
24 |
25 |
26 |
27 | Processors
28 | How many processors to assign to the WSL 2 VM.
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Swap size
37 | How much swap space to add to the WSL 2 VM, 0 for no swap file. Swap storage is disk-based RAM used when memory demand exceeds limit on hardware device.
38 |
39 |
40 | GB
41 |
42 |
43 |
44 |
45 | Swap file
46 | A Windows path to the swap virtual hard disk
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | Kernel path
55 | A Windows path to a custom Linux kernel.
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Kernel commandline
64 | Additional kernel command line arguments.
65 |
66 |
67 |
68 |
69 |
70 |
71 | Localhost forwarding
72 | Setting specifying if ports bound to wildcard or localhost in the WSL 2 VM should be connectable from the host via localhost:port
73 |
74 |
75 |
76 |
77 |
78 |
79 | Page reporing
80 | Setting enables Windows to reclaim unused memory allocated to WSL 2 virtual machine
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | GUI applications
89 | Turn on or off support for GUI applications (WSLg) in WSL.
90 |
91 |
92 |
93 |
94 |
95 |
96 | Debug console
97 | Turn on or off an output console Window that shows the contents of dmesg upon start of a WSL 2 distro instance.
98 |
99 |
100 |
101 |
102 |
103 |
104 | Nested virtualization
105 | Turn on or off nested virtualization, enabling other nested VMs to run inside WSL 2.
106 |
107 |
108 |
109 |
110 |
111 |
112 | Virtual machine idle timeout
113 | The number of milliseconds that a VM is idle, before it is shut down.
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/easyWSL/SettingsPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using Microsoft.UI.Xaml.Controls.Primitives;
4 | using System;
5 | using System.Management;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using Windows.Storage.Pickers;
9 | using easyWslLib;
10 |
11 | namespace easyWSL
12 | {
13 | public sealed partial class SettingsPage : Page
14 | {
15 | private Helpers helpers = new();
16 | public SettingsPage()
17 | {
18 | this.InitializeComponent();
19 | string configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".wslconfig");
20 | SetMax();
21 | if (File.Exists(configPath))
22 | {
23 | LoadConfig(configPath);
24 | }
25 | else
26 | {
27 | SetDefaults();
28 | }
29 | if(Environment.OSVersion.Version.Build >= 22000)
30 | {
31 | windows11OptionsStackPanel.Visibility = Visibility.Visible;
32 | }
33 |
34 | }
35 |
36 | private double GetRam()
37 | {
38 | double ramInMB = 0.0;
39 | ManagementObjectSearcher Search = new ManagementObjectSearcher("Select * From Win32_ComputerSystem");
40 | foreach (ManagementObject Mobject in Search.Get())
41 | {
42 |
43 | ramInMB = Convert.ToDouble(Mobject["TotalPhysicalMemory"]) / 1048576;
44 | }
45 | return ramInMB;
46 | }
47 |
48 | private void SetMax()
49 | {
50 | int processors = Environment.ProcessorCount;
51 | processorsSlider.Maximum = processors;
52 |
53 | double memoryMax = Math.Round(Convert.ToDouble(GetRam()) / 1024, 1);
54 | memoryNumberBox.Maximum = memoryMax;
55 | }
56 |
57 | private void SetDefaults()
58 | {
59 | // Memory
60 | double halfOfMemory = Convert.ToInt32(GetRam() / 2);
61 | if(halfOfMemory < 8192)
62 | {
63 | memoryNumberBox.Value = Math.Round(halfOfMemory / 1024, 1);
64 | }
65 | else
66 | {
67 | memoryNumberBox.Value = 8;
68 | }
69 |
70 |
71 | // Processors
72 | int processors = Environment.ProcessorCount;
73 | processorsSlider.Value = processors;
74 |
75 | // Swap size
76 | double swapSize = Convert.ToDouble(GetRam() / 4);
77 | swapSizeNumberBox.Value = Math.Round(swapSize / 1024, 1);
78 |
79 | // Swap File
80 | string swapPath = $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\\AppData\\Local\\Temp\\swap.vhdx";
81 | swapFileTextBox.Text = swapPath;
82 |
83 | // Kernel path
84 |
85 | // Kernel commandline
86 |
87 | // Localhost forwarding
88 | localhostForwardingToggle.IsOn = true;
89 |
90 | // Page Reporting
91 | pageReportingToggle.IsOn = true;
92 |
93 | // Gui applications
94 | guiApplicationsToggle.IsOn = true;
95 |
96 | // Debug Console
97 | debugConsoleToggle.IsOn = false;
98 |
99 | // Nested Virtualization
100 | nestedVirtualisationToggle.IsOn = true;
101 |
102 | // VMIdle Timeout
103 | vmIdleTimeoutNumberBox.Value = 60000;
104 | }
105 |
106 | private void LoadConfig(string path)
107 | {
108 | Dictionary configParsed = new();
109 | foreach (string line in File.ReadLines(path))
110 | {
111 | bool isFirstLine = line.Contains("[wsl2]") || line.Contains("[WSL2]");
112 | if (!isFirstLine)
113 | {
114 | string currentLine = line;
115 | currentLine = line.ReplaceLineEndings();
116 | currentLine = currentLine.Replace(Environment.NewLine, "");
117 |
118 | string[] kvp = currentLine.Split("=");
119 | if(kvp.Length == 2)
120 | {
121 | configParsed.Add(kvp[0], kvp[1]);
122 | }
123 | }
124 | }
125 |
126 | // Memory
127 | if (configParsed.ContainsKey("memory"))
128 | {
129 | string memory = configParsed["memory"];
130 | if (memory.Contains("MB"))
131 | {
132 | double memoryD = Math.Round(Convert.ToDouble(memory.Replace("MB", "")) / 1024, 1);
133 | memoryNumberBox.Value = memoryD;
134 | }
135 | else if (memory.Contains("GB"))
136 | {
137 | int memoryINT = Convert.ToInt32(memory.Replace("GB", ""));
138 | memoryNumberBox.Value = memoryINT;
139 | }
140 | }
141 | // Processors
142 | if (configParsed.ContainsKey("processors"))
143 | {
144 | int processors = Convert.ToInt32(configParsed["processors"]);
145 | processorsSlider.Value = processors;
146 | }
147 | // Swap size
148 | if (configParsed.ContainsKey("swap"))
149 | {
150 | string swapSize = configParsed["swap"];
151 | if (swapSize.Contains("MB"))
152 | {
153 | double swapSizeD = Math.Round(Convert.ToDouble(swapSize.Replace("MB", "")) / 1024, 1);
154 | swapSizeNumberBox.Value = swapSizeD;
155 | }
156 | else if (swapSize.Contains("GB"))
157 | {
158 | int swapSizeINT = Convert.ToInt32(swapSize.Replace("GB", ""));
159 | swapSizeNumberBox.Value = swapSizeINT;
160 | }
161 | }
162 | // Swap File
163 | if (configParsed.ContainsKey("swapfile"))
164 | {
165 | string swapPath = configParsed["swapfile"];
166 | swapPath = swapPath.Replace(@"\\", @"\");
167 | swapFileTextBox.Text = swapPath;
168 | }
169 | // Kernel path
170 | if (configParsed.ContainsKey("kernel"))
171 | {
172 | string kernelPath = configParsed["kernel"];
173 | kernelPath = kernelPath.Replace(@"\\", @"\");
174 | kernelPathTextBox.Text = kernelPath;
175 | }
176 | // Kernel commandline
177 | if (configParsed.ContainsKey("kernel"))
178 | {
179 | string kernelCmd = configParsed["kernelCommandLine"];
180 | kernelCommandLineTextBox.Text = kernelCmd;
181 | }
182 | // Localhost forwarding
183 | if (configParsed.ContainsKey("localhostForwarding"))
184 | {
185 | bool localhostForwarding = Convert.ToBoolean(configParsed["localhostForwarding"]);
186 | localhostForwardingToggle.IsOn = localhostForwarding;
187 | }
188 | // Page Reporting
189 | if (configParsed.ContainsKey("pageReporting"))
190 | {
191 | bool pageReporting = Convert.ToBoolean(configParsed["pageReporting"]);
192 | pageReportingToggle.IsOn = pageReporting;
193 | }
194 | // Gui applications
195 | if (configParsed.ContainsKey("guiApplications"))
196 | {
197 | bool guiApplications = Convert.ToBoolean(configParsed["guiApplications"]);
198 | guiApplicationsToggle.IsOn = guiApplications;
199 | }
200 | // Debug Console
201 | if (configParsed.ContainsKey("debugConsole"))
202 | {
203 | bool debugConsole = Convert.ToBoolean(configParsed["debugConsole"]);
204 | debugConsoleToggle.IsOn = debugConsole;
205 | }
206 | // Nested Virtualization
207 | if (configParsed.ContainsKey("nestedVirtualization"))
208 | {
209 | bool nestedVirtualization = Convert.ToBoolean(configParsed["nestedVirtualization"]);
210 | nestedVirtualisationToggle.IsOn = nestedVirtualization;
211 | }
212 | // VMIdle Timeout
213 | if (configParsed.ContainsKey("vmIdleTimeout"))
214 | {
215 | int vmIdleTimeout = Convert.ToInt32(configParsed["vmIdleTimeout"]);
216 | vmIdleTimeoutNumberBox.Value = vmIdleTimeout;
217 | }
218 |
219 | }
220 |
221 | private void processorsSlider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
222 | {
223 | if (processorsTextBlock != null)
224 | {
225 | processorsTextBlock.Text = $"{processorsSlider.Value} cores";
226 |
227 | }
228 |
229 | }
230 |
231 | private void vmIdleTimeoutNumberBox_ValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args)
232 | {
233 | if (vmIdleTimeoutTextBlock != null)
234 | {
235 | double timeoutInSec = Math.Round(vmIdleTimeoutNumberBox.Value / 1000, 2);
236 | vmIdleTimeoutTextBlock.Text = $"{timeoutInSec} seconds";
237 | }
238 | }
239 |
240 | private void revertToDefaultsButton_Click(object sender, RoutedEventArgs e)
241 | {
242 | SetDefaults();
243 | }
244 |
245 | private async void applyButton_Click(object sender, RoutedEventArgs e)
246 | {
247 | string configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".wslconfig");
248 | if (File.Exists(configPath))
249 | {
250 | File.Delete(configPath);
251 | }
252 |
253 | List configLines = new() { "[WSL]" };
254 |
255 | // Memory
256 | int memoryINT = (int)Math.Round(memoryNumberBox.Value * 1024, 0);
257 | string memory = memoryINT.ToString() + "MB";
258 | configLines.Add($"memory={memory}");
259 |
260 | // Processors
261 | int processors = Convert.ToInt32(processorsSlider.Value);
262 | configLines.Add($"processors={processors}");
263 |
264 | // Swap size
265 | int swapSizeINT = (int)Math.Round(swapSizeNumberBox.Value, 0);
266 | string swapSize = swapSizeINT.ToString() + "GB";
267 | configLines.Add($"swap={swapSize}");
268 |
269 | // Swap File
270 | string swapPath = swapFileTextBox.Text;
271 | swapPath = swapPath.Replace(@"\", @"\\");
272 | configLines.Add($"swapfile={swapPath}");
273 |
274 | // Kernel path
275 | string kernelPath = kernelPathTextBox.Text;
276 | if(File.Exists(kernelPath))
277 | {
278 | configLines.Add($"kernel={kernelPath}");
279 | }
280 |
281 | // Kernel commandline
282 | string commandline = kernelCommandLineTextBox.Text;
283 | commandline = commandline.Replace(@"\", @"\\");
284 | configLines.Add($"kernelCommandLine={commandline}");
285 |
286 | // Localhost forwarding
287 | string localForwarding = localhostForwardingToggle.IsOn.ToString().ToLower();
288 | configLines.Add($"localhostForwarding={localForwarding}");
289 |
290 | // Page Reporting
291 | string pageReporting = pageReportingToggle.IsOn.ToString().ToLower();
292 | configLines.Add($"pageReporting={pageReporting}");
293 |
294 | // Gui applications
295 | string guiApp = guiApplicationsToggle.IsOn.ToString().ToLower();
296 | configLines.Add($"guiApplications={guiApp}");
297 |
298 | // Debug Console
299 | string debug = debugConsoleToggle.IsOn.ToString().ToLower();
300 | configLines.Add($"debugConsole={debug}");
301 |
302 | // Nested Virtualization
303 | string nested = nestedVirtualisationToggle.IsOn.ToString().ToLower();
304 | configLines.Add($"nestedVirtualization={nested}");
305 |
306 | // VMIdle Timeout
307 | int timeout = Convert.ToInt32(vmIdleTimeoutNumberBox.Value);
308 | configLines.Add($"vmIdleTimeout={timeout}");
309 |
310 | File.AppendAllLines(configPath, configLines);
311 |
312 | ContentDialog confirmDialog = new ContentDialog();
313 | confirmDialog.XamlRoot = applyButton.XamlRoot;
314 | confirmDialog.Title = $"Do you want to relaunch WSL?";
315 | confirmDialog.Content = "In order to apply the changes you have to relaunch WSL. Be aware that this is going to stop all distributions that are running at the moment.";
316 | confirmDialog.SecondaryButtonText = "No";
317 | confirmDialog.PrimaryButtonText = "Yes";
318 | confirmDialog.DefaultButton = ContentDialogButton.Primary;
319 |
320 | var result = await confirmDialog.ShowAsync();
321 |
322 | if (result == ContentDialogResult.Primary)
323 | {
324 | await helpers.ExecuteProcessAsynch("wsl.exe", "--shutdown");
325 | }
326 | }
327 | private async void swapFileButton_Click(object sender, RoutedEventArgs e)
328 | {
329 | FileOpenPicker filePicker = new FileOpenPicker();
330 |
331 | IntPtr hwnd = (App.Current as App).MainWindowWindowHandle;
332 | WinRT.Interop.InitializeWithWindow.Initialize(filePicker, hwnd);
333 | filePicker.FileTypeFilter.Add(".vhdx");
334 | var swapFilePath = await filePicker.PickSingleFileAsync();
335 | if(swapFilePath != null)
336 | {
337 | swapFileTextBox.Text = swapFilePath.Path;
338 | }
339 | }
340 |
341 | private async void kernelPathButton_Click(object sender, RoutedEventArgs e)
342 | {
343 | FileOpenPicker filePicker = new FileOpenPicker();
344 |
345 | IntPtr hwnd = (App.Current as App).MainWindowWindowHandle;
346 | WinRT.Interop.InitializeWithWindow.Initialize(filePicker, hwnd);
347 | filePicker.FileTypeFilter.Add("*");
348 | var kernelFilePath = await filePicker.PickSingleFileAsync();
349 | if(kernelFilePath != null)
350 | {
351 | kernelPathTextBox.Text = kernelFilePath.Path;
352 | }
353 | }
354 |
355 | private async void aboutButton_Click(object sender, RoutedEventArgs e)
356 | {
357 | AboutDialog aboutDialogContent = new();
358 | ContentDialog aboutDialog = new ContentDialog();
359 | aboutDialog.XamlRoot = aboutButton.XamlRoot;
360 | aboutDialog.Title = $"About";
361 | aboutDialog.Content = aboutDialogContent;
362 | aboutDialog.DefaultButton = ContentDialogButton.Close;
363 | aboutDialog.CloseButtonText = "Close";
364 |
365 | await aboutDialog.ShowAsync();
366 | }
367 | }
368 | }
369 |
--------------------------------------------------------------------------------
/easyWSL/WelcomeWindow.xaml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 | WSL is not installed on your system
13 |
14 | This application requires
15 | Windows Subsystem For Linux enabled on your machine. Please install it and relaunch the application.
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/easyWSL/WelcomeWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using Microsoft.UI.Xaml.Controls.Primitives;
4 | using Microsoft.UI.Xaml.Data;
5 | using Microsoft.UI.Xaml.Input;
6 | using Microsoft.UI.Xaml.Media;
7 | using Microsoft.UI.Xaml.Navigation;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.IO;
11 | using System.Linq;
12 | using System.Runtime.InteropServices.WindowsRuntime;
13 | using Windows.Foundation;
14 | using Windows.Foundation.Collections;
15 | using Windows.Media.Capture;
16 |
17 | // To learn more about WinUI, the WinUI project structure,
18 | // and more about our project templates, see: http://aka.ms/winui-project-info.
19 |
20 | namespace easyWSL
21 | {
22 | public sealed partial class WelcomeWindow : Window
23 | {
24 | public WelcomeWindow()
25 | {
26 | this.InitializeComponent();
27 | }
28 |
29 | private void closeButton_Click(object sender, RoutedEventArgs e)
30 | {
31 | var app = Application.Current;
32 | app.Exit();
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/easyWSL/WslSdk.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Threading.Tasks;
6 | using easyWslLib;
7 |
8 | namespace easyWSL
9 | {
10 | class WslSdk
11 | {
12 | private Helpers helpers = new();
13 |
14 | public static Dictionary InstalledDistros = new Dictionary { };
15 | public class InstalledDistrosProperties
16 | {
17 | public string name { get; set; }
18 | public string path { get; set; }
19 | public string state { get; set; }
20 | public string version { get; set; }
21 | public string regkey { get; set; }
22 | public string size { get; set; }
23 | }
24 |
25 | public static bool CheckIfWSLInstalled()
26 | {
27 | var currentUserReg = Registry.CurrentUser;
28 | var lxssPathReg = currentUserReg.OpenSubKey(Path.Combine("SOFTWARE", "Microsoft", "Windows", "CurrentVersion", "Lxss"), false);
29 | if (lxssPathReg == null)
30 | {
31 | return false;
32 | }
33 | else
34 | {
35 | return true;
36 | }
37 | }
38 |
39 | public static async Task GetInstalledDistributions()
40 | {
41 | InstalledDistros.Clear();
42 |
43 | var currentUserReg = Registry.CurrentUser;
44 | var lxssPathReg = currentUserReg.OpenSubKey(Path.Combine("SOFTWARE", "Microsoft", "Windows", "CurrentVersion", "Lxss"), false);
45 |
46 | String[] distrosRegkeyNames = lxssPathReg.GetSubKeyNames();
47 | foreach (string distroRegkeyName in distrosRegkeyNames)
48 | {
49 | var distroRegkey = lxssPathReg.OpenSubKey(distroRegkeyName, false);
50 | var distroName = distroRegkey.GetValue("DistributionName");
51 | var distroPath = distroRegkey.GetValue("BasePath");
52 | var distroVersion = distroRegkey.GetValue("Version");
53 | string distroState = "Stopped";
54 |
55 |
56 | if (distroName != null && distroPath != null && distroState != null && distroVersion != null)
57 | {
58 | string sizeString = "0.0 GB";
59 | if (Directory.Exists(distroPath.ToString()))
60 | {
61 | DirectoryInfo distroDir = new DirectoryInfo(distroPath.ToString());
62 | long sizeBytes = Helpers.DirSize(distroDir);
63 | double sizeGigaBytes = (double)sizeBytes / 1024 / 1024 / 1024;
64 | sizeString = $"{String.Format("{0:F2}", sizeGigaBytes)} GB";
65 | }
66 |
67 |
68 | InstalledDistros.Add(distroName.ToString(), new InstalledDistrosProperties() { name = distroName.ToString(),
69 | path = distroPath.ToString(),
70 | state = distroState.ToString(),
71 | version = distroVersion.ToString(),
72 | regkey = distroRegkeyName,
73 | size = sizeString
74 | });
75 | }
76 | distroRegkey.Close();
77 | }
78 | lxssPathReg.Close();
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/easyWSL/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 | true/PM
12 | PerMonitorV2, PerMonitor
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/easyWSL/dep/WindowsHelloBridge.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/dep/WindowsHelloBridge.exe
--------------------------------------------------------------------------------
/easyWSL/dep/bsdtar.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/dep/bsdtar.exe
--------------------------------------------------------------------------------
/easyWSL/dep/bzip2.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/dep/bzip2.dll
--------------------------------------------------------------------------------
/easyWSL/dep/libarchive2.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/dep/libarchive2.dll
--------------------------------------------------------------------------------
/easyWSL/dep/pam_wsl_hello.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/dep/pam_wsl_hello.so
--------------------------------------------------------------------------------
/easyWSL/dep/zlib1.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/dep/zlib1.dll
--------------------------------------------------------------------------------
/easyWSL/easyWSL.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | net6.0-windows10.0.19041.0
5 | 10.0.17763.0
6 | easyWSL
7 | app.manifest
8 | x86;x64;arm64
9 | win10-x86;win10-x64;win10-arm64
10 | win10-$(Platform).pubxml
11 | true
12 | true
13 | easyWSL.ico
14 | Red Code Labs
15 | True
16 | False
17 | True
18 | True
19 | True
20 | Auto
21 | x64
22 | 0
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | MSBuild:Compile
76 |
77 |
78 |
79 |
80 | MSBuild:Compile
81 |
82 |
83 |
84 |
85 | MSBuild:Compile
86 |
87 |
88 |
89 |
90 | MSBuild:Compile
91 |
92 |
93 |
94 |
95 | MSBuild:Compile
96 |
97 |
98 | Always
99 |
100 |
101 | Always
102 |
103 |
104 | Always
105 |
106 |
107 | Always
108 |
109 |
110 | Always
111 |
112 |
113 | Always
114 |
115 |
116 | MSBuild:Compile
117 |
118 |
119 | MSBuild:Compile
120 |
121 |
122 | MSBuild:Compile
123 |
124 |
125 | MSBuild:Compile
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/easyWSL/easyWSL.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/easyWSL/a2835df30929f0eef2d60c762d8ef767f849ff5a/easyWSL/easyWSL.ico
--------------------------------------------------------------------------------
/easyWSLcmd/PlatformHelpers.cs:
--------------------------------------------------------------------------------
1 | using easyWslLib;
2 |
3 | namespace easyWslCmd
4 | {
5 | internal class PlatformHelpers:IPlatformHelpers
6 | {
7 | private void Copy(Stream inStream, FileInfo destination)
8 | {
9 | if (!destination.Directory.Exists)
10 | {
11 | destination.Directory.Create();
12 | }
13 | using var outStream = destination.OpenWrite();
14 | inStream.CopyTo(outStream);
15 | }
16 | public Task CopyFileAsync(string sourcePath, string destinationPath)
17 | {
18 | using var inStream = File.OpenRead(sourcePath);
19 | Copy(inStream, new FileInfo(destinationPath));
20 | return Task.CompletedTask;
21 | }
22 |
23 | public async Task DownloadFileAsync(Uri uri, IEnumerable> headers, FileInfo destinationPath)
24 | {
25 | var httpClient = new HttpClient();
26 | foreach (var header in headers)
27 | {
28 | httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
29 | }
30 |
31 | try
32 | {
33 | await using var inStream = await httpClient.GetStreamAsync(uri);
34 | Copy(inStream, destinationPath);
35 | }
36 | catch (System.Net.Http.HttpRequestException e)
37 | {
38 | }
39 | }
40 |
41 | public string TarCommand => "tar.exe";
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/easyWSLcmd/Program.cs:
--------------------------------------------------------------------------------
1 | // See https://aka.ms/new-console-template for more information
2 |
3 | using System.CommandLine;
4 | using System.Diagnostics;
5 | using easyWslCmd;
6 | using easyWslLib;
7 |
8 | var rootCommand = new RootCommand("Easy WSL");
9 |
10 | var distroName = new Option("--name", "Name to assign to the new WSL distro");
11 | var imageName = new Option("--image", "dockerhub image to base new distro on");
12 | var outputPath = new Option("--output", "Where to install the distro");
13 | outputPath.SetDefaultValue(new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "easyWSL")));
14 |
15 | var import = new Command("import", "Import a WSL distro from a dockerhub image");
16 | import.Add(distroName);
17 | import.Add(imageName);
18 | import.Add(outputPath);
19 | rootCommand.AddCommand(import);
20 |
21 |
22 |
23 | import.SetHandler(async (name, image, output) =>
24 | {
25 | if (string.IsNullOrWhiteSpace(name))
26 | {
27 | throw new ArgumentException("missing required parameter", nameof(name));
28 | }
29 |
30 | if (string.IsNullOrWhiteSpace(image))
31 | {
32 | throw new ArgumentException("missing required parameter", nameof(image));
33 | }
34 | var tempPath = Path.Combine(Path.GetTempPath(), "easyWSL");
35 | var downloader = new DockerDownloader(tempPath, new PlatformHelpers());
36 |
37 | output = output.CreateSubdirectory(name);
38 |
39 |
40 | Console.WriteLine($"Downloading {image}");
41 | await downloader.DownloadImage(image);
42 | Console.WriteLine("Combining layers");
43 | await downloader.CombineLayers();
44 | Console.WriteLine("Registering distro");
45 | Process.Start("wsl.exe", new[] { $"--import", name, output.FullName, Path.Combine(tempPath, "install.tar.bz") }).WaitForExit();
46 | }, distroName, imageName, outputPath);
47 |
48 | return await rootCommand.InvokeAsync(args);
--------------------------------------------------------------------------------
/easyWSLcmd/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "easyWSLcmd": {
4 | "commandName": "Project",
5 | "commandLineArgs": "import --name test --image redhat/ubi8"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/easyWSLcmd/easyWSLcmd.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 | x64
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/easyWslLib/DockerDownloader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Net.Http;
8 | using System.Text.Json;
9 | using System.Text.RegularExpressions;
10 | using System.Threading.Tasks;
11 |
12 | namespace easyWslLib
13 | {
14 |
15 | public class DockerDownloader
16 | {
17 | private Helpers helpers = new();
18 |
19 | private List layersPaths = new();
20 |
21 | private readonly string tmpDirectory;
22 | private readonly IPlatformHelpers platformHelpers;
23 |
24 | public DockerDownloader(string tmpDirectory, IPlatformHelpers platformHelpers)
25 | {
26 | this.tmpDirectory = tmpDirectory;
27 | this.platformHelpers = platformHelpers;
28 | }
29 |
30 |
31 | public class autorizationResponse
32 | {
33 | public string token { get; set; }
34 | public string access_token { get; set; }
35 |
36 | public int expires_in { get; set; }
37 |
38 | public string issued_at { get; set; }
39 |
40 | }
41 |
42 | public class DockerException : Exception
43 | {
44 | public DockerException()
45 | {
46 | }
47 | }
48 |
49 | public async Task DownloadImage(string distroImage)
50 | {
51 | if (!Directory.Exists(tmpDirectory))
52 | {
53 | Directory.CreateDirectory(tmpDirectory);
54 | }
55 |
56 | DirectoryInfo tmpDirectoryInfo = new DirectoryInfo(tmpDirectory);
57 | foreach (FileInfo file in tmpDirectoryInfo.EnumerateFiles())
58 | {
59 | file.Delete();
60 | }
61 |
62 |
63 | string repository = "";
64 | string tag = "";
65 | string registry = "registry-1.docker.io";
66 | string authorizationUrl = "https://auth.docker.io/token";
67 | string registryUrl = "registry.docker.io";
68 |
69 | if (distroImage.Contains('/'))
70 | {
71 | string[] imageArray = distroImage.Split('/');
72 | if (imageArray.Length < 2)
73 | {
74 | throw (new DockerException());
75 | }
76 |
77 | if (imageArray[1].Contains(':'))
78 | {
79 | tag = imageArray[1].Split(':')[1];
80 | repository = distroImage.Split(':')[0];
81 | }
82 | else
83 | {
84 | tag = "latest";
85 | repository = distroImage;
86 | }
87 | }
88 | else
89 | {
90 | string[] imageArray = distroImage.Split(':');
91 | if (imageArray.Length < 2)
92 | {
93 | throw (new DockerException());
94 | }
95 | string imgage = imageArray[0];
96 | tag = imageArray[1];
97 | repository = $"library/{imgage}";
98 | }
99 |
100 | var autorizationResponse = JsonSerializer.Deserialize(helpers.GetRequest($"{authorizationUrl}?service={registryUrl}&scope=repository:{repository}:pull"));
101 | string layersResponse;
102 | try
103 | {
104 | layersResponse = helpers.GetRequestWithHeader($"https://{registry}/v2/{repository}/manifests/{tag}", autorizationResponse.token, "application/vnd.docker.distribution.manifest.v2+json");
105 | }
106 | catch (WebException ex)
107 | {
108 | throw (new DockerException());
109 | }
110 |
111 | MatchCollection layersRegex = Regex.Matches(layersResponse, @"sha256:\w{64}");
112 | var layersList = layersRegex.Cast().Select(match => match.Value).ToList();
113 | layersList.RemoveAt(0);
114 |
115 | MatchCollection layersSizeRegex = Regex.Matches(layersResponse, @"""size"": \d*");
116 | var layersSizeList = layersSizeRegex.Cast().Select(match => Convert.ToInt32(match.Value.Remove(0, 8))).ToList();
117 |
118 |
119 | Trace.WriteLine(tmpDirectory);
120 | Directory.CreateDirectory(tmpDirectory);
121 |
122 | int layersCount = 0;
123 | foreach (string layer in layersList)
124 | {
125 | layersCount++;
126 |
127 | autorizationResponse = JsonSerializer.Deserialize(helpers.GetRequest($"{authorizationUrl}?service={registryUrl}&scope=repository:{repository}:pull"));
128 |
129 | string layerName = $"layer{layersCount}.tar.bz";
130 | string layerPath = $"{tmpDirectory}\\{layerName}";
131 |
132 | layersPaths.Add(layerPath);
133 |
134 | var headers = new KeyValuePair[]
135 | {
136 | new KeyValuePair(HttpRequestHeader.Authorization.ToString(), $"Bearer {autorizationResponse.token}"),
137 | new KeyValuePair(HttpRequestHeader.Accept.ToString(), "application/vnd.docker.distribution.manifest.v2+json"),
138 | };
139 | await platformHelpers.DownloadFileAsync(new Uri($"https://{registry}/v2/{repository}/blobs/{layer}"), headers,
140 | new FileInfo(Path.Combine(tmpDirectory, layerName)));
141 | }
142 | }
143 |
144 | public async Task CombineLayers()
145 | {
146 | var installTarPath = Path.Combine(tmpDirectory, "install.tar.bz");
147 |
148 | if (layersPaths.Count == 1)
149 | {
150 | await platformHelpers.CopyFileAsync(layersPaths[0], installTarPath);
151 | }
152 | else
153 | {
154 | string concatTarCommand = $" cf {installTarPath}";
155 | foreach (var layerPath in layersPaths)
156 | {
157 | concatTarCommand += $" @{layerPath}";
158 | }
159 | Trace.WriteLine(concatTarCommand);
160 | await helpers.ExecuteProcessAsynch(platformHelpers.TarCommand, concatTarCommand);
161 | Trace.WriteLine("combining completed");
162 | }
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/easyWslLib/Helpers.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Net;
4 | using System.Security.Cryptography;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace easyWslLib
9 | {
10 | public class Helpers
11 | {
12 | public string GetRequest(string url)
13 | {
14 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
15 | HttpWebResponse response = (HttpWebResponse)request.GetResponse();
16 | request.Credentials = CredentialCache.DefaultCredentials;
17 | Stream receiveStream = response.GetResponseStream();
18 | StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
19 | string responseStream = readStream.ReadToEnd();
20 | response.Close();
21 | readStream.Close();
22 | return responseStream;
23 | }
24 | public string GetRequestWithHeader(string url, string token, string type)
25 | {
26 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
27 | request.Headers.Add("Authorization", "Bearer " + token);
28 | request.Accept = type;
29 | HttpWebResponse response;
30 | try
31 | {
32 | response = (HttpWebResponse)request.GetResponse();
33 | }
34 | catch(WebException ex)
35 | {
36 | throw ex;
37 | }
38 | Stream receiveStream = response.GetResponseStream();
39 | StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
40 | string responseStream = readStream.ReadToEnd();
41 | response.Close();
42 | readStream.Close();
43 | return responseStream;
44 | }
45 |
46 | public string ComputeSha256Hash(byte[] rawData)
47 | {
48 | using (SHA256 sha256Hash = SHA256.Create())
49 | {
50 | byte[] bytes = sha256Hash.ComputeHash(rawData);
51 |
52 | StringBuilder builder = new StringBuilder();
53 | for (int i = 0; i < bytes.Length; i++)
54 | {
55 | builder.Append(bytes[i].ToString("x2"));
56 | }
57 | return builder.ToString();
58 | }
59 | }
60 | public async Task ExecuteProcessAsynch(string exe, string arguments)
61 | {
62 | Process proc = new Process();
63 | proc.StartInfo.CreateNoWindow = true;
64 | proc.StartInfo.UseShellExecute = false;
65 | proc.StartInfo.FileName = exe;
66 | proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
67 | proc.StartInfo.Arguments = arguments;
68 | proc.Start();
69 | await proc.WaitForExitAsync().ConfigureAwait(false);
70 | }
71 |
72 | public async Task StartWSLDistroAsync(string distroName)
73 | {
74 | Process proc = new Process();
75 | proc.StartInfo.UseShellExecute = false;
76 | proc.StartInfo.FileName = "wsl.exe";
77 | proc.StartInfo.Arguments = $"-d {distroName}";
78 | proc.Start();
79 | }
80 | public async Task ExecuteCommandInWSLAsync(string distroName, string command)
81 | {
82 | Process proc = new Process();
83 | proc.StartInfo.CreateNoWindow = true;
84 | proc.StartInfo.UseShellExecute = false;
85 | proc.StartInfo.FileName = "wsl.exe";
86 | proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
87 | proc.StartInfo.Arguments = $"-d {distroName} -- {command}";
88 | proc.Start();
89 | await proc.WaitForExitAsync().ConfigureAwait(false);
90 | }
91 |
92 | public string ExecuteProcessAndGetOutputAsynch(string exe, string arguments)
93 | {
94 | using Process process = Process.Start(new ProcessStartInfo()
95 | {
96 | CreateNoWindow = true,
97 | UseShellExecute = false,
98 | RedirectStandardOutput = true,
99 | FileName = exe,
100 | Arguments = arguments,
101 | WindowStyle = ProcessWindowStyle.Hidden
102 | });
103 | StringBuilder outputBuilder = new();
104 | process.OutputDataReceived += (_, eventArgs) =>
105 | {
106 | if (!string.IsNullOrEmpty(eventArgs.Data))
107 | outputBuilder.Append(eventArgs.Data);
108 | };
109 | process.BeginOutputReadLine();
110 | process.WaitForExit();
111 | return outputBuilder.ToString();
112 | }
113 | public static long DirSize(DirectoryInfo d)
114 | {
115 | long size = 0;
116 |
117 | FileInfo[] fis = d.GetFiles();
118 | foreach (FileInfo fi in fis)
119 | {
120 | size += fi.Length;
121 | }
122 |
123 | DirectoryInfo[] dis = d.GetDirectories();
124 | foreach (DirectoryInfo di in dis)
125 | {
126 | size += DirSize(di);
127 | }
128 | return size;
129 | }
130 |
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/easyWslLib/IPlatformHelpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net.Http;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace easyWslLib
10 | {
11 | public interface IPlatformHelpers
12 | {
13 | public Task CopyFileAsync(string sourcePath, string destinationPath);
14 |
15 | public Task DownloadFileAsync(Uri uri, IEnumerable> headers,
16 | FileInfo destinationPath);
17 |
18 | public string TarCommand { get; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/easyWslLib/easyWslLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------