Commits (5)
......@@ -12,6 +12,7 @@ description = "A look at a defining Microsoft project"
gitlab = "https://gitlab.com/experience-longhorn/longhorn-ms"
featured_image = "/images/header-01.jpg"
custom_css = [ "dist/custom.css" ]
mainSections = [ "articles" ]
category = "categories"
......@@ -36,12 +36,12 @@ Since many features in Longhorn were originally being developer separately from
Before using the Longhorn feature installer, be sure to install .NET framework version 1. You can download [the redistributable here](https://www.microsoft.com/en-us/download/details.aspx?id=96).
1. Extract the zip -_Make sure the path to this directory contains no spaces or the installer will break_
2. Execute _AvalonInstallWrapper_.bat
1. Extract the zip - _Make sure the path to this directory contains no spaces or the installer will break_
2. Execute `AvalonInstallWrapper.bat`
3. Wait for the installer to copy some files
4. A Windows Component Wizard will appear
5. Select the components you wish to install
6. When prompted for a directory, point to the _LonghornFiles_ directory in the extracted folder
6. When prompted for a directory, point to the `LonghornFiles` directory in the extracted folder
7. Wait for the wizard to finish
[3683 feature installer for NT 5.x](/download/3683-feature-installer-nt-5-x.zip)
......@@ -11,14 +11,14 @@ categories:
_This used to be hosted on the initial website two years ago (whoa, time flies). I completely forgot about re-uploading it until now. Enjoy!_
Build 4093 was originally leaked at 28 August 2006 as a heavily edited ISO. As the original 4093 ISO wasn't bootable the C0d3rz team made a bootable ISO using 4033's winpe. They edited startnet.cmd to start 4093's setup from the usa\_4093\_x86fre.pro\_longhorn directory in the ISO.
[Build 4093](/builds/4093/) was originally leaked at 28 August 2006 as a heavily edited ISO. As the original 4093 ISO wasn't bootable the C0d3rz team made a bootable ISO using 4033's winpe. They edited startnet.cmd to start 4093's setup from the usa\_4093\_x86fre.pro\_longhorn directory in the ISO.
Until today (1st May 2014) the C0d3rz ISO was to be used if one would like to install 4093. Not anymore, as Lukáš in collaboration with Melcher were able to put the original 4093 WinPE to work. We, from longhorn.MS, proudly present to you, the proper release of Longhorn Build 4093 with its own PE!
Install notes:
*Mount or burn image
*Install with this key: `TCP8W-T8PQJ-WWRRH-QH76C-99FBW`
* Unzip
* Mount or burn image
* Install with this key: `TCP8W-T8PQJ-WWRRH-QH76C-99FBW`
We would like to thank Hounsell for hosting our website and letting us host the download of the fixed copy.
title: 'Managed C++: Clearing the C# myth'
author: Thomas Hounsell
type: post
date: 2019-07-15T09:00:00+00:00
url: /managed-cpp-in-longhorn
- Research
- Development
Reading up on Longhorn's development process, and you'll read a lot about how the shell or user interface was redeveloped using the .NET Framework, and many people infer that this meant using C#, including a number of Microsoft employees. This has then been blamed for the terrible performance of Longhorn and in particular, the rampant memory leaks it had.
It's time for a back to basics lesson on how the Longhorn shell was developed, to help dispell some of those common myths and get to the core of what went wrong in Longhorn's shell.
### Architecture
Let's take a step back for a moment and consider the shell improvements from a wider view. We know that the traditional `explorer.exe` shell has been steadily developed using native Windows APIs and C++ right from its inception. A cursory inspection of any Longhorn build will reveal that this remains largely unchanged. This is how builds for other architectures, such as AMD64, work and feel much like XP, with Windows Explorer working in very much the same way.
At this early stage, .NET was only supported on the 32-bit x86 platform. .NET Framework v2.0 would eventually add support for both IA-64 and AMD64, but this did not arrive ahead of Longhorn's development reset. While Longhorn for IA-64 and AMD64 both included _WoW64_ (Windows on Windows 64), which allowed 32-bit applications such as .NET to run, their `explorer.exe` shell was 64-bit, and you cannot mix architectures within the same process.
This betrays a little of how the "new" shell was implemented - it was not an independent new shell, but rather a (rather complex) plugin for Windows Explorer. The .NET Framework allows for the creation of COM components within managed libraries and so this is how it was implemented. Indeed, this is how many such changes had also been implemented in Whistler / Windows XP, albeit they were exclusively native C++ rather than using the .NET Framework. This is also how Longhorn can fallback gracefully to an XP-style experience - it attempts to load the new Longhorn extension via COM first, and if that fails, falls back to the XP-style COM experience. In the unusual event even that fails, you would end up with something akin to the "Classic" view, or Windows 2000 style.
### The Language?
So then, if they're using .NET, it's got to be C# right? It's _the_ .NET language. First-class support and the hot new thing Microsoft were pushing to developers at every opportunity. They even already had some experience of shipping a Windows component written in C# - Windows XP's Media Center was a purely managed C# affair for the most part, with only a small number of components written in C++.
For reasons we can only speculate, the Shell team did not use C# much, if at all, within the Longhorn shell. Instead, they used Microsoft's variant of C++ called *Managed C++*. This is related, but not quite the same as the more modern *C++/CLI*, which is Microsoft's second attempt at a C++ dialect for the .NET Framework, and probably one that was very much influenced by the painful experience of the Longhorn Shell.
There are a few probable reasons for this decision. One is that the C# language was very much in it's infancy. It is important to recall that at this early stage, C# was not the proven, mature language it is today. There was no support for generics, to take one example.
Furthermore, developers who had extensive experience with the language were few and mostly associated with the .NET team itself. Instead, what the Shell team did have an overabundance of was developers trained and highly experienced with C++. It would have likely seemed the wiser decision to limit the amount of on-the-job training by going with something that very much bridged the gap.
A third factor was likely the ease of interoperability with existing native code with Managed C++. The story in C# for interoperability is not horrible per se, but it was and still is from a developers point of view, more rigid and more work. In C#, you're required to use a Platform Invoke, or P/Invoke, that requires you to redefine the export in C#. For example, this export from the Windows SDK headers...
{{<highlight cpp>}}
INT ShellAboutW(HWND hWnd, LPCWSTR szApp, LPCWSTR szOtherStuff, HICON hIcon);
...would become this P/Invoke definition in C#:
{{<highlight csharp>}}
static extern int ShellAbout(IntPtr hWnd, string szApp, string szOtherStuff, IntPtr hIcon);
Rather than go through and create all these redefinitions - which may also expose information that was limited before to private internal-only header files - using Managed C++ allowed MS to simply reference native functions in much the same way as they would in native C++, for example in this toy program (this is C++/CLI rather than MC++, which doesn't have the gcnew keyword):
{{<highlight cpp>}}
#include <string>
#include "windows.h"
#include "shellapi.h"
#pragma comment(lib, "shell32.lib")
using namespace System;
const char* show_shell_about()
return ShellAbout(NULL, L"C++/CLI Sandbox", L"Hi from the Experience Longhorn project", NULL) == TRUE
? "True"
: "False";
int main()
auto result = show_shell_about();
System::Console::Write("Result: ");
System::Console::WriteLine(gcnew System::String(result));
return 0;
This would have likely been considered a huge benefit to the Shell team which, with strict performance goals, were likely to be passing across the native / managed boundary quite frequently. Unlike C#, this also allows you to have a "mixed-mode" library or executable, which contains both unmanaged and managed code.
If you were to decompile the compiled version of the toy program above, you'd note some key differences from typical .NET executables. Using an IL decompiler, such as dnSpy, you'd notice that a lot of placeholder classes and structs that represent various structures defined in the Windows SDK headers. In a <Module> class, you'd find the methods that were in the global namespace, such as the `main()` and `show_shell_about()` from the toy program above. In here is our ShellAboutW() import stub, that dnSpy decompiles to the following C#:
{{<highlight csharp>}}
[DllImport("", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal unsafe static extern int ShellAboutW(HWND__*, char*, char*, HICON__*);
The `show_shell_about()` method decompiles as so:
{{<highlight csharp>}}
internal unsafe static sbyte* show_shell_about()
return ref (<Module>.ShellAboutW(null, (char*)(&<Module>.?A0x37975f1f.unnamed-global-1), (char*)(&<Module>.?A0x37975f1f.unnamed-global-0), null) != 1)
? ref <Module>.?A0x37975f1f.unnamed-global-3
: ref <Module>.?A0x37975f1f.unnamed-global-2;
You can see that the strings have been pulled out into the executables' `.rdata` (read-only data) section and that .NET refers to them simply using automatically generated fields.
If you were to open this executable in a more typical decompiler, such as IDA Pro in PE mode, you'd see that unlike typical P/Invokes, a MC++ reference is also included in the imports table of the executable.
### Okay, but what does it all mean?
Well, Managed C++ came with some issues that Microsoft tried to rectify with C++/CLI. In particular, their overloading of the `new` keyword made it more difficult as to which objects would be collected by the managed garbage collector, and which objects had to be cleaned up manually by the developer. The potential outcome of such confusion would be that some objects never got collected or cleaned up, introducing a memory leak. Certainly, it is quite possible to introduce memory leaks in purely managed code, though not in the same way, but it does strike me that this is possibly a major contributing factor to Longhorn's well documented problems in this area. It's also noteworthy that they made the C++/CLI changes in line with the .NET 2.0 / Visual Studio 2005 release, after the Longhorn reset.
\ No newline at end of file
title: "4053.main"
type: build
url: /builds/4053/
build_tag: "6.0.4053.main.031022-1720"
build_arch: [ "x86" ]
build_m: 7
install_date: "2003-10-23"
install_key: "CKY24-Q8QRH-X3KMR-C6BCY-T847Y"
This build was compiled on 2003-10-22 and leaked on 2004-03-01 by an unknown source together with some screenshots. It is very similar to 4051 and 4052 but had bugfixes for several networking and icon issues. It still has the Luna and Windows Classic themes with it but also the new Slate theme.
But also Build 4053 has bugs:
* Creating new user accounts via the GUI doesn't work. Use "control userpasswords2" for this.
* "Personal Folders" in Outlook shows the HTML source instead of the fully rendered page.
* "History" isn't working in Internet Explorer.
* "Frequently used items" in the Start Menu does reset every reboot.
* It seems to have some problems with driver signatures so every device installation needs to be confirmed.
......@@ -13,14 +13,26 @@
{{ $section_name := index (.Site.Params.mainSections) 0 }}
<div class="pa3 pa4-ns w-100 w-70-ns center">
{{ range $key, $value := .Site.Taxonomies }}
<section class="w-100">
<h1 class="flex-non">{{ humanize $key }}</h1>
{{ range $value }}
<h2 class="f5 fw4 dib mv1 mr3">
<a href="{{ .Page.RelPermalink }}" class="link black dim">{{ .Page.Title }}</a>
{{ end }}
{{ end }}
{{/* Use $section_name to get the section title. Use "with" to only show it if it exists */}}
{{ with .Site.GetPage "section" $section_name }}
<h1 class="flex-none">
<h1 class="flex-none mb0">
{{ $.Param "recent_copy" | default (i18n "recentTitle" .) }}
{{ end }}
{{ $n_posts := $.Param "recent_posts_number" | default 3 }}
{{ $n_posts := $.Param "recent_posts_number" | default 5 }}
<section class="w-100 mw8">
{{/* Range through the first $n_posts items of the section */}}
......@@ -31,17 +43,6 @@
{{ end }}
{{ range $key, $value := .Site.Taxonomies }}
<section class="w-100">
<h1 class="f3">{{ humanize $key }}</h1>
{{ range $value }}
<h2 class="f5 fw4 dib mv1 mr3">
<a href="{{ .Page.RelPermalink }}" class="link black dim">{{ .Page.Title }}</a>
{{ end }}
{{ end }}
{{/* As above, Use $section_name to get the section title, and URL. Use "with" to only show it if it exists */}}
{{ with .Site.GetPage "section" $section_name }}
<a href="{{ .RelPermalink }}" class="link db f6 mt4 pa2 br3 bg-mid-gray white dim w4 tc">{{ i18n "allTitle" . }}</a>