Unblocking .NET DLLs on Mac Catalina

There’s a new headache in town. If you run self-hosted Azure DevOps agent, or .NET Core project, on a Mac running Catalina (v 10.15+) and Microsoft Edge, you will have noticed a new behavior where the OS prevents the .NET assembly from working.

This is because it has been marked with a “quarantine” file attribute when it was downloaded with the browser. It’s a security measure that I’m familiar with on Windows. I would typically remove this attribute by right clicking on the file, select “Properties”, then check “Unblock”:

Unblocking a ZIP file on Windows.

At this point, I suspected I had a good idea of what was happening, I just needed to figure out how to check for and remove it on a Mac. I reached out to the dev community on twitter and asked around. Thanks to Eric Lawrence who pointed me to the Chromium code change that shows it is indeed quarantining the file(s).

After reviewing how to read and remove file attributes, indeed I found a com.apple.quarantine attribute on the tarball archive file that is downloaded from Azure DevOps. Since it contains all the .NET assemblies, we can just unblock the tar.gz file and all contents will unblock as well.


Let’s go back to Azure DevOps Agent Pool page, where you download your agent package (see here if you’ve never done this before).

Click the download button to download the tarball file

Now that the file is in the downloads folder, we can use the xattr command to list the attributes. In my case, I’m checking the downloaded compressed file.

xattr vsts-agent-oxs-x64-2.159.2.tar.gz
You’ll see the attributes listed with the xattr command

Bingo! Notice the com.apple.quarantine attribute? That’s the one causing this headache. Now we can see the attribute name, we can remove it by calling the the xattr command again with -d attributeName fileName parameters .

xattr -d com.apple.quarantine vsts-agent-oxs-x64-2.159.2.tar.gz
You will need to give Terminal permission to access the Downloads folder.

Finally list the attributes again to confirm the quarantine has been removed:

Confirm the attribute was removed

Now you can finish extracting the tarball and setting up the agent (or running your .NET core application).

Preparing apps for Windows X and Surface Duo or Neo Devices.

Preface – This is not Microsoft-provided information, just my guess after digging around the Microsoft.UI.Xaml source code after a conversation on Twitter. This is not coming from any MVP-NDA or other NDA source. I will update this post when official information becomes available.

There is suspiciously missing control from the XAML Gallery app – TwoPaneView. It’s in the WinUI 2.2 release, but there’s no documentation, guidance or examples of it being used (yet). The only thing you’ll find is the API reference, which is automatically generated during the build process.

It didn’t go completely unnoticed, another MVP Fons Sonnemans, did find the control in the preview SDK and wrote this blog post. However, now armed with the knowledge of two-screen devices and Windows X on the horizon, I wanted to dig deeper.

API Support

If you look at the Fon’s demo, it might seem like all the control is good for right now is visual state changes that occur within a single Window. If this is what we’ll use for multi-window-single-instance apps, there needs to be some sort of OS level event that bubles useful information up the API. This iswhere my conjecture begins…

I reviewed the source code of the control and found some interesting code in DisplayRegionHelper::GetRegionInfo()

It appears to check if the display region is WindowingEnvironmentKind::Tiled from calling a WinRT API WindowingEnvironment::GetForCurrentView() . Then, the most interesting part that I think supports multi-screen setups, is regions = winrt::Windows::UI::WindowManagement::DisplayRegion::GetRegionsForCurrentView()

Here’s the snippet with the aforementioned lines highlighted:

 winrt::WindowingEnvironment environment{ nullptr };
            environment = winrt::WindowingEnvironment::GetForCurrentView();
        } catch(...) {}

        // Verify that the window is Tiled
        if (environment)
            if (environment.Kind() == winrt::WindowingEnvironmentKind::Tiled)
                winrt::IVectorView<winrt::Windows::UI::WindowManagement::DisplayRegion> regions = winrt::Windows::UI::WindowManagement::DisplayRegion::GetRegionsForCurrentView();
                info.RegionCount = std::min(regions.Size(), c_maxRegions);

                // More than one region
                if (info.RegionCount == 2)
                    winrt::Rect windowRect = WindowRect();

                    if (windowRect.Width > windowRect.Height)
                        info.Mode = winrt::TwoPaneViewMode::Wide;
                        float width = windowRect.Width / 2;
                        info.Regions[0] = { 0, 0, width, windowRect.Height };
                        info.Regions[1] = { width, 0, width, windowRect.Height };
                        info.Mode = winrt::TwoPaneViewMode::Tall;
                        float height = windowRect.Height / 2;
                        info.Regions[0] = { 0, 0, windowRect.Width, height };
                        info.Regions[1] = { 0, height, windowRect.Width, height };

This is the basis of my theory, I could be way off. If I’m wrong, what’s the worst thing that happened? I was forced to think about my application in a multi-window environment? Win-win!


There are no official demos of this that I could find. However, the same source code also had a UITest! I isolated that UI test in a runnable project, that is what you see a recording of in the tweet embedded above. You can download the project from here DuoNeoTest.zip

Note: I did not set the InsiderSDK as the Target SDK. If you do have SDK 18990 installed and have a device running insider preview, just change the target in the project properties.