Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
156640 stories
·
33 followers

Why Care About Agent Authored Code Quality

1 Share

A developer I was chatting with recently raised a question I’ve been hearing more and more:

Since we can produce code so fast now, does the code actually matter that much? Assuming the code fulfills the requirement through actual user testing, who cares what the code looks like? We’re just sitting at another layer of abstraction. Once we got 2nd and 3rd generation languages, did you care what assembly they output - as long as it did what you wanted?

Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

Packaging and Package Identity for .NET apps with WinApp CLI on Windows

1 Share

Package identity has often been a pain point for developers looking to build apps that integrate with Windows APIs. Many modern Windows features, like push notifications or the AI APIs, are gated behind package identity. For Windows apps that are unpackaged by default (like .NET console or WPF applications), this meant wrestling with package manifests, build configurations, and certs to bring your app up to speed.

With the WinApp CLI, you can quickly tackle the problem of package identity, both in the context of local running and debugging, and for packaging applications as MSIX for distribution. WinApp is compatible with any .NET desktop framework.

WinApp CLI welcome visual

The WinApp CLI enables a whole host of development related features, but we’ll be highlighting two key capabilities to start:

  1. The WinApp CLI integrates with existing dotnet tooling to enable you to test your application with package identity via dotnet run
  2. The WinApp CLI makes packaging applications as MSIX easy via winapp pack

If you want to follow along with the examples in this post, install the CLI with winget:

winget install Microsoft.winappcli --source winget

What is package identity and why would I want it?

Package identity is a unique identifier for your application within Windows. You can think of granting package identity as “registering” your application within the operating system.

An app that has been granted identity can more effectively integrate with the Windows platform, enabling access to Windows APIs and features that would otherwise be blocked without it. Some of the capabilities that become available with package identity include, but aren’t limited to:

  • Background tasks
  • Push and toast notifications
  • Share target
  • File handlers
  • Windows AI APIs

Long story short, package identity enables a richer experience for your application on Windows.

Let’s take a look at how WinApp can make the process of granting package identity straightforward.

dotnet run with Package Identity

Once you have the WinApp CLI installed, running your .NET app with identity is easy:

1. Initialize your project with WinApp

Run this command from the root of your project:

winapp init . --use-defaults

The init command takes care of all the prerequisites for enabling identity:

  • Ensures the TargetFramework specified in your .csproj is targeting a compatible version of the Windows platform. This is required to properly enable access to Windows APIs.
  • Adds two package references to your .csproj:
    • Microsoft.WindowsAppSDK — This dependency is the Windows SDK itself, and grants access to Windows APIs.
    • Microsoft.Windows.SDK.BuildTools.WinApp — This is the NuGet package for WinApp itself. This dependency enables seamless dotnet run usage via WinApp.
  • Generates both a Package.appxmanifest and required asset files, placed in an Assets directory. These files are required to grant package identity.

Output from WinApp init command

If you want more control over the init experience, run without the --use-defaults flag. This will give you control over versioning, package and publisher name, and allow you to manage which dependencies are added to your project. Additionally, if you already have any of the prerequisites handled by the init command, WinApp will not overwrite them.

2. Debug with dotnet run

Once your project has been initialized with WinApp, you can run your app as you would normally:

dotnet run

Because the WinApp NuGet package is referenced by your project, dotnet run will call on WinApp to run your application. This will launch your app with package identity, allowing you to easily add and test Windows features within your app.

If you want to unregister your application and clean up any app data after launching with identity, run this command from the project root:

winapp unregister

Additionally, if you want to disable running as a packaged application, you can add this property to your .csproj:

<PropertyGroup>
  <WindowsPackageType>None</WindowsPackageType>
</PropertyGroup>

Passing arguments to WinApp

To customize run behavior, you can pass arguments to WinApp by setting properties in your .csproj:

<PropertyGroup>
  <!-- Specify a manifest path -->
  <WinAppManifestPath>$(MSBuildProjectDirectory)\custom\Package.appxmanifest</WinAppManifestPath>

  <!-- Pass arguments to the app on launch -->
  <WinAppLaunchArgs>--debug --verbose</WinAppLaunchArgs>

  <!-- Launch via execution alias (for console apps needing terminal I/O) -->
  <WinAppRunUseExecutionAlias>true</WinAppRunUseExecutionAlias>

  <!-- Capture OutputDebugString + crash diagnostics -->
  <WinAppRunDebugOutput>true</WinAppRunDebugOutput>

  <!-- Register identity but don't launch (attach your own debugger) -->
  <WinAppRunNoLaunch>true</WinAppRunNoLaunch>
</PropertyGroup>

For more details on how exactly the WinApp CLI works with dotnet under the hood and for further guidance on customizing the run experience, check out the dotnet run support docs.

3. Adding an execution alias for console applications (optional)

If you are building a console application, and want output to remain in the current terminal window, you will need to add an execution alias:

  1. Add the required alias to your Package.appxmanifest with WinApp CLI:
winapp manifest add-alias
  1. Tell your application to use the alias by opening your .csproj and adding this property to any <PropertyGroup>:
<WinAppRunUseExecutionAlias>true</WinAppRunUseExecutionAlias>

That’s it

Those two steps (or three for console applications) are all that’s required to run your application with identity locally. Your app will now be able to access Windows features and APIs (like notifications, file handlers, and background tasks) that were gated behind identity.

Now what about packaging?

Creating MSIX packages with winapp pack

The WinApp CLI provides pack and cert generate commands to streamline the MSIX creation process. Assuming you’ve initialized your project with winapp init, this is all it takes to create an installable MSIX:

  1. Build your application in Release configuration:
dotnet build -c Release
  1. Package your application with the pack command, specifying your output directory. Run this command from your project root:
winapp pack .\bin\Release\net10.0-windows10.0.26100.0\win-arm64

The pack command will output a signed MSIX.

Testing locally

If you need to test locally, you will need to sign your package with a self-signed certificate.

WinApp can generate certificates via the winapp cert generate command and install them via winapp cert install.

For a more in-depth breakdown for these scenarios with .NET, check out the full .NET guide.

What else can WinApp do?

Though package identity and packaging are the core of WinApp CLI right now, it has other tools for Windows developers as well. Some highlights:

  • Run Windows build tools directly with the tool command
  • Command line based inspection and interaction with UI elements with the ui command
  • Access to the Microsoft Store CLI via the store command
  • Support for other Windows-compatible frameworks for developers that work with a variety of stacks.

Check out the Usage documentation for a full breakdown of available commands.

Don’t like the terminal? Use the WinApp VS Code extension instead

If you prefer to stay inside Visual Studio Code, and want an integrated debugging experience, we recently launched a VS Code Extension to expose WinApp CLI functionality through VS Code commands.

WinApp VS Code extension header in VS Code extension marketplace

With some brief configuration, you can enable a “F5” debug experience, allowing you to launch and test your app from within VS Code with a button press. To enable F5 debugging via the WinApp extension:

  1. Update your launch.json to include a winapp configuration:
{
  "version": "0.3.0",
  "configurations": [
    {
      "type": "winapp",
      "request": "launch",
      "name": "WinApp: Launch and Attach"
    }
  ]
}
  1. Automate the build process by defining a build task in .vscode/tasks.json:
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build",
      "command": "dotnet",
      "type": "process",
      "args": ["build", "${workspaceFolder}"],
      "problemMatcher": "$msCompile"
    }
  ]
}
  1. Update the winapp configuration in your launch.json to reference your build task:
{
  "type": "winapp",
  "request": "launch",
  "name": "WinApp: Launch and Attach",
  "preLaunchTask": "build"
}

Now, hitting F5 will automatically build and launch your application.

Access WinApp commands via the command palette

In addition to integrated launching, the extension also exposes much of the CLI’s functionality via the VS Code command palette. Hit Ctrl+Shift+P to open the command palette and type winapp to see available commands, including packaging and signing commands.

Visual Studio Code command palette opened with WinApp commands available

For more info on the WinApp extension, check out the release blog post.

Get Started

Install the CLI and try it on your next project:

winget install Microsoft.winappcli --source winget

Or if you prefer to try out the extension, visit the Visual Studio Marketplace.

If you find any issues, have a feature request, or want to check out the source for winapp, head over to our GitHub Repository.

For more getting started resources for .NET, check out the .NET guide or one of these samples:

Happy packaging!

The post Packaging and Package Identity for .NET apps with WinApp CLI on Windows appeared first on .NET Blog.

Read the whole story
alvinashcraft
16 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Automating your Visual Studio extension builds with GitHub Actions

1 Share

If you’re building and maintaining Visual Studio extensions, you’ve probably ended up with some sort of build and publishing workflow – whether it’s manual, scripted, or stitched together over time.

This post is for extension authors who want a simple, repeatable way to build, version, and publish their VSIX files using GitHub Actions.

I’m going to show how I do this across my own extensions.

github action publish image

I’ve been using this approach for a long time, and over time I pulled the most repetitive pieces into a few small reusable actions, so I don’t have to keep rewriting the same logic in every repo.

Those are:

  • vsix-version-stamp – keeps your versioning in sync
  • publish-vsixgallery – publishes CI builds for testing
  • publish-marketplace – publishes to the Visual Studio Marketplace

You can use them independently or together, but I tend to use all three.

If you want to see this wired up in a real repo, take a look at Start Screen.

A real workflow

Here’s a simplified setup very similar to what I use across my extensions today:

name: Build
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: windows-latest

    env:
      Configuration: Release
      VsixManifestPath: src\source.extension.vsixmanifest
      VsixSourcePath: src\source.extension.cs

    steps:
      - uses: actions/checkout@v6

      - name: Setup MSBuild
        uses: microsoft/setup-msbuild@v3

      - name: Restore
        run: msbuild /t:Restore

      - name: Version stamp
        uses: madskristensen/vsix-version-stamp@v2
        with:
          manifest-file: ${{ env.VsixManifestPath }}
          vsix-token-source-file: ${{ env.VsixSourcePath }}

      - name: Build
        run: msbuild /p:Configuration=$(Configuration)

      - name: Publish to VSIX Gallery
        uses: madskristensen/publish-vsixgallery@v1
        with:
          vsix-file: '**/*.vsix'

      - name: Publish to Marketplace
        uses: madskristensen/publish-marketplace@v2
        with:
          extension-file: '**/*.vsix'
          publish-manifest-file: vs-publish.json
          personal-access-code: ${{ secrets.VS_MARKETPLACE_TOKEN }}

This is essentially the full pipeline – version, build, package, and publish.

From here, you can tweak when publishing happens (for example, only on releases), but the core setup tends to stay the same.

Keeping your version in sync

Versioning is one of those things that’s easy to get wrong.

The vsix-version-stamp action updates your version during the build, so you don’t have to think about it.

It works especially well together with the VSIX Synchronizer extension, which generates a .cs file from your .vsixmanifest.

That gives you:

  • A single source of truth
  • Version available in code
  • No manual edits before publishing

It’s completely optional, but once you start using it, it tends to stick.

Publishing to the Visual Studio Marketplace

Once you have a VSIX, publishing it to the Marketplace is straightforward.

You only need a single secret:

  • VS_MARKETPLACE_TOKEN
- name: Publish to Marketplace
  uses: madskristensen/publish-marketplace@v2
  with:
    extension-file: '**/*.vsix'
    publish-manifest-file: vs-publish.json
    personal-access-code: ${{ secrets.VS_MARKETPLACE_TOKEN }}

That’s it.

The VSIX contains the extension metadata, and the publish manifest fills in the rest.

Publishing to a VSIX Gallery (for CI builds and testing)

The publish-vsixgallery action serves a different purpose.

It’s for quickly sharing builds.

I primarily use it when I want someone to try out a fix or validate a change before it goes to the Marketplace.

- name: Publish to VSIX Gallery
  uses: madskristensen/publish-vsixgallery@v1
  with:
    vsix-file: '**/*.vsix'

That’s what VSIX galleries are great for – fast, lightweight distribution without the overhead of a full release.

Works with your own gallery too

VSIX Gallery is open source, so you can host your own instance if you want.

The GitHub Action supports a configurable gallery-url, so it’s not tied to a specific hosted gallery.

- name: Publish to VSIX Gallery
  uses: madskristensen/publish-vsixgallery@v1
  with:
    vsix-file: '**/*.vsix'
    gallery-url: 'https://your-gallery.example.com'

That lets you use the same workflow whether you’re targeting a public gallery or something you host yourself.

Mixing and matching

You don’t have to use all three actions.

Some common setups:

Minimal

  • Build + Marketplace publish

CI-focused

  • Build + VSIX Gallery publish

Full pipeline

  • Version stamping + build + gallery + Marketplace

Use what fits your workflow.

When to use what

  • VSIX Gallery Use this for testing, sharing builds, and quick validation
  • Visual Studio Marketplace Use this for official releases

Most extensions benefit from using both:

  • CI builds go to a gallery
  • Stable builds go to the Marketplace

Wrap-up

This is the setup I use across my extensions.

It keeps things predictable, makes it easy to share builds, and removes most of the repetitive steps from the release process.

You don’t need to adopt all of it. Start with the parts that make sense for your workflow and build from there.

The post Automating your Visual Studio extension builds with GitHub Actions appeared first on Visual Studio Blog.

Read the whole story
alvinashcraft
27 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

The evolution of window and class extra bytes in Windows

1 Share

Windows provides a family of functions for accessing so-called “extra bytes”. There are two categories of extra bytes: Class extra bytes (which belong to the window class) and window extra bytes (which belong to each window created from that class). Applications can request extra bytes at class registration, and those are accessed at increasing offsets starting at zero. The system also defines a number of extra bytes, and those use negative offsets.

We’re going to look at the system-defined offsets.

In 16-bit Windows, these were the available extra bytes and the function you used to read them:

Name Size Accessor Notes
GCW_MENUNAME int16_t GetClassWord  
GCW_HBRBACKGROUND int16_t GetClassWord  
GCW_HCURSOR int16_t GetClassWord  
GCW_HICON int16_t GetClassWord  
GCW_HMODULE int16_t GetClassWord  
GCW_CBWNDEXTRA int16_t GetClassWord  
GCW_CBCLSEXTRA int16_t GetClassWord  
GCL_WNDPROC int32_t GetClassLong  
GCW_STYLE int16_t GetClassWord  
GCW_ATOM int16_t GetClassWord Added in Windows 3.1
GWL_WNDPROC int32_t GetWindowLong  
GWW_HINSTANCE int16_t GetWindowWord  
GWW_HWNDPARENT int16_t GetWindowWord  
GWW_ID int16_t GetWindowWord  
GWL_STYLE int32_t GetWindowLong  
GWL_EXSTYLE int32_t GetWindowLong Added in Windows 3.0
DWL_MSGRESULT int32_t GetWindowLong For dialog windows
DWL_DLGPROC int32_t GetWindowLong For dialog windows
DWL_USER int32_t GetWindowLong For dialog windows

There is clearly a naming pattern here for class and window bytes.

The first letter G stands for Get. The second letter C or W stands for Class or Window. And the third letter W or L stands for Word or Long

For window bytes that apply only to dialog windows, the first letter changes to D for “dialog”. These values are zero or positive, since they are really just extra bytes registered to the standard dialog class.

Now, in 16-bit Windows, handles were 16-bit values, but in 32-bit Windows, they expand to 32-bit values, so 32-bit Windows changed the functions from Get­Something­Word to Get­Something­Long, and the prefixes correspondingly changed from W to from L. So our table now looks like this:

Name 16-bit prefix/size 32-bit prefix/size
MENUNAME GCW_ int16_t GCL_ int32_t ◱
HBRBACKGROUND GCW_ int16_t GCL_ int32_t ◱
HCURSOR GCW_ int16_t GCL_ int32_t ◱
HICON GCW_ int16_t GCL_ int32_t ◱
HMODULE GCW_ int16_t GCL_ int32_t ◱
CBWNDEXTRA GCW_ int16_t GCL_ int32_t ◱
CBCLSEXTRA GCW_ int16_t GCL_ int32_t ◱
WNDPROC GCL_ int32_t GCL_ int32_t ◱
STYLE GCW_ int16_t GCL_ int32_t ◱
ATOM GCW_ int16_t GCW_ int16_t
HICONSM   GCL_ int32_t 💥
WNDPROC GWL_ int32_t GWL_ int32_t ◱
HWNDPARENT GWW_ int16_t GWL_ int32_t ◱
ID GWW_ int16_t GWL_ int32_t ◱
STYLE GWL_ int32_t GWL_ int32_t
EXSTYLE GWL_ int32_t GWL_ int32_t
USERDATA   GWL_ int32_t 💥
MSGRESULT DWL_ int32_t DWL_ int32_t
DLGPROC DWL_ int32_t DWL_ int32_t
USER DWL_ int32_t DWL_ int32_t

The ◱ symbol represents a value that got bigger, and the 💥 symbol represents values that did not exist in 16-bit Windows.

Even though control IDs are typically small integers, the space for them was expanded from a 16-bit value to a 32-bit value because some people were using it to hold pointers or handles. (One way to create a process-wide unique number is to allocate memory and use its address.)

The next step in the evolution of extra bytes is the conversion from 32-bit to 64-bit Windows. Pointers and handles expand to 64-bit values on 64-bit Windows, so all of the extra bytes that are used to (or could be used to) hold a handle or pointer were expanded to a 64-bit version.

To make it possible to write code that targets both 32-bit and 64-bit Windows, the design of 64-bit Windows didn’t make the hard break that 32-bit Windows did from 16-bit Windows. Instead, they introduced new functions that accept pointer-sized integers, which are 32-bit values on 32-bit Windows and 64-bit values on 64-bit Windows. That way, you just use those new functions everywhere, and they will expand on 64-bit systems and remain the same on 32-bit systems.

The new functions have names like Get­Window­Long­Ptr, and the corresponding prefixes were changed to GWLP_ and so on.

Name 16-bit prefix/size 32-bit prefix/size 32/64-bit prefix/size
MENUNAME GCW_ int16_t GCL_ int32_t ◱ GCLP_ intptr_t ◱
HBRBACKGROUND GCW_ int16_t GCL_ int32_t ◱ GCLP_ intptr_t ◱
HCURSOR GCW_ int16_t GCL_ int32_t ◱ GCLP_ intptr_t ◱
HICON GCW_ int16_t GCL_ int32_t ◱ GCLP_ intptr_t ◱
HMODULE GCW_ int16_t GCL_ int32_t ◱ GCLP_ intptr_t ◱
CBWNDEXTRA GCW_ int16_t GCL_ int32_t ◱ GCL_ int32_t
CBCLSEXTRA GCW_ int16_t GCL_ int32_t ◱ GCL_ int32_t
WNDPROC GCL_ int32_t GCL_ int32_t ◱ GCLP_ intptr_t ◱
STYLE GCW_ int16_t GCL_ int32_t ◱ GCL_ int32_t
ATOM GCW_ int16_t GCW_ int16_t GCW_ int16_t
HICONSM   GCL_ int32_t 💥 GCLP_ intptr_t ◱
WNDPROC GWL_ int32_t GWL_ int32_t ◱ GWLP_ intptr_t ◱
HWNDPARENT GWW_ int16_t GWL_ int32_t ◱ GWLP_ intptr_t ◱
ID GWW_ int16_t GWL_ int32_t ◱ GWLP_ intptr_t ◱
STYLE GWL_ int32_t GWL_ int32_t GWL_ int32_t
EXSTYLE GWL_ int32_t GWL_ int32_t GWL_ int32_t
USERDATA   GWL_ int32_t 💥 GWLP_ intptr_t ◱
MSGRESULT DWL_ int32_t DWL_ int32_t DWLP_ intptr_t ◱
DLGPROC DWL_ int32_t DWL_ int32_t DWLP_ intptr_t ◱
USER DWL_ int32_t DWL_ int32_t DWLP_ intptr_t ◱

From the prefix on the name of the extra bytes, you can read off which function it is meant to be used with.

Prefix Function
GCW_ ↔ GetClassWord GWW_ ↔ GetWindowWord
GCL_ ↔ GetClassLong GWL_ ↔ GetWindowLong
GCLP_ ↔ GetClassLongPtr GWLP_ ↔ GetWindowLongPtr

The weirdo is DWLP_ because it needs to encode both the type of window that it can be used with (D = dialog) as well as the function name it goes with (WindowLongPtr).

As a concession, Windows lets you pass GCL_ and GWL_ values to Get­Class­Long­Ptr and Get­Window­Long­Ptr (respectively) even though they are intended to be used with Get­Class­Long and Get­Window­Long (respectively). If you do that, you get the corresponding 32-bit value zero-extended if necessary to be the size of a pointer.² This is seen primarily in the case of GWL_ID because most people don’t use the full range of IDs, so if you’re willing to live within the 32-bit subset, you can just pretend that the values are not pointer-sized.³

“Why bother changing all the prefixes? Doesn’t that just create a lot of busy work for people porting from 32-bit code to 64-bit code?”

Yes, but it’s good busy work. The point is to force build breaks at places where you need to make fixes, because you have to call the function that accesses a pointer-sized integer rather than a 32-bit integer; otherwise you suffer from integer truncation bugs.

¹ This is a common prefixing convention for classic Win32. For example, the operation parameter to Show­Window is prefixed SW_; the flags to Set­Window­Pos are prefixed SWP_; and the relationship parameter for Get­Window is prefixed GW_.

² The use of the GWL_ values with Set­Window­Long­Ptr is a bit more problematic. It looks like you’re storing a pointer-sized integer, but only the bottom 32 bits are honored.

³ The ID is unusual in that it is defined both as GWL_ID and GWLP_ID. All of the other values are defined with only one prefix.

The post The evolution of window and class extra bytes in Windows appeared first on The Old New Thing.

Read the whole story
alvinashcraft
31 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

.NET app returning a blank 500? Not with exception handlers

1 Share

Is your ASP.NET Core Web API returning a blank 500 response? Learn how to fix it properly using exception handlers.

The page .NET app returning a blank 500? Not with exception handlers appeared on Round The Code.

Read the whole story
alvinashcraft
37 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Docker Desktop Linux X11 Forwarding from Container to Host

1 Share

Oh boy, this was much harder than I expected. I found guides and articles and blog posts on how to do this, but none of them worked for me.

What I wanted to do

I like running applications in containers; I wrote a post about running Claude Code in a container. For a while, I’ve wanted to run GUI applications in a container and forward the X11 display from the container to the host.

Why do this? Sometimes I don’t want to install an application on my host computer; it might be something I won’t use often, or it might be something I want to try out, or it could be something I’m a little concerned about and don’t want it to have access to my personal files (like Claude Code).

How to do it

There are a few steps.

The Dockerfile

It starts with a Dockerfile. I’m going to use ubuntu:24.04 as the base image, and install a few things including xeyes, because that is like the “Hello World” of X11 applications. I also include gedit because it is a nice, simple text editor that is a bit more complex than xeyes.

This is the Dockerfile -

FROM ubuntu:24.04

# avoid interactive prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive 

# Update package lists and upgrade installed packages
RUN apt-get update && apt-get upgrade -y 

# install lots of stuff, some might not be necessary, you can tweak it to see what works
RUN apt-get install -y \ 
 x11-apps \
 x11-utils \
 x11-xserver-utils \
 dbus-x11 \
 at-spi2-core \
 gedit \
 fonts-dejavu-core \
 fonts-liberation \
 adwaita-icon-theme \
 gnome-themes-extra \
 wget \
 curl \
 gpg \
 sudo \
 && rm -rf /var/lib/apt/lists/*

CMD ["/bin/bash"]

Nothing too special or fancy here.

socat

What? socat?

This is the thing that solves the problem that is unique to Docker Desktop. It allows you to forward the X11 display from the container to the host.

You probably need to install it. I didn’t have it, and I’ve been using Linux for a long time. On Ubuntu, you can install it with -

sudo apt install socat

Now run it with this -

socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:/tmp/.X11-unix/X0

  • TCP-LISTEN:6000 - listen for TCP connections on port 6000, which is the default port for X11.
  • reuseaddr - allows the socket to be reused, which is useful if you need to restart the socat command.
  • fork - allows multiple connections to be handled simultaneously.
  • UNIX-CLIENT:/tmp/.X11-unix/X0 - this is the path to the X11 socket on the host, which is where the X11 server is listening for connections.

Keep this running in a terminal.

xhost

In another terminal, run this command -

xhost +local:

This allows any local user to connect to the X11 server. This is necessary for the container to be able to connect to the X11 server on the host.

Run the container

Finally, run the container with this command -

docker run -it --rm \
 --add-host=host.docker.internal:host-gateway \
 -e DISPLAY=host.docker.internal:0 \
 --ipc=host \
 --name x11_local \
 x11_local
  • --add-host=host.docker.internal:host-gateway - adds a host entry for host.docker.internal, which is the hostname that Docker Desktop uses to refer to the host machine. This is necessary for the container to be able to connect to the X11 server on the host.
  • -e DISPLAY=host.docker.internal:0 - sets the DISPLAY environment variable in the container to point to the X11 server on the host. The :0 part is the display number, which is usually 0 for the first display.
  • --ipc=host - allows the container to share the IPC namespace with the host, some GTK/GL apps require this.
  • --name x11_local - this is the container name.
  • `x11_local’ - the name of the image to run.

When you’re in the container, run xeyes or gedit, and you should see the application running on your host computer.

Conclusion

It works!
It works!
Read the whole story
alvinashcraft
42 seconds ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories