Full Code of microsoft/wslg for AI

main 1a8c3c64aa15 cached
83 files
286.3 KB
76.2k tokens
153 symbols
1 requests
Download .txt
Showing preview only (306K chars total). Download the full file or copy to clipboard to get everything.
Repository: microsoft/wslg
Branch: main
Commit: 1a8c3c64aa15
Files: 83
Total size: 286.3 KB

Directory structure:
gitextract_cowqssw9/

├── .dockerignore
├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.yml
│       ├── config.yml
│       └── feature_request.yml
├── .gitignore
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── Microsoft.WSLg.nuspec
├── Microsoft.WSLg.targets
├── README.md
├── SECURITY.md
├── WSLDVCPlugin/
│   ├── RegisterWSLPlugin.reg
│   ├── UpdateRCVersion.ps1
│   ├── WSLDVCCallback.cpp
│   ├── WSLDVCCallback.h
│   ├── WSLDVCFileDB.cpp
│   ├── WSLDVCFileDB.h
│   ├── WSLDVCListenerCallback.cpp
│   ├── WSLDVCListenerCallback.h
│   ├── WSLDVCPlugin.cpp
│   ├── WSLDVCPlugin.def
│   ├── WSLDVCPlugin.h
│   ├── WSLDVCPlugin.rc
│   ├── WSLDVCPlugin.sln
│   ├── WSLDVCPlugin.vcxproj
│   ├── WSLDVCPlugin.vcxproj.filters
│   ├── WSLDVCPlugin.vcxproj.user
│   ├── cpp.hint
│   ├── dllmain.cpp
│   ├── framework.h
│   ├── pch.cpp
│   ├── pch.h
│   ├── rdpapplist.h
│   ├── resource.h
│   ├── utils.cpp
│   └── utils.h
├── WSLGd/
│   ├── FontMonitor.cpp
│   ├── FontMonitor.h
│   ├── Makefile
│   ├── ProcessMonitor.cpp
│   ├── ProcessMonitor.h
│   ├── common.h
│   ├── lxwil.h
│   ├── main.cpp
│   ├── meson.build
│   └── precomp.h
├── azure-pipelines.yml
├── build-and-export.sh
├── cgmanifest.json
├── config/
│   ├── BUILD.md
│   ├── default_wslg.pa
│   ├── local.conf
│   ├── weston.ini
│   ├── wsl.conf
│   └── xwayland_log.patch
├── debuginfo/
│   ├── FreeRDP2.list
│   ├── FreeRDP3.list
│   ├── WSLGd.list
│   ├── gen_debuginfo.sh
│   ├── rdpapplist.list
│   └── weston.list
├── devops/
│   ├── common-linux.yml
│   ├── common-win.yml
│   ├── getversion.ps1
│   ├── updateversion.ps1
│   └── version_functions.ps1
├── docs/
│   └── install-sample-gui-apps.sh
├── msi/
│   └── updateversion.ps1
├── package/
│   ├── wslg.rdp
│   └── wslg_desktop.rdp
├── rdpapplist/
│   ├── meson.build
│   ├── rdpapplist_common.c
│   ├── rdpapplist_common.h
│   ├── rdpapplist_protocol.h
│   ├── rdpapplist_server.h
│   └── server/
│       ├── meson.build
│       ├── rdpapplist_main.c
│       └── rdpapplist_main.h
├── samples/
│   └── container/
│       ├── Containers.md
│       ├── build.sh
│       └── run.sh
└── vendor/
    └── .preserve

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
.git
.github
.gitlab
.gitlab-ci
.vs
out
tmp


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: "Bug report"
description: " Report a bug in WSLg"
labels: "bug"
body:
- type: input
  id: build-number
  attributes:
    label: "Windows build number:"
    description: "run `[Environment]::OSVersion` for powershell, or `ver` for cmd"
    placeholder: "22000.100"
  validations:
    required: true
- type: input
  id: distribution_version
  attributes:
    label: "Your Distribution version:"
    description: "On Debian or Ubuntu run `lsb_release -r` in WSL"
    placeholder: "20.04"
  validations:
    required: true
- type: textarea
  id: wsl-version
  attributes:
    label: "Your WSL versions:"
    description: "run `wsl --version` on Windows command prompt"
  validations:
    required: true
- type: markdown
  attributes:
    value: |
      **(Optional) Verifiy using the latest release of WSL/WSLg**:
      
      It is always good idea to verify the issue is still reproducible on the latest WSL/WSLg release whenever possible. WSL/WSLg can be updated from https://aka.ms/wslstorepage, or when Microsoft Store is not accessible from your environment, by downloading the latest release package (.msixbundle) from https://github.com/microsoft/WSL/releases.
- type: textarea
  id: reproduce-steps
  attributes:
    label: "Steps to reproduce:"
    placeholder: |
      1.
      2.
      3.
  validations:
    required: true
- type: textarea
  id: logs
  attributes:
    description: |
      Collect WSL logs if needed by following these instructions: https://github.com/Microsoft/WSL/blob/master/CONTRIBUTING.md#8-detailed-logs
      
      * Attach WSLg logs from  `/mnt/wslg`

      You can access the wslg logs using explorer at: `\\wsl$\<Distro-Name>\mnt\wslg` (e.g.: `\\wsl$\Ubuntu-20.04\mnt\wslg`)

      * `pulseaudio.log`
      * `weston.log`
      * `stderr.log`
    label: "WSL logs:"
    placeholder: |
      Drag and drop files into this input field to upload them.
  validations:
    required: false 
- type: textarea
  id: dumps
  attributes:
    label: "WSL dumps:"
    description: "Attach any dump files from `%tmp%\\wsl-crashes`, such as core.weston, if exists. If running an older version, the dump files can be found in `/mnt/wslg/dumps`"
  validations:
    required: false
- type: textarea
  id: expected-behavior
  attributes:
    label: "Expected behavior:"
    description: "A description of what you're expecting, possibly containing screenshots or reference material"
  validations:
    required: false
- type: textarea
  id: actual-behavior
  attributes:
    label: "Actual behavior:"
    description: "What's actually happening?"
  validations:
    required: true    


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: Report issues on WSL in general
    url: https://github.com/microsoft/WSL/issues/new/choose
    about: Please report issues not related to WSLg


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: "Feature request"
description: "Suggest a feature for WSLg"
labels: "enhancement"
body:
- type: textarea
  attributes:
    label: "Is your feature request related to a problem:"
    description: "A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]"
  validations:
    required: false
- type: textarea
  attributes:
    label: "Describe the solution you'd like:"
    description: "A clear and concise description of what you want to happen."
  validations:
    required: true
- type: textarea
  attributes:
    label: "Describe alternatives you've considered:"
    description: "A clear and concise description of any alternative solutions or features you've considered."
  validations:
    required: true
- type: textarea
  attributes:
    label: "Additional context:"
    description: "Add any other context or screenshots about the feature request here."
  validations:
    required: false


================================================
FILE: .gitignore
================================================
.vscode/*
vendor/*
!vendor/.preserve
out
tmp
*.nupkg
*.tar
*.vhd
WSLGd/*.d
WSLGd/*.o
WSLGd/WSLGd
WSLDVCPlugin/.vs
WSLDVCPlugin/x64
WSLDVCPlugin/ARM64/Debug/
WSLDVCPlugin/ARM64/Release/
WSLDVCPlugin/WSLDVCPlugin.aps

================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

This project welcomes contributions and suggestions. Most contributions require you to
agree to a Contributor License Agreement (CLA) declaring that you have the right to,
and actually do, grant us the rights to use your contribution. For details, visit
https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need
to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the
instructions provided by the bot. You will only need to do this once across all repositories using our CLA.

This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

# Building the WSLg System Distro

The heart of WSLg is what we call the WSL system distro. This is where the Weston compositor, XWayland and the PulseAudio server are running. The system distro runs these components and projects their communication sockets into the user distro. Every user distro is paired with a unique instance of the system distro. There is a single version of the system distro on disk which is instantiated in memory when a user distro is launched.

The system distro is essentially a Linux container packaged and distributed as a vhd. The system distro is accessible to the user, but is mounted read-only. Any changes made by the user to the system distro while it is running are discarded when WSL is restarted. Although a user can log into the system distro, it is not meant to be used as a general purpose user distro. The reason behind this choice is due to the way we service WSLg. When updating WSLg we simply replace the existing system distro with a new one. If the user had data embedded into the system distro vhd, this data would be lost.

For folks who want to tinker with or customize their system distro, we give the ability to run a private version of the system distro. When running a private version of WSLg, Windows will load and run your private and ignore the Microsoft published one. If you update your WSL setup (`wsl --update`), the Microsoft published WSLg vhd will be updated, but you will continue to be running your private. You can switch between the Microsoft pulished WSLg system distro and a private one at any time although it does require restarting WSL (`wsl --shutdown`).

The WSLg system distro is built using docker build. We essentially start from a [Azure Linux 3.0](https://github.com/microsoft/azurelinux) base image, install various packages, then build and install version of Weston, FreeRDP and PulseAudio from our mirror repo. This repository contains a Dockerfile and supporting tools to build the WSLg container and convert the container into an ext4 vhd that Windows will load as the system distro.

## Build instructions

0. Install and start Docker in a Linux or WSL 2 environment.

```
    sudo apt-get update
    sudo apt install docker.io golang-go
    sudo dockerd
```

1. Clone the WSLg project:

```
    git clone https://github.com/microsoft/wslg wslg
```

2. Clone the FreeRDP, Weston and PulseAudio mirror. These need to be located in a **vendor** sub-directory where you clone the wslg project (e.g. wslg/vendor), this is where our docker build script expects to find the source code. Make sure to checkout the **working** branch from each of these projects, the **main** branch references the upstream code.

    ```bash
    git clone https://github.com/microsoft/FreeRDP-mirror wslg/vendor/FreeRDP -b working
    git clone https://github.com/microsoft/weston-mirror wslg/vendor/weston -b working
    git clone https://github.com/microsoft/PulseAudio-mirror wslg/vendor/pulseaudio -b working
    git clone https://github.com/microsoft/DirectX-Headers.git wslg/vendor/DirectX-Headers-1.0 -b v1.608.0
    git clone https://gitlab.freedesktop.org/mesa/mesa.git wslg/vendor/mesa -b mesa-23.1.0
    ```

2. Create the VHD:

    2.1 From the parent directory where you cloned `wslg` clone `hcsshim` which contains `tar2ext4` and will be used to create the system distro vhd
    ```
    git clone --branch v0.8.9 --single-branch https://github.com/microsoft/hcsshim.git
    ```
    
    2.2 From the parent directory build and export the docker image:
    ```
    sudo docker build -t system-distro-x64  ./wslg  --build-arg SYSTEMDISTRO_VERSION=`git --git-dir=wslg/.git rev-parse --verify HEAD` --build-arg SYSTEMDISTRO_ARCH=x86_64
    sudo docker export `sudo docker create system-distro-x64` > system_x64.tar
    ```
    
    2.3 Create the system distro vhd using `tar2ext4`
    
    ```bash
    cd hcsshim/cmd/tar2ext4
    go run tar2ext4.go -vhd -i ../../../system_x64.tar -o ../../../system.vhd
    ```
    
    This will create system distro image `system.vhd`

## Installing a private version of the WSLg system distro

You can tell WSL to load a private version of WSLg by adding the following option in your `.wslconfig` file (located in `C:\Users\MyUser\.wslconfig`).

```
    [wsl2]
    systemDistro=C:\\Files\\system.vhd
```    
    
You need to restart WSL for this change to take effect. From an elevated command prompt execute `wsl --shutdown`. When WSL is launched again, Windows will load your private vhd as the system distro. 
    
## Inspecting the WSLg system distro at runtime

If the system distro isn't working correctly or you need to inspect what is running inside the system distro you can get a terminal into the system distro by running the following command from an elevated command prompt.

```
    wsl --system -d [DistroName]
```
There is an instance of the system distro running for every user distro running. `DistroName` refers to the name of the user distro for which you want the paired system distro. If you omit `DistroName`, you will get a terminal into the system distro paired with your default WSL user distro.

Please keep in mind that the system distro is loaded read-only from it's backing VHD. For example, if you need to install tools (say a debugger or an editor) in the system distro, you want to do this in the Dockerfile that builds the system distro so it gets into the private vhd that you are running. You can dynamically install new packages once your have a terminal into the system distro, but any changes you make will be discarded when WSL is restarted.

## Building a debug version

To build a debug version of the system distro, the docker build argument SYSTEMDISTRO_DEBUG_BUILD needs to be set and passed the value of "true". The following command would substitute the docker build command in step 3.2.2 of the "Build Instructions" section.

```
    sudo docker build -t system-distro-x64  ./wslg  --build-arg SYSTEMDISTRO_VERSION=`git --git-dir=wslg/.git rev-parse --verify HEAD` --build-arg SYSTEMDISTRO_ARCH=x86_64 --build-arg SYSTEMDISTRO_DEBUG_BUILD=true
```
The resulting system distro VHD will have useful development packages installed like gdb and will have compiled all runtime dependencies with the "debug" buildtype for Meson, rather than "release".

# mstsc plugin

On the Windows side of the world, WSLg leverages the native `mstsc.exe` RDP client and a plugin for that client which handles WSLg integration into the start menu. The source code for this plugin is available as open source as part of the WSLg repo [here](https://github.com/microsoft/wslg/tree/main/WSLDVCPlugin).

It was important for us building WSLg to ensure that all protocols between Linux and Windows be fully documented and available to everyone to reuse. While almost all of the communication over RDP between Linux/Weston and Windows goes through standard and officially documented [Windows Protocols](https://docs.microsoft.com/en-us/openspecs/windows_protocols/MS-WINPROTLP/92b33e19-6fff-496b-86c3-d168206f9845) associated with the RDP standard, we needed just a bit of custom communication between Linux and Windows to handle integration into the start menu. We thought about adding some official RDP protocol for this, but this was too specific to WSLg and not broadly applicable to arbitrary RDP based solution.

So instead we opted to use a custom RDP channel between the WSLg RDP Server running inside of Weston and the WSLg RDP plugin hosted by mstsc. Such custom dynamic channel are part of the RDP specification, but requires that both the RDP server and RDP client support that channel for it to be used. This is the path we took for WSLg where Weston exposes a custom RDP channel for WSLg integration. In the spirit of fully documenting all channels of communication between Linux and Windows, we're making the source code for plugin which handles the Windows side of this custom RDP channel available as part of the WSLg project.

This custom channel and associated plugin are quite small and simple. In a nutshell, Weston enumerates all installed applications inside of the user Linux distro (i.e. application which have an explicit [desktop file](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html)) and exposes this list of applications, along with command line to launch them and icon to represent them, over this custom RDP channel. The mstsc plugin processes that list and creates links in the Windows Start Menu for these applications so they can be launch directly from it.

## Building the mstsc plugin

The [source code](https://github.com/microsoft/wslg/tree/main/WSLDVCPlugin) for the plugin has a visual studio project file that can be use to build it. You can download and install the free [Visual Studio Community Edition](https://visualstudio.microsoft.com/vs/community/) to build it.

## Registering a private mstsc plugin

The plugin is registered with mstsc through the registry. By default this is set to load the plugin that ships as part of the official WSLg package. If you need to run a private, you'll nee to modify this registry key to reference your privately built plugin, for example using a registry file like below.

```
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Default\AddIns\WSLDVCPlugin]
"Name"="C:\\users\\MyUser\\Privates\\WSLDVCPlugin.dll"
```


================================================
FILE: Dockerfile
================================================
# Create a builder image with the compilers, etc. needed
FROM mcr.microsoft.com/azurelinux/base/core:3.0 AS build-env

# Install all the required packages for building. This list is probably
# longer than necessary.
RUN echo "== Install Git/CA certificates ==" && \
    tdnf install -y \
        git \
        ca-certificates

RUN echo "== Install Core dependencies ==" && \
    tdnf install -y \
        alsa-lib \
        alsa-lib-devel  \
        autoconf  \
        automake  \
        binutils  \
        bison  \
        build-essential  \
        cairo \
        cairo-devel \
        clang  \
        clang-devel  \
        cmake  \
        dbus  \
        dbus-devel  \
        dbus-glib  \
        dbus-glib-devel  \
        diffutils  \
        elfutils-devel  \
        file-libs  \
        flex  \
        fontconfig-devel  \
        gawk  \
        gcc  \
        gettext  \
        glibc-devel  \
        glib-schemas \
        gobject-introspection  \
        gobject-introspection-devel  \
        harfbuzz  \
        harfbuzz-devel  \
        kernel-headers  \
        intltool \
        libatomic_ops  \
        libcap-devel  \
        libffi  \
        libffi-devel  \
        libgudev  \
        libgudev-devel  \
        libjpeg-turbo  \
        libjpeg-turbo-devel  \
        libltdl  \
        libltdl-devel  \
        libpng-devel  \
        librsvg2-devel \
        libtiff  \
        libtiff-devel  \
        libusb  \
        libusb-devel  \
        libwebp  \
        libwebp-devel  \
        libxml2 \
        libxml2-devel  \
        make  \
        meson  \
        newt  \
        nss  \
        nss-libs  \
        openldap  \
        openssl-devel  \
        pam-devel  \
        pango  \
        pango-devel  \
        patch  \
        perl-XML-Parser \
        polkit-devel  \
        python3-devel \
        python3-mako  \
        python3-markupsafe \
        sed \
        sqlite-devel \
        systemd-devel  \
        tar \
        unzip  \
        vala  \
        vala-devel  \
        vala-tools  \
        zlib-devel

RUN echo "== Install UI dependencies ==" && \
    tdnf    install -y \
            libdrm-devel \
            libepoxy-devel \
            libevdev \
            libevdev-devel \
            libinput \
            libinput-devel \
            libpciaccess-devel \
            libSM-devel \
            libsndfile \
            libsndfile-devel \
            libXcursor \
            libXcursor-devel \
            libXdamage-devel \
            libXfont2-devel \
            libXi \
            libXi-devel \
            libxkbcommon-devel \
            libxkbfile-devel \
            libXrandr-devel \
            libxshmfence-devel \
            libXtst \
            libXtst-devel \
            libXxf86vm-devel \
            wayland-devel \
            wayland-protocols-devel \
            xkbcomp \
            xkeyboard-config \
            xorg-x11-server-Xwayland-devel \
            xorg-x11-util-macros

# Create an image with builds of FreeRDP and Weston
FROM build-env AS dev

ARG WSLG_VERSION="<current>"
ARG WSLG_ARCH="x86_64"
ARG SYSTEMDISTRO_DEBUG_BUILD
ARG FREERDP_VERSION=2

WORKDIR /work
RUN echo "WSLg (" ${WSLG_ARCH} "):" ${WSLG_VERSION} > /work/versions.txt
RUN echo "Built at:" `date --utc` >> /work/versions.txt

RUN echo "Azure Linux:" `cat /etc/os-release | head -2 | tail -1` >> /work/versions.txt

#
# Build runtime dependencies.
#

ENV BUILDTYPE=${SYSTEMDISTRO_DEBUG_BUILD:+debug}
ENV BUILDTYPE=${BUILDTYPE:-debugoptimized}
RUN echo "== System distro build type:" ${BUILDTYPE} " =="

ENV BUILDTYPE_NODEBUGSTRIP=${SYSTEMDISTRO_DEBUG_BUILD:+debug}
ENV BUILDTYPE_NODEBUGSTRIP=${BUILDTYPE_NODEBUGSTRIP:-release}
RUN echo "== System distro build type (no debug strip):" ${BUILDTYPE_NODEBUGSTRIP} " =="

# FreeRDP is always built with RelWithDebInfo
ENV BUILDTYPE_FREERDP=${BUILDTYPE_FREERDP:-RelWithDebInfo}
RUN echo "== System distro build type (FreeRDP):" ${BUILDTYPE_FREERDP} " =="

ENV WITH_DEBUG_FREERDP=${SYSTEMDISTRO_DEBUG_BUILD:+ON}
ENV WITH_DEBUG_FREERDP=${WITH_DEBUG_FREERDP:-OFF}
RUN echo "== System distro build type (FreeRDP Debug Options):" ${WITH_DEBUG_FREERDP} " =="

ENV DESTDIR=/work/build
ENV PREFIX=/usr
ENV PKG_CONFIG_PATH=${DESTDIR}${PREFIX}/lib/pkgconfig:${DESTDIR}${PREFIX}/lib/${WSLG_ARCH}-linux-gnu/pkgconfig:${DESTDIR}${PREFIX}/share/pkgconfig
ENV C_INCLUDE_PATH=${DESTDIR}${PREFIX}/include/freerdp${FREERDP_VERSION}:${DESTDIR}${PREFIX}/include/winpr${FREERDP_VERSION}:${DESTDIR}${PREFIX}/include/wsl/stubs:${DESTDIR}${PREFIX}/include
ENV CPLUS_INCLUDE_PATH=${C_INCLUDE_PATH}
ENV LIBRARY_PATH=${DESTDIR}${PREFIX}/lib
ENV LD_LIBRARY_PATH=${LIBRARY_PATH}
ENV CC=/usr/bin/gcc
ENV CXX=/usr/bin/g++

# Setup DebugInfo folder
COPY debuginfo /work/debuginfo
RUN chmod +x /work/debuginfo/gen_debuginfo.sh

# Build DirectX-Headers
COPY vendor/DirectX-Headers-1.0 /work/vendor/DirectX-Headers-1.0
WORKDIR /work/vendor/DirectX-Headers-1.0
RUN /usr/bin/meson --prefix=${PREFIX} build \
        --buildtype=${BUILDTYPE_NODEBUGSTRIP} \
        -Dbuild-test=false && \
    ninja -C build -j8 install && \
    echo 'DirectX-Headers:' `git --git-dir=/work/vendor/DirectX-Headers-1.0/.git rev-parse --verify HEAD` >> /work/versions.txt

# Build mesa with the minimal options we need.
COPY vendor/mesa /work/vendor/mesa
WORKDIR /work/vendor/mesa
RUN /usr/bin/meson --prefix=${PREFIX} build \
        --buildtype=${BUILDTYPE_NODEBUGSTRIP} \
        -Dgallium-drivers=swrast,d3d12 \
        -Dvulkan-drivers= \
        -Dllvm=disabled && \
    ninja -C build -j8 install && \
    echo 'mesa:' `git --git-dir=/work/vendor/mesa/.git rev-parse --verify HEAD` >> /work/versions.txt

# Build PulseAudio
COPY vendor/pulseaudio /work/vendor/pulseaudio
WORKDIR /work/vendor/pulseaudio
RUN /usr/bin/meson --prefix=${PREFIX} build \
        --buildtype=${BUILDTYPE_NODEBUGSTRIP} \
        -Ddatabase=simple \
        -Ddoxygen=false \
        -Dgsettings=disabled \
        -Dtests=false && \
    ninja -C build -j8 install && \
    echo 'pulseaudio:' `git --git-dir=/work/vendor/pulseaudio/.git rev-parse --verify HEAD` >> /work/versions.txt

# Build FreeRDP
COPY vendor/FreeRDP /work/vendor/FreeRDP
WORKDIR /work/vendor/FreeRDP
RUN cmake -G Ninja \
        -B build \
        -DCMAKE_INSTALL_PREFIX=${PREFIX} \
        -DCMAKE_INSTALL_LIBDIR=${PREFIX}/lib \
        -DCMAKE_BUILD_TYPE=${BUILDTYPE_FREERDP} \
        -DWITH_DEBUG_ALL=${WITH_DEBUG_FREERDP} \
        -DWITH_ICU=ON \
        -DWITH_SERVER=ON \
        -DWITH_CHANNEL_GFXREDIR=ON \
        -DWITH_CHANNEL_RDPAPPLIST=ON \
        -DWITH_CLIENT=OFF \
        -DWITH_CLIENT_COMMON=OFF \
        -DWITH_CLIENT_CHANNELS=OFF \
        -DWITH_CLIENT_INTERFACE=OFF \
        -DWITH_LIBSYSTEMD=OFF \
        -DWITH_WAYLAND=OFF \
        -DWITH_X11=OFF \
        -DWITH_PROXY=OFF \
        -DWITH_SHADOW=OFF \
        -DWITH_SAMPLE=OFF && \
    ninja -C build -j8 install && \
    echo 'FreeRDP:' `git --git-dir=/work/vendor/FreeRDP/.git rev-parse --verify HEAD` >> /work/versions.txt

WORKDIR /work/debuginfo
RUN if [ -z "$SYSTEMDISTRO_DEBUG_BUILD" ] ; then \
        echo "== Strip debug info: FreeRDP ==" && \
        /work/debuginfo/gen_debuginfo.sh /work/debuginfo/FreeRDP${FREERDP_VERSION}.list /work/build; \
    fi

# Build rdpapplist RDP virtual channel plugin
COPY rdpapplist /work/rdpapplist
WORKDIR /work/rdpapplist
RUN /usr/bin/meson --prefix=${PREFIX} build \
        --buildtype=${BUILDTYPE} && \
    ninja -C build -j8 install

WORKDIR /work/debuginfo
RUN if [ -z "$SYSTEMDISTRO_DEBUG_BUILD" ] ; then \
        echo "== Strip debug info: rdpapplist ==" && \
        /work/debuginfo/gen_debuginfo.sh /work/debuginfo/rdpapplist.list /work/build; \
    fi

# Build Weston
COPY vendor/weston /work/vendor/weston
WORKDIR /work/vendor/weston
RUN /usr/bin/meson --prefix=${PREFIX} build \
        --buildtype=${BUILDTYPE} \
        -Dbackend-default=rdp \
        -Dbackend-drm=false \
        -Dbackend-drm-screencast-vaapi=false \
        -Dbackend-headless=false \
        -Dbackend-wayland=false \
        -Dbackend-x11=false \
        -Dbackend-fbdev=false \
        -Dcolor-management-colord=false \
        -Dscreenshare=false \
        -Dsystemd=false \
        -Dwslgd=true \
        -Dremoting=false \
        -Dpipewire=false \
        -Dshell-fullscreen=false \
        -Dcolor-management-lcms=false \
        -Dshell-ivi=false \
        -Dshell-kiosk=false \
        -Ddemo-clients=false \
        -Dsimple-clients=[] \
        -Dtools=[] \
        -Dresize-pool=false \
        -Dwcap-decode=false \
        -Dtest-junit-xml=false && \
    ninja -C build -j8 install && \
    echo 'weston:' `git --git-dir=/work/vendor/weston/.git rev-parse --verify HEAD` >> /work/versions.txt

WORKDIR /work/debuginfo
RUN if [ -z "$SYSTEMDISTRO_DEBUG_BUILD" ] ; then \
        echo "== Strip debug info: weston ==" && \
        /work/debuginfo/gen_debuginfo.sh /work/debuginfo/weston.list /work/build; \
    fi

# Build WSLGd Daemon
ENV CC=/usr/bin/clang
ENV CXX=/usr/bin/clang++

COPY WSLGd /work/WSLGd
WORKDIR /work/WSLGd
RUN /usr/bin/meson --prefix=${PREFIX} build \
        --buildtype=${BUILDTYPE} && \
    ninja -C build -j8 install

WORKDIR /work/debuginfo
RUN if [ -z "$SYSTEMDISTRO_DEBUG_BUILD" ] ; then \
        echo "== Strip debug info: WSLGd ==" && \
        /work/debuginfo/gen_debuginfo.sh /work/debuginfo/WSLGd.list /work/build; \
    fi

# Gather debuginfo to a tar file
WORKDIR /work/debuginfo
RUN if [ -z "$SYSTEMDISTRO_DEBUG_BUILD" ] ; then \
        echo "== Compress debug info: /work/debuginfo/system-debuginfo.tar.gz ==" && \
        tar -C /work/build/debuginfo -czf system-debuginfo.tar.gz ./ ; \
    fi

########################################################################
########################################################################

## Create the distro image with just what's needed at runtime

FROM mcr.microsoft.com/azurelinux/base/core:3.0 AS runtime

RUN echo "== Install Core/UI Runtime Dependencies ==" && \
    tdnf    install -y \
            busybox \
            ca-certificates \
            cairo \
            chrony \
            containerd2 \
            containernetworking-plugins \
            runc \
            dbus \
            dbus-glib \
            dhcpcd \
            docker-buildx \
            docker-cli \
            e2fsprogs \
            freefont \
            gzip \
            icu \
            iptables \
            kmod \
            libinput \
            libjpeg-turbo \
            libltdl \
            libpng \
            librsvg2 \
            libsndfile \
            libwayland-client \
            libwayland-server \
            libwayland-cursor \
            libwebp \
            libXcursor \
            libxkbcommon \
            libXrandr \
            iproute \
            moby-engine \
            nftables \
            pango \
            procps-ng \
            rpm \
            sed \
            systemd-libs \
            tar \
            tzdata \
            util-linux \
            xcursor-themes \
            xorg-x11-server-Xwayland \
            xorg-x11-server-utils

# Install busybox utilities
RUN /sbin/busybox --install -s

# Remove unnecessary packages and files to reduce image size
ARG SYSTEMDISTRO_DEBUG_BUILD
RUN if [ -z "$SYSTEMDISTRO_DEBUG_BUILD" ] ; then \
        echo "== Removing unnecessary packages ==" && \
        # Remove build tools and packages not needed at runtime \
        rpm -e --nodeps \
            cracklib-dicts \
            gcc \
            gcc-c++ \
            libpkgconf \
            llvm \
            perl \
            pkgconf \
            pkgconf-m4 \
            pkgconf-pkg-config \
            python3 \
            python3-libs && \
        # Remove all perl subpackages \
        rpm -e --nodeps $(rpm -qa | grep -- '^perl-') && \
        # Remove all -devel packages \
        rpm -e --nodeps $(rpm -qa | grep -- '-devel') && \
        # Remove systemd components (except systemd-libs which is needed by weston) \
        rpm -e --nodeps $(rpm -qa | grep -- '^systemd-' | grep -v systemd-libs) && \
        # Remove orphaned packages \
        tdnf autoremove -y && \
        echo "== Removing unnecessary files ==" && \
        # Remove docs, man pages, locales, gtk-doc \
        rm -rf /usr/share/man /usr/share/info /usr/share/locale /usr/share/gtk-doc && \
        find /usr/share/doc -mindepth 1 -maxdepth 1 -type d -exec rm -rf {} + && \
        # Remove unused Mesa driver \
        rm -f /usr/lib64/dri/virtio_gpu_dri.so && \
        # Remove hardware database (not needed in WSL) \
        rm -rf /usr/share/hwdata/* && \
        # Remove temporary files, logs, caches, and systemd catalog \
        rm -rf /tmp/* /var/tmp/* /var/log/* /var/cache/* /usr/lib/systemd/catalog/*; \
    else \
        echo "== Install development aid packages ==" && \
        tdnf install -y \
             gdb \
             azurelinux-repos-debug \
             nano \
             vim \
             wayland-debuginfo \
             xorg-x11-server-debuginfo; \
    fi

# Clear the tdnf cache to make the image smaller
RUN tdnf clean all

# Create wslg user.
RUN useradd -u 1000 --create-home wslg && \
    mkdir /home/wslg/.config && \
    chown wslg /home/wslg/.config

# Copy config files.
COPY config/wsl.conf /etc/wsl.conf
COPY config/weston.ini /home/wslg/.config/weston.ini
COPY config/local.conf /etc/fonts/local.conf

# Copy default icon file.
COPY resources/linux.png /usr/share/icons/wsl/linux.png

# Copy the built artifacts from the build stage.
COPY --from=dev /work/build/usr/ /usr/
COPY --from=dev /work/build/etc/ /etc/

# Append WSLg setttings to pulseaudio.
COPY config/default_wslg.pa /etc/pulse/default_wslg.pa
RUN cat /etc/pulse/default_wslg.pa >> /etc/pulse/default.pa
RUN rm /etc/pulse/default_wslg.pa

# Copy the licensing information for PulseAudio
COPY --from=dev /work/vendor/pulseaudio/GPL \
                /work/vendor/pulseaudio/LGPL \
                /work/vendor/pulseaudio/LICENSE \
                /work/vendor/pulseaudio/NEWS \
                /work/vendor/pulseaudio/README /usr/share/doc/pulseaudio/

# Copy the licensing information for Weston
COPY --from=dev /work/vendor/weston/COPYING /usr/share/doc/weston/COPYING

# Copy the licensing information for FreeRDP
COPY --from=dev /work/vendor/FreeRDP/LICENSE /usr/share/doc/FreeRDP/LICENSE

# copy the documentation and licensing information for mesa
COPY --from=dev /work/vendor/mesa/docs /usr/share/doc/mesa/

COPY --from=dev /work/versions.txt /etc/versions.txt

CMD /usr/bin/WSLGd


================================================
FILE: LICENSE
================================================
    MIT License

    Copyright (c) Microsoft Corporation.

    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:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    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


================================================
FILE: Microsoft.WSLg.nuspec
================================================
<?xml version="1.0"?>
<!--
***********************************************************************************************
Copyright (C) Microsoft Corporation. All rights reserved.
***********************************************************************************************
-->
<package>
  <metadata>
    <id>Microsoft.WSLg</id>
    <version>0.2.12</version>
    <authors>Microsoft</authors>
    <owners>Microsoft, WSL Team</owners>
    <projectUrl>https://github.com/microsoft/wslg/</projectUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>WSLg support package.</description>
    <summary>Enabling the Windows Subsystem for Linux to include support for Wayland and X server related scenarios.</summary>
    <releaseNotes>ebad9c1</releaseNotes>
    <copyright>Copyright © Microsoft Corporation 2021</copyright>
    <tags>native</tags>
  </metadata>
  <files>
    <file src="Microsoft.WSLg.targets" target="build/Microsoft.WSLg.targets" />
    <file src="package/system_x64.vhd" target="build/native/bin/x64/system.vhd" />
    <file src="package/system-debuginfo_x64.tar.gz" target="build/native/bin/x64/system-debuginfo.tar.gz" />
    <file src="package/WSLDVCPlugin_x64.dll" target="build/native/bin/x64/WSLDVCPlugin.dll" />
    <file src="package/WSLDVCPlugin_x64.pdb" target="build/native/bin/x64/WSLDVCPlugin.pdb" />
    <file src="package/system_ARM64.vhd" target="build/native/bin/arm64/system.vhd" />
    <file src="package/system-debuginfo_ARM64.tar.gz" target="build/native/bin/arm64/system-debuginfo.tar.gz" />
    <file src="package/WSLDVCPlugin_ARM64.dll" target="build/native/bin/arm64/WSLDVCPlugin.dll" />
    <file src="package/WSLDVCPlugin_ARM64.pdb" target="build/native/bin/arm64/WSLDVCPlugin.pdb" />

    <file src="package/wslg.rdp" target="build/native/bin/wslg.rdp" />
    <file src="package/wslg_desktop.rdp" target="build/native/bin/wslg_desktop.rdp" />
  </files>
</package>


================================================
FILE: Microsoft.WSLg.targets
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
***********************************************************************************************
Copyright (C) Microsoft Corporation. All rights reserved.
***********************************************************************************************
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <None Include="$(MSBuildThisFileDirectory)native\bin\$(Platform)\system.vhd">
      <Link>system.vhd</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="$(MSBuildThisFileDirectory)native\bin\$(Platform)\WSLDVCPlugin.dll">
      <Link>WSLDVCPlugin.dll</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="$(MSBuildThisFileDirectory)native\bin\wslg.rdp">
      <Link>wslg.rdp</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Include="$(MSBuildThisFileDirectory)native\bin\wslg_desktop.rdp">
      <Link>wslg_desktop.rdp</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
</Project>


================================================
FILE: README.md
================================================
# Welcome to WSLg
WSLg is short for *Windows Subsystem for Linux GUI* and the purpose of the project is to enable support for running Linux GUI applications (X11 and Wayland) on Windows in a fully integrated desktop experience. 

WSLg provides an integrated experience for developers, scientists or enthusiasts that prefer or need to run Windows on their PC but also need the ability to run tools or applications which work best, or exclusively, in a Linux environment. While users can accomplish this today using a multiple system setup, with individual PC dedicated to Windows and Linux, virtual machine hosting either Windows or Linux, or an XServer running on Windows and projected into WSL, WSLg provides a more integrated, user friendly and productive alternative.

WSLg strives to make Linux GUI applications feel native and natural to use on Windows. From integration into the Start Menu for launch to appearing in the task bar, alt-tab experience to enabling cut/paste across Windows and Linux applications, WSLg enables a seamless desktop experience and workflow leveraging Windows and Linux applications.

![WSLg Integrated Desktop](/docs/WSLg_IntegratedDesktop.png)

# Installing WSLg

## Pre-requisites

- WSLg is supported on both Windows 11 and Windows 10. Windows 10 users must ensure their Windows 10 installation is fully up to date by visiting Windows Update and installing all available updates.

- WSLg is available both as part of the Windows 11 WSL inbox support as well as through the  [Windows Subsystem for Linux from the Microsoft Store](https://aka.ms/wslstorepage). It is highly recommended to use the Microsoft Store version of WSL, which supports both Windows 10 and Windows 11, and contains the most up to date version of WSL and WSLg.

- Make sure to update your graphics driver to the latest driver available from your GPU manufacturer's website to benefit from GPU acceleration in your WSL environment.
     
   
## Install instructions (Fresh Install - no prior WSL installation)

From a command prompt with administrator privileges, run the command `wsl --install -d Ubuntu`, then reboot if prompted.

After reboot the installation will continue. You'll be asked to enter a username and password. These will be your Linux credentials, they can be anything you want and don't have to match your Windows credentials.

Voilà! WSL and WSLg are installed and ready to be used!

## Install instructions (Existing WSL install)

If you have an existing WSL installation without WSLg and want to update to the latest version of WSL which includes WSLg, run the command `wsl --update` from an elevated command prompt. 

Please note that WSLg is only compatible with WSL 2 and will not work for WSL distribution configured to work in WSL 1 mode. Verify that your Linux distro is configured for running in WSL 2 mode, if not switch to WSL 2. While you can continue to run Linux distro in WSL 1 mode after installing WSLg if you so desired, a distro configured to run in WSL 1 mode will not be able to communicate with WSLg and will not be able to run GUI applications.

You can list your currently installed distro and the version of WSL they are configured for using the following command from an elevated command prompt.

```powershell
   wsl --list -v
```
If running in version 1 mode, switch to version 2. This can take a while.

```powershell
   wsl --set-version _distro_name_ 2
```

Restart WSL by running this command from an elevated command prompt, make sure to save any pending work first:

```powershell
    wsl --shutdown
```

## Updating WSL + WSLg

To update to the latest version of WSL and WSLg released for preview, simply run `wsl --update` from an elevated command prompt or powershell. 

You'll need to restart WSL for the changes to take effect. You can restart WSL by running `wsl --shutdown` from an elevated command prompt. If WSL was currently running, it will shutdown, make sure to first save any in progress work! WSL will be automatically restarted the next time you launch a WSL application or terminal.

## First Launch

If you have installed the `Ubuntu` Linux distro per these instructions, you'll find an `Ubuntu` icon in your start menu, launch it. This will launch the WSL 2 VM, launch the Ubuntu WSL distro in that VM and give you a terminal to interact with it. Voilà! You're running Linux on Windows! 

If you would like to explore additional Linux distributions built for WSL, you can use the `wsl --list --online` command from an elevated command prompt to enumerate the list of available distributions for your system. You can have multiple Linux distributions installed within WSL and they will happily coexist side-by-side, so don't be scared to experiment and try things out.

Congrats you are done and ready to use GUI apps! 

## Install and run GUI apps

If you want to get started with some GUI apps, you can run the following commands from your Linux terminal to download and install some popular applications. If you are using a different distribution than Ubuntu, it may be using a different package manager. 

```powershell

## Update list of available packages
sudo apt update

## Gedit
sudo apt install gedit -y

## GIMP
sudo apt install gimp -y

## Nautilus
sudo apt install nautilus -y

## VLC
sudo apt install vlc -y

## X11 apps
sudo apt install x11-apps -y

## Google Chrome
cd /tmp
sudo wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo dpkg -i google-chrome-stable_current_amd64.deb 
sudo apt install --fix-broken -y
sudo dpkg -i google-chrome-stable_current_amd64.deb

## Microsoft Teams
cd /tmp
sudo curl -L -o "./teams.deb" "https://teams.microsoft.com/downloads/desktopurl?env=production&plat=linux&arch=x64&download=true&linuxArchiveType=deb"
sudo apt install ./teams.deb -y

## Microsoft Edge Dev Browser
sudo curl https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-dev/microsoft-edge-dev_118.0.2060.1-1_amd64.deb -o /tmp/edge.deb
sudo apt install /tmp/edge.deb -y
```

Once these applications are installed, you'll find them in your start menu under the distro name. For example `Ubuntu -> Microsoft Edge`. You can also launch these from your terminal window using the commands:

* `xcalc`, `xclock`, `xeyes` 
* `gimp`
* `gedit ~/.bashrc` 
* `nautilus`
* `vlc`
* `google-chrome`
* `teams`
* `microsoft-edge`

# WSLg Architecture Overview

![WSLg Architecture Overview](/docs/WSLg_ArchitectureOverview.png)

## User Distro
The user distro is essentially the WSL distribution you are using for your Linux work. You can use the command `wsl --list --online` from an elevated Windows command prompt to list the WSL distributions available on your system. You can run multiple user distros side-by-side and they will peacefully coexist, so don't be afraid of trying out new distro. Each user distro will be paired with a unique instance of the system distro, but you can still interact across GUI applications running in different user distro seamlessly, such as cut/paste between them. The underlying containerization of the various userspace should be invisible to you.

All user and system distros for a particular Windows user run within the same WSL virtual machine against a single instance of the Linux kernel. Different Windows users on a PC have their own VM and instance of WSL. Your Linux environment is guaranteed to always be your own and not shared with other Windows users on the same PC.

## WSLg System Distro
The system distro is where all of the magic happens. The system distro is a containerized Linux environment where the WSLg XServer, Wayland server and Pulse Audio server are running. Communication socket for each of these servers are projected into the user distro so client applications can connect to them. We preconfigure the user distro environment variables DISPLAY, WAYLAND_DISPLAY and PULSE_SERVER to refer these servers by default so WSLg lights up out of the box.

Users wanting to use different servers than the one provided by WSLg can change these environment variables. User can also choose to turn off the system distro entirely by adding the following entry in their `.wslconfig` file (located at `c:\users\MyUser\.wslconfig`). This will turn off support for GUI applications in WSL.

```
[wsl2]
guiApplications=false
```

The system distro is based on the Microsoft [Azure Linux 3.0](https://github.com/microsoft/azurelinux). This is a minimal Linux environment, just enough to run the various pieces of WSLg. For details on how to build and deploy a private system distro please see our [build instructions](CONTRIBUTING.md).

Every WSL 2 user distro is paired with its own instance of the system distro. The system distro runs partially isolated from the user distro to which it is paired, in its own NS/PID/UTS namespace but shares other namespaces such as IPC, to allow for shared memory optimization across the boundary. 

While a user can get a terminal into the system distro, the system distro is not meant to be used directly by users. Every instance of the system distro is loaded read-only from its backing VHD. Any modifications, made to the in-memory instance of the system distro (such as installing new packages or creating a new file), are effectively discarded when WSL is restarted. The reason we do this is to enable a servicing model for the system distro where we replace the old one with the new one without having to worry about migrating any user data contained within. We use a read-only mapping such that the user gets a well known discard behavior on any changes, every time WSL is restarted, instead of getting a surprise when WSL is serviced. 

Although the Microsoft published WSLg system distro as read-only, we do want to encourage folks to tinker with it and experiment. Although we expect very few folks to actually need or want to do that, we've shared detailed instruction on our [contributing](CONTRIBUTING.md) page on how to both build and deploy a private version of the system distro. Most users who just want to use GUI applications in WSL don't need to worry about those details.

## WSLGd
**WSLGd** is the first process to launch after **init**. **WSLGd** launches **Weston** (with XWayland), **PulseAudio** and establishes the RDP connection by launching **mstsc.exe** on the host in silent mode. The RDP connection will remain active and ready to show a new GUI applications being launch on a moment's notice, without any connection establishment delays. **WSLGd** then monitors these processes and if they exit by error (say as a result of a crash), it automatically restarts them.

## Weston
Weston is the Wayland project reference compositor and the heart of WSLg. For WSLg, we've extended the existing RDP backend of libweston to teach it how to remote applications rather than monitor/desktop. We've also added various functionality to it, such as support for multi-monitor, cut/paste, audio in/out, etc...

The application integration is achieved through an RDP technology called RAIL (Remote Application Integrated Locally) and VAIL (Virtualized Application Integrated Locally). The main difference between RAIL and VAIL is how pixels are transported across from the RDP server to the RDP client. In RAIL, it is assumed that the Server and Client are running on different physical systems communicating over the network and thus pixels need to be copied over the RDP transport. In VAIL, it is understood that the Server and Client are on the same physical system and can share memory across the Guest/Host VM boundary. We've added support for both RAIL and VAIL to the libweston RDP backend, although for WSLg only the VAIL support is effectively used. While building WSLg, we first implemented RAIL while the necessary pieces enabling the switch to VAIL were being developed in parallel. We decided to keep that support in as it could reuse in other interesting scenarios outside of WSLg, for example for remoting application from a Pi running Linux. To share memory between the Linux guest and Windows host we use virtio-fs.

### RAIL-Shell
Weston is modular and has various shells today, such as the desktop shell, fullscreen shell (aka kiosk), and automative shell. For WSLg we introduced a new shell called the RAIL Shell. The purpose of the RAIL Shell is to help with the remoting of individual windows from Linux to Windows, as such the shell is very simplistic and doesn't involve any actual widgets or shell owned pixels.

## FreeRDP
Weston leverages FreeRDP to implement its backend RDP Server. FreeRDP is used to encode all communications going from the RDP Server (in Weston) to the RDP Client (mstsc on Windows) according to the RDP protocol specifications. It is also used to decode all traffic coming from the RDP Client into the RDP server.

## Pulse Audio Plugin
For audio in (microphone) and out (speakers/headphone) WSLg runs a PulseAudio server. WSLg uses a [sink plugin](https://github.com/microsoft/pulseaudio-mirror/blob/working/src/modules/rdp/module-rdp-sink.c) for audio out, and a [source plugin](https://github.com/microsoft/pulseaudio-mirror/blob/working/src/modules/rdp/module-rdp-source.c) for audio in. These plugins effectively transfer audio samples between the PulseServer and the Weston RDP Server. The audio streams are merged by the Weston RDP Server onto the RDP transport, effectively enabling audio in/out in the Weston RDP backend across all scenarios (Desktop/RAIL/VAIL style remoting), including WSLg.

## WSL Dynamic Virtual Channel Plugin (WSLDVCPlugin)
WSLg makes use of a custom RDP virtual channel between the Weston RDP Server and the mstsc RDP Client running on the Windows host. This channel is used by Weston to enumerate all Linux GUI applications (i.e. applications which have a desktop file entry of type gui) along with their launch command line and icon. The open source [WSLDVCPlugin](https://github.com/microsoft/wslg/tree/main/WSLDVCPlugin) processes the list of Linux GUI applications sent over this channel and creates links for them in the Windows start menu.

# OpenGL accelerated rendering in WSLg

While WSLg works with or without virtual GPU support, if you intend to run graphics intensive applications such as Blender or Gazebo, it is best to be running on a system with a GPU and driver that can support WSL. An overview of our vGPU architecture and how we make it possible for Linux applications to access the GPU in WSL is available at our [DirectX blog](https://devblogs.microsoft.com/directx/directx-heart-linux/).

Support for OpenGL accelerated rendering is made possible through the work our D3D team has done with Collabora and the Mesa community on creating a [d3d12 Gallium driver](https://devblogs.microsoft.com/directx/in-the-works-opencl-and-opengl-mapping-layers-to-directx/). 

Support for Linux, including support for WSLg, has been upstream and part of the Mesa 21.0 release. To take advantage of this acceleration, you'll need to update the version of Mesa installed in your user distro. It also requires that your distro vendor chose to build and publish the new d3d12 Gallium driver to their package repository. We're working with the various WSL distro publishers to inform them of these changes.

Please note that for the first release of WSLg, vGPU interops with the Weston compositor through system memory. If running on a discrete GPU, this effectively means that the rendered data is copied from VRAM to system memory before being presented to the compositor within WSLg, and uploaded onto the GPU again on the Windows side. As a result, there is a performance penalty proportionate to the presentation rate. At very high frame rates such as 600fps on a discrete GPU, that overhead can be as high as 50%. At lower frame rate or on integrated GPU, performance much closer to native can be achieved depending on the workload. Using a vGPU still provides a very significant performance and experience improvement over using a software renderer despite this v1 limitation.

# WSLg Code Flow
WSLg builds on the great work of the Linux community and makes use of a large number of open source projects. Most components are used as-is from their upstream version and didn't require any changes to light up in WSLg. Some components at the heart of WSLg, in particular Weston, FreeRDP and PulseAudio, required changes to enable the rich WSLg integration. These changes aren't yet upstream. Microsoft is working with the community to share these contributions back with each project such that, over time, WSLg can be built from upstream component directly, without the need for any WSLg specific modifications.

All of these in-flight contributions are kept in Microsoft mirror repos. We keep these mirrors up to date with upstream releases and stage our WSLg changes in those repos. WSLg pulls and builds code from these mirror repos as part of our Insider WSLg Preview releases. These mirrors are public and accessible to everyone. Curious developers can take a peek at early stages of our contribution by looking at code in those mirrors, keeping in mind that the final version of the code will likely look different once the contribution reaches the upstream project and is adapted based on the feedback receives by the various project owners. All of our mirrors follow the same model. There is a **main** branch which correspond to the upstream branch at our last synchronization point. We update the **main** branch from time to time to pick update from the upstream project. There is also a **working** branch that contains all of our in-flight changes. WSLg is built using the **working** branch from each of the mirror projects.

The projects that WSLg maintains mirrors for will change over time as in-flight contributions evolve. Once some contributions are upstream, it may no longer be necessary to maintain a mirror, at which point it will be removed and WSLg will start to leverage the upstream version of the component directly. As we light up new functionality in WSLg, new mirrors may be introduced to stage contributions to new components. As such, expect the list of mirrors to change overtime.

At this point in time, we have the following project mirrors for currently in-flight contributions.

| Project | Upstream Repo | WSLg Mirror |
|---|---|---|
| Weston | https://github.com/wayland-project/weston | https://github.com/microsoft/Weston-mirror|
| FreeRDP | https://github.com/FreeRDP/FreeRDP | https://github.com/microsoft/FreeRDP-mirror |
| PulseAudio | https://github.com/pulseaudio/pulseaudio | https://github.com/microsoft/PulseAudio-mirror |

The following is a high level overview of the currently in-flight contributions to each project contained within these mirrors.

## Weston
WSLg leverages Weston as the Wayland compositor bridging the Linux and Windows worlds using RDP technology to remote application content between them. Weston already had an RDP backend, but it was limited to single-monitor-desktop remoting. We've greatly enhanced that RDP backend to include advanced functionality, such as multi-monitor support, clipboard integration for copy/paste, and audio in/out. We've enabled new remoting modes called RAIL (Remote Application Integrated Locally) and VAIL (Virtualized Application Integrated Locally), where individual applications, rather than desktops/monitors, are remoted. These changes are not specific to WSLg; they add functionality to the existing RDP backend and are reusable in other scenarios as well (i.e. using the new Weston RDP backend to remote application running on a Raspberry Pi to another device running an RDP client).


To enable rich integration in WSLg, we've also added a small plugin to the RDP backend specific to WSLg. In Weston, the plugin is responsible for attaching to the user distro and searching for installed applications (aka the desktop file). The plugin sends the Windows host a list of all applications found along with their launch commands and icons. On the Windows host, an open source [mstsc plugin](https://github.com/microsoft/wslg/tree/main/WSLDVCPlugin) part of the WSLg project uses that information to create shortcuts for these Linux applications to the Windows Start Menu.

We've also fixed several bugs impacting various applications. Generally, these were problems that impacted Weston in all modes and were not specific to WSLg.

## FreeRDP
Weston currently uses FreeRDP for its RDP Backend. WSLg continues to leverage FreeRDP and we have added support for a new RDP Protocol/Channel to enable VAIL optimized scenario as well as support for the WSLg plugin. We've also fixed various bugs that were impacting interops with mstsc or causing instability.

## PulseAudio
For PulseAudio, our contributions focused on a sink and a source plugin that shuffle audio data between PulseAudio and the Weston RDP backend such that the audio data can be integrated over the RDP connection back to the host. There are no changes to the core of PulseAudio outside of adding these new plugins.

# Contributing

If you would like to tinker with or contribute to WSLg, please see our [CONTRIBUTING](CONTRIBUTING.md) page for details, including how to build and run private a version of WSLg.

# Reporting a non-security issues

For non-security related issues, such as reporting a bug or making a suggestion for a new feature, please use this project's [issues tracker](https://github.com/microsoft/wslg/issues).

# Reporting security issues

To report security issues with WSLg or any other Microsoft products, please follow the instructions detailed [here](SECURITY.md).

# Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.


================================================
FILE: SECURITY.md
================================================
## Security

Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).

If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.

## Reporting Security Issues

**Please do not report security vulnerabilities through public GitHub issues.**

Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).

If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).

You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).

Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:

  * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
  * Full paths of source file(s) related to the manifestation of the issue
  * The location of the affected source code (tag/branch/commit or direct URL)
  * Any special configuration required to reproduce the issue
  * Step-by-step instructions to reproduce the issue
  * Proof-of-concept or exploit code (if possible)
  * Impact of the issue, including how an attacker might exploit the issue

This information will help us triage your report more quickly.

If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.

## Preferred Languages

We prefer all communications to be in English.

## Policy

Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).

================================================
FILE: WSLDVCPlugin/RegisterWSLPlugin.reg
================================================
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Default\AddIns\WSLDVCPlugin]
"Name"="C:\\WINDOWS\\system32\\WSLDVCPlugin.dll"


================================================
FILE: WSLDVCPlugin/UpdateRCVersion.ps1
================================================

$version = [string](gitversion /showvariable AssemblySemFileVer)
$versionComma = $version.Replace(".", ",")
$informationalVersion = [string](gitversion /showvariable InformationalVersion)

$content = (Get-Content -Encoding "windows-1252" -Path ".\WSLDVCPlugin.rc")
$content = $content.Replace("1,0,0,1", $versionComma);
$content = $content.Replace("1.0.0.1", $version);
$content = $content.Replace("InformationalVersion", $InformationalVersion);

Set-Content -Encoding "windows-1252" -Path ".\WSLDVCPlugin.rc" -Value $content

================================================
FILE: WSLDVCPlugin/WSLDVCCallback.cpp
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include <safeint.h>
#include "utils.h"
#include "rdpapplist.h"
#include "WSLDVCCallback.h"
#include "WSLDVCFileDB.h"

constexpr LPCWSTR c_WSL_registry_path = L"Software\\Microsoft\\Windows\\CurrentVersion\\Lxss";
constexpr LPCWSTR c_WSLg_window_id = L"WslgServerWindowId";
constexpr LPCWSTR c_Working_dir = L"%windir%\\system32";

//
// This channel simply sends all the received messages back to the server. 
//
class WSLDVCCallback :
    public RuntimeClass<
    RuntimeClassFlags<ClassicCom>,
    IWTSVirtualChannelCallback>
{
public:
   
    HRESULT 
        RuntimeClassInitialize(
            IWTSVirtualChannel* pChannel
        )
    {
        HRESULT hr = WSLDVCFileDB_CreateInstance(NULL, &m_spFileDB);
        if (SUCCEEDED(hr))
        {
            m_spChannel = pChannel;

            InitializeCriticalSection(&m_crit);
            m_bCriticalSectionInitialized = true;
        }
        return hr;
    }

    HRESULT
        ReadAppListHeader(
            _Inout_ UINT64 *size,
            _Inout_ const BYTE** buffer,
            _Out_ RDPAPPLIST_HEADER *appListHeader
        )
    {
        const BYTE* cur;
        UINT64 len;

        assert(size);
        assert(buffer);
        assert(appListHeader);

        cur = *buffer;
        len = *size;

        ReadUINT32(appListHeader->cmdId, cur, len);
        ReadUINT32(appListHeader->length, cur, len);

        *buffer = cur;
        *size = len;

        return S_OK;

    Error_Read:

        return E_FAIL;
    }

    HRESULT
        ReadAppListServerCaps(
            _Inout_ UINT64 *size,
            _Inout_ const BYTE** buffer,
            _Out_ RDPAPPLIST_SERVER_CAPS_PDU *serverCaps
        )
    {
        const BYTE* cur;
        UINT64 len;

        assert(size);
        assert(buffer);
        assert(serverCaps);

        cur = *buffer;
        len = *size;

        ReadUINT16(serverCaps->version, cur, len);
        ReadSTRING(serverCaps->appListProviderName, cur, len, true);
        if (serverCaps->version >= 4)
        {
            ReadSTRING(serverCaps->appListProviderUniqueId, cur, len, true);
        }
    
        *buffer = cur;
        *size = len;
        
        return S_OK;

    Error_Read:

        return E_FAIL;
    }

    HRESULT
        ReadAppListUpdate(
            _Inout_ UINT64* size,
            _Inout_ const BYTE** buffer,
            _Out_ RDPAPPLIST_UPDATE_APPLIST_PDU* updateAppList
        )
    {
        const BYTE* cur;
        UINT64 len;

        assert(size);
        assert(buffer);
        assert(updateAppList);

        cur = *buffer;
        len = *size;

        ReadUINT32(updateAppList->flags, cur, len);
        CheckRequiredFlags(updateAppList->flags,
            (RDPAPPLIST_FIELD_ID | RDPAPPLIST_FIELD_EXECPATH | RDPAPPLIST_FIELD_DESC));
        if (updateAppList->flags & RDPAPPLIST_FIELD_ID)
        {
            ReadSTRING(updateAppList->appId, cur, len, true);
        }
        if (updateAppList->flags & RDPAPPLIST_FIELD_GROUP)
        {
            ReadSTRING(updateAppList->appGroup, cur, len, false);
        }
        if (updateAppList->flags & RDPAPPLIST_FIELD_EXECPATH)
        {
            ReadSTRING(updateAppList->appExecPath, cur, len, true);
        }
        if (updateAppList->flags & RDPAPPLIST_FIELD_WORKINGDIR)
        {
            ReadSTRING(updateAppList->appWorkingDir, cur, len, false);
        }
        if (updateAppList->flags & RDPAPPLIST_FIELD_DESC)
        {
            ReadSTRING(updateAppList->appDesc, cur, len, true);
        }

        *buffer = cur;
        *size = len;

        return S_OK;

    Error_Read:

        return E_FAIL;
    }

    HRESULT
        ReadAppListIconData(
            _Inout_ UINT64* size,
            _Inout_ const BYTE** buffer,
            _Out_ RDPAPPLIST_ICON_DATA* iconData
        )
    {
        const BYTE* cur;
        HRESULT hr;
        UINT64 len;
        ICON_HEADER* pIconHeader;
        BITMAPINFOHEADER* pIconBitmapInfo;
        void* pIconBits;

        assert(size);
        assert(buffer);
        assert(iconData);

        cur = *buffer;
        len = *size;

        ZeroMemory(iconData, sizeof(*iconData));
        ReadUINT32(iconData->flags, cur, len);
        ReadUINT32(iconData->iconWidth, cur, len);
        ReadUINT32(iconData->iconHeight, cur, len);
        ReadUINT32(iconData->iconStride, cur, len);
        ReadUINT32(iconData->iconBpp, cur, len);
        ReadUINT32(iconData->iconFormat, cur, len);
        ReadUINT32(iconData->iconBitsLength, cur, len);

        hr = UIntAdd(sizeof(ICON_HEADER) + sizeof(BITMAPINFOHEADER), iconData->iconBitsLength, &iconData->iconFileSize);
        if (FAILED(hr)) {
            return hr;
        }

        iconData->iconFileData = (BYTE*)LocalAlloc(LPTR, iconData->iconFileSize);
        if (!iconData->iconFileData)
        {
            return E_OUTOFMEMORY;
        }

        pIconHeader = (ICON_HEADER*)iconData->iconFileData;
        pIconBitmapInfo = (BITMAPINFOHEADER*)(pIconHeader + 1);
        pIconBits = (void*)(pIconBitmapInfo + 1);

        ReadBYTES(pIconBits, cur, iconData->iconBitsLength, len);

        pIconHeader->idReserved = 0; // Reserved (must be 0)
        pIconHeader->idType = 1;     // Resource Type (1 for icons)
        pIconHeader->idCount = 1;    // How many images?
        pIconHeader->idEntries[0].bWidth = (BYTE)iconData->iconWidth;   // Width, in pixels, of the image
        pIconHeader->idEntries[0].bHeight = (BYTE)iconData->iconHeight; // Height, in pixels, of the image
        pIconHeader->idEntries[0].bColorCount = 0;                      // Number of colors in image (0 if >=8bpp)
        pIconHeader->idEntries[0].bReserved = 0;                        // Reserved ( must be 0)
        pIconHeader->idEntries[0].wPlanes = 0;                          // Color Planes
        pIconHeader->idEntries[0].wBitCount = (WORD)iconData->iconBpp;  // Bits per pixel
        pIconHeader->idEntries[0].dwBytesInRes = sizeof(BITMAPINFOHEADER) + iconData->iconBitsLength; // How many bytes in this resource?
        pIconHeader->idEntries[0].dwImageOffset = sizeof(ICON_HEADER);  // Where in the file is this image?

        pIconBitmapInfo->biSize = sizeof(BITMAPINFOHEADER);
        pIconBitmapInfo->biWidth = iconData->iconWidth;
        pIconBitmapInfo->biHeight = iconData->iconHeight * 2;
        pIconBitmapInfo->biPlanes = 1;
        pIconBitmapInfo->biBitCount = (WORD)iconData->iconBpp;
        pIconBitmapInfo->biCompression = BI_RGB;
        pIconBitmapInfo->biSizeImage = iconData->iconBitsLength;
        pIconBitmapInfo->biXPelsPerMeter = 0;
        pIconBitmapInfo->biYPelsPerMeter = 0;
        pIconBitmapInfo->biClrUsed = 0;
        pIconBitmapInfo->biClrImportant = 0;

#ifdef _DEBUG
        // Verify ICON.
        {
            HICON hIcon = CreateIconFromResource((BYTE*)pIconBitmapInfo, iconData->iconFileSize - sizeof(ICON_HEADER), TRUE, 0x00030000);
            if (!hIcon)
            {
                DebugPrint(L"CreateIconFromResource() failed\n");
            }
            else
            {
                DestroyIcon(hIcon);
            }
        }
#endif // _DEBUG

        *buffer = cur;
        *size = len;

        return S_OK;

    Error_Read:

        if (iconData->iconFileData)
        {
            LocalFree(iconData->iconFileData);
        }
        ZeroMemory(iconData, sizeof(*iconData));

        return E_FAIL;
    }
    
    HRESULT
        ReadAppListDelete(
            _Inout_ UINT64* size,
            _Inout_ const BYTE** buffer,
            _Out_ RDPAPPLIST_DELETE_APPLIST_PDU* deleteAppList
        )
    {
        const BYTE* cur;
        UINT64 len;
        
        assert(size);
        assert(buffer);
        assert(deleteAppList);

        cur = *buffer;
        len = *size;

        ReadUINT32(deleteAppList->flags, cur, len);
        CheckRequiredFlags(deleteAppList->flags,
            RDPAPPLIST_FIELD_ID);
        if (deleteAppList->flags & RDPAPPLIST_FIELD_ID)
        {
            ReadSTRING(deleteAppList->appId, cur, len, true);
        }
        if (deleteAppList->flags & RDPAPPLIST_FIELD_GROUP)
        {
            ReadSTRING(deleteAppList->appGroup, cur, len, false);
        }

        *buffer = cur;
        *size = len;

        return S_OK;

    Error_Read:

        return E_FAIL;
    }

    HRESULT
        ReadAssociateWindowId(
            _Inout_ UINT64* size,
            _Inout_ const BYTE** buffer,
            _Out_ RDPAPPLIST_ASSOCIATE_WINDOW_ID_PDU* associateWindowId
        )
    {
        const BYTE* cur;
        UINT64 len;

        assert(size);
        assert(buffer);
        assert(associateWindowId);

        cur = *buffer;
        len = *size;

        ReadUINT32(associateWindowId->flags, cur, len);
        CheckRequiredFlags(associateWindowId->flags,
            RDPAPPLIST_FIELD_ID | RDPAPPLIST_FIELD_WINDOW_ID);
        ReadUINT32(associateWindowId->appWindowId, cur, len);
        if (associateWindowId->flags & RDPAPPLIST_FIELD_ID)
        {
            ReadSTRING(associateWindowId->appId, cur, len, true);
        }
        if (associateWindowId->flags & RDPAPPLIST_FIELD_GROUP)
        {
            ReadSTRING(associateWindowId->appGroup, cur, len, false);
        }
        if (associateWindowId->flags & RDPAPPLIST_FIELD_EXECPATH)
        {
            ReadSTRING(associateWindowId->appExecPath, cur, len, true);
        }
        if (associateWindowId->flags & RDPAPPLIST_FIELD_DESC)
        {
            ReadSTRING(associateWindowId->appDesc, cur, len, true);
        }

        *buffer = cur;
        *size = len;

        return S_OK;

    Error_Read:

        return E_FAIL;
    }

    HRESULT
        OnSyncStart()
    {
        DebugPrint(L"OnSyncStart():\n");
        if (m_spFileDBSync.Get())
        {
            DebugPrint(L"Server asks start sync, but already in sync mode.\n");
            return E_FAIL;
        }

        HRESULT hr = WSLDVCFileDB_CreateInstance(NULL, &m_spFileDBSync);
        if (FAILED(hr))
        {
            return hr;
        }

        // Add all files under menu path at sync start.
        // This will also adds icon file pointed by .lnk file.
        // Any files not reported during sync, will be removed at end.
        m_spFileDBSync->addAllFilesAsFileIdAt(m_appMenuPath);

        return S_OK;
    }

    HRESULT
        OnSyncEnd(bool cleanupFiles = true)
    {
        DebugPrint(L"OnSyncEnd():\n");
        if (m_spFileDBSync.Get())
        {
            if (cleanupFiles)
            {
                m_spFileDBSync->deleteAllFileIdFiles();
            }
            m_spFileDBSync->OnClose();
            m_spFileDBSync = nullptr;
        }
        return S_OK;
    }

    HRESULT
        OnCaps(
            _Inout_ UINT64* size,
            _Inout_ const BYTE** buffer
        )
    {
        HRESULT hr;
        WCHAR szAppProviderGUID[40];

        // Buffer read scope
        {
            const BYTE* cur;
            UINT64 len;

            assert(size);
            assert(buffer);

            cur = *buffer;
            len = *size;

            hr = ReadAppListServerCaps(&len, &cur, &m_serverCaps);
            if (FAILED(hr))
            {
                return hr;
            }

            *buffer = cur;
            *size = len;
        }

        memcpy(m_appProvider, m_serverCaps.appListProviderName, m_serverCaps.appListProviderNameLength);
        if ((wcsstr(m_appProvider, L"..") != NULL) ||
            (wcsstr(m_appProvider, L"\"") != NULL) ||
            (wcsstr(m_appProvider, L" ") != NULL))
        {
            DebugPrint(L"provider name can't contain '..', '\"' or space, %s\n", m_appProvider);
            return E_FAIL;
        }

        if (m_serverCaps.version >= 4)
        {
            if ((swprintf_s(szAppProviderGUID, ARRAYSIZE(szAppProviderGUID), L"{%s}",
                           &m_serverCaps.appListProviderUniqueId[0]) < 0) ||
                (szAppProviderGUID[0] == L'\0'))
            {
                DebugPrint(L"swprintf_s failed on %s\n", m_serverCaps.appListProviderUniqueId);
                return E_FAIL;
            }

            hr = CLSIDFromString(szAppProviderGUID, &m_appProviderGUID);
            if (FAILED(hr))
            {
                DebugPrint(L"CLSIDFromString failed on %s\n", szAppProviderGUID);
                return hr;
            }
        }

        hr = BuildMenuPath(ARRAYSIZE(m_appMenuPath), m_appMenuPath, m_appProvider, true);
        if (FAILED(hr))
        {
            return hr;
        }
        DebugPrint(L"AppMenuPath: %s\n", m_appMenuPath);

        hr = BuildIconPath(ARRAYSIZE(m_iconPath), m_iconPath, m_appProvider, true);
        if (FAILED(hr))
        {
            return hr;
        }
        DebugPrint(L"IconPath: %s\n", m_iconPath);

        // Read the registry key that declares where the WSL package is installed.
        HKEY key;
        hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_WSL_registry_path, 0, KEY_READ, &key));
        if (FAILED(hr))
        {
            DebugPrint(L"RegOpenKeyExW failed\n");
            return hr;
        }

        DWORD valueSize = sizeof(m_expandedPathObj);
        hr = HRESULT_FROM_WIN32(RegGetValueW(key, L"Msi", L"InstallLocation", (RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ), nullptr, m_expandedPathObj, &valueSize));
        RegCloseKey(key);
        if (FAILED(hr))
        {
            DebugPrint(L"RegGetValueW failed\n");
            return hr;
        }

        if (wcscat_s(m_expandedPathObj, ARRAYSIZE(m_expandedPathObj), L"\\wslg.exe") != 0)
        {
            return E_FAIL;
        }
        DebugPrint(L"WSLg.exe: %s\n", m_expandedPathObj);

        if (ExpandEnvironmentStringsW(c_Working_dir, m_expandedWorkingDir, ARRAYSIZE(m_expandedWorkingDir)) == 0)
        {
            DebugPrint(L"Failed to expand working dir: %s : %d\n", c_Working_dir, GetLastError());
            return E_FAIL;
        }
        DebugPrint(L"WSL.exe working dir: %s\n", m_expandedWorkingDir);

        if (!GetLocaleName(m_clientLanguageId, sizeof m_clientLanguageId))
        {
            strcpy_s(m_clientLanguageId, sizeof m_clientLanguageId, "en_US");
        }

        // Reply back header (8 bytes) + version (2 bytes) to server.
        #pragma pack(push,1)
        struct {
            RDPAPPLIST_HEADER capsHeader;
            RDPAPPLIST_CLIENT_CAPS_PDU caps;
        } replyBuf = {};
        #pragma pack(pop)

        replyBuf.capsHeader.cmdId = RDPAPPLIST_CMDID_CAPS;
        if (m_serverCaps.version >= RDPAPPLIST_CHANNEL_VERSION)
        {
            replyBuf.capsHeader.length = sizeof replyBuf;
            replyBuf.caps.version = RDPAPPLIST_CHANNEL_VERSION;
            if (strncpy_s(replyBuf.caps.clientLanguageId, m_clientLanguageId, sizeof replyBuf.caps.clientLanguageId) != 0)
            {
                return E_FAIL;
            }
        }
        else
        {
            DebugPrint(L"Invalid server version : %d\n", m_serverCaps.version);
            hr = E_FAIL;
        }
        if (SUCCEEDED(hr))
        {
            hr = m_spChannel->Write(replyBuf.capsHeader.length, (BYTE*)&replyBuf, nullptr);
            if (FAILED(hr))
            {
                DebugPrint(L"m_spChannel->Write failed, hr = %x\n", hr);
            }
        }

        m_handShakeComplated = SUCCEEDED(hr) ? true : false;

        return hr;
    }

    HRESULT
        OnUpdateAppList(
            _Inout_ UINT64* size,
            _Inout_ const BYTE** buffer
        )
    {
        HRESULT hr;
        RDPAPPLIST_UPDATE_APPLIST_PDU updateAppList = {};
        RDPAPPLIST_ICON_DATA iconData = {};
        WCHAR linkPath[MAX_PATH] = {};
        WCHAR iconPath[MAX_PATH] = {};
        WCHAR exeArgs[MAX_PATH] = {};
        WCHAR key[MAX_PATH] = {};
        bool hasIcon = false;

        // Buffer read scope
        {
            const BYTE* cur;
            UINT64 len;

            assert(size);
            assert(buffer);

            cur = *buffer;
            len = *size;

            hr = ReadAppListUpdate(&len, &cur, &updateAppList);
            if (FAILED(hr))
            {
                return hr;
            }

            if (updateAppList.flags & RDPAPPLIST_FIELD_ICON)
            {
                hr = ReadAppListIconData(&len, &cur, &iconData);
                if (FAILED(hr))
                {
                    return hr;
                }
            }

            *buffer = cur;
            *size = len;
        }

        if (updateAppList.flags & RDPAPPLIST_HINT_SYNC_START)
        {
            hr = OnSyncStart();
            if (FAILED(hr))
            {
                return hr;
            }
            assert(m_spFileDBSync.Get());
        }

        if (updateAppList.flags & (RDPAPPLIST_HINT_SYNC | RDPAPPLIST_HINT_SYNC_END))
        {
            if (!m_spFileDBSync.Get())
            {
                DebugPrint(L"Server sends sync or sync end flag without starting sync mode. flags %x\n", updateAppList.flags);
                return E_FAIL;
            }
        }

        if (updateAppList.appGroupLength)
        {
            if ((wcsstr(updateAppList.appGroup, L"..") != NULL) ||
                (wcsstr(updateAppList.appGroup, L"\"") != NULL))
            {
                DebugPrint(L"group name can't contain '..' or '\"', %s\n", updateAppList.appGroup);
                return E_FAIL;
            }
        }

        if ((wcsstr(updateAppList.appId, L"..") != NULL) ||
            (wcsstr(updateAppList.appId, L"\"") != NULL))
        {
            DebugPrint(L"app id can't contain '..' or '\"', %s\n", updateAppList.appId);
            return E_FAIL;
        }

        if ((wcsstr(updateAppList.appDesc, L"..") != NULL) ||
            (wcsstr(updateAppList.appDesc, L"\"") != NULL))
        {
            DebugPrint(L"app desc can't contain '..' or '\"', %s\n", updateAppList.appDesc);
            return E_FAIL;
        }

        // Double quote (") is valid file/path char in Linux, but currently wsl.exe/wslg.exe
        // can't handle to give the path contains " with --cd options, thus until this get fixed,
        // block the path contains ".
        if ((wcsstr(updateAppList.appWorkingDir, L"\"") != NULL))
        {
            DebugPrint(L"app working dir can't contain '\"', %s\n", updateAppList.appWorkingDir);
            return E_FAIL;
        }

        if (updateAppList.flags & RDPAPPLIST_FIELD_ICON)
        {
            if (wcscpy_s(iconPath, ARRAYSIZE(iconPath), m_iconPath) != 0)
            {
                return E_FAIL;
            }
            if (!CreateDirectoryW(iconPath, NULL))
            {
                if (ERROR_ALREADY_EXISTS != GetLastError())
                {
                    DebugPrint(L"Failed to create %s\n", iconPath);
                    return E_FAIL;
                }
            }
            if (updateAppList.appGroupLength)
            {
                if ((wcscat_s(iconPath, ARRAYSIZE(iconPath), L"\\") != 0) ||
                    (wcscat_s(iconPath, ARRAYSIZE(iconPath), updateAppList.appGroup) != 0))
                {
                    return E_FAIL;
                }
                if (!CreateDirectoryW(iconPath, NULL))
                {
                    if (ERROR_ALREADY_EXISTS != GetLastError())
                    {
                        DebugPrint(L"Failed to create %s\n", iconPath);
                        return E_FAIL;
                    }
                }
            }
            if ((wcscat_s(iconPath, ARRAYSIZE(iconPath), L"\\") != 0) ||
                (wcscat_s(iconPath, ARRAYSIZE(iconPath), updateAppList.appId) != 0) ||
                (wcscat_s(iconPath, ARRAYSIZE(iconPath), L".ico") != 0))
            {
                return E_FAIL;
            }
        }

        if (wcscpy_s(linkPath, ARRAYSIZE(linkPath), m_appMenuPath) != 0)
        {
            return E_FAIL;
        }
        if (!CreateDirectoryW(linkPath, NULL))
        {
            if (ERROR_ALREADY_EXISTS != GetLastError())
            {
                DebugPrint(L"Failed to create %s\n", linkPath);
                return E_FAIL;
            }
        }
        if (updateAppList.appGroupLength)
        {
            if ((wcscat_s(linkPath, ARRAYSIZE(linkPath), L"\\") != 0) ||
                (wcscat_s(linkPath, ARRAYSIZE(linkPath), updateAppList.appGroup) != 0))
            {
                return E_FAIL;
            }
            if (!CreateDirectoryW(linkPath, NULL))
            {
                if (ERROR_ALREADY_EXISTS != GetLastError())
                {
                    DebugPrint(L"Failed to create %s\n", linkPath);
                    return E_FAIL;
                }
            }
        }
        // Use description to name link file since this is name shows up
        // at StartMenu UI. SHSetLocalizedName can't be uses since this 
        // is not in resource.
        if ((wcscat_s(linkPath, ARRAYSIZE(linkPath), L"\\") != 0) ||
            (wcscat_s(linkPath, ARRAYSIZE(linkPath), updateAppList.appDesc) != 0) ||
            (wcscat_s(linkPath, ARRAYSIZE(linkPath), L".lnk") != 0))
        {
            return E_FAIL;
        }

        // build wsl.exe/wslg.exe command line.
        // -d : distro name, space is not allowed in distro name,
        //      thus wrapping by double-quoto is not accepted by wsl.exe/wslg.exe
        // --cd : current directory, wrap by double-quote.
        // -- : executable path with arguments. All string after '--' will be treated as Linux command line,
        //      thus wrapping by double-quoto is not accepted by wsl.exe/wslg.exe
        if ((swprintf_s(exeArgs, ARRAYSIZE(exeArgs), L"-d %s --cd \"%s\" -- %s",
                       m_appProvider, 
                       updateAppList.appWorkingDirLength ? updateAppList.appWorkingDir : L"~",
                       updateAppList.appExecPath) < 0) ||
            (exeArgs[0] == L'\0'))
        {
            return E_FAIL;
        }

        key[0] = L'\0';
        if (updateAppList.appGroupLength)
        {
            if ((wcscat_s(key, ARRAYSIZE(key), updateAppList.appGroup) != 0) ||
                (wcscat_s(key, ARRAYSIZE(key), L"\\") != 0))
            {
                return E_FAIL;
            }
        }
        if (wcscat_s(key, ARRAYSIZE(key), updateAppList.appId) != 0)
        {
            return E_FAIL;
        }
        hr = m_spFileDB->OnFileAdded(key, linkPath, iconPath, m_expandedPathObj, exeArgs);
        if (FAILED(hr))
        {
            return hr;
        }

        if (updateAppList.flags & RDPAPPLIST_FIELD_ICON)
        {
            if (SUCCEEDED(CreateIconFile(iconData.iconFileData, iconData.iconFileSize, iconPath)))
            {
                hasIcon = true;
            }
            else
            {
                DebugPrint(L"Failed to create icon file %s\n", iconPath);
                // Icon is optional, so keep going.
            }
        }

        /* ignore error from create link */
        /* This also updates if m_expandedPathObj (wsl.exe or wslg.exe) is changed. */
        CreateShellLink((LPCWSTR)linkPath,
                (LPCWSTR)m_expandedPathObj,
                (LPCWSTR)exeArgs,
                (LPCWSTR)m_expandedWorkingDir,
                (LPCWSTR)updateAppList.appDesc,
                hasIcon ? (LPCWSTR)iconPath : NULL);
        
        if (updateAppList.flags & RDPAPPLIST_HINT_SYNC)
        {
            // During sync mode, remove added file to sync DB, so these won't be cleaned up at end.
            assert(m_spFileDBSync.Get());
            m_spFileDBSync->OnFileRemoved(linkPath);
            if (hasIcon)
            {
                m_spFileDBSync->OnFileRemoved(iconPath);
            }
        }

        if (updateAppList.flags & RDPAPPLIST_HINT_SYNC_END)
        {
            OnSyncEnd();
        }

        return S_OK;
    }

    HRESULT
        OnDeleteAppList(
            _Inout_ UINT64* size,
            _Inout_ const BYTE** buffer
        )
    {
        HRESULT hr;
        RDPAPPLIST_DELETE_APPLIST_PDU deleteAppList = {};
        WCHAR linkPath[MAX_PATH] = {};
        WCHAR iconPath[MAX_PATH] = {};
        WCHAR key[MAX_PATH] = {};

        // Buffer read scope
        {
            const BYTE* cur;
            UINT64 len;

            assert(size);
            assert(buffer);

            cur = *buffer;
            len = *size;

            hr = ReadAppListDelete(&len, &cur, &deleteAppList);
            if (FAILED(hr))
            {
                return hr;
            }

            *buffer = cur;
            *size = len;
        }

        key[0] = L'\0';
        if (deleteAppList.appGroupLength)
        {
            if ((wcscpy_s(key, ARRAYSIZE(key), deleteAppList.appGroup) != 0) ||
                (wcscat_s(key, ARRAYSIZE(key), L"\\") != 0))
            {
                return E_FAIL;
            }
        }
        if (wcscat_s(key, ARRAYSIZE(key), deleteAppList.appId) != 0)
        {
            return E_FAIL;
        }

        hr = m_spFileDB->FindFiles(key, linkPath, ARRAYSIZE(linkPath), iconPath, ARRAYSIZE(iconPath));
        if (FAILED(hr))
        {
            DebugPrint(L"OnDeleteAppList(): key %s not found\n", key);
            return E_FAIL;
        }

        if ((linkPath[0] != L'\0') && !DeleteFileW(linkPath))
        {
            DebugPrint(L"DeleteFile(%s) failed, error %x\n", linkPath, GetLastError());
        }
        DebugPrint(L"Delete Path Link: %s\n", linkPath);

        if ((iconPath[0] != L'\0') && !DeleteFileW(iconPath))
        {
            DebugPrint(L"DeleteFile(%s) failed, error %x\n", iconPath, GetLastError());
        }
        DebugPrint(L"Delete Icon Path: %s\n", iconPath);

        m_spFileDB->OnFileRemoved(key);

        return S_OK;
    }

    typedef struct _WslgWindowData
    {
        UINT64 windowId;
        LPCWSTR displayName;
        LPCWSTR relaunchCommandline;
        LPCWSTR iconPath;
    } WslgWindowData;

    static BOOL CALLBACK
        FindWslgWindow(
            _In_ HWND hwnd,
            _In_ LPARAM args
        )
    {
        WslgWindowData* wndData = (WslgWindowData*)args;
        HANDLE windowId = GetPropW(hwnd, c_WSLg_window_id);
        if (windowId == (HANDLE)wndData->windowId)
        {
            UpdateTaskBarInfo(hwnd,
                wndData->relaunchCommandline,
                wndData->displayName,
                wndData->iconPath);
            return FALSE; /* found it, stop enum */
        }
        return TRUE; /* keep enum next one */
    }

    HRESULT
        OnAssociateWindowId(
            _Inout_ UINT64* size,
            _Inout_ const BYTE** buffer
        )
    {
        HRESULT hr;
        RDPAPPLIST_ASSOCIATE_WINDOW_ID_PDU associateWindowId = {};
        WCHAR tmpBuf[MAX_PATH] = {};
        WCHAR tmpPath[MAX_PATH] = {};
        WCHAR tmpArgs[MAX_PATH] = {};
        WCHAR iconPath[MAX_PATH] = {};
        WCHAR exePath[MAX_PATH] = {};

        if (m_serverCaps.version < 4)
        {
            DebugPrint(L"associate window id requires version 4 or newer: current version: %d\n",
                m_serverCaps.version < 4);
            return E_FAIL;
        }

        // Buffer read scope
        {
            const BYTE* cur;
            UINT64 len;

            assert(size);
            assert(buffer);

            cur = *buffer;
            len = *size;

            hr = ReadAssociateWindowId(&len, &cur, &associateWindowId);
            if (FAILED(hr))
            {
                return hr;
            }

            *buffer = cur;
            *size = len;
        }

        /* construct full unique window id */
        UINT64 windowId = (UINT64)m_appProviderGUID.Data1;
        windowId <<= 32;
        windowId |= associateWindowId.appWindowId;
        if (windowId == 0LL)
        {
            DebugPrint(L"windowId can't be 0\n");
            return E_FAIL;
        }

        tmpBuf[0] = L'\0';
        if (associateWindowId.appGroupLength)
        {
            if ((wcscpy_s(tmpBuf, ARRAYSIZE(tmpBuf), associateWindowId.appGroup) != 0) ||
                (wcscat_s(tmpBuf, ARRAYSIZE(tmpBuf), L"\\") != 0))
            {
                return E_FAIL;
            }
        }
        if (wcscat_s(tmpBuf, ARRAYSIZE(tmpBuf), associateWindowId.appId) != 0)
        {
            return E_FAIL;
        }

        DebugPrint(L"AssociateWindowId AppId: %s\n", tmpBuf);
        DebugPrint(L"    Desc: %s\n", associateWindowId.appDesc);
        DebugPrint(L"    Full Unique WindowId: 0x%p\n", windowId); /* somehow PRIx64 doesn't work here */
        DebugPrint(L"    CmdLine from server: %s\n", associateWindowId.appExecPath);

        hr = m_spFileDB->FindFiles(tmpBuf,
            NULL, 0,
            iconPath, ARRAYSIZE(iconPath),
            tmpPath, ARRAYSIZE(tmpPath),
            tmpArgs, ARRAYSIZE(tmpArgs));
        if (SUCCEEDED(hr))
        {
            if ((swprintf_s(exePath, ARRAYSIZE(exePath), L"%s %s",
                tmpPath, tmpArgs) < 0) ||
                (exePath[0] == L'\0'))
            {
                return E_FAIL;
            }
        }
        else if (associateWindowId.appExecPath[0] != '\0')
        {
            /* if key is not present, reconstruct using server side exe path */
            if ((swprintf_s(exePath, ARRAYSIZE(exePath), L"%s -d %s --cd \"%s\" -- %s",
                m_expandedPathObj,
                m_appProvider,
                L"~",
                associateWindowId.appExecPath) < 0) ||
                (exePath[0] == L'\0'))
            {
                return E_FAIL;
            }

            /* TODO: must provide default icon */
        }

        DebugPrint(L"    CmdLine at local: %s\n", exePath);
        DebugPrint(L"    Icon Path at local: %s\n", iconPath);
 
        WslgWindowData data = {};
        data.windowId = windowId;
        data.displayName = associateWindowId.appDesc[0] != '\0' ? associateWindowId.appDesc : NULL;
        data.relaunchCommandline = exePath[0] != '\0' ? exePath : NULL;      
        data.iconPath = iconPath[0] != '\0' ? iconPath : NULL;

        EnumWindows(FindWslgWindow, (LPARAM)&data);

        return S_OK;
    }

    //
    // IWTSVirtualChannelCallback interface
    //

    STDMETHODIMP 
        OnDataReceived(
            ULONG cbSize,
            __RPC__in_ecount_full(cbSize) BYTE* pBuffer
        )
    {
        HRESULT hr = S_OK;
        const BYTE* cur = pBuffer;
        UINT64 len = cbSize;

        if (m_bChannelClosed)
        {
            return S_OK;
        }

        EnterCriticalSection(&m_crit);
        DebugPrint(L"OnDataReceived enter, size = %d\n", len);

        while (len && !m_bChannelClosed)
        {
            RDPAPPLIST_HEADER appListHeader = {};

            hr = ReadAppListHeader(&len, &cur, &appListHeader);
            if (FAILED(hr))
            {
                DebugPrint(L"Failed to read applist header\n");
                break;
            }

            if (appListHeader.cmdId == RDPAPPLIST_CMDID_CAPS)
            {
                hr = OnCaps(&len, &cur);
                if (FAILED(hr))
                {
                    break;
                }
            }
            else if (m_handShakeComplated == false)
            {
                // Caps exchange must be completed before processing
                // any other messages.  
                DebugPrint(L"HandShake is not completed\n");
                hr = E_FAIL;
                break;
            }
            else if (appListHeader.cmdId == RDPAPPLIST_CMDID_UPDATE_APPLIST)
            {
                hr = OnUpdateAppList(&len, &cur);
                if (FAILED(hr))
                {
                    break;
                }
            }
            else if (appListHeader.cmdId == RDPAPPLIST_CMDID_DELETE_APPLIST)
            {
                hr = OnDeleteAppList(&len, &cur);
                if (FAILED(hr))
                {
                    break;
                }
            }
            else if (appListHeader.cmdId == RDPAPPLIST_CMDID_DELETE_APPLIST_PROVIDER)
            {
                // Nothing to do.
            }
            else if (appListHeader.cmdId == RDPAPPLIST_CMDID_ASSOCIATE_WINDOW_ID)
            {
                hr = OnAssociateWindowId(&len, &cur);
                if (FAILED(hr))
                {
                    break;
                }
            }
            else
            {
                DebugPrint(L"Unknown command id:%d, Length %d\n", appListHeader.cmdId, appListHeader.length);
                hr = E_FAIL;
                break;
            }

            assert(len <= cbSize);
        }

        DebugPrint(L"OnDataReceived returns hr = %x\n", hr);
        LeaveCriticalSection(&m_crit);

        return hr;
    }

    STDMETHODIMP 
        OnClose()
    {
        m_bChannelClosed = true;

        EnterCriticalSection(&m_crit);
        DebugPrint(L"OnClose enter\n");

        // Make sure sync mode is cancelled.
        OnSyncEnd(false);

        m_spFileDB->OnClose();
        m_spFileDB = nullptr;

        DebugPrint(L"OnClose returns hr = %x\n", S_OK);
        LeaveCriticalSection(&m_crit);

        return S_OK;
    }

protected:

    virtual 
        ~WSLDVCCallback() 
    {
        if (m_bCriticalSectionInitialized)
        {
            DeleteCriticalSection(&m_crit);
        }
    }

private:
        
    ComPtr<IWTSVirtualChannel> m_spChannel;

    ComPtr<IWSLDVCFileDB> m_spFileDB;
    ComPtr<IWSLDVCFileDB> m_spFileDBSync; // valid only during sync.

    CRITICAL_SECTION m_crit = {};
    bool m_bCriticalSectionInitialized = false;

    bool m_bChannelClosed = false;
    bool m_handShakeComplated = false;

    RDPAPPLIST_SERVER_CAPS_PDU m_serverCaps = {};
    GUID m_appProviderGUID = {};
    WCHAR m_appProvider[MAX_PATH] = {};
    WCHAR m_appMenuPath[MAX_PATH] = {};
    WCHAR m_iconPath[MAX_PATH] = {};
    WCHAR m_expandedPathObj[MAX_PATH] = {};
    WCHAR m_expandedWorkingDir[MAX_PATH] = {};
    CHAR m_clientLanguageId[RDPAPPLIST_LANG_SIZE] = {};
};

HRESULT
    WSLDVCCallback_CreateInstance(
        IWTSVirtualChannel* pChannel,
        IWTSVirtualChannelCallback** ppCallback
    )
{
    return MakeAndInitialize<WSLDVCCallback>(ppCallback, pChannel);
}


================================================
FILE: WSLDVCPlugin/WSLDVCCallback.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once

#include <tsvirtualchannels.h>

HRESULT
WSLDVCCallback_CreateInstance(
    IWTSVirtualChannel* pChannel,
    IWTSVirtualChannelCallback** ppCallback
);

================================================
FILE: WSLDVCPlugin/WSLDVCFileDB.cpp
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "utils.h"
#include "WSLDVCFileDB.h"

#include <string>
#include <set>

class fileEntry
{
public:

    wstring getFileId() const
    {
        return m_fileId;
    }
    wstring getLinkFilePath() const
    {
        return m_linkFilePath;
    }
    wstring getIconFilePath() const
    {
        return m_iconFilePath;
    }
    wstring getExeFilePath() const
    {
        return m_exeFilePath;
    }
    wstring getExeFileArgs() const
    {
        return m_exeFileArgs;
    }

    //fileEntry(const fileEntry& r)
    //{
    //    m_fileId = r.m_fileId;
    //    m_linkFilePath = r.m_linkFilePath;
    //    m_iconFilePath = r.m_iconFilePath;
    //    m_exeFilePath = r.m_exeFilePath;
    //    m_exeFileArgs = r.m_exeFileArgs;
    //}
    fileEntry(const wchar_t* fileId,
        const wchar_t* linkPath = L"",
        const wchar_t* iconPath = L"",
        const wchar_t* exePath = L"",
        const wchar_t* exeArgs = L"")
    {
        assert(fileId);
        m_fileId = fileId;
        m_linkFilePath = linkPath;
        m_iconFilePath = iconPath;
        m_exeFilePath = exePath;
        m_exeFileArgs = exeArgs;
    }

    bool operator< (LPWSTR key) const
    {
        wstring r = key;
        return getFileId() < r;
    }
    bool operator< (const fileEntry& r) const
    {
        return getFileId() < r.getFileId();
    }

private:

    wstring m_fileId;
    wstring m_linkFilePath;
    wstring m_iconFilePath;
    wstring m_exeFilePath;
    wstring m_exeFileArgs;
};

class WSLDVCFileDB :
    public RuntimeClass<
    RuntimeClassFlags<ClassicCom>,
    IWSLDVCFileDB>
{
public:

    HRESULT
        RuntimeClassInitialize(
            void* pContext
        )
    {
        UNREFERENCED_PARAMETER(pContext);
        DebugPrint(L"RuntimeClassInitialize(): WSLDVCFileDB iniitalized: %p\n", this);
        return S_OK;
    }

    STDMETHODIMP
        OnLnkFileAdded(_In_z_ LPCWSTR lnkFile)
    {
        WCHAR iconFile[MAX_PATH] = {};

        {
            fileEntry fileEntry(lnkFile);
            m_fileEntries.insert(fileEntry);
        }

        if (SUCCEEDED(GetIconFileFromShellLink(ARRAYSIZE(iconFile), iconFile, lnkFile)))
        {
            fileEntry fileEntry(iconFile);
            m_fileEntries.insert(fileEntry);
        }

        DebugPrint(L"OnLnkFileAdded():\n");
        DebugPrint(L"\tlnkFile: %s\n", lnkFile);
        DebugPrint(L"\ticonFile: %s\n", iconFile);

        return S_OK;
    }

    STDMETHODIMP
        OnFileAdded(_In_z_ LPCWSTR key,
            _In_opt_z_ LPCWSTR linkFilePath,
            _In_opt_z_ LPCWSTR iconFilePath,
            _In_opt_z_ LPCWSTR exeFilePath,
            _In_opt_z_ LPCWSTR exeFileArgs)
    {
        HRESULT hr = S_OK;
        set<fileEntry>::iterator it;
        std::pair<set<fileEntry>::iterator, bool> p;

        DebugPrint(L"OnFileAdded():\n");
        DebugPrint(L"\tkey: %s\n", key);
        if (linkFilePath && lstrlenW(linkFilePath))
        {
            DebugPrint(L"\tlinkFilePath: %s\n", linkFilePath);
        }
        else
        {
            linkFilePath = L"";
        }
        if (iconFilePath && lstrlenW(iconFilePath))
        {
            DebugPrint(L"\ticonFilePath: %s\n", iconFilePath);
        }
        else
        {
            iconFilePath = L"";
        }
        if (exeFilePath && lstrlenW(exeFilePath))
        {
            DebugPrint(L"\texeFilePath: %s\n", exeFilePath);
        }
        else
        {
            exeFilePath = L"";
        }
        if (exeFileArgs && lstrlenW(exeFileArgs))
        {
            DebugPrint(L"\texeFileArgs: %s\n", exeFileArgs);
        }
        else
        {
            exeFileArgs = L"";
        }

        fileEntry fileEntry(key, linkFilePath, iconFilePath, exeFilePath, exeFileArgs);
        p = m_fileEntries.insert(fileEntry);
        if (!p.second)
        {
            if (p.first->getLinkFilePath().compare(linkFilePath) != 0 ||
                p.first->getIconFilePath().compare(iconFilePath) != 0 ||
                p.first->getExeFilePath().compare(exeFilePath) != 0 ||
                p.first->getExeFileArgs().compare(exeFileArgs) != 0)
            {
                DebugPrint(L"\tKey: %s is already exists and has different path data\n", key);
                DebugPrint(L"\tlinkFilePath: %s\n", p.first->getLinkFilePath().c_str());
                DebugPrint(L"\ticonFilePath: %s\n", p.first->getIconFilePath().c_str());
                DebugPrint(L"\texeFilePath: %s\n", p.first->getExeFilePath().c_str());
                DebugPrint(L"\texeFileArgs: %s\n", p.first->getExeFileArgs().c_str());
                // TODO: implement update existing by erase and add.         
                // DebugAssert(false);
                hr = E_FAIL;
            }
        }

        return hr;
    }

    STDMETHODIMP
        OnFileRemoved(_In_z_ LPCWSTR key)
    {
        HRESULT hr = S_OK;
        set<fileEntry>::iterator it;

        DebugPrint(L"OnFileRemoved()\n");
        DebugPrint(L"\tkey: %s\n", key);

        it = m_fileEntries.find(key);
        if (it != m_fileEntries.end())
        {
            DebugPrint(L"\tKey found:\n");
            if (it->getLinkFilePath().length())
            {
                DebugPrint(L"\tlinkPath: %s\n", it->getLinkFilePath().c_str());
            }
            if (it->getIconFilePath().length())
            {
                DebugPrint(L"\ticonPath: %s\n", it->getIconFilePath().c_str());
            }
            if (it->getExeFilePath().length())
            {
                DebugPrint(L"\texePath: %s\n", it->getExeFilePath().c_str());
            }
            if (it->getExeFileArgs().length())
            {
                DebugPrint(L"\texeArgs: %s\n", it->getExeFileArgs().c_str());
            }
            m_fileEntries.erase(it);
        }
        else
        {
            DebugPrint(L"Key not found\n");
            hr = E_FAIL;
        }

        return hr;
    }

    STDMETHODIMP FindFiles(
        _In_z_ LPCWSTR key, 
        _Out_writes_z_(linkFilePathSize) LPWSTR linkFilePath, UINT32 linkFilePathSize,
        _Out_writes_z_(iconFilePathSize) LPWSTR iconFilePath, UINT32 iconFilePathSize,
        _Out_writes_z_(exeFilePathSize) LPWSTR exeFilePath, UINT32 exeFilePathSize,
        _Out_writes_z_(exeFileArgsSize) LPWSTR exeFileArgs, UINT32 exeFileArgsSize)
    {
        set<fileEntry>::iterator it;

        assert((linkFilePath && linkFilePathSize) ||
               (linkFilePath == nullptr && linkFilePathSize == 0));
        assert((iconFilePath && iconFilePathSize) ||
               (iconFilePath == nullptr && iconFilePathSize == 0));
        assert((exeFilePath && exeFilePathSize) ||
               (exeFilePath == nullptr && exeFilePathSize == 0));
        assert((exeFileArgs && exeFileArgsSize) ||
               (exeFileArgs == nullptr && exeFileArgsSize == 0));

        if (linkFilePath) *linkFilePath = NULL;
        if (iconFilePath) *iconFilePath = NULL;
        if (exeFilePath) *exeFilePath = NULL;
        if (exeFileArgs) *exeFileArgs = NULL;

        DebugPrint(L"FindFiles()\n");
        DebugPrint(L"\tkey: %s\n", key);

        it = m_fileEntries.find(key);
        if (it != m_fileEntries.end())
        {
            DebugPrint(L"\tKey found:\n");
            DebugPrint(L"\tlinkPath: %s\n", it->getLinkFilePath().c_str());
            DebugPrint(L"\ticonPath: %s\n", it->getIconFilePath().c_str());
            DebugPrint(L"\texePath: %s\n", it->getExeFilePath().c_str());
            DebugPrint(L"\texeArgs: %s\n", it->getExeFileArgs().c_str());
            if (linkFilePath)
            {
                if (wcscpy_s(linkFilePath, linkFilePathSize, it->getLinkFilePath().c_str()) != 0)
                {
                    return E_FAIL;
                }
            }
            if (iconFilePath)
            {
                if (wcscpy_s(iconFilePath, iconFilePathSize, it->getIconFilePath().c_str()) != 0)
                {
                    return E_FAIL;
                }
            }
            if (exeFilePath)
            {
                if (wcscpy_s(exeFilePath, exeFilePathSize, it->getExeFilePath().c_str()) != 0)
                {
                    return E_FAIL;
                }
            }
            if (exeFileArgs)
            {
                if (wcscpy_s(exeFileArgs, exeFileArgsSize, it->getExeFileArgs().c_str()) != 0)
                {
                    return E_FAIL;
                }
            }
            return S_OK;
        }
        else
        {
            DebugPrint(L"\tKey NOT found:\n");
            return E_FAIL;
        }
    }

    STDMETHODIMP 
        addAllFilesAsFileIdAt(_In_z_ LPCWSTR path)
    {
        //DebugPrint(L"Dump directory: %s\n", path);
        return addAllSubFolderFiles(path);
    }

    STDMETHODIMP
        deleteAllFileIdFiles()
    {
        set<fileEntry>::iterator it;

        DebugPrint(L"deleteAllFileIdFiles() - files to delete %d\n", m_fileEntries.size());

        for (it = m_fileEntries.begin(); it != m_fileEntries.end(); ) {
            DebugPrint(L"\tDelete %s\n", it->getFileId().c_str());
            if (!DeleteFileW(it->getFileId().c_str()))
            {
                DebugPrint(L"DeleteFile(%s) failed\n", it->getFileId().c_str());
            }
            else
            {
                wstring::size_type found = it->getFileId().find_last_of(L"\\");
                if (found != wstring::npos)
                {
                    wstring dir = it->getFileId().substr(0, found);
                    if (PathIsDirectoryEmptyW(dir.c_str()))
                    {
                        DebugPrint(L"%s is empty, removing\n", dir.c_str());
                        if (!RemoveDirectory(dir.c_str()))
                        {
                            DebugPrint(L"Failed to remove %s\n", dir.c_str());
                        }
                    }
                }
            }
            it = m_fileEntries.erase(it);
        }

        return S_OK;
    }

    STDMETHODIMP
        OnClose()
    {
        DebugPrint(L"OnClose(): WSLDVCFileDB closed: %p\n", this);

        m_fileEntries.clear();

        return S_OK;
    }

protected:

    HRESULT
        addAllSubFolderFiles(_In_z_ const wchar_t* path)
    {
        WIN32_FIND_DATA data = {};
        wstring s = path;
        s += L"\\*";

        HANDLE hFind = FindFirstFile(s.c_str(), &data);
        do {
            wstring sub = path;
            sub += L"\\";
            sub += data.cFileName;

            if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                wstring file = data.cFileName;

                if (file == L"." || file == L"..")
                {
                    continue;
                }

                //DebugPrint(L"\tdirectory: %s\n", sub.c_str());
                if (PathIsDirectoryEmptyW(sub.c_str()))
                {
                    DebugPrint(L"%s is empty, removing\n", sub.c_str());
                    if (!RemoveDirectory(sub.c_str()))
                    {
                        DebugPrint(L"Failed to remove %s\n", sub.c_str());
                    }
                }
                else
                {
                    addAllSubFolderFiles(sub.c_str());
                }
            }
            else
            {
                LPCWSTR ext = PathFindExtensionW(data.cFileName);
                if (wcscmp(ext, L".lnk") == 0)
                {
                     //DebugPrint(L"\t\tlnk file: %s\n", sub.c_str());
                     OnLnkFileAdded(sub.c_str());
                }
            }
        } while (FindNextFile(hFind, &data));
        FindClose(hFind);

        return S_OK;
    }

    virtual
        ~WSLDVCFileDB()
    {
    }

private:
    set<fileEntry> m_fileEntries;
};

HRESULT
WSLDVCFileDB_CreateInstance(
    void* pContext,
    IWSLDVCFileDB** ppFileDB
)
{
    return MakeAndInitialize<WSLDVCFileDB>(ppFileDB, pContext);
}


================================================
FILE: WSLDVCPlugin/WSLDVCFileDB.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once

MIDL_INTERFACE("5802f934-1683-4e81-bb5a-7a0c29a2b1c7")
IWSLDVCFileDB : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE addAllFilesAsFileIdAt(
        _In_z_ LPCWSTR path) = 0;
    virtual HRESULT STDMETHODCALLTYPE deleteAllFileIdFiles() = 0;

    virtual HRESULT STDMETHODCALLTYPE OnFileAdded(
        _In_z_ LPCWSTR key, 
        _In_opt_z_ LPCWSTR linkFilePath, 
        _In_opt_z_ LPCWSTR iconFilePath,
        _In_opt_z_ LPCWSTR exeFilePath,
        _In_opt_z_ LPCWSTR exeFileArgs) = 0;
    virtual HRESULT STDMETHODCALLTYPE OnFileRemoved(
        _In_z_ LPCWSTR key) = 0;

    virtual HRESULT STDMETHODCALLTYPE FindFiles(
        _In_z_ LPCWSTR key,
        _Out_writes_z_(linkFilePathSize) LPWSTR linkFilePath, UINT32 linkFilePathSize,
        _Out_writes_z_(iconFilePathSize) LPWSTR iconFilePath, UINT32 iconFilePathSize,
        _Out_writes_z_(exeFilePathSize) LPWSTR exeFilePath = nullptr, UINT32 exeFilePathSize = 0,
        _Out_writes_z_(exeFileArgsSize) LPWSTR exeFileArgs = nullptr, UINT32 exeFileArgsSize = 0) = 0;

    virtual HRESULT STDMETHODCALLTYPE OnClose(void) = 0;
};

HRESULT
WSLDVCFileDB_CreateInstance(
    void* pContext,
    IWSLDVCFileDB** ppFileDB
);


================================================
FILE: WSLDVCPlugin/WSLDVCListenerCallback.cpp
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "WSLDVCListenerCallback.h"
#include "WSLDVCCallback.h"

class WSLDVCListenerCallback :
    public RuntimeClass<
    RuntimeClassFlags<ClassicCom>,
    IWTSListenerCallback>
{
public:

    HRESULT
        RuntimeClassInitialize()
    {
        return S_OK;
    }

    //
    // IWTSListenerCallback interface
    //
    STDMETHODIMP
        OnNewChannelConnection(
            __RPC__in_opt IWTSVirtualChannel* pChannel,
            __RPC__in_opt BSTR data,
            __RPC__out BOOL* pbAccept,
            __RPC__deref_out_opt IWTSVirtualChannelCallback** ppCallback
        )
    {
        UNREFERENCED_PARAMETER(data);

        HRESULT hr = WSLDVCCallback_CreateInstance(pChannel, ppCallback);
        if (SUCCEEDED(hr))
        {
            *pbAccept = TRUE;
        }

        return hr;
    }

protected:

    virtual
        ~WSLDVCListenerCallback()
    {

    }
};

HRESULT
WSLDVCListenerCallback_CreateInstance(
    IWTSListenerCallback** ppCallback
)
{
    return MakeAndInitialize<WSLDVCListenerCallback>(ppCallback);
}


================================================
FILE: WSLDVCPlugin/WSLDVCListenerCallback.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once

#include <tsvirtualchannels.h>

HRESULT
WSLDVCListenerCallback_CreateInstance(
    IWTSListenerCallback** ppCallback
);

================================================
FILE: WSLDVCPlugin/WSLDVCPlugin.cpp
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "WSLDVCPlugin.h"
#include "WSLDVCListenerCallback.h"

//
// Using Windows Runtime C++ Template Library(WRL) to implement COM objects.
// See:
//    https://docs.microsoft.com/en-us/cpp/cppcx/wrl/how-to-instantiate-wrl-components-directly?view=vs-2019
//    https://docs.microsoft.com/en-us/cpp/cppcx/wrl/how-to-create-a-classic-com-component-using-wrl?view=vs-2019
//

class WSLDVCPlugin :
    public RuntimeClass<
    RuntimeClassFlags<ClassicCom>,
    IWTSPlugin>
{
public:

    HRESULT
        RuntimeClassInitialize()
    {
        return S_OK;
    }

    //
    // IWTSPlugin interface
    //
    STDMETHODIMP
        Initialize(
            __RPC__in_opt IWTSVirtualChannelManager* pChannelMgr
        )
    {
        HRESULT hr = S_OK;
        ComPtr<IWTSListenerCallback> spListenerCallback;
        ComPtr<IWTSListener> spListener;

        hr = WSLDVCListenerCallback_CreateInstance(&spListenerCallback);
        if (SUCCEEDED(hr))
        {
            hr = pChannelMgr->CreateListener(DVC_NAME, 0, spListenerCallback.Get(), &spListener);
        }

        return hr;
    }

    STDMETHODIMP
        Connected()
    {
        return S_OK;
    }

    STDMETHODIMP
        Disconnected(
            DWORD dwDisconnectCode
        )
    {
        UNREFERENCED_PARAMETER(dwDisconnectCode);
        return S_OK;
    }

    STDMETHODIMP
        Terminated()
    {
        return S_OK;
    }

protected:

    virtual
        ~WSLDVCPlugin()
    {
    }

private:

    const char* DVC_NAME = "Microsoft::Windows::RDS::RemoteApplicationList";
};

HRESULT
WSLDVCPlugin_CreateInstance(
    IWTSPlugin** ppPlugin
)
{
    return MakeAndInitialize<WSLDVCPlugin>(ppPlugin);
}


================================================
FILE: WSLDVCPlugin/WSLDVCPlugin.def
================================================
LIBRARY "WSLDVCPlugin.dll"

EXPORTS
    VirtualChannelGetInstance PRIVATE

================================================
FILE: WSLDVCPlugin/WSLDVCPlugin.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once

#include <tsvirtualchannels.h>

HRESULT
WSLDVCPlugin_CreateInstance(
    IWTSPlugin** ppPlugin
);


================================================
FILE: WSLDVCPlugin/WSLDVCPlugin.rc
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// English (United States) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Version
//

VS_VERSION_INFO VERSIONINFO
 FILEVERSION 1,0,0,1
 PRODUCTVERSION 1,0,0,1
 FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x40004L
 FILETYPE 0x2L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904b0"
        BEGIN
            VALUE "CompanyName", " Microsoft Corporation. All rights reserved"
            VALUE "FileDescription", "WSL Remote Application List Plug-in"
            VALUE "FileVersion", "1.0.0.1"
            VALUE "InternalName", "WSLDVCPl.dll"
            VALUE "LegalCopyright", " Microsoft Corporation. All rights reserved"
            VALUE "OriginalFilename", "WSLDVCPl.dll"
            VALUE "ProductName", "WSL Remote Application List Plug-in"
            VALUE "ProductVersion", "InformationalVersion"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END

#endif    // English (United States) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED



================================================
FILE: WSLDVCPlugin/WSLDVCPlugin.sln
================================================

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30413.136
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WSLDVCPlugin", "WSLDVCPlugin.vcxproj", "{B7E66936-5220-4D77-AA10-C26A7F6F42D6}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|ARM64 = Debug|ARM64
		Debug|x64 = Debug|x64
		Debug|x86 = Debug|x86
		Release|ARM64 = Release|ARM64
		Release|x64 = Release|x64
		Release|x86 = Release|x86
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Debug|ARM64.ActiveCfg = Debug|ARM64
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Debug|ARM64.Build.0 = Debug|ARM64
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Debug|x64.ActiveCfg = Debug|x64
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Debug|x64.Build.0 = Debug|x64
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Debug|x86.ActiveCfg = Debug|Win32
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Debug|x86.Build.0 = Debug|Win32
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Release|ARM64.ActiveCfg = Release|ARM64
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Release|ARM64.Build.0 = Release|ARM64
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Release|x64.ActiveCfg = Release|x64
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Release|x64.Build.0 = Release|x64
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Release|x86.ActiveCfg = Release|Win32
		{B7E66936-5220-4D77-AA10-C26A7F6F42D6}.Release|x86.Build.0 = Release|Win32
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {EB2D28FB-D37D-4286-A386-7723B34EF53F}
	EndGlobalSection
EndGlobal


================================================
FILE: WSLDVCPlugin/WSLDVCPlugin.vcxproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|ARM64">
      <Configuration>Debug</Configuration>
      <Platform>ARM64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|ARM64">
      <Configuration>Release</Configuration>
      <Platform>ARM64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <VCProjectVersion>16.0</VCProjectVersion>
    <Keyword>Win32Proj</Keyword>
    <ProjectGuid>{b7e66936-5220-4d77-aa10-c26a7f6f42d6}</ProjectGuid>
    <RootNamespace>WSLDVCPlugin</RootNamespace>
    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
    <SpectreMitigation>Spectre</SpectreMitigation>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
    <SpectreMitigation>Spectre</SpectreMitigation>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
    <SpectreMitigation>Spectre</SpectreMitigation>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
    <SpectreMitigation>Spectre</SpectreMitigation>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
    <SpectreMitigation>Spectre</SpectreMitigation>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
    <SpectreMitigation>Spectre</SpectreMitigation>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="Shared">
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <LinkIncremental>true</LinkIncremental>
    <TargetName>WSLDVCPlugin</TargetName>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <LinkIncremental>false</LinkIncremental>
    <TargetName>WSLDVCPlugin</TargetName>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <LinkIncremental>true</LinkIncremental>
    <TargetName>WSLDVCPlugin</TargetName>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
    <LinkIncremental>true</LinkIncremental>
    <TargetName>WSLDVCPlugin</TargetName>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <LinkIncremental>false</LinkIncremental>
    <TargetName>WSLDVCPlugin</TargetName>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
    <LinkIncremental>false</LinkIncremental>
    <TargetName>WSLDVCPlugin</TargetName>
  </PropertyGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>WIN32;_DEBUG;WSLDVCPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <ControlFlowGuard>Guard</ControlFlowGuard>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>WIN32;NDEBUG;WSLDVCPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <ControlFlowGuard>Guard</ControlFlowGuard>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <ClCompile>
      <WarningLevel>Level4</WarningLevel>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_DEBUG;WSLDVCPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
      <TreatWarningAsError>true</TreatWarningAsError>
      <ControlFlowGuard>Guard</ControlFlowGuard>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>DebugFull</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <StripPrivateSymbols>
      </StripPrivateSymbols>
      <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
    <ClCompile>
      <WarningLevel>Level4</WarningLevel>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_DEBUG;WSLDVCPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
      <TreatWarningAsError>true</TreatWarningAsError>
      <ControlFlowGuard>Guard</ControlFlowGuard>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>DebugFull</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <StripPrivateSymbols>
      </StripPrivateSymbols>
      <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <WarningLevel>Level4</WarningLevel>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>NDEBUG;WSLDVCPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
      <TreatWarningAsError>true</TreatWarningAsError>
      <ControlFlowGuard>Guard</ControlFlowGuard>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>DebugFull</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
    <ClCompile>
      <WarningLevel>Level4</WarningLevel>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>NDEBUG;WSLDVCPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
      <TreatWarningAsError>true</TreatWarningAsError>
      <ControlFlowGuard>Guard</ControlFlowGuard>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>DebugFull</GenerateDebugInformation>
      <EnableUAC>false</EnableUAC>
      <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup>
    <None Include="cpp.hint" />
    <None Include="WSLDVCPlugin.def" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="framework.h" />
    <ClInclude Include="rdpapplist.h" />
    <ClInclude Include="resource.h" />
    <ClInclude Include="utils.h" />
    <ClInclude Include="pch.h" />
    <ClInclude Include="WSLDVCCallback.h" />
    <ClInclude Include="WSLDVCFileDB.h" />
    <ClInclude Include="WSLDVCListenerCallback.h" />
    <ClInclude Include="WSLDVCPlugin.h" />
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="dllmain.cpp" />
    <ClCompile Include="pch.cpp">
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
    </ClCompile>
    <ClCompile Include="utils.cpp" />
    <ClCompile Include="WSLDVCCallback.cpp" />
    <ClCompile Include="WSLDVCFileDB.cpp" />
    <ClCompile Include="WSLDVCListenerCallback.cpp" />
    <ClCompile Include="WSLDVCPlugin.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="WSLDVCPlugin.rc" />
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
  </ImportGroup>
</Project>

================================================
FILE: WSLDVCPlugin/WSLDVCPlugin.vcxproj.filters
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <Filter Include="Source Files">
      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
      <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
    </Filter>
    <Filter Include="Header Files">
      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
      <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
    </Filter>
    <Filter Include="Resource Files">
      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
    </Filter>
  </ItemGroup>
  <ItemGroup>
    <None Include="cpp.hint" />
    <None Include="WSLDVCPlugin.def">
      <Filter>Source Files</Filter>
    </None>
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="framework.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="WSLDVCPlugin.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="pch.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="WSLDVCListenerCallback.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="WSLDVCCallback.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="utils.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="rdpapplist.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="resource.h">
      <Filter>Header Files</Filter>
    </ClInclude>
    <ClInclude Include="WSLDVCFileDB.h">
      <Filter>Header Files</Filter>
    </ClInclude>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="WSLDVCPlugin.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="dllmain.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="pch.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="WSLDVCListenerCallback.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="WSLDVCCallback.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="utils.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="WSLDVCFileDB.cpp">
      <Filter>Source Files</Filter>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="WSLDVCPlugin.rc">
      <Filter>Resource Files</Filter>
    </ResourceCompile>
  </ItemGroup>
</Project>

================================================
FILE: WSLDVCPlugin/WSLDVCPlugin.vcxproj.user
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup />
</Project>

================================================
FILE: WSLDVCPlugin/cpp.hint
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#define WSLDVCPLUGIN_API __declspec(dllexport)
#define WSLDVCPLUGIN_API __declspec(dllimport)


================================================
FILE: WSLDVCPlugin/dllmain.cpp
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "utils.h"
#include "WSLDVCPlugin.h"
#include "WSLDVCFileDB.h"
#include <cchannel.h>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    UNREFERENCED_PARAMETER(hModule);
    UNREFERENCED_PARAMETER(lpReserved);

    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

extern "C"
{
    __declspec(dllexport) HRESULT VCAPITYPE
        VirtualChannelGetInstance(
            _In_ REFIID refiid,
            _Inout_ ULONG* pNumObjs,
            _Out_opt_ VOID** ppObjArray
        )
    {
        HRESULT hr = S_OK;
        ComPtr<IWTSPlugin> spPlugin;

        if (refiid != __uuidof(IWTSPlugin))
        {
            return E_NOINTERFACE;
        }

        if (ppObjArray == NULL)
        {
            *pNumObjs = 1;
            return S_OK;
        }

        hr = WSLDVCPlugin_CreateInstance(&spPlugin);
        if (SUCCEEDED(hr))
        {
            *pNumObjs = 1;
            ppObjArray[0] = spPlugin.Detach();
        }

        return hr;
    }

    __declspec(dllexport) HRESULT WINAPI
        RemoveAppProvider(
            _In_z_ LPCWSTR appProvider
        )
    {
        HRESULT hr;
        WCHAR appMenuPath[MAX_PATH] = {};
        ComPtr<IWSLDVCFileDB> spFileDB;

        if (!appProvider)
        {
            DebugPrint(L"appProvider parameter is required\n");
            return E_INVALIDARG;
        }

        hr = BuildMenuPath(ARRAYSIZE(appMenuPath), appMenuPath, appProvider, false);
        if (FAILED(hr))
        {
            return hr;
        }
        DebugPrint(L"AppMenuPath: %s\n", appMenuPath);

        if (!IsDirectoryPresent(appMenuPath))
        {
            DebugPrint(L"%s is not present\n", appMenuPath);
            return S_OK; // no program menu exists for given provider, simply exit.
        }

        hr = WSLDVCFileDB_CreateInstance(NULL, &spFileDB);
        if (FAILED(hr))
        {
            DebugPrint(L"failed to instance WSLDVCFileDB\n");
            return hr;
        }

        spFileDB->addAllFilesAsFileIdAt(appMenuPath);
        spFileDB->deleteAllFileIdFiles();
        spFileDB->OnClose();
        spFileDB = nullptr;

        return hr;
    }
}


================================================
FILE: WSLDVCPlugin/framework.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once

#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <iostream>
#include <cassert>
#include <string>
#include <windows.h>
#include <wrl.h>
#include <objidl.h>   /* For IPersistFile */
#include <shlobj.h>   /* For IShellLink */
#include <shlwapi.h>  /* For PathIsDirectoryEmpty */
#include <shellapi.h> /* For SHGetPropertyStoreForWindow */
#include <propvarutil.h> /* For InitPropVariantFromString */
#include <propkey.h> /* For PKEY_* */
#include <inttypes.h> /* For PRIx64 */


================================================
FILE: WSLDVCPlugin/pch.cpp
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"

// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.


================================================
FILE: WSLDVCPlugin/pch.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.

#ifndef PCH_H
#define PCH_H

// add headers that you want to pre-compile here
#include "framework.h"

using namespace Microsoft::WRL;
using namespace std;

#endif //PCH_H


================================================
FILE: WSLDVCPlugin/rdpapplist.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once

//
// RDP APPLIST protocol header.
//
#define RDPAPPLIST_CMDID_CAPS 0x00000001
#define RDPAPPLIST_CMDID_UPDATE_APPLIST 0x00000002
#define RDPAPPLIST_CMDID_DELETE_APPLIST 0x00000003
#define RDPAPPLIST_CMDID_DELETE_APPLIST_PROVIDER 0x00000004
/* added from version 4 */
#define RDPAPPLIST_CMDID_ASSOCIATE_WINDOW_ID 0x00000005

#define RDPAPPLIST_FIELD_ID         0x00000001
#define RDPAPPLIST_FIELD_GROUP      0x00000002
#define RDPAPPLIST_FIELD_EXECPATH   0x00000004
#define RDPAPPLIST_FIELD_DESC       0x00000008
#define RDPAPPLIST_FIELD_ICON       0x00000010
#define RDPAPPLIST_FIELD_PROVIDER   0x00000020
#define RDPAPPLIST_FIELD_WORKINGDIR 0x00000040
#define RDPAPPLIST_FIELD_WINDOW_ID  0x00000080

/* RDPAPPLIST_UPDATE_APPLIST_PDU */
#define RDPAPPLIST_HINT_NEWID      0x00010000 /* new appId vs update existing appId. */
#define RDPAPPLIST_HINT_SYNC       0x00100000 /* In sync mode (use with _NEWID). */
#define RDPAPPLIST_HINT_SYNC_START 0x00200000 /* Sync appId start (use with _SYNC). */
#define RDPAPPLIST_HINT_SYNC_END   0x00400000 /* Sync appId end (use with _SYNC). */

#define RDPAPPLIST_CHANNEL_VERSION 4

#define RDPAPPLIST_HEADER_SIZE 8

#define RDPAPPLIST_LANG_SIZE 32

#define RDPAPPLIST_MAX_STRING_SIZE 512
#define RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR (RDPAPPLIST_MAX_STRING_SIZE/sizeof(WCHAR))

typedef struct _RDPAPPLIST_HEADER
{
    UINT32 cmdId;
    UINT32 length;
} RDPAPPLIST_HEADER;

typedef struct _RDPAPPLIST_CLIENT_CAPS_PDU
{
    UINT16 version;
    /* ISO 639 (Language name) and ISO 3166 (Country name) connected with '_', such as en_US, ja_JP */
    char clientLanguageId[RDPAPPLIST_LANG_SIZE];
} RDPAPPLIST_CLIENT_CAPS_PDU;

typedef struct _RDPAPPLIST_SERVER_CAPS_PDU
{
    UINT16 version;
    UINT16 appListProviderNameLength;
    WCHAR appListProviderName[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
    /* added from version 4 */
    UINT16 appListProviderUniqueIdLength;
    WCHAR appListProviderUniqueId[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
} RDPAPPLIST_SERVER_CAPS_PDU;

typedef struct _RDPAPPLIST_UPDATE_APPLIST_PDU
{
    UINT32 flags;
    UINT16 appIdLength;
    WCHAR appId[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
    UINT16 appGroupLength;
    WCHAR appGroup[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
    UINT16 appExecPathLength;
    WCHAR appExecPath[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
    UINT16 appWorkingDirLength;
    WCHAR appWorkingDir[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
    UINT16 appDescLength;
    WCHAR appDesc[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
} RDPAPPLIST_UPDATE_APPLIST_PDU;

typedef struct _RDPAPPLIST_ICON_DATA
{
    UINT32 flags;
    UINT32 iconWidth;
    UINT32 iconHeight;
    UINT32 iconStride;
    UINT32 iconBpp;
    UINT32 iconFormat;
    UINT32 iconBitsLength;
    UINT32 iconFileSize;
    BYTE* iconFileData;
} RDPAPPLIST_ICON_DATA;

typedef struct _RDPAPPLIST_DELETE_APPLIST_PDU
{
    UINT32 flags;
    UINT16 appIdLength;
    WCHAR appId[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
    UINT16 appGroupLength;
    WCHAR appGroup[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
} RDPAPPLIST_DELETE_APPLIST_PDU;

typedef struct _RDPAPPLIST_DELETE_APPLIST_PROVIDER_PDU
{
    UINT32 flags;
    UINT16 appListProviderNameLength;
    WCHAR appListProviderName[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
} RDPAPPLIST_DELETE_APPLIST_PROVIDER_PDU;

/* added from version 4 */
typedef struct _RDPAPPLIST_ASSOCIATE_WINDOW_ID
{
    UINT32 flags;
    UINT32 appWindowId;
    UINT16 appIdLength;
    WCHAR appId[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
    UINT16 appGroupLength;
    WCHAR appGroup[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
    UINT16 appExecPathLength;
    WCHAR appExecPath[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
    UINT16 appDescLength;
    WCHAR appDesc[RDPAPPLIST_MAX_STRING_SIZE_IN_WCHAR];
} RDPAPPLIST_ASSOCIATE_WINDOW_ID_PDU;

//
// Read macro.
//
#define LSTR(x) L ## x

// ReadUINT16(dest, source, remaining)
#define ReadUINT16(o, p, r)    \
    if (r >= sizeof(UINT16)) { \
        o = (*(UINT16*)(p));   \
        (p) += sizeof(UINT16); \
        (r) -= sizeof(UINT16); \
    } else {                   \
        DebugPrint(L"Failed to read " LSTR(#o) L"\n"); \
        goto Error_Read;       \
    }

// ReadUINT32(dest, source, remaining)
#define ReadUINT32(o, p, r)    \
    if (r >= sizeof(UINT32)) { \
        o = (*(UINT32*)(p));   \
        (p) += sizeof(UINT32); \
        (r) -= sizeof(UINT32); \
    } else {                   \
        DebugPrint(L"Failed to read " LSTR(#o) L"\n"); \
        goto Error_Read;       \
    }

// ReadBYTES(dest, source, lengthToCopy, RemainingSource)
#define ReadBYTES(o, p, l, r)  \
    if (r >= l) {              \
        memcpy((o), (p), (l)); \
        (p) += l;              \
        (r) -= (l);            \
    } else {                   \
        DebugPrint(L"Failed to read " LSTR(#o) L"\n"); \
        goto Error_Read;       \
    }

// ReadSTRING(dest, source, RemainingSource, required)
#define ReadSTRING(o, p, r, required) \
    ReadUINT16(o ## Length, p, r); \
    if (o ## Length + sizeof(WCHAR) > sizeof(o)) { \
        DebugPrint(L"Failed to read " LSTR(#o) L"\n"); \
        goto Error_Read; \
    } if (o ## Length) { \
        ReadBYTES(o, p, o ## Length, r); \
        o[o ## Length / sizeof(WCHAR)] = L'\0'; \
    } else if (required) { \
        DebugPrint(LSTR(#o) L" is required\n"); \
        goto Error_Read; \
    }

#define CheckRequiredFlags(flags, required) \
    { \
        auto f = (flags) & (required); \
        if (f != (required)) { \
            DebugPrint(L"missing required flags. Given:%x vs Required:%x\n", flags, required); \
            goto Error_Read; \
        } \
    }


================================================
FILE: WSLDVCPlugin/resource.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by WSLDVCPlugin.rc

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif


================================================
FILE: WSLDVCPlugin/utils.cpp
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "utils.h"

#ifdef DBG_MESSAGE
void DebugPrint(const wchar_t* format, ...)
{
    WCHAR buf[512] = {};
    va_list args;

    va_start(args, format);
    wvsprintfW(buf, format, args);
    va_end(args);

    OutputDebugStringW(buf);
}
#endif // DBG_MESSAGE

_Use_decl_annotations_
BOOL IsDirectoryPresent(LPCWSTR lpszPath)
{
    DWORD dwAttrib = GetFileAttributes(lpszPath);

    return (dwAttrib != INVALID_FILE_ATTRIBUTES && 
           (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

_Use_decl_annotations_
HRESULT
CreateShellLink(LPCWSTR lpszPathLink,
    LPCWSTR lpszPathObj,
    LPCWSTR lpszArgs,
    LPCWSTR lpszWorkingDir,
    LPCWSTR lpszDesc,
    LPCWSTR lpszPathIcon)
{
    HRESULT hr;
    IShellLink* psl;

    DebugPrint(L"CreateShellLink:\n");
    DebugPrint(L"\tPath Link: %s\n", lpszPathLink);
    DebugPrint(L"\tPath Exe: %s\n", lpszPathObj);
    DebugPrint(L"\tExe args: %s\n", lpszArgs);
    DebugPrint(L"\tWorkingDir: %s\n", lpszWorkingDir);
    DebugPrint(L"\tDesc: %s\n", lpszDesc);
    if (lpszPathIcon && lstrlenW(lpszPathIcon))
    {
        DebugPrint(L"\tIcon Path: %s\n", lpszPathIcon);
    }
    else
    {
        lpszPathIcon = nullptr;
    }

    // Get a pointer to the IShellLink interface. It is assumed that CoInitialize
    // has already been called.
    hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
    if (SUCCEEDED(hr))
    {
        IPersistFile* ppf;

        // Set the path to the shortcut target and add the description. 
        psl->SetPath(lpszPathObj);
        psl->SetArguments(lpszArgs);
        if (lpszPathIcon)
        {
            psl->SetIconLocation(lpszPathIcon, 0);
        }
        psl->SetDescription(lpszDesc);
        psl->SetWorkingDirectory(lpszWorkingDir);
        psl->SetShowCmd(SW_SHOWMINNOACTIVE);

        // Query IShellLink for the IPersistFile interface, used for saving the 
        // shortcut in persistent storage. 
        hr = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
        if (SUCCEEDED(hr))
        {
            // Save the link by calling IPersistFile::Save. 
            hr = ppf->Save(lpszPathLink, TRUE);
            ppf->Release();
        }
        psl->Release();
    }

    DebugPrint(L"\tresult: %x\n", hr);
    return hr;
}

_Use_decl_annotations_
HRESULT
GetIconFileFromShellLink(
    UINT32 iconPathSize, 
    LPWSTR iconPath,
    LPCWSTR lnkPath)
{
    HRESULT hr;
    IShellLink* psl;

    DebugPrint(L"GetIconFileFromShellLink:\n");
    DebugPrint(L"\tPath Link: %s\n", lnkPath);

    assert(iconPathSize);
    assert(iconPath);
    *iconPath = L'\0';

    hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
    if (SUCCEEDED(hr))
    {
        IPersistFile* ppf;
        hr = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
        if (SUCCEEDED(hr))
        {
            hr = ppf->Load(lnkPath, STGM_READ);
            if (SUCCEEDED(hr))
            {
                int dummy = 0;
                hr = psl->GetIconLocation(iconPath, iconPathSize, &dummy);
            }
            ppf->Release();
        }
        psl->Release();
    }

    DebugPrint(L"\tresult: %x\n", hr);
    if (SUCCEEDED(hr))
    {
        DebugPrint(L"\ticonPath: %s\n", iconPath);
    }

    return hr;
}

_Use_decl_annotations_
HRESULT
CreateIconFile(BYTE* pBuffer,
    UINT32 cbSize,
    LPCWSTR lpszIconFile)
{
    HRESULT hr = S_OK;
    HANDLE hFile;

    DebugPrint(L"CreateIconFile: %s\n", lpszIconFile);

    hFile = CreateFileW(lpszIconFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        DebugPrint(L"CreateFile(%s) failed, error %d\n", lpszIconFile, GetLastError());
        hr = E_FAIL;
    }
    else
    {        
        if (!WriteFile(hFile, pBuffer, cbSize, NULL, NULL))
        {
            DebugPrint(L"WriteFile(%s) failed, error %d\n", lpszIconFile, GetLastError());
            hr = E_FAIL;
        }
        
        CloseHandle(hFile);
    }

    DebugPrint(L"\tresult: %x\n", hr);
    return hr;
}

#define MAX_LOCALE_CODE 9

_Use_decl_annotations_
BOOL GetLocaleName(char* localeName, int localeNameSize)
{
    char langCode[MAX_LOCALE_CODE] = {};
    char countryName[MAX_LOCALE_CODE] = {};
    int result = 0;

    assert(localeName);
    localeName[0] = '\0';

    LCID lcid = MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT);
    result = GetLocaleInfoA(lcid,
        LOCALE_SISO639LANGNAME,
        langCode,
        MAX_LOCALE_CODE) != 0;
    if ((result == 0) ||
        (strcpy_s(localeName, localeNameSize, langCode) != 0) ||
        (strcat_s(localeName, localeNameSize, "_") != 0))
    {
        return FALSE;
    }

    result = GetLocaleInfoA(lcid,
        LOCALE_SISO3166CTRYNAME,
        countryName,
        MAX_LOCALE_CODE) != 0;
    if ((result == 0) ||
        (strcat_s(localeName, localeNameSize, countryName) != 0))
    {
        return FALSE;
    }

    return TRUE;
}

_Use_decl_annotations_
HRESULT BuildMenuPath(
    UINT32 appMenuPathSize,
    LPWSTR appMenuPath,
    LPCWSTR appProvider,
    bool isCreateDir)
{
    PWSTR knownFolderPath = NULL; // free by CoTaskMemFree. 
    SHGetKnownFolderPath(FOLDERID_StartMenu, 0, NULL, &knownFolderPath);
    if (!knownFolderPath)
    {
        DebugPrint(L"SHGetKnownFolderPath(FOLDERID_StartMenu) failed\n");
        return E_FAIL;
    }
    int ret = swprintf_s(appMenuPath, appMenuPathSize, L"%s\\Programs\\%s", knownFolderPath, appProvider);
    CoTaskMemFree(knownFolderPath);
    if (ret < 0)
    {
        DebugPrint(L"swprintf_s for appMenuPath failed");
        return E_FAIL;
    }
    if (isCreateDir)
    {
        if (!CreateDirectoryW(appMenuPath, NULL))
        {
            if (ERROR_ALREADY_EXISTS != GetLastError())
            {
                DebugPrint(L"Failed to create %s\n", appMenuPath);
                return E_FAIL;
            }
        }
    }

    return S_OK;
}

_Use_decl_annotations_
HRESULT BuildIconPath(
    UINT32 iconPathSize,
    LPWSTR iconPath,
    LPCWSTR appProvider,
    bool isCreateDir)
{
    WCHAR prefix[] = L"WSLDVCPlugin\\";

    UINT32 lenTempPath;
    lenTempPath = GetTempPathW(iconPathSize, iconPath);
    if (!lenTempPath)
    {
        DebugPrint(L"GetTempPathW failed\n");
        return E_FAIL;
    }

    if ((lenTempPath + ARRAYSIZE(prefix) + wcslen(appProvider)) > iconPathSize)
    {
        DebugPrint(L"provider name length check failed, length %d\n",  wcslen(appProvider));
        return E_FAIL;
    }

    if (wcscat_s(iconPath, iconPathSize, prefix) != 0)
    {
        return E_FAIL;
    }
    if (isCreateDir)
    {
        if (!CreateDirectoryW(iconPath, NULL))
        {
            if (ERROR_ALREADY_EXISTS != GetLastError())
            {
                DebugPrint(L"Failed to create %s\n", iconPath);
                return E_FAIL;
            }
        }
    }
    if (wcscat_s(iconPath, iconPathSize, appProvider) != 0)
    {
        return E_FAIL;
    }
    if (isCreateDir)
    {
        if (!CreateDirectoryW(iconPath, NULL))
        {
            if (ERROR_ALREADY_EXISTS != GetLastError())
            {
                DebugPrint(L"Failed to create %s\n", iconPath);
                return E_FAIL;
            }
        }
    }

    return S_OK;
}

HRESULT
UpdateTaskBarInfo(
    HWND hwnd,
    _In_z_ LPCWSTR relaunchCmdline,
    _In_z_ LPCWSTR displayName,
    _In_z_ LPCWSTR iconPath)
{
    HRESULT hr;
    PROPVARIANT propvar;

    DebugPrint(L"UpdateTaskBarInfo: 0x%p\n", hwnd);
    DebugPrint(L"    relaunchCmdline: %s\n", relaunchCmdline);
    DebugPrint(L"    displayName: %s\n", displayName);
    DebugPrint(L"    iconPath: %s\n", iconPath);

    IPropertyStore* ps = NULL;

    hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&ps));
    if (FAILED(hr))
    {
        DebugPrint(L"SHGetPropertyStoreForWindow failed: 0x%x\n", hr);
        return hr;
    }

    BOOL bPinToTaskbar = relaunchCmdline && displayName && iconPath;
    if (bPinToTaskbar)
    {
        hr = InitPropVariantFromString(displayName, &propvar);
        if (FAILED(hr))
        {
            DebugPrint(L"InitPropVariantFromString failed: 0x%x\n", hr);
            return hr;
        }

        hr = ps->SetValue(PKEY_AppUserModel_RelaunchDisplayNameResource, propvar);
        PropVariantClear(&propvar);
        if (FAILED(hr))
        {
            DebugPrint(L"SetValue(PKEY_AppUserModel_RelaunchDisplayNameResource failed: 0x%x\n", hr);
            return hr;
        }

        hr = InitPropVariantFromString(iconPath, &propvar);
        if (FAILED(hr))
        {
            DebugPrint(L"InitPropVariantFromString failed: 0x%x\n", hr);
            return hr;
        }

        hr = ps->SetValue(PKEY_AppUserModel_RelaunchIconResource, propvar);
        PropVariantClear(&propvar);
        if (FAILED(hr))
        {
            DebugPrint(L"SetValue(PKEY_AppUserModel_RelaunchIconResource failed: 0x%x\n", hr);
            return hr;
        }

        hr = InitPropVariantFromString(relaunchCmdline, &propvar);
        if (FAILED(hr))
        {
            DebugPrint(L"InitPropVariantFromString failed: 0x%x\n", hr);
            return hr;
        }

        hr = ps->SetValue(PKEY_AppUserModel_RelaunchCommand, propvar);
        PropVariantClear(&propvar);
        if (FAILED(hr))
        {
            DebugPrint(L"SetValue(PKEY_AppUserModel_RelaunchCommand failed: 0x%x\n", hr);
            return hr;
        }
    }
    else
    {
        hr = InitPropVariantFromBoolean(TRUE, &propvar);
        if (FAILED(hr))
        {
            DebugPrint(L"InitPropVariantFromBoolean failed: 0x%x\n", hr);
            return hr;
        }

        hr = ps->SetValue(PKEY_AppUserModel_PreventPinning, propvar);
        PropVariantClear(&propvar);
        if (FAILED(hr))
        {
            DebugPrint(L"SetValue(PKEY_AppUserModel_PreventPinning failed: 0x%x\n", hr);
            return hr;
        }
    }

    ps->Release();

    return S_OK;
}

#if ENABLE_WSL_SIGNATURE_CHECK
// Link with the Wintrust.lib file.
#pragma comment (lib, "wintrust")

#include <Softpub.h>
#include <wincrypt.h>
#include <wintrust.h>

_Use_decl_annotations_
BOOL IsFileTrusted(LPCWSTR pwszFile)
{
    BOOL bTrusted = FALSE;

    LONG lStatus;
    DWORD dwLastError;

    // Initialize the WINTRUST_FILE_INFO structure.
    WINTRUST_FILE_INFO FileData;
    memset(&FileData, 0, sizeof(FileData));
    FileData.cbStruct = sizeof(WINTRUST_FILE_INFO);
    FileData.pcwszFilePath = pwszFile;
    FileData.hFile = NULL;
    FileData.pgKnownSubject = NULL;

    /*
    WVTPolicyGUID specifies the policy to apply on the file
    WINTRUST_ACTION_GENERIC_VERIFY_V2 policy checks:
    
    1) The certificate used to sign the file chains up to a root 
    certificate located in the trusted root certificate store. This 
    implies that the identity of the publisher has been verified by 
    a certification authority.
    
    2) In cases where user interface is displayed (which this example
    does not do), WinVerifyTrust will check for whether the  
    end entity certificate is stored in the trusted publisher store,  
    implying that the user trusts content from this publisher.
    
    3) The end entity certificate has sufficient permission to sign 
    code, as indicated by the presence of a code signing EKU or no 
    EKU.
    */

    GUID WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
    WINTRUST_DATA WinTrustData;

    // Initialize the WinVerifyTrust input data structure.

    // Default all fields to 0.
    memset(&WinTrustData, 0, sizeof(WinTrustData));

    WinTrustData.cbStruct = sizeof(WinTrustData);
    
    // Use default code signing EKU.
    WinTrustData.pPolicyCallbackData = NULL;

    // No data to pass to SIP.
    WinTrustData.pSIPClientData = NULL;

    // Disable WVT UI.
    WinTrustData.dwUIChoice = WTD_UI_NONE;

    // No revocation checking.
    WinTrustData.fdwRevocationChecks = WTD_REVOKE_NONE; 

    // Verify an embedded signature on a file.
    WinTrustData.dwUnionChoice = WTD_CHOICE_FILE;

    // Verify action.
    WinTrustData.dwStateAction = WTD_STATEACTION_VERIFY;

    // Verification sets this value.
    WinTrustData.hWVTStateData = NULL;

    // Not used.
    WinTrustData.pwszURLReference = NULL;

    // This is not applicable if there is no UI because it changes 
    // the UI to accommodate running applications instead of 
    // installing applications.
    WinTrustData.dwUIContext = 0;

    // Set pFile.
    WinTrustData.pFile = &FileData;

    // WinVerifyTrust verifies signatures as specified by the GUID 
    // and Wintrust_Data.
    lStatus = WinVerifyTrust(
        NULL,
        &WVTPolicyGUID,
        &WinTrustData);

    switch (lStatus) 
    {
        case ERROR_SUCCESS:
            //Signed file:
            //    - Hash that represents the subject is trusted.
            //    - Trusted publisher without any verification errors.
            DebugPrint(L"The file \"%s\" is signed and the signature was verified.\n",
                pwszFile);
            bTrusted = TRUE;
            break;
        
        case TRUST_E_NOSIGNATURE:
            // The file was not signed or had a signature 
            // that was not valid.
            // Get the reason for no signature.
            dwLastError = GetLastError();
            if (TRUST_E_NOSIGNATURE == dwLastError ||
                    TRUST_E_SUBJECT_FORM_UNKNOWN == dwLastError ||
                    TRUST_E_PROVIDER_UNKNOWN == dwLastError) 
            {
                // The file was not signed.
                DebugPrint(L"The file \"%s\" is not signed.\n",
                    pwszFile);
            } 
            else 
            {
                // The signature was not valid or there was an error 
                // opening the file.
                DebugPrint(L"An unknown error occurred trying to "
                    L"verify the signature of the \"%s\" file.\n",
                    pwszFile);
            }
            break;

        case TRUST_E_EXPLICIT_DISTRUST:
            // The hash that represents the subject or the publisher 
            // is not allowed by the admin or user.
            DebugPrint(L"The signature is present, but specifically disallowed.\n");
            break;

        case TRUST_E_SUBJECT_NOT_TRUSTED:
            DebugPrint(L"The signature is present, but not trusted.\n");
            break;

        default:
            // The UI was disabled in dwUIChoice or the admin policy 
            // has disabled user trust. lStatus contains the 
            // publisher or time stamp chain error.
            DebugPrint(L"Error is: 0x%x.\n", lStatus);
            break;
    }

    // Any hWVTStateData must be released by a call with close.
    WinTrustData.dwStateAction = WTD_STATEACTION_CLOSE;

    lStatus = WinVerifyTrust(
        NULL,
        &WVTPolicyGUID,
        &WinTrustData);

    return bTrusted;
}
#endif // ENABLE_WSL_SIGNATURE_CHECK


================================================
FILE: WSLDVCPlugin/utils.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once

//#ifdef _DEBUG
#define DBG_MESSAGE
//#endif // _DEBUG

#ifdef DBG_MESSAGE
void DebugPrint(const wchar_t* format, ...);
#define DebugAssert(exp) assert(exp)
#else
#define DebugPrint
#define DebugAssert
#endif // DBG_MESSAGE

// Set to 1 to enable digital signature check.
#define ENABLE_WSL_SIGNATURE_CHECK 0

BOOL
IsDirectoryPresent(_In_z_ LPCWSTR lpszPath);

HRESULT
CreateShellLink(_In_z_ LPCWSTR lpszPathLink,
    _In_z_ LPCWSTR lpszPathObj, 
    _In_z_ LPCWSTR lpszArgs,
    _In_z_ LPCWSTR lpszWorkingDir,
    _In_z_ LPCWSTR lpszDesc, 
    _In_opt_z_ LPCWSTR lpszPathIcon);

HRESULT
GetIconFileFromShellLink(
    UINT32 iconPathSize, 
    _Out_writes_z_(iconPathSize) LPWSTR iconPath,
    _In_z_ LPCWSTR lnkPath);

HRESULT
CreateIconFile(_In_reads_bytes_(cbSize) BYTE* pBuffer,
    UINT32 cbSize,
    _In_z_ LPCWSTR lpszIconFile);

BOOL
GetLocaleName(_Out_writes_z_(localeNameSize) char* localeName,
    int localeNameSize);

HRESULT
BuildMenuPath(
    UINT32 appMenuPathSize,
    _Out_writes_z_(appMenuPathSize) LPWSTR appMenuPath,
    _In_z_ LPCWSTR appProvider,
    bool isCreateDir);

HRESULT
BuildIconPath(
    UINT32 iconPathSize,
    _Out_writes_z_(iconPathSize) LPWSTR iconPath,
    _In_z_ LPCWSTR appProvider,
    bool isCreateDir);

HRESULT
UpdateTaskBarInfo(
    HWND hwnd,
    _In_z_ LPCWSTR relaunchCmdline,
    _In_z_ LPCWSTR displayName,
    _In_z_ LPCWSTR iconPath);

#if ENABLE_WSL_SIGNATURE_CHECK
BOOL
IsFileTrusted(_In_z_ LPCWSTR pwszFile);
#else
#define IsFileTrusted(pwszFile) (true)
#endif // ENABLE_WSL_SIGNATURE_CHECK

#pragma pack(1)
//
// .ICO file format header
//

// Icon entry struct
typedef struct _ICON_DIR_ENTRY
{
    BYTE    bWidth;         // Width, in pixels, of the image
    BYTE    bHeight;        // Height, in pixels, of the image
    BYTE    bColorCount;    // Number of colors in image (0 if >=8bpp)
    BYTE    bReserved;      // Reserved ( must be 0)
    WORD    wPlanes;        // Color Planes
    WORD    wBitCount;      // Bits per pixel
    DWORD   dwBytesInRes;   // How many bytes in this resource?
    DWORD   dwImageOffset;  // Where in the file is this image?
} ICON_DIR_ENTRY;

// Icon directory struct
typedef struct _ICON_HEADER
{
    WORD           idReserved;   // Reserved (must be 0)
    WORD           idType;       // Resource Type (1 for icons)
    WORD           idCount;      // How many images?
    ICON_DIR_ENTRY idEntries[1]; // An entry for each image (idCount of 'em)
} ICON_HEADER;
#pragma pack()


================================================
FILE: WSLGd/FontMonitor.cpp
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "FontMonitor.h"
#include "common.h"

#define DEFAULT_FONT_PATH "/usr/share/fonts"
#define USER_DISTRO_FONT_PATH USER_DISTRO_MOUNT_PATH DEFAULT_FONT_PATH
#define ALT_FONT_PATH "/usr/share/X11/fonts"
#define ALT_DISTRO_FONT_PATH USER_DISTRO_MOUNT_PATH ALT_FONT_PATH

constexpr auto c_fontsdir = "fonts.dir";
constexpr auto c_xset = "/usr/bin/xset";

wslgd::FontFolder::FontFolder(int fd, const char *path)
{
    LOG_INFO("FontMonitor: start monitoring %s", path);

    m_path = path;

    /* check if folder is already ready to be added to font path */
    try {
        std::filesystem::path fonts_dir(path);
        fonts_dir /= c_fontsdir;
        if (access(fonts_dir.c_str(), R_OK) == 0) {
            ModifyX11FontPath(true);
        }
        m_fd.reset(dup(fd));
        THROW_LAST_ERROR_IF(!m_fd);
        /* add watch for install or uninstall of fonts on this folder */
        THROW_LAST_ERROR_IF((m_wd = inotify_add_watch(m_fd.get(), path, IN_CREATE|IN_CLOSE_WRITE|IN_DELETE|IN_MOVED_TO|IN_MOVED_FROM)) < 0);
    }
    CATCH_LOG();
}

wslgd::FontFolder::~FontFolder()
{
    LOG_INFO("FontMonitor: stop monitoring %s", m_path.c_str());

    ModifyX11FontPath(false);

    /* if still under watch, remove it */
    if (m_wd >= 0) {
        inotify_rm_watch(m_fd.get(), m_wd);
        m_wd = -1;
    }
}

bool wslgd::FontFolder::ExecuteShellCommand(std::vector<const char*>&& argv)
{
    bool success = false;
    int childPid = -1, waitPid = -1;
    std::string cmd;

    try {
        THROW_LAST_ERROR_IF((childPid = fork()) < 0);
        if (childPid == 0) {
            /* move this process to own process group to avoid interfere with Process Monitor */
            THROW_LAST_ERROR_IF(setpgid(0, 0) < 0);
            THROW_LAST_ERROR_IF(execvp(argv[0], const_cast<char *const *>(argv.data())) < 0);
        } else if (childPid > 0) {
            /* move child to own process group to avoid interfere with Process Monitor */
            THROW_LAST_ERROR_IF(setpgid(childPid, childPid) < 0);
        }
    }
    CATCH_LOG();

    // Ensure that the child process exits.
    if (childPid == 0) {
        _exit(1);
    }

    if (childPid > 0) try {
        int status;
        THROW_LAST_ERROR_IF((waitPid = waitpid(childPid, &status, 0)) < 0);
        if (WIFEXITED(status)) {
            success = (WEXITSTATUS(status) == 0);
        }
    }
    CATCH_LOG();

    try {
        for (std::vector<const char *>::iterator it = argv.begin(); *it != nullptr; it++) {
            cmd += *it;
            cmd += " ";
        }
        LOG_INFO("FontMonitor: pid:%d exited with %s, %s",
            waitPid, success ? "success" : "fail", cmd.c_str());
    }
    CATCH_LOG();

    return success;
}

void wslgd::FontFolder::ModifyX11FontPath(bool isAdd)
{
    std::vector<const char*> argv;
    sleep(2); /* workaround for optional fonts.alias, wait 2 sec before invoking xset */
    if (m_isPathAdded != isAdd) try {
        argv.push_back(c_xset);
        argv.push_back(isAdd ? "+fp" : "-fp");
        argv.push_back(m_path.c_str());
        argv.push_back(nullptr);
        if (ExecuteShellCommand(std::move(argv))) {
            m_isPathAdded = isAdd;
            /* let X server reread font database */
            argv.clear();
            argv.push_back(c_xset);
            argv.push_back("fp");
            argv.push_back("rehash");
            argv.push_back(nullptr);
            ExecuteShellCommand(std::move(argv));
        }
    }
    CATCH_LOG();
}

wslgd::FontMonitor::FontMonitor()
{
}

void wslgd::FontMonitor::DumpMonitorFolders()
{
    try {
        std::map<std::string, std::unique_ptr<FontFolder>>::iterator it;
        for (it = m_fontMonitorFolders.begin(); it != m_fontMonitorFolders.end(); it++)
            LOG_INFO("FontMonitor: monitoring %s, and it is %s to X11 font path", it->first.c_str(),
                it->second->IsPathAdded() ? "added" : "*not* added");
    }
    CATCH_LOG();
}

void wslgd::FontMonitor::AddMonitorFolder(const char *path)
{
    try {
        std::string monitorPath(path);
        // checkf if path is tracked already.
        if (m_fontMonitorFolders.find(monitorPath) == m_fontMonitorFolders.end()) {
            std::unique_ptr<FontFolder> fontFolder(new FontFolder(m_fd.get(), path));
            if (fontFolder.get()->GetWd() >= 0) {
                m_fontMonitorFolders.insert(std::make_pair(std::move(monitorPath), std::move(fontFolder)));
                // If this is mount path, only track under X11 folder if it's already exist.
                if (strcmp(path, USER_DISTRO_FONT_PATH) == 0) {
                    if (std::filesystem::exists(USER_DISTRO_FONT_PATH "/X11")) {
                        AddMonitorFolder(USER_DISTRO_FONT_PATH "/X11");
                    }
                } else {
                    // Otherwise, add all existing subfolders to track.
                    for (auto& dir_entry : std::filesystem::directory_iterator{path}) {
                        if (dir_entry.is_directory()) {
                            AddMonitorFolder(dir_entry.path().c_str());
                        }
                    }
                }
            }
        } else {
            LOG_INFO("FontMonitor: %s is already tracked", path);
        }
    }
    CATCH_LOG();
}

void wslgd::FontMonitor::RemoveMonitorFolder(const char *path)
{
    LOG_INFO("FontMonitor: removing monitoring %s", path);

    try {
        std::string monitorPath(path);
        m_fontMonitorFolders.erase(monitorPath);
    }
    CATCH_LOG();
}

void wslgd::FontMonitor::HandleFolderEvent(struct inotify_event *event)
{
    try {
        std::map<std::string, std::unique_ptr<FontFolder>>::iterator it;
        for (it = m_fontMonitorFolders.begin(); it != m_fontMonitorFolders.end(); it++) {
            if (event->wd == it->second->GetWd()) {
                if (event->mask & (IN_CREATE|IN_MOVED_TO)) {
                    bool addMonitorFolder = true;
                    std::filesystem::path fullPath(it->second->GetPath());
                    if (fullPath.compare(USER_DISTRO_FONT_PATH) == 0) {
                        /* Immediately under mount folder, only monitor "X11" and its subfolder */
                        addMonitorFolder = (strcmp(event->name, "X11") == 0);
                    }
                    if (addMonitorFolder) {
                        fullPath /= event->name;
                        AddMonitorFolder(fullPath.c_str());
                    }
                } else if (event->mask & (IN_DELETE|IN_MOVED_FROM)) {
                    std::filesystem::path fullPath(it->second->GetPath());
                    fullPath /= event->name;
                    RemoveMonitorFolder(fullPath.c_str());
                }
                break;
            }
        }
    }
    CATCH_LOG();
}

void wslgd::FontMonitor::HandleFontsDirEvent(struct inotify_event *event)
{
    try {
        std::map<std::string, std::unique_ptr<FontFolder>>::iterator it;
        for (it = m_fontMonitorFolders.begin(); it != m_fontMonitorFolders.end(); it++) {
            if (event->wd == it->second->GetWd()) {
                if (event->mask & (IN_CREATE|IN_CLOSE_WRITE|IN_MOVED_TO)) {
                    std::filesystem::path fonts_dir(it->second->GetPath());
                    fonts_dir /= event->name;
                    THROW_LAST_ERROR_IF(access(fonts_dir.c_str(), R_OK) != 0);
                    it->second->ModifyX11FontPath(true);
                } else if (event->mask & (IN_DELETE|IN_MOVED_FROM)) {
                    it->second->ModifyX11FontPath(false);
                }
                break;
            }
        }
    }
    CATCH_LOG();
}
        
void* wslgd::FontMonitor::FontMonitorThread(void *context)
{
    FontMonitor *This = reinterpret_cast<FontMonitor*>(context);
    struct inotify_event *event;
    int len, cur;
    char buf[10 * (sizeof *event + 256)];

    LOG_INFO("FontMonitor: monitoring thread started.");

    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);

    // Dump currently tracking folders.
    This->DumpMonitorFolders();

    // Start listening folder add/remove.
    for (;;) {
        len = read(This->GetFd(), buf, sizeof buf);
        cur = 0;
        while (cur < len) {
            event = (struct inotify_event *)&buf[cur];
            if (event->len) {
                if (event->mask & IN_ISDIR) {
                    // A directory is added or removed.
                    This->HandleFolderEvent(event);
                } else if (strcmp(event->name, c_fontsdir) == 0) {
                    // A fonts.dir is added or removed.
                    This->HandleFontsDirEvent(event);
                }
            }
            cur += (sizeof *event + event->len);
        }
    }
    // never hit here.
    assert(true);

    return 0;
}

int wslgd::FontMonitor::Start()
{
    bool succeeded = false;

    assert(m_fontMonitorFolders.empty());
    assert(!m_fontMonitorThread);

    try {
        // xset must be installed.
        THROW_LAST_ERROR_IF(access(c_xset, X_OK) < 0);

        // if user distro mount folder does not exist, bail out.
        THROW_LAST_ERROR_IF_FALSE(std::filesystem::exists(USER_DISTRO_MOUNT_PATH));

        bool userDistroFontPathExists = std::filesystem::exists(USER_DISTRO_FONT_PATH);
        bool altDistroFontPathExists = std::filesystem::exists(ALT_DISTRO_FONT_PATH);

        // and check fonts path inside user distro.
        THROW_LAST_ERROR_IF_FALSE(userDistroFontPathExists || altDistroFontPathExists);

        // start monitoring on mounted font folder.
        wil::unique_fd fd(inotify_init());
        THROW_LAST_ERROR_IF(!fd);
        m_fd.reset(fd.release());
        
        // add both the default and alternative font paths if they exist.
        if (userDistroFontPathExists) {
            AddMonitorFolder(USER_DISTRO_FONT_PATH);
        }
        if (altDistroFontPathExists) {
            AddMonitorFolder(ALT_DISTRO_FONT_PATH);
        }

        // Create font folder monitor thread.
        THROW_LAST_ERROR_IF(pthread_create(&m_fontMonitorThread, NULL, FontMonitorThread, (void*)this) < 0);

        succeeded = true;
    }
    CATCH_LOG();

    if (!succeeded) {
        Stop();
        return -1;
    }

    return 0;
}

void wslgd::FontMonitor::Stop()
{
    // Stop font folder monitor thread.
    if (m_fontMonitorThread) {
        pthread_cancel(m_fontMonitorThread);
        pthread_join(m_fontMonitorThread, NULL);
        m_fontMonitorThread = 0;
    }

    // Remove both the default and alternative font paths if they were added.
    if (m_fontMonitorFolders.find(USER_DISTRO_FONT_PATH) != m_fontMonitorFolders.end()) {
        RemoveMonitorFolder(USER_DISTRO_FONT_PATH);
    }
    if (m_fontMonitorFolders.find(ALT_DISTRO_FONT_PATH) != m_fontMonitorFolders.end()) {
        RemoveMonitorFolder(ALT_DISTRO_FONT_PATH);
    }

    m_fontMonitorFolders.clear();
    m_fd.reset();

    LOG_INFO("FontMonitor: monitoring stopped.");
}


================================================
FILE: WSLGd/FontMonitor.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "precomp.h"

namespace wslgd
{
    class FontFolder
    {
    public:
        FontFolder(int fd, const char *path);
        ~FontFolder();

        void ModifyX11FontPath(bool add);

        static bool ExecuteShellCommand(std::vector<const char*>&& argv);

        int GetFd() const { return m_fd.get(); }
        int GetWd() const { return m_wd; }

        bool IsPathAdded() const { return m_isPathAdded; }
        const char *GetPath() const { return m_path.c_str(); }

    private:
        wil::unique_fd m_fd; /* from FontMonitor's inotify_init() */
        int m_wd = -1; /* from inotify_add_watch() for this folder */
        std::string m_path; /* this folder path */
        bool m_isPathAdded = false; /* whether font path is added to X11 font path */
    };

    class FontMonitor
    {
    public:
        FontMonitor();
        ~FontMonitor() { Stop(); }

        FontMonitor(const FontMonitor&) = delete;
        void operator=(const FontMonitor&) = delete;

        int Start();
        void Stop();

        static void* FontMonitorThread(void *context);

        void AddMonitorFolder(const char *path);
        void RemoveMonitorFolder(const char *path);
        void DumpMonitorFolders();

        void HandleFolderEvent(struct inotify_event *event);
        void HandleFontsDirEvent(struct inotify_event *event);

        int GetFd() const { return m_fd.get(); }

    private:
        wil::unique_fd m_fd; /* from inotify_init() */
        std::map<std::string, std::unique_ptr<FontFolder>> m_fontMonitorFolders{};
        pthread_t m_fontMonitorThread = 0;
    };
}


================================================
FILE: WSLGd/Makefile
================================================
CXX := clang++
LDFLAGS := -lcap
TARGET := WSLGd
SRC_DIRS := .
INSTALL := install -p
INSTALL_PREFIX= $(DESTDIR)/$(PREFIX)/bin

SRCS := $(shell find $(SRC_DIRS) -name "*.cpp" -or -name "*.c" -or -name "*.s")
OBJS := $(addsuffix .o,$(basename $(SRCS)))
DEPS := $(OBJS:.o=.d)

INC_DIRS := $(shell find $(SRC_DIRS) -type d)
INC_FLAGS := $(addprefix -I,$(INC_DIRS))

CPPFLAGS := $(INC_FLAGS) -MMD -MP -std=c++17

$(TARGET): $(OBJS)
	$(CXX) $(LDFLAGS) $(OBJS) -o $@ $(LOADLIBES) $(LDLIBS)

.PHONY: clean
clean:
	$(RM) $(TARGET) $(OBJS) $(DEPS)

install:
	$(INSTALL) $(TARGET) $(INSTALL_PREFIX)

-include $(DEPS)

================================================
FILE: WSLGd/ProcessMonitor.cpp
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "ProcessMonitor.h"
#include "common.h"

wslgd::ProcessMonitor::ProcessMonitor(const char* userName)
{
    THROW_ERRNO_IF(ENOENT, !(m_user = getpwnam(userName)));
}

passwd* wslgd::ProcessMonitor::GetUserInfo() const
{
    return m_user;
}

extern char **environ;

int wslgd::ProcessMonitor::LaunchProcess(
    std::vector<std::string>&& argv,
    std::vector<cap_value_t>&& capabilities,
    std::vector<std::string>&& env)
{
    int childPid;
    THROW_LAST_ERROR_IF((childPid = fork()) < 0);

    if (childPid == 0) try {
        // Construct a null-terminated argument array.
        std::vector<const char*> arguments;
        for (auto &arg : argv) {
            arguments.push_back(arg.c_str());
        }

        arguments.push_back(nullptr);

        // If any capabilities were specified, set the keepcaps flag so they are not lost across setuid.
        if (!capabilities.empty()) {
            THROW_LAST_ERROR_IF(prctl(PR_SET_KEEPCAPS, 1) < 0);
        }

        // Construct a null-terminated environment array.
        std::vector<const char*> environments;
        for (char **c = environ; *c; c++) {
            environments.push_back(*c);
        }
        for (auto &s : env) {
            if (s.size()) {
                environments.push_back(s.c_str());
            }
        }

        environments.push_back(nullptr);

        // Set user settings.
        THROW_LAST_ERROR_IF(setgid(m_user->pw_gid) < 0);
        THROW_LAST_ERROR_IF(initgroups(m_user->pw_name, m_user->pw_gid) < 0);
        THROW_LAST_ERROR_IF(setuid(m_user->pw_uid) < 0);
        THROW_LAST_ERROR_IF(chdir(m_user->pw_dir) < 0);

        // Apply additional capabilities to the process.
        if (!capabilities.empty()) {
            cap_t caps{};
            THROW_LAST_ERROR_IF((caps = cap_get_proc()) == NULL);
            auto freeCapabilities = wil::scope_exit([&caps]() { cap_free(caps); });
            THROW_LAST_ERROR_IF(cap_set_flag(caps, CAP_PERMITTED, capabilities.size(), capabilities.data(), CAP_SET) < 0);
            THROW_LAST_ERROR_IF(cap_set_flag(caps, CAP_EFFECTIVE, capabilities.size(), capabilities.data(), CAP_SET) < 0);
            THROW_LAST_ERROR_IF(cap_set_flag(caps, CAP_INHERITABLE, capabilities.size(), capabilities.data(), CAP_SET) < 0);
            THROW_LAST_ERROR_IF(cap_set_proc(caps) < 0);
            for (auto &cap : capabilities) {
                THROW_LAST_ERROR_IF(cap_set_ambient(cap, CAP_SET) < 0);
            }
        }

        // Run the process as the specified user.
        THROW_LAST_ERROR_IF(execvpe(arguments[0], const_cast<char *const *>(arguments.data()), const_cast<char *const *>(environments.data())) < 0);
    }
    CATCH_LOG();

    // Ensure that the child process exits.
    if (childPid == 0) {
        _exit(1);
    }

    m_children[childPid] = ProcessInfo{std::move(argv), std::move(capabilities), std::move(env)};
    return childPid;
}

int wslgd::ProcessMonitor::Run() try {
    std::map<std::string, std::vector<time_t>> crashes;

    for (;;) {
        // Reap any zombie child processes and re-launch any tracked processes.
        int pid;
        int status;

        /* monitor only processes within same group as caller */
        THROW_LAST_ERROR_IF((pid = waitpid(0, &status, 0)) <= 0);

        auto found = m_children.find(pid);
        if (found != m_children.end()) {
            if (!found->second.argv.empty()) {
                std::string cmd;
                for (auto &arg : found->second.argv) {
                    cmd += arg.c_str();
                    cmd += " ";
                }

                if (WIFEXITED(status)) {
                    LOG_INFO("pid %d exited with status %d, %s", pid, WEXITSTATUS(status), cmd.c_str());
                } else if (WIFSIGNALED(status)) {
                    LOG_INFO("pid %d terminated with signal %d, %s", pid, WTERMSIG(status), cmd.c_str());
                } else {
                    LOG_ERROR("pid %d return unknown status %d, %s", pid, status, cmd.c_str());
                }

                auto& crashTimestamps = crashes[cmd];
                auto now = time(nullptr);
                crashTimestamps.erase(std::remove_if(crashTimestamps.begin(), crashTimestamps.end(), [&](auto ts) { return ts < now - 60; }), crashTimestamps.end());
                crashTimestamps.emplace_back(now);

                if (crashTimestamps.size() > 10) {
                    LOG_INFO("%s exited more than 10 times in 60 seconds, not starting it again", cmd.c_str());
                } else {
                    LaunchProcess(std::move(found->second.argv), std::move(found->second.capabilities), std::move(found->second.env));
                }
            }

            m_children.erase(found);

        } else {
            LOG_INFO("untracked pid %d exited with status 0x%x.", pid, status);
        }
    }

    return 0;
}
CATCH_RETURN_ERRNO();


================================================
FILE: WSLGd/ProcessMonitor.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "precomp.h"

namespace wslgd
{
    class ProcessMonitor
    {
    public:
        ProcessMonitor(const char* username);
        ProcessMonitor(const ProcessMonitor&) = delete;
        void operator=(const ProcessMonitor&) = delete;

        passwd* GetUserInfo() const;
        int LaunchProcess(std::vector<std::string>&& argv,
                          std::vector<cap_value_t>&& capabilities = {},
                          std::vector<std::string>&& env = {});
        int Run();

    private:
        struct ProcessInfo
        {
            std::vector<std::string> argv;
            std::vector<cap_value_t> capabilities;
            std::vector<std::string> env;
        };

        std::map<int, ProcessInfo> m_children{};
        passwd* m_user;
    };
}


================================================
FILE: WSLGd/common.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#define SHARE_PATH "/mnt/wslg"
#define USER_DISTRO_MOUNT_PATH SHARE_PATH "/distro"

void LogPrint(int level, const char *func, int line, const char *fmt, ...) noexcept;
#define LOG_LEVEL_EXCEPTION 3
#define LOG_LEVEL_ERROR     4
#define LOG_LEVEL_INFO      5
#define LOG_ERROR(fmt, ...) LogPrint(LOG_LEVEL_ERROR, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define LOG_INFO(fmt, ...) LogPrint(LOG_LEVEL_INFO, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)


================================================
FILE: WSLGd/lxwil.h
================================================
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once

namespace wil
{

#define FAIL_FAST() raise(SIGABRT);
#define FAIL_FAST_CAUGHT_EXCEPTION() FAIL_FAST()
#define FAIL_FAST_IF(condition) if ((condition)) { FAIL_FAST() }

typedef void LogFunction(const char *message, const char *exceptionDescription) noexcept;
inline LogFunction *g_LogExceptionCallback{};

namespace details
{
    struct FailureInfo
    {
        const char *File;
        int Line;
        const char *Function;
    };
}

class ResultException : public std::exception
{
public:
    ResultException(int result, details::FailureInfo info) noexcept
        : m_Result{ result }, m_Info{ info }
    {
    }

    ~ResultException() noexcept
    {
        delete[] m_What;
    }

    const char *what() const noexcept override
    {
        constexpr size_t bufferSize = 4096;
        if (m_What == nullptr)
        {
            m_What = new(std::nothrow) char[bufferSize]{};
            if (m_What == nullptr)
            {
                return strerror(m_Result);
            }

            snprintf(m_What, bufferSize, "%s @%s:%d (%s)\n", strerror(m_Result), m_Info.File, m_Info.Line, m_Info.Function);
        }

        return m_What;
    }

    int GetErrorCode() const noexcept
    {
        return m_Result;
    }

private:
    mutable char *m_What{};
    int m_Result;
    details::FailureInfo m_Info;
};

namespace details
{
    template <typename TLambda>
    class lambda_call
    {
    public:
        lambda_call(const lambda_call&) = delete;
        lambda_call& operator=(const lambda_call&) = delete;
        lambda_call& operator=(lambda_call&& other) = delete;

        explicit lambda_call(TLambda&& lambda) noexcept : m_lambda(std::move(lambda))
        {
            static_assert(std::is_same<decltype(lambda()), void>::value, "scope_exit lambdas must not have a return value");
            static_assert(!std::is_lvalue_reference<TLambda>::value && !std::is_rvalue_reference<TLambda>::value,
                "scope_exit should only be directly used with a lambda");
        }

        lambda_call(lambda_call&& other) noexcept : m_lambda(std::move(other.m_lambda)), m_call(other.m_call)
        {
            other.m_call = false;
        }

        ~lambda_call() noexcept
        {
            reset();
        }

        // Ensures the scope_exit lambda will not be called
        void release() noexcept
        {
            m_call = false;
        }

        // Executes the scope_exit lambda immediately if not yet run; ensures it will not run again
        void reset() noexcept
        {
            if (m_call)
            {
                m_call = false;
                m_lambda();
            }
        }

        // Returns true if the scope_exit lambda is still going to be executed
        explicit operator bool() const noexcept
        {
            return m_call;
        }

    protected:
        TLambda m_lambda;
        bool m_call = true;
    };

    inline void ThrowErrorIf(bool condition, int error, FailureInfo info)
    {
        if (condition)
        {
            throw ::wil::ResultException(error, info);
        }
    }

    inline void LogFailure(const char *message, const char *exceptionDescription) noexcept
    {
        auto callback = g_LogExceptionCallback;
        if (callback != nullptr)
        {
            callback(message, exceptionDescription);
        }
        else
        {
            if (message != nullptr)
            {
                fputs(message, stderr);
                fputs("\n", stderr);
            }

            if (exceptionDescription != nullptr)
            {
                fputs("Exception: ", stderr);
                fputs(exceptionDescription, stderr);
                fputs("\n", stderr);
            }
        }
    }

    inline void LogCaughtException(const char *message)
    {
        try
        {
            throw;
        }
        catch (const std::exception &ex)
        {
            LogFailure(message, ex.what());
        }
        catch (...)
        {
            LogFailure(message, nullptr);
        }
    }
}

inline int ResultFromCaughtException()
{
    try
    {
        throw;
    }
    catch (wil::ResultException &ex)
    {
        return ex.GetErrorCode();
    }
    catch (std::bad_alloc &)
    {
        return ENOMEM;
    }
    catch (...)
    {
    }

    // Unknown exception type.
    return EINVAL;
}

#define __WIL_ERROR_INFO { __FILE__, __LINE__, __FUNCTION__ }

#define THROW_ERRNO(Error) throw ::wil::ResultException(Error, __WIL_ERROR_INFO)
#define THROW_ERRNO_IF(Error, Condition) ::wil::details::ThrowErrorIf((Condition), (Error), __WIL_ERROR_INFO)
#define THROW_ERRNO_IF_FALSE(Error, Condition) ::wil::details::ThrowErrorIf(!(Condition), (Error), __WIL_ERROR_INFO)
#define THROW_LAST_ERROR_IF(Condition) THROW_ERRNO_IF(errno, (Condition));
#define THROW_LAST_ERROR_IF_FALSE(Condition) THROW_ERRNO_IF_FALSE(errno, (Condition))

#define THROW_INVALID() THROW_ERRNO(EINVAL)
#define THROW_INVALID_IF(Condition) THROW_ERRNO_IF(EINVAL, (Condition))
#define THROW_UNEXPECTED_IF(Condition) THROW_ERRNO_IF(EINVAL, (Condition))

#define LOG_CAUGHT_EXCEPTION() ::wil::details::LogCaughtException(nullptr);
#define LOG_CAUGHT_EXCEPTION_MSG(msg) ::wil::details::LogCaughtException(msg);
#define RETURN_CAUGHT_EXCEPTION() return -::wil::ResultFromCaughtException()
#define CATCH_RETURN() catch (...) { RETURN_CAUGHT_EXCEPTION(); }
#define CATCH_RETURN_ERRNO() \
    catch (...) \
    { \
        LOG_CAUGHT_EXCEPTION(); \
        errno = ::wil::ResultFromCaughtException(); \
        return -1; \
    }

#define CATCH_LOG() catch (...) { LOG_CAUGHT_EXCEPTION(); }
#define CATCH_LOG_MSG(msg) catch (...) { LOG_CAUGHT_EXCEPTION_MSG(msg); }

class unique_dir
{
public:
    static constexpr DIR* invalid_dir = nullptr;

    unique_dir(DIR* dir = invalid_dir) noexcept
        : m_Dir{ dir }
    {
    }

    ~unique_dir() noexcept
    {
        reset();
    }

    unique_dir(const unique_dir &) = delete;
    unique_dir& operator=(const unique_dir &) = delete;

    unique_dir(unique_dir &&other) noexcept
        : m_Dir{ other.m_Dir }
    {
        other.m_Dir = invalid_dir;
    }

    unique_dir& operator=(unique_dir &&other) noexcept
    {
        std::swap(m_Dir, other.m_Dir);
        return *this;
    }

    explicit operator bool() const noexcept
    {
        return m_Dir != invalid_dir;
    }

    DIR* get() const noexcept
    {
        return m_Dir;
    }

    void reset(DIR *dir = invalid_dir) noexcept
    {
        if (m_Dir != invalid_dir)
        {
            closedir(m_Dir);
        }

        m_Dir = dir;
    }

    DIR* release() noexcept
    {
        DIR *dir = m_Dir;
        m_Dir = invalid_dir;
        return dir;
    }

    friend void swap(unique_dir &dir1, unique_dir &dir2)
    {
        std::swap(dir1.m_Dir, dir2.m_Dir);
    }

private:
    DIR *m_Dir;
};

class unique_fd
{
public:
    static constexpr int invalid_fd = -1;

    unique_fd(int fd = invalid_fd) noexcept
        : m_Fd{ fd }
    {
    }

    ~unique_fd() noexcept
    {
        reset();
    }

    unique_fd(const unique_fd &) = delete;
    unique_fd& operator=(const unique_fd &) = delete;

    unique_fd(unique_fd &&other) noexcept
        : m_Fd{ other.m_Fd }
    {
        other.m_Fd = invalid_fd;
    }

    unique_fd& operator=(unique_fd &&other) noexcept
    {
        std::swap(m_Fd, other.m_Fd);
        return *this;
    }

    explicit operator bool() const noexcept
    {
        return m_Fd >= 0;
    }

    int get() const noexcept
    {
        return m_Fd;
    }

    void reset(int fd = invalid_fd) noexcept
    {
        if (m_Fd >= 0)
        {
            close(m_Fd);
        }

        m_Fd = fd;
    }

    int release() noexcept
    {
        int fd = m_Fd;
        m_Fd = invalid_fd;
        return fd;
    }

    friend void swap(unique_fd &fd1, unique_fd &fd2)
    {
        std::swap(fd1.m_Fd, fd2.m_Fd);
    }

private:
    int m_Fd;
};

class unique_file
{
public:
    static constexpr FILE* invalid_file = nullptr;

    unique_file(FILE* file = invalid_file) noexcept
        : m_File{ file }
    {
    }

    ~unique_file() noexcept
    {
        reset();
    }

    unique_file(const unique_file &) = delete;
    unique_file& operator=(const unique_file &) = delete;

    unique_file(unique_file &&other) noexcept
        : m_File{ other.m_File }
    {
        other.m_File = invalid_file;
    }

    unique_file& operator=(unique_file &&other) noexcept
    {
        std::swap(m_File, other.m_File);
        return *this;
    }

    explicit operator bool() const noexcept
    {
        return m_File != invalid_file;
    }

    FILE* get() const noexcept
    {
        return m_File;
    }

    void reset(FILE *file = invalid_file) noexcept
    {
        if (m_File != invalid_file)
        {
            fclose(m_File);
        }

        m_File = file;
    }

    FILE* release() noexcept
    {
        FILE *file = m_File;
        m_File = invalid_file;
        return file;
    }

    friend void swap(unique_file &file1, unique_file &file2)
    {
        std::swap(file1.m_File, file2.m_File);
    }

private:
    FILE *m_File;
};

/** Returns an object that executes the given lambda when destroyed.
Capture the object with 'auto'; use reset() to execute the lambda early or release() to avoid
execution.  Exceptions thrown in the lambda will fail-fast; use scope_exit_log to avoid. */
template <typename TLambda>
[[nodiscard]] inline auto scope_exit(TLambda&& lambda) noexcept
{
    return details::lambda_call<TLambda>(std::forward<TLambda>(lambda));
}

namespace details
{
    template <unsigned long long flag>
    struct verify_single_flag_helper
    {
        static_assert((flag != 0) && ((flag & (flag - 1)) == 0), "Single flag expected, zero or multiple flags found");
        static const unsigned long long value = flag;
    };

    // Use size-specific casts to avoid sign extending numbers -- avoid warning C4310: cast truncates constant value
    #define __WI_MAKE_UNSIGNED(val) \
        (sizeof(val) == 1 ? static_cast<unsigned char>(val) : \
         sizeof(val) == 2 ? static_cast<unsigned short>(val) : \
         sizeof(val) == 4 ? static_cast<unsigned long>(val) :  \
         static_cast<unsigned long long>(val))
    #define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1)))
    #define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val))

    template <typename TVal, typename TFlags>
    inline constexpr bool AreAllFlagsSetHelper(TVal val, TFlags flags)
    {
        return ((val & flags) == static_cast<decltype(val & flags)>(flags));
    }

    template <typename TVal>
    inline constexpr bool IsSingleFlagSetHelper(TVal val)
    {
        return __WI_IS_SINGLE_FLAG_SET(val);
    }

    template <typename TVa
Download .txt
gitextract_cowqssw9/

├── .dockerignore
├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.yml
│       ├── config.yml
│       └── feature_request.yml
├── .gitignore
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── Microsoft.WSLg.nuspec
├── Microsoft.WSLg.targets
├── README.md
├── SECURITY.md
├── WSLDVCPlugin/
│   ├── RegisterWSLPlugin.reg
│   ├── UpdateRCVersion.ps1
│   ├── WSLDVCCallback.cpp
│   ├── WSLDVCCallback.h
│   ├── WSLDVCFileDB.cpp
│   ├── WSLDVCFileDB.h
│   ├── WSLDVCListenerCallback.cpp
│   ├── WSLDVCListenerCallback.h
│   ├── WSLDVCPlugin.cpp
│   ├── WSLDVCPlugin.def
│   ├── WSLDVCPlugin.h
│   ├── WSLDVCPlugin.rc
│   ├── WSLDVCPlugin.sln
│   ├── WSLDVCPlugin.vcxproj
│   ├── WSLDVCPlugin.vcxproj.filters
│   ├── WSLDVCPlugin.vcxproj.user
│   ├── cpp.hint
│   ├── dllmain.cpp
│   ├── framework.h
│   ├── pch.cpp
│   ├── pch.h
│   ├── rdpapplist.h
│   ├── resource.h
│   ├── utils.cpp
│   └── utils.h
├── WSLGd/
│   ├── FontMonitor.cpp
│   ├── FontMonitor.h
│   ├── Makefile
│   ├── ProcessMonitor.cpp
│   ├── ProcessMonitor.h
│   ├── common.h
│   ├── lxwil.h
│   ├── main.cpp
│   ├── meson.build
│   └── precomp.h
├── azure-pipelines.yml
├── build-and-export.sh
├── cgmanifest.json
├── config/
│   ├── BUILD.md
│   ├── default_wslg.pa
│   ├── local.conf
│   ├── weston.ini
│   ├── wsl.conf
│   └── xwayland_log.patch
├── debuginfo/
│   ├── FreeRDP2.list
│   ├── FreeRDP3.list
│   ├── WSLGd.list
│   ├── gen_debuginfo.sh
│   ├── rdpapplist.list
│   └── weston.list
├── devops/
│   ├── common-linux.yml
│   ├── common-win.yml
│   ├── getversion.ps1
│   ├── updateversion.ps1
│   └── version_functions.ps1
├── docs/
│   └── install-sample-gui-apps.sh
├── msi/
│   └── updateversion.ps1
├── package/
│   ├── wslg.rdp
│   └── wslg_desktop.rdp
├── rdpapplist/
│   ├── meson.build
│   ├── rdpapplist_common.c
│   ├── rdpapplist_common.h
│   ├── rdpapplist_protocol.h
│   ├── rdpapplist_server.h
│   └── server/
│       ├── meson.build
│       ├── rdpapplist_main.c
│       └── rdpapplist_main.h
├── samples/
│   └── container/
│       ├── Containers.md
│       ├── build.sh
│       └── run.sh
└── vendor/
    └── .preserve
Download .txt
SYMBOL INDEX (153 symbols across 19 files)

FILE: WSLDVCPlugin/WSLDVCCallback.cpp
  class WSLDVCCallback (line 17) | class WSLDVCCallback :
    method HRESULT (line 24) | HRESULT
    method HRESULT (line 40) | HRESULT
    method HRESULT (line 70) | HRESULT
    method HRESULT (line 104) | HRESULT
    method HRESULT (line 155) | HRESULT
    method HRESULT (line 257) | HRESULT
    method HRESULT (line 296) | HRESULT
    method HRESULT (line 344) | HRESULT
    method HRESULT (line 368) | HRESULT
    method HRESULT (line 384) | HRESULT
    method HRESULT (line 528) | HRESULT
    method HRESULT (line 776) | HRESULT
    type _WslgWindowData (line 847) | struct _WslgWindowData
    method BOOL (line 855) | static BOOL CALLBACK
    method HRESULT (line 874) | HRESULT
    method STDMETHODIMP (line 993) | STDMETHODIMP
    method STDMETHODIMP (line 1082) | STDMETHODIMP
  function HRESULT (line 1136) | HRESULT

FILE: WSLDVCPlugin/WSLDVCFileDB.cpp
  class fileEntry (line 10) | class fileEntry
    method wstring (line 14) | wstring getFileId() const
    method wstring (line 18) | wstring getLinkFilePath() const
    method wstring (line 22) | wstring getIconFilePath() const
    method wstring (line 26) | wstring getExeFilePath() const
    method wstring (line 30) | wstring getExeFileArgs() const
    method fileEntry (line 43) | fileEntry(const wchar_t* fileId,
  class WSLDVCFileDB (line 76) | class WSLDVCFileDB :
    method HRESULT (line 83) | HRESULT
    method STDMETHODIMP (line 93) | STDMETHODIMP
    method STDMETHODIMP (line 116) | STDMETHODIMP
    method STDMETHODIMP (line 185) | STDMETHODIMP
    method STDMETHODIMP (line 225) | STDMETHODIMP FindFiles(
    method STDMETHODIMP (line 296) | STDMETHODIMP
    method STDMETHODIMP (line 303) | STDMETHODIMP
    method STDMETHODIMP (line 338) | STDMETHODIMP
    method HRESULT (line 350) | HRESULT
  function HRESULT (line 409) | HRESULT

FILE: WSLDVCPlugin/WSLDVCListenerCallback.cpp
  class WSLDVCListenerCallback (line 7) | class WSLDVCListenerCallback :
    method HRESULT (line 14) | HRESULT
    method STDMETHODIMP (line 23) | STDMETHODIMP
  function HRESULT (line 51) | HRESULT

FILE: WSLDVCPlugin/WSLDVCPlugin.cpp
  class WSLDVCPlugin (line 14) | class WSLDVCPlugin :
    method HRESULT (line 21) | HRESULT
    method STDMETHODIMP (line 30) | STDMETHODIMP
    method STDMETHODIMP (line 48) | STDMETHODIMP
    method STDMETHODIMP (line 54) | STDMETHODIMP
    method STDMETHODIMP (line 63) | STDMETHODIMP
  function HRESULT (line 81) | HRESULT

FILE: WSLDVCPlugin/dllmain.cpp
  function BOOL (line 9) | BOOL APIENTRY DllMain( HMODULE hModule,
  function HRESULT (line 30) | __declspec(dllexport) HRESULT VCAPITYPE
  function HRESULT (line 61) | __declspec(dllexport) HRESULT WINAPI

FILE: WSLDVCPlugin/rdpapplist.h
  type RDPAPPLIST_HEADER (line 39) | typedef struct _RDPAPPLIST_HEADER
  type RDPAPPLIST_CLIENT_CAPS_PDU (line 45) | typedef struct _RDPAPPLIST_CLIENT_CAPS_PDU
  type RDPAPPLIST_SERVER_CAPS_PDU (line 52) | typedef struct _RDPAPPLIST_SERVER_CAPS_PDU
  type RDPAPPLIST_UPDATE_APPLIST_PDU (line 62) | typedef struct _RDPAPPLIST_UPDATE_APPLIST_PDU
  type RDPAPPLIST_ICON_DATA (line 77) | typedef struct _RDPAPPLIST_ICON_DATA
  type RDPAPPLIST_DELETE_APPLIST_PDU (line 90) | typedef struct _RDPAPPLIST_DELETE_APPLIST_PDU
  type RDPAPPLIST_DELETE_APPLIST_PROVIDER_PDU (line 99) | typedef struct _RDPAPPLIST_DELETE_APPLIST_PROVIDER_PDU
  type RDPAPPLIST_ASSOCIATE_WINDOW_ID_PDU (line 107) | typedef struct _RDPAPPLIST_ASSOCIATE_WINDOW_ID

FILE: WSLDVCPlugin/utils.cpp
  function DebugPrint (line 7) | void DebugPrint(const wchar_t* format, ...)
  function _Use_decl_annotations_ (line 20) | _Use_decl_annotations_
  function _Use_decl_annotations_ (line 29) | _Use_decl_annotations_
  function _Use_decl_annotations_ (line 90) | _Use_decl_annotations_
  function _Use_decl_annotations_ (line 134) | _Use_decl_annotations_
  function _Use_decl_annotations_ (line 168) | _Use_decl_annotations_
  function _Use_decl_annotations_ (line 203) | _Use_decl_annotations_
  function _Use_decl_annotations_ (line 239) | _Use_decl_annotations_
  function HRESULT (line 296) | HRESULT
  function _Use_decl_annotations_ (line 399) | _Use_decl_annotations_

FILE: WSLDVCPlugin/utils.h
  type ICON_DIR_ENTRY (line 80) | typedef struct _ICON_DIR_ENTRY
  type ICON_HEADER (line 93) | typedef struct _ICON_HEADER

FILE: WSLGd/FontMonitor.cpp
  type inotify_event (line 173) | struct inotify_event
  type inotify_event (line 202) | struct inotify_event
  type inotify_event (line 226) | struct inotify_event
  type inotify_event (line 243) | struct inotify_event

FILE: WSLGd/FontMonitor.h
  function namespace (line 6) | namespace wslgd

FILE: WSLGd/ProcessMonitor.cpp
  function passwd (line 11) | passwd* wslgd::ProcessMonitor::GetUserInfo() const

FILE: WSLGd/ProcessMonitor.h
  function namespace (line 6) | namespace wslgd

FILE: WSLGd/lxwil.h
  function namespace (line 5) | namespace wil
  function namespace (line 66) | namespace details
  function ResultFromCaughtException (line 169) | inline int ResultFromCaughtException()
  function class (line 218) | class unique_dir
  function DIR (line 253) | DIR* get() const noexcept
  function reset (line 258) | void reset(DIR *dir = invalid_dir) noexcept
  function DIR (line 268) | DIR* release() noexcept
  function class (line 284) | class unique_fd
  function class (line 350) | class unique_file
  function FILE (line 385) | FILE* get() const noexcept
  function reset (line 390) | void reset(FILE *file = invalid_file) noexcept
  function FILE (line 400) | FILE* release() noexcept
  function noexcept (line 420) | [[nodiscard]] inline auto scope_exit(TLambda&& lambda) noexcept
  function namespace (line 425) | namespace details
  function AreAllFlagsSetHelper (line 444) | bool AreAllFlagsSetHelper(TVal val, TFlags flags)
  function IsSingleFlagSetHelper (line 450) | bool IsSingleFlagSetHelper(TVal val)
  function IsClearOrSingleFlagSetHelper (line 456) | bool IsClearOrSingleFlagSetHelper(TVal val)
  type type (line 491) | typedef unsigned long long type;

FILE: WSLGd/main.cpp
  function LogPrint (line 47) | void LogPrint(int level, const char *func, int line, const char *fmt, .....
  function LogException (line 69) | void LogException(const char *message, const char *exceptionDescription)...
  function IsNumeric (line 75) | bool IsNumeric(char *str)
  function ToServiceId (line 84) | std::string ToServiceId(unsigned int port)
  function TranslateWindowsPath (line 95) | std::string TranslateWindowsPath(const char * Path)
  function GetEnvBool (line 118) | bool GetEnvBool(const char *EnvName, bool DefaultValue)
  function GetVmId (line 137) | std::string GetVmId()
  function SetupOptionalEnv (line 153) | void SetupOptionalEnv()
  function SetupReadyNotify (line 187) | int SetupReadyNotify(const char *socket_path)
  function WaitForReadyNotify (line 207) | void WaitForReadyNotify(int notifyFd)
  type envVar (line 226) | struct envVar{ const char* name; const char* value; bool override; }
  type rlimit (line 354) | struct rlimit

FILE: rdpapplist/rdpapplist_common.c
  function UINT (line 37) | UINT rdpapplist_read_header(wStream* s, RDPAPPLIST_HEADER* header)
  function UINT (line 55) | UINT rdpapplist_write_header(wStream* s, const RDPAPPLIST_HEADER* header)

FILE: rdpapplist/rdpapplist_protocol.h
  type _RDPAPPLIST_HEADER (line 67) | struct _RDPAPPLIST_HEADER
  type RDPAPPLIST_HEADER (line 73) | typedef struct _RDPAPPLIST_HEADER RDPAPPLIST_HEADER;
  type _RDPAPPLIST_SERVER_CAPS_PDU (line 75) | struct _RDPAPPLIST_SERVER_CAPS_PDU
  type RDPAPPLIST_SERVER_CAPS_PDU (line 82) | typedef struct _RDPAPPLIST_SERVER_CAPS_PDU RDPAPPLIST_SERVER_CAPS_PDU;
  type _RDPAPPLIST_CLIENT_CAPS_PDU (line 84) | struct _RDPAPPLIST_CLIENT_CAPS_PDU
  type RDPAPPLIST_CLIENT_CAPS_PDU (line 91) | typedef struct _RDPAPPLIST_CLIENT_CAPS_PDU RDPAPPLIST_CLIENT_CAPS_PDU;
  type _RDPAPPLIST_ICON_DATA (line 93) | struct _RDPAPPLIST_ICON_DATA {
  type RDPAPPLIST_ICON_DATA (line 108) | typedef struct _RDPAPPLIST_ICON_DATA RDPAPPLIST_ICON_DATA;
  type _RDPAPPLIST_UPDATE_APPLIST_PDU (line 112) | struct _RDPAPPLIST_UPDATE_APPLIST_PDU
  type RDPAPPLIST_UPDATE_APPLIST_PDU (line 125) | typedef struct _RDPAPPLIST_UPDATE_APPLIST_PDU RDPAPPLIST_UPDATE_APPLIST_...
  type _RDPAPPLIST_DELETE_APPLIST_PDU (line 129) | struct _RDPAPPLIST_DELETE_APPLIST_PDU
  type RDPAPPLIST_DELETE_APPLIST_PDU (line 137) | typedef struct _RDPAPPLIST_DELETE_APPLIST_PDU RDPAPPLIST_DELETE_APPLIST_...
  type _RDPAPPLIST_DELETE_APPLIST_PROVIDER_PDU (line 141) | struct _RDPAPPLIST_DELETE_APPLIST_PROVIDER_PDU
  type RDPAPPLIST_DELETE_APPLIST_PROVIDER_PDU (line 148) | typedef struct _RDPAPPLIST_DELETE_APPLIST_PROVIDER_PDU RDPAPPLIST_DELETE...
  type _RDPAPPLIST_ASSOCIATE_WINDOW_ID_PDU (line 150) | struct _RDPAPPLIST_ASSOCIATE_WINDOW_ID_PDU
  type RDPAPPLIST_ASSOCIATE_WINDOW_ID_PDU (line 160) | typedef struct _RDPAPPLIST_ASSOCIATE_WINDOW_ID_PDU RDPAPPLIST_ASSOCIATE_...

FILE: rdpapplist/rdpapplist_server.h
  type RdpAppListServerPrivate (line 29) | typedef struct _rdpapplist_server_private RdpAppListServerPrivate;
  type RdpAppListServerContext (line 30) | typedef struct _rdpapplist_server_context RdpAppListServerContext;
  type UINT (line 32) | typedef UINT (*psRdpAppListOpen)(RdpAppListServerContext* context);
  type UINT (line 33) | typedef UINT (*psRdpAppListClose)(RdpAppListServerContext* context);
  type UINT (line 35) | typedef UINT (*psRdpAppListCaps)(RdpAppListServerContext* context, const...
  type UINT (line 36) | typedef UINT (*psRdpAppListUpdate)(RdpAppListServerContext* context, con...
  type UINT (line 37) | typedef UINT (*psRdpAppListDelete)(RdpAppListServerContext* context, con...
  type UINT (line 38) | typedef UINT (*psRdpAppListDeleteProvider)(RdpAppListServerContext* cont...
  type UINT (line 39) | typedef UINT (*psRdpAppListAssociateWindowId)(RdpAppListServerContext* c...
  type UINT (line 41) | typedef UINT (*psRdpAppListClientCaps)(RdpAppListServerContext* context,...
  type _rdpapplist_server_context (line 43) | struct _rdpapplist_server_context

FILE: rdpapplist/server/rdpapplist_main.c
  function UINT (line 47) | static UINT rdpapplist_recv_caps_pdu(wStream* s, RdpAppListServerContext...
  function UINT (line 72) | static UINT rdpapplist_server_receive_pdu(RdpAppListServerContext* conte...
  function UINT (line 119) | static UINT rdpapplist_server_handle_messages(RdpAppListServerContext* c...
  function DWORD (line 198) | static DWORD WINAPI rdpapplist_server_thread_func(LPVOID arg)
  function wStream (line 247) | static wStream* rdpapplist_server_single_packet_new(UINT32 cmdId, UINT32...
  function UINT (line 279) | static UINT rdpapplist_server_packet_send(RdpAppListServerContext* conte...
  function UINT (line 309) | static UINT rdpapplist_send_caps(RdpAppListServerContext *context, const...
  function UINT (line 344) | static UINT rdpapplist_send_update_applist(RdpAppListServerContext* cont...
  function UINT (line 457) | static UINT rdpapplist_send_delete_applist(RdpAppListServerContext* cont...
  function UINT (line 503) | static UINT rdpapplist_send_delete_applist_provider(RdpAppListServerCont...
  function UINT (line 534) | static UINT rdpapplist_send_associate_window_id(RdpAppListServerContext*...
  function UINT (line 617) | static UINT rdpapplist_server_open(RdpAppListServerContext* context)
  function UINT (line 698) | static UINT rdpapplist_server_close(RdpAppListServerContext* context)
  function RdpAppListServerContext (line 729) | RdpAppListServerContext* rdpapplist_server_context_new(HANDLE vcm)
  function rdpapplist_server_context_free (line 774) | void rdpapplist_server_context_free(RdpAppListServerContext* context)

FILE: rdpapplist/server/rdpapplist_main.h
  type _rdpapplist_server_private (line 23) | struct _rdpapplist_server_private
Condensed preview — 83 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (314K chars).
[
  {
    "path": ".dockerignore",
    "chars": 44,
    "preview": ".git\n.github\n.gitlab\n.gitlab-ci\n.vs\nout\ntmp\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 2609,
    "preview": "name: \"Bug report\"\ndescription: \" Report a bug in WSLg\"\nlabels: \"bug\"\nbody:\n- type: input\n  id: build-number\n  attribute"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 197,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Report issues on WSL in general\n    url: https://github.com/microso"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 939,
    "preview": "name: \"Feature request\"\ndescription: \"Suggest a feature for WSLg\"\nlabels: \"enhancement\"\nbody:\n- type: textarea\n  attribu"
  },
  {
    "path": ".gitignore",
    "chars": 214,
    "preview": ".vscode/*\nvendor/*\n!vendor/.preserve\nout\ntmp\n*.nupkg\n*.tar\n*.vhd\nWSLGd/*.d\nWSLGd/*.o\nWSLGd/WSLGd\nWSLDVCPlugin/.vs\nWSLDVC"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 10474,
    "preview": "# Contributing\r\n\r\nThis project welcomes contributions and suggestions. Most contributions require you to\r\nagree to a Con"
  },
  {
    "path": "Dockerfile",
    "chars": 14589,
    "preview": "# Create a builder image with the compilers, etc. needed\nFROM mcr.microsoft.com/azurelinux/base/core:3.0 AS build-env\n\n#"
  },
  {
    "path": "LICENSE",
    "chars": 1141,
    "preview": "    MIT License\n\n    Copyright (c) Microsoft Corporation.\n\n    Permission is hereby granted, free of charge, to any pers"
  },
  {
    "path": "Microsoft.WSLg.nuspec",
    "chars": 1974,
    "preview": "<?xml version=\"1.0\"?>\r\n<!--\r\n*******************************************************************************************"
  },
  {
    "path": "Microsoft.WSLg.targets",
    "chars": 1217,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<!--\r\n**************************************************************************"
  },
  {
    "path": "README.md",
    "chars": 22243,
    "preview": "# Welcome to WSLg\r\nWSLg is short for *Windows Subsystem for Linux GUI* and the purpose of the project is to enable suppo"
  },
  {
    "path": "SECURITY.md",
    "chars": 2702,
    "preview": "## Security\n\nMicrosoft takes the security of our software products and services seriously, which includes all source cod"
  },
  {
    "path": "WSLDVCPlugin/RegisterWSLPlugin.reg",
    "chars": 181,
    "preview": "Windows Registry Editor Version 5.00\r\n\r\n[HKEY_CURRENT_USER\\Software\\Microsoft\\Terminal Server Client\\Default\\AddIns\\WSLD"
  },
  {
    "path": "WSLDVCPlugin/UpdateRCVersion.ps1",
    "chars": 536,
    "preview": "\r\n$version = [string](gitversion /showvariable AssemblySemFileVer)\r\n$versionComma = $version.Replace(\".\", \",\")\r\n$informa"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCCallback.cpp",
    "chars": 35375,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#include \"pch.h\"\r\n#include <safeint.h>\r\n#in"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCCallback.h",
    "chars": 250,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#pragma once\r\n\r\n#include <tsvirtualchannels"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCFileDB.cpp",
    "chars": 12420,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#include \"pch.h\"\r\n#include \"utils.h\"\r\n#incl"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCFileDB.h",
    "chars": 1322,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#pragma once\r\n\r\nMIDL_INTERFACE(\"5802f934-16"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCListenerCallback.cpp",
    "chars": 1192,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#include \"pch.h\"\r\n#include \"WSLDVCListenerC"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCListenerCallback.h",
    "chars": 217,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#pragma once\r\n\r\n#include <tsvirtualchannels"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCPlugin.cpp",
    "chars": 1861,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#include \"pch.h\"\r\n#include \"WSLDVCPlugin.h\""
  },
  {
    "path": "WSLDVCPlugin/WSLDVCPlugin.def",
    "chars": 76,
    "preview": "LIBRARY \"WSLDVCPlugin.dll\"\r\n\r\nEXPORTS\r\n    VirtualChannelGetInstance PRIVATE"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCPlugin.h",
    "chars": 197,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#pragma once\r\n\r\n#include <tsvirtualchannels"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCPlugin.rc",
    "chars": 2446,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#include \"resource.h\"\r\n\r\n#define APSTUDIO_R"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCPlugin.sln",
    "chars": 1820,
    "preview": "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 16\r\nVisualStudioVersion = 16.0.3"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCPlugin.vcxproj",
    "chars": 14942,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/ms"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCPlugin.vcxproj.filters",
    "chars": 2755,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbui"
  },
  {
    "path": "WSLDVCPlugin/WSLDVCPlugin.vcxproj.user",
    "chars": 166,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"Current\" xmlns=\"http://schemas.microsoft.com/developer/m"
  },
  {
    "path": "WSLDVCPlugin/cpp.hint",
    "chars": 173,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#define WSLDVCPLUGIN_API __declspec(dllexpo"
  },
  {
    "path": "WSLDVCPlugin/dllmain.cpp",
    "chars": 2563,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#include \"pch.h\"\r\n#include \"utils.h\"\r\n#incl"
  },
  {
    "path": "WSLDVCPlugin/framework.h",
    "chars": 655,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#pragma once\r\n\r\n#define WIN32_LEAN_AND_MEAN"
  },
  {
    "path": "WSLDVCPlugin/pch.cpp",
    "chars": 200,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#include \"pch.h\"\r\n\r\n// When you are using p"
  },
  {
    "path": "WSLDVCPlugin/pch.h",
    "chars": 710,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n// pch.h: This is a precompiled header file"
  },
  {
    "path": "WSLDVCPlugin/rdpapplist.h",
    "chars": 5890,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#pragma once\r\n\r\n//\r\n// RDP APPLIST protocol"
  },
  {
    "path": "WSLDVCPlugin/resource.h",
    "chars": 483,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n//{{NO_DEPENDENCIES}}\r\n// Microsoft Visual "
  },
  {
    "path": "WSLDVCPlugin/utils.cpp",
    "chars": 15631,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#include \"pch.h\"\r\n#include \"utils.h\"\r\n\r\n#if"
  },
  {
    "path": "WSLDVCPlugin/utils.h",
    "chars": 2663,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#pragma once\r\n\r\n//#ifdef _DEBUG\r\n#define DB"
  },
  {
    "path": "WSLGd/FontMonitor.cpp",
    "chars": 11405,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#include \"FontMonitor.h\"\r\n#include \"common."
  },
  {
    "path": "WSLGd/FontMonitor.h",
    "chars": 1744,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#pragma once\r\n#include \"precomp.h\"\r\n\r\nnames"
  },
  {
    "path": "WSLGd/Makefile",
    "chars": 604,
    "preview": "CXX := clang++\nLDFLAGS := -lcap\nTARGET := WSLGd\nSRC_DIRS := .\nINSTALL := install -p\nINSTALL_PREFIX= $(DESTDIR)/$(PREFIX)"
  },
  {
    "path": "WSLGd/ProcessMonitor.cpp",
    "chars": 5095,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#include \"ProcessMonitor.h\"\r\n#include \"comm"
  },
  {
    "path": "WSLGd/ProcessMonitor.h",
    "chars": 894,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#pragma once\r\n#include \"precomp.h\"\r\n\r\nnames"
  },
  {
    "path": "WSLGd/common.h",
    "chars": 541,
    "preview": "// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n#pragma once\n#define SHARE_PATH \"/mnt/wslg\"\n#"
  },
  {
    "path": "WSLGd/lxwil.h",
    "chars": 13703,
    "preview": "// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n#pragma once\n\nnamespace wil\n{\n\n#define FAIL_F"
  },
  {
    "path": "WSLGd/main.cpp",
    "chars": 19457,
    "preview": "// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n#include \"precomp.h\"\n#include \"common.h\"\n#inc"
  },
  {
    "path": "WSLGd/meson.build",
    "chars": 655,
    "preview": "project('WSLGd', 'cpp',\n  version : '0.1',\n  default_options : ['cpp_std=c++17'])\n\nconfig_h = configuration_data()\n\ndep_"
  },
  {
    "path": "WSLGd/precomp.h",
    "chars": 884,
    "preview": "// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT license.\r\n#include <sys/capability.h>\r\n#include <sys/"
  },
  {
    "path": "azure-pipelines.yml",
    "chars": 14727,
    "preview": "resources:\n  repositories:\n  - repository: FreeRDP\n    type: github\n    endpoint: GitHub connection 1\n    name: microsof"
  },
  {
    "path": "build-and-export.sh",
    "chars": 581,
    "preview": "#!/bin/bash\nset -e\n\necho \"=== Building Docker image ===\"\ndocker build -f Dockerfile -t system-distro-x64 . \\\n    --build"
  },
  {
    "path": "cgmanifest.json",
    "chars": 1392,
    "preview": "{\n    \"Registrations\": [ \n        {\n            \"Component\": { \n                \"Type\": \"git\", \n                \"Git\": {"
  },
  {
    "path": "config/BUILD.md",
    "chars": 3397,
    "preview": "# Introduction\r\n\r\nThis repository contains a Dockerfile and supporting tools to build the WSL GUI system distro image.\r\n"
  },
  {
    "path": "config/default_wslg.pa",
    "chars": 290,
    "preview": "\n### WSLG specific ###\n### Load Windows's default sound from Windows volume.\n.ifexists /mnt/c/Windows/Media/Windows Defa"
  },
  {
    "path": "config/local.conf",
    "chars": 249,
    "preview": "<?xml version=\"1.0\"?>\n<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n<fontconfig>\n    <!-- Add fonts directory mapped from use"
  },
  {
    "path": "config/weston.ini",
    "chars": 61,
    "preview": "[xwayland]\ndisable_access_control=true\n\n[input-method]\npath=\n"
  },
  {
    "path": "config/wsl.conf",
    "chars": 49,
    "preview": "[boot]\ncommand=/usr/bin/WSLGd\n[user]\ndefault=wslg"
  },
  {
    "path": "config/xwayland_log.patch",
    "chars": 491,
    "preview": "diff -Naur xorg-server-1.20.9/hw/xwayland/xwayland.c xorg-server-1.20.9-patch/hw/xwayland/xwayland.c\n--- xorg-server-1.2"
  },
  {
    "path": "debuginfo/FreeRDP2.list",
    "chars": 207,
    "preview": "/usr/bin/winpr-hash\r\n/usr/bin/winpr-makecert\r\n/usr/lib/libfreerdp-server2.so.2.4.0\r\n/usr/lib/libfreerdp2.so.2.4.0\r\n/usr/"
  },
  {
    "path": "debuginfo/FreeRDP3.list",
    "chars": 207,
    "preview": "/usr/bin/winpr-hash\r\n/usr/bin/winpr-makecert\r\n/usr/lib/libfreerdp-server3.so.3.0.0\r\n/usr/lib/libfreerdp3.so.3.0.0\r\n/usr/"
  },
  {
    "path": "debuginfo/WSLGd.list",
    "chars": 16,
    "preview": "/usr/bin/WSLGd\r\n"
  },
  {
    "path": "debuginfo/gen_debuginfo.sh",
    "chars": 362,
    "preview": "#!/bin/bash\n\nfunction split_debuginfo(){\n    file=${1//[$'\\t\\r\\n']}\n    dir=$(dirname \"$file\")\n    mkdir -p $2/debuginfo"
  },
  {
    "path": "debuginfo/rdpapplist.list",
    "chars": 44,
    "preview": "usr/lib/rdpapplist/librdpapplist-server.so\r\n"
  },
  {
    "path": "debuginfo/weston.list",
    "chars": 477,
    "preview": "usr/bin/weston\r\nusr/bin/weston-launch\r\nusr/bin/weston-screenshooter\r\nusr/lib/libweston-9.so.0.0.0\r\nusr/lib/libweston-des"
  },
  {
    "path": "devops/common-linux.yml",
    "chars": 576,
    "preview": "steps:\n  - task: DockerInstaller@0\n    inputs:\n      dockerVersion: '20.10.7'\n      releaseType: 'stable'\n\n  - script: w"
  },
  {
    "path": "devops/common-win.yml",
    "chars": 95,
    "preview": "steps:\n- script: 'choco install gitversion.portable --pre'\n  displayName: 'Install GitVersion'\n"
  },
  {
    "path": "devops/getversion.ps1",
    "chars": 107,
    "preview": ". .\\devops\\version_functions.ps1\r\n\r\n$version = Get-VersionInfo \"version\" \"-beta\"\r\n\r\nWrite-Output $version\r\n"
  },
  {
    "path": "devops/updateversion.ps1",
    "chars": 344,
    "preview": "param ([string] $XmlFile, [string] $xpath, [string] $name, [string] $buildSeparator = \".\", [string] $type = \"version\")\r\n"
  },
  {
    "path": "devops/version_functions.ps1",
    "chars": 4452,
    "preview": "function Get-XmlNamespaceManager([xml]$XmlDocument, [string]$NamespaceURI = \"\")\r\n{\r\n    # If a Namespace URI was not giv"
  },
  {
    "path": "docs/install-sample-gui-apps.sh",
    "chars": 818,
    "preview": "#!/bin/bash\n\n# Make sure you are using Ubuntu 20.04!!\n\napt update\n\n## Gedit\n\napt install gedit -y\n\n## GIMP\n\napt install "
  },
  {
    "path": "msi/updateversion.ps1",
    "chars": 2678,
    "preview": "param ([string] $XmlFile = $null )\r\n\r\nfunction Get-XmlNamespaceManager([xml]$XmlDocument, [string]$NamespaceURI = \"\")\r\n{"
  },
  {
    "path": "package/wslg.rdp",
    "chars": 190,
    "preview": "audiocapturemode:i:2\r\nauthentication level:i:0\r\ndisableconnectionsharing:i:1\r\nenablecredsspsupport:i:0\r\nhvsocketenabled:"
  },
  {
    "path": "package/wslg_desktop.rdp",
    "chars": 125,
    "preview": "audiocapturemode:i:2\r\nauthentication level:i:0\r\ndisableconnectionsharing:i:1\r\nenablecredsspsupport:i:0\r\nhvsocketenabled:"
  },
  {
    "path": "rdpapplist/meson.build",
    "chars": 1731,
    "preview": "project('rdpapplist',\r\n    'c',\r\n    version : '2.0.0',\r\n)\r\n\r\nname_rdpapplist = meson.project_name()\r\nversion_rdpapplist"
  },
  {
    "path": "rdpapplist/rdpapplist_common.c",
    "chars": 1593,
    "preview": "/**\n * FreeRDP: A Remote Desktop Protocol Implementation\n * RDPXXXX Remote Application List Virtual Channel Extension\n *"
  },
  {
    "path": "rdpapplist/rdpapplist_common.h",
    "chars": 1131,
    "preview": "/**\n * FreeRDP: A Remote Desktop Protocol Implementation\n * RDPXXXX Remote Application List Virtual Channel Extension\n *"
  },
  {
    "path": "rdpapplist/rdpapplist_protocol.h",
    "chars": 5689,
    "preview": "/**\n * FreeRDP: A Remote Desktop Protocol Implementation\n * RDPXXXX Remote Application List Virtual Channel Extension\n *"
  },
  {
    "path": "rdpapplist/rdpapplist_server.h",
    "chars": 2731,
    "preview": "/**\n * FreeRDP: A Remote Desktop Protocol Implementation\n * RDPXXXX Remote Application List Virtual Channel Extension\n *"
  },
  {
    "path": "rdpapplist/server/meson.build",
    "chars": 835,
    "preview": "dep_frdp_server = dependency('freerdp-server3', version: '>= 3.0.0', required: false)\nif not dep_frdp_server.found()\n   "
  },
  {
    "path": "rdpapplist/server/rdpapplist_main.c",
    "chars": 21994,
    "preview": "/**\n * FreeRDP: A Remote Desktop Protocol Implementation\n * RDPXXXX Remote Application List Virtual Channel Extension\n *"
  },
  {
    "path": "rdpapplist/server/rdpapplist_main.h",
    "chars": 1045,
    "preview": "/**\n * FreeRDP: A Remote Desktop Protocol Implementation\n * RDPXXXX Remote Application List Virtual Channel Extension\n *"
  },
  {
    "path": "samples/container/Containers.md",
    "chars": 4885,
    "preview": "# Containerizing GUI applications with WSLg\n\nFor containerized applications to work properly under WSLg developers need "
  },
  {
    "path": "samples/container/build.sh",
    "chars": 230,
    "preview": "#!/bin/bash\n\nsudo docker build -t xclock -f Dockerfile.xclock .\nsudo docker build -t glxinfo -f Dockerfile.glxinfo .\nsud"
  },
  {
    "path": "samples/container/run.sh",
    "chars": 1285,
    "preview": "#!/bin/bash\n\n# Containerized version of xclock\nsudo docker run -it -v /tmp/.X11-unix:/tmp/.X11-unix -v /mnt/wslg:/mnt/ws"
  },
  {
    "path": "vendor/.preserve",
    "chars": 0,
    "preview": ""
  }
]

About this extraction

This page contains the full source code of the microsoft/wslg GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 83 files (286.3 KB), approximately 76.2k tokens, and a symbol index with 153 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!