A new website about Visual Studio resources

I have launched a new website to help developers using Visual Studio / Visual Studio Code / Visual Studio Team Services / Team Foundation Server:

http://www.visualstudioresources.com/

visualstudioresources

And this is a cross post that explains my motivation to do it:

Back in 1995, when I started to work on a company, Internet didn’t exist as we know it today. At that time, Microsoft supplied its Microsoft Developer Network (MSDN) Library with technical information for developers in a couple of CDs that arrived to the office quarterly:

msdnlibrary

You could read the content of your interest in a few days, and then you didn’t receive more content until the next quarter. There were some printed magazines, but they were monthly and you read them in a few days. Content and resources were scarce. Today, more than 20 years later, the problem is the opposite: there is so many content in so many ways that we have to use search engines to find what we need. Also, the way we learn today is not the way we used to learn 20 years ago, at least not for every developer. Some people still prefer to read the official documentation from the manufacturer. Other people prefer to learn with thick, comprehensive, books. Other prefer small articles and posts. Others don’t like to read so much and prefer videos or courses. Yet others prefer to learn reading code of samples or other apps.

I’ve created this site with the goal of providing a huge directory of resources of any kind to learn about the Visual Studio family: Visual Studio, Visual Studio Code, Visual Studio Team Services and Team Foundation Server. It is not a website about .NET or about programming languages (C#, VB.NET, F#, etc.), but only about the “tooling”, the development environments and the application lifecycle management (ALM) systems that Microsoft provides to developers to become more productive.

This website starts small and modest, but hopefully it will grow in the next months and years to reach the depth of the Visual Studio Extensibility (VSX) that I created years ago for developers extending Visual Studio.

I hope you like it.

Email this to someoneTweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+

Another two strange cases of MZ-Tools 8.0 for VBA could not be loaded

It seems that the path to load a .NET-based add-in in the VBA editor of Office is full of mines. In the last couple of days I have discovered two new scenarios (apart from this and this) that can cause “<Add-In> could not be loaded”. I will document them here for future reference for me or other people:

This first one happens if you are not using a COM Shim, you are using .NET Framework 2.0 and the version of Office is 2000, 2002 (XP) or 2003. It is due to a lockback policy that “prevents the .NET Framework 2.0 common language runtime (CLR) from initializing when the .NET Framework 2.0 is hosted in either the Word process space or the Excel process space. The policy restriction limits Word and Excel from loading versions of the .NET Framework that are later than version 1.1. Therefore, the .NET Framework 2.0 assemblies cannot load”. It is documented in the Knowledge Base article You cannot load a .NET Framework 2.0 assembly from Visual Basic for Applications in Word 2003 and earlier versions, or in Excel 2003 and earlier versions and fortunately has a solution through a registry key HKEY_CLASSES_ROOT\Interface\ {000C0601-0000-0000-C000-000000000046} that can be created. I was aware of this problem since long time ago and my setup created that registry key if Office 2003 or 2002 was detected. However, I noticed that my add-in was failing for Office 2000 (yes, I have users running Office 2000 on old PCs) because I added support for Office 2000 later and I didn’t update the setup to check that version. This case is difficult to diagnose because the following script works perfectly (I mean, the problem only manifests loading the assembly in Office):

Dim o
Set o = CreateObject("MZTools8VBA")

The other problem was also hard to diagnose. It happened when using a COM Shim to load the CLR using code from the COM Shim wizards provided by Microsoft, which calls the CorBindToRuntimeEx function passing NULL to the first pwszVersion parameter. A NULL value means to load the highest CLR installed below CLR 4.0, which should be CLR 2.0. However, on Windows XP I got HRESULT 0x80131700. Fortunately I found on a comments of the post COM Shim CLR Loader Bug that the problem could be solved passing the specific version “v2.0.50727”, which certainly fixed the problem for me.

Email this to someoneTweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+

The strange case of System.InvalidCastException (“Unable to cast COM object of type ‘System.__ComObject’ to class type System.Windows.Forms.UserControl”) showing toolwindow

In my last post I explained a problem that can happen when a .NET-based add-in for the VBA/VB6 editor that uses a COM Shim tries to use any Common Language Runtime (CLR) that it’s installed on the target machine (CLR 2.0 or CLR 4.0), assuming that the add-in is built with .NET Framework 2.0/3.0/3.5 and therefore can use CLR 2.0 or CLR 4.0. This post is about other problem that can happen.

The ideal approach, that was the first that I implemented, is that the COM Shim uses at run-time the first detected CLR that it supports. In my case the logic was:

  • First it detects if CLR 2.0 is installed. If so, use it.
  • If not, it detects if CLR 4.0 is installed. If so, uses it.

This would be the best approach because at any given time, it uses the available(s) CLRs. The default CLRs installed by Windows are:

  • Windows XP: Neither CLR 2.0 nor CLR 4.0
  • Windows Vista, Windows 7: CLR 2.0
  • Windows 8, Windows 8.1, Windows 10: CLR 4.0

And life would be great if that were the whole picture. However, most add-ins need to create and show toolwindows. For a .NET-based add-in, this is somewhat tricky because toolwindows need ActiveX UserDocuments, and .NET doesn’t support UserDocuments. However, a UserControl can be used instead with some tricks as I explained in HOWTO: Create a toolwindow for the VBA editor of Office from an add-in with Visual Studio .NET. The UserControl, being .NET-based, needs to be registered as a COM component, and .NET-based COM components need to specify the required runtime version of the .NET Framework in the Windows registry, in the RuntimeVersion value inside the InProcServer32 key:

HKEY_CLASSES_ROOT\progid
   (default) = progId
HKEY_CLASSES_ROOT\progid\CLSID
   (default) = clsid
HKEY_CLASSES_ROOT\CLSID\{clsid}
   (default) = progid
HKEY_CLASSES_ROOT\CLSID\{clsid}\InProcServer32
   (default) = mscoree.dll
   Class = ClassName
   ThreadingModel = Both
   Assembly = Stringized_assembly_reference
   Codebase = path_of_private_assembly
   RuntimeVersion = version_of_the_runtime
...

toolwindowcontrolregistration

This registration of the usercontrol used by the toolwindow must be done by the installer at setup-time. And I used in the installer the same logic than in the COM Shim: first it detects CLR 2.0 and, if not available, then it detects CLR 4.0.

However, a couple of my users reported this exception when clicking a button that should show a toolwindow:

System.InvalidCastException: Unable to cast COM object of type ‘System.__ComObject’ to class type ‘System.Windows.Forms.UserControl’. Instances of types that represent COM components cannot be cast to types that do not represent COM components; however they can be cast to interfaces as long as the underlying COM component supports QueryInterface calls for the IID of the interface.

Toolwindows are created calling the CreateToolWindow function, that receives in the second parameter the ProgId of the ActiveX UserDocument that the function will create internally and return as output last parameter:

Windows.CreateToolWindow (AddInInst, ProgID, Caption, GuidPosition, DocObj) As Window

And that exception happened in a line of code that casts the usercontrol instance returned by the last output parameter (which should be a UserControl instance) to the System.Windows.Forms.UserControl type. Why?

It took me a while to discover the cause (the scenarios are so messy that often I need to go walking or swimming to get clarity thinking about the problem…). But at some point I realized that there were some scenarios where the runtime version of the usercontrol and the runtime version of the COM Shim would mismatch:

The first one is that you have Windows 10, which by default provides .NET Framework 4.0 (CLR 4.0) and not .NET Framework 2.0/3.0/3.5 (CLR 2.0). The user runs the setup of the add-in. The usercontrol is registered with the only runtime available (v4.0.30319). When the add-in is loaded the COM Shim uses the only runtime available, also v4.0.30319. So far so good. However, one day the user installs .NET Framework 2.0/3.0/3.5, which is an optional feature of Windows 10:

windowsoptionalfeatures

In this case, the usercontrol remains registered with runtime version v4.0.30319 (unless the user runs the installer again) but the COM Shim, that detects CLRs at runtime, not at setup time, would use CLR 2.0! (because its logic gives it preference over v4.0.30319). And this causes the System.InvalidCastException, because the CreateToolwindow returns an instance of a CLR 4.0 usercontrol. Since such thing cannot enter the CLR 2.0 of the COM Shim, a System.__ComObject wrapper is created, that cannot be cast to a UserControl type.

The second scenario is that you have Windows 10, which by default provides .NET Framework 4.0 (CLR 4.0) but the user has installed .NET Framework 2.0/3.0/3.5 (CLR 2.0). The user runs the setup of the add-in. The usercontrol is registered with the runtime v2.0.50727 (because the logic gives it preference over v4.0.30319). When the add-in is loaded the COM Shim uses also the preferred runtime v2.0.50727 over v4.0.30319. So far so good. However, one day the user uninstalls the optional .NET Framework 2.0/3.0/3.5. In this case, the usercontrol remains registered with runtime version v2.0.50727 (unless the user runs the installer again) but the COM Shim, that detects CLRs at runtime, not at setup time, would use CLR 4.0 instead of CLR 2.0! This causes a different exception, though.

So I thought a different approach: rather than using whatever CLR is installed on the machine (with preference for CLR 2.0 over CLR 4.0), I use the default CLR that should be installed on each Windows version:

  • Windows XP: none. But the setup enforces that .NET Framework 2.0 must be installed.
  • Windows Vista, Windows 7: .Net Framework 2.0
  • Windows 8, Windows 8.1, Windows 10: .Net Framework 4.0

This approach makes the COM Shim immune to installation/uninstallation of additional .NET Framework installations (because default .NET Frameworks are OS components and cannot be uninstalled, only optional .NET Frameworks can be uninstalled).

And while this approach seemed fine, another two users reported the same System.InvalidCastException, both cases on Windows 10 using VB6.  More walking and swimming :-). The reason is that VB6 can be set to run in compatibility mode with Windows XP:

windowsxpcompatibility

What happens if a user is running Windows 7 and sets VB6 with compatibility mode with Windows XP? The setup was run on Windows 7, so the usercontrol is registered with runtime version v2.0.50727 (the default for Windows 7). When VB6 in compatibility mode loads the COM Shim, it thinks that it is running on Windows XP and uses the .NET Framework 2.0 that the setup required. So nothing bad happens.

But what happens if the user is running Windows 10 and sets VB6 with compatibility mode with Windows XP? The setup was run on Windows 10 (not in compatibility mode), so the usercontrol is registered with runtime version v4.0.30319 (the default for Windows 10). When VB6 in compatibility mode loads the COM Shim, it thinks that it is running on Windows XP and uses the .NET Framework 2.0 that the setup required. So a System.InvalidCastException happens when performing the cast operation due to the mix of runtime versions.

The best solution, that I hope that fixes this problem for good, is to make the COM Shim to read the registered runtime version of the usercontrol of the toolwindow from the Windows registry, and use the matching CLR. With this approach, it doesn’t matter what Windows version the COM Shim thinks it is running on, because it won’t use the default CLR of that Windows version, but the one that was determined at setup-time.

Update (September 12, 2016): it happens that there is a way to register a .NET component as a COM component indicating that it can work with CLR 2.0 and CLR 4.0. Instead of using the RuntimeVersion name mentioned above, you can use the SupportedRuntimeVersions name and provide a list of CLRs in order of preference:

HKEY_CLASSES_ROOT\progid
   (default) = progId
HKEY_CLASSES_ROOT\progid\CLSID
   (default) = clsid
HKEY_CLASSES_ROOT\CLSID\{clsid}
   (default) = progid
HKEY_CLASSES_ROOT\CLSID\{clsid}\InProcServer32
   (default) = mscoree.dll
   Class = ClassName
   ThreadingModel = Both
   Assembly = Stringized_assembly_reference
   Codebase = path_of_private_assembly
   SupportedRuntimeVersions = v2.0.50727;v4.0
...

See the post In-Proc SxS and Migration Quick Start. So, original approach for the COM Shim would work:

  • First it detects if CLR 2.0 is installed. If so, use it.
  • If not, it detects if CLR 4.0 is installed. If so, uses it.
Email this to someoneTweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+

The strange case of mscoree.dll and the CLRCreateInstance function

Since I started three months ago to use a COM Shim for my .NET-based MZ-Tools 8.0 for VBA/VB6 to achieve isolation and .NET CLR independence, nobody had reported a single problem. However, last week two different problems were reported. This post is about the first one. The error was that on Windows XP, MZ-Tools 8.0 for VB/VBA failed to load. This dreaded error message:

MZTools8CouldNotBeLoaded

Fortunately, the user reported that installing .NET Framework 4.0 the problem was solved, but why?

The error message is hiding the actual error code and description (Microsoft started to show that crucial piece of information when an add-in fails to load in Visual Studio .NET 2002). I was able to reproduce the problem so I didn’t need to contact the user for further information. An approach to know why an add-in is failing to load on any IDE is to use Process Monitor to capture disk and registry activity and then guess what is failing (some missing registry entry or file, etc.). Unfortunately the current version of Process Monitor doesn’t support Windows XP, and although I couldn’t find an earlier version to download, I found the Registry Monitor and File Monitor utilities (that later became unified in Process Monitor) and they worked on Windows XP.  However, I couldn’t guess the problem. I always forget that the easiest way to know the actual error is to create a VBScript with this code that creates a COM object with the ProgId of the add-in (typically “MyAddIn.Connect”):

Dim o
Set o = CreateObject("MZTools8VBA")

And executing that script shows the actual problem:

ProcedureEntryNotFound

ProcedureEntryNotFoundScript

So, the problem was that the CLRCreateInstance function (used by the COM Shim to load the Common Language Runtime, CLR) was not found in mscoree.dll. At this point I knew I needed another tool: depends.exe (Dependency Walker).

The CLRCreateInstance function was introduced in .NET Framework 4.0 to load the CLR 4.0. The CLR 2.0 used by .NET Frameworks 2.0, 3.0 and 3.5 is loaded with a different function. My COM Shim has this logic:

  • First it detects if .NET Framework 2.0 is installed. If so, it uses the old API to load CLR 2.0.
  • If not, it detects if .NET Framework 4.0 is installed. If so, it uses the new CLRCreateInstance API to load CLR 4.0.

Note: this logic that uses run-time detection of the CLR rather than setup-time detection is flawed by a different reason that caused the second reported problem (for another post).

Since my setup requires .NET Framework 2.0 on Windows XP, I knew that the CLRCreateInstance function was not called on Windows XP. However, since my COM Shim dll linked it at compile-time:

COMShimOld

the whole dll of the COM Shim failed to load if that function is missing in mscoree.dll (it seems that my knowledge of C/C++ linking is very limited, I thought I would get a runtime error only if the COM Shim tried to call that missing function…).

This explained why installing .NET Framework 4.0 solved the problem: even if the COM Shim still used CLR 2.0 (by the logic above), the mscoree.dll updated by .NET Framework 4.0 provided the missing CLRCreateInstance function and the COM Shim dll didn’t fail to load.

But a question remained that got me puzzled for several days: nobody had reported this problem on Windows 7, that lacks .NET Framework 4.0 (it installs only .NET Framework 3.5 / CLR 2.0), in these three months. And since my setup doesn’t require .NET Framework 4.0 on Windows 7, I was sure that not all users have .NET Framework 4.0 installed. I should have received tons of bug reports with the same problem. But not a single one… The first thing I did was to check the version of mscoree.dll of my virtual machines with Windows 7. First surprise: they showed 4.0.xxxx, although .NET Framework 4.0 was not installed:

mscoreeWindows7WithSP1

Then I used depends.exe to check that the mscoree.dll of Windows 7 certainly exports the CLRCreateInstance function:

mscoreeExportedFunctionsWindows7WithSP1

So, how is that Windows 7, lacking .NET Framework 4.0, has a mscoree.dll version 4.0.xxxx that provides the CLRCreateInstance method to load the CLR 4.0?

At this point I decided to start from scratch: I created a virtual machine with Windows 7 RTM, and suprise, surprise, mscoree.dll was version 2.0.xxxx:

mscoreeWindows7WithoutSP1

and certainly lacked the CLRCreateInstance method:

mscoreeExportedFunctionsWindows7WithoutSP1

And of course, installing VB6 or Office and MZ-Tools 8.0 reproduced the problem that happened on Windows XP.

It was only after I applied SP1 to Windows 7, that mscoree.dll was updated to version 4.0.xxxx. So, it seems that Microsoft decided to include the mscoree.dll version 4.0.xxxx of .NET Framework 4.0 in the service pack 1 of Windows 7, even if that service pack doesn’t include .NET Framework 4.0.

Of course the best solution is to use dynamic linking at run-time using GetProcAddress so that the COM Shim can be loaded even if the CLRCreateInstance function is missing:

COMShimNew

For the record, Windows 10 uses version 10.0.xxxx of mscoree.dll:

mscoreeWindows10

Case closed.

Email this to someoneTweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+

HOWTO: Knowing the folder where Visual Studio is loading its own dlls from

If you are extending the Team Explorer toolwindow of Visual Studio, you will know it is particularly tricky, because it is not well documented, and you require some interfaces that are defined in dlls used by the Team Explorer. For example, I wrote a series of articles last year such as:

In all of them I had to specify the Visual Studio assemblies and location, such as:

  • Microsoft.TeamFoundation.Controls.dll (in folder C:\Program Files (x86)\Microsoft Visual Studio <version>\Common7\IDE\ReferenceAssemblies\v4.5\)
  • Microsoft.TeamFoundation.Git.Controls.dll (in folder C:\Program Files (x86)\Microsoft Visual Studio <version>\Common7\IDE\ReferenceAssemblies\v4.5\)

It happens that the name of the assembly and/or the folder can change from one version of Visual Studio to the next (and maybe from one update to another in the same Visual Studio version). Even the interfaces can change, but that’s another story.

To know the assemblies and folders that a Visual Studio process (devenv.exe) is using, you can use the invaluable Process Explorer tool. Here you can see it running two instances of devenv.exe (one for Visual Studio 2013 and another for VS 2015):

ProcessExplorer

You can configure it to show the dlls loaded by the selected process, clicking the View > Show Lower Pane and the View > Lower Pane View > DLLs:

ProcessExplorerViewDlls

With this tool you can find that Visual Studio 2013 loads Microsoft.VisualStudio.TeamFoundation.WorkItemTracking.dll from the GAC (C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.VisualStudio.TeamFoundation.WorkItemTracking\v4.0_12.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.TeamFoundation.WorkItemTracking.dll):

WorkItemTrackingDllVS2013

While Visual Studio 2015 loads it (surprise, surprise) from the folder that contains its Team Explorer extension (C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\<randomname>\Microsoft.VisualStudio.TeamFoundation.WorkItemTracking.dll):

WorkItemTrackingDllVS2015

Finally, Process Explorer provides also a Search dialog to find all the processes that have loaded a dll with some name:

ProcessExplorerSearch

Email this to someoneTweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+

New Visual Studio “15” Preview 3 Feature: Roaming Extension Manager

Just in case you missed it, the Preview 3 of Visual Studio “15” introduces the Roaming Extension Manager, that allows you “to keep track of all your favorite extensions across all of your development environments. Roaming your extensions keeps track of the extensions you have installed by creating a synchronized list in the cloud. “:

RoamingExtensionManager

You have some details and explanation of behavior/icons in the Release Notes.

Email this to someoneTweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+

dotnetConf 2016 Session: The Power of Roslyn: Improving Your Productivity with Live Code Analyzers

Dustin Campbell gave yet another session about the .NET Compiler Platform (“Roslyn”), this time at the dotnetConf 2016 and about live code analyzers:

“Two years ago we open sourced the C# and Visual Basic compilers and exposed their functionality through APIs as the Roslyn project. The Roslyn analyzer API empowers the developer community to be more productive by lighting up their own features in the editor in real-time—meaning anyone can write a refactoring, code fix, code style rule, or code diagnostic. Come learn more about the Roslyn project and what these live analyzers mean for you and your productivity.”

I have added this  session to the Videos section of this website, but here you have the link:

Email this to someoneTweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+

Component Diagnostics extension for VSX developers

There are tons of extensions on the Visual Studio Gallery, but most of them are for general developers using Visual Studio, or for some specific development technology (Web, etc.). However, there are some extensions that are specific to Visual Studio Extensibility (VSX) developers. The most known is the Extensibility Tools extension by Mads Kristensen, but there are also little gems from other Microsoft members.

I found a reference to this one by Paul Harrington that I didn’t know in a thread of the MSDN VSX forum:

Component Diagnostics
“Provides real-time diagnostics output of various sub-systems within Visual Studio. Access the toolwindow from the Help menu.”

ComponentDiagnostics

It supports VS 2012, 2013 and 2015. Despite the name, it’s an extension to diagnose Visual Studio subsystems such as:

  • Document Tab Well
  • File Change Service
  • Main Frame Data Model
  • Navigation History
  • OLE Component Manager
  • Package Manager
  • Running Document Table
  • Scrollbar Theming
  • Selection and UIContext
  • Solution
  • Task Scheduler Service

If your extension interacts with one of those subsystems, you can find it very useful. Furthermore, it is extensible so your own extension can provide diagnostics on that tool using the IVsDiagnosticsProvider interface.

Email this to someoneTweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+

Category “Addins” still present on Add Command dialog of VS 2015

Although VS 2015 removed support for add-ins and the Tools > Add-In Manager of past versions was removed, there is still at least one place where its past existence surfaces:

Click the Tools > Customize… menu to show the Customize dialog and click the Commands tab:

CustomizeDialog

Then click the Add Command… button and you will see the Addins item in the Categories list:

AddCommandDialog

I have reported this minor bug on Microsoft Connect.

Email this to someoneTweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+

Achieving .NET CLR independence of .NET-based add-ins for the VBA editor of Office

Being quite a perfectionist, there was one issue in older versions of my MZ-Tools 8.0 add-in for VBA that was not right for me: it required .NET Framework 2.0, which is installed by default on Windows 7, but not on Windows 8, Windows 8.1 or Windows 10, it’s an optional feature:

NetFrameworkOptionalFeature

So, installing MZ-Tools 8.0 for VBA on Windows 8.0 or higher likely you would get this dialog:

NetFramework20Required

And then the user should download and install .NET Framework 2.0. Needless to say, this is mostly inconvenient and can hurt sales, so I thought it would be nice if MZ-Tools 8.0 for VBA could use whatever higher .NET Framework was installed on Windows (such as .NET Framework 4.0 on Windows 8 or higher).

Before digging on how this can be done, some basics: a .NET Framework is composed of a Common Language Runtime (CLR), which contains the virtual machine for .NET, with the JIT compiler, garbage collector, etc. and a set of libraries. So:

.NET Framework = CLR + libraries

There are three kinds of .NET Frameworks:

  1. .NET Frameworks that provide their own, new, CLR. This has been the case for:
    • .NET Framework 1.0 (provides CLR 1.0)
    • .NET Framework 1.1 (provides CLR 1.1)
    • .NET Framework 2.0 (provides CLR 2.0)
    • .NET Framework 4.0 (provides CLR 4.0)
  2. .NET Frameworks that don’t provide a new CLR, they just provide new libraries. This has been the case for:
    • .NET Framework 3.0 (uses CLR 2.0 of .NET Framework 2.0)
    • .NET Framework 3.5 (uses CLR 2.0 of .NET Framework 2.0)
  3. .NET Frameworks that provide an update of an existing CLR (backwards compatible). This has been the case for:
    • .NET Framework 4.5, 4.5.1, 4.5.2, 4.6, etc. (all of them update the CLR 4.0).

A native process (such as an Office app) must load a CLR in order to load a managed (.NET) add-in. CLRs 1.0, 1.1 and 2.0 cannot coexist in the same process, that is, once of those CLRs is loaded, no other CLR version can be loaded in the same process. However, CLR 4.0 can coexist with CLR 2.0 in the same process.

Note: from this point, I will talk only about CLR 2.0 / CLR 4.0 (.NET Framework 2.0 or higher).

So, there are two aspects that an add-in for VBA must consider:

  • The lowest .NET Framework that it requires, which depends on the libraries used by the add-in. MZ-Tools 8.0 uses WinForms (not WPF), so it can work on .NET Framework 2.0 (it doesn’t require libraries from .NET Framework 3.x or 4.x).
  • The lowest CLR that it requires, which depends on the lowest .NET Framework that it requires. Since MZ-Tools 8.0 requires .NET Framework 2.0, the lowest CLR is 2.0.

Now the question is: given a native process that must load a .NET-based add-in, which CLR version does it use?.

An .exe process can have an .exe.config file that specifies the supported/required runtimes of the .NET Framework. Visual Studio uses such .config files but Office apps don’t, so they don’t care about CLR versions (they can use CLR 2.0, CLR 4.0, or even both at the same time). By no means an add-in should install an .exe.config file to modify the behavior of an Office application regarding supported/required CLRs, because it can break other add-ins.

However, there are still two approaches that an add-in written against .NET 2.0 (or .NET 3.x, which also uses CLR 2.0) can use to work with CLR 2.0 on systems that have it installed (such as Windows 7) and with CLR 4.0 on systems that don’t have CLR 2.0 installed but have CLR 4.0 installed (such as Windows 8, Windows 8.1 or Windows 10):

The first approach is to make the setup to detect which CLR versions are installed on the system:

  • If CLR 2.0 is detected, register the .NET assembly for COM Interop setting the Runtimeversion value of the InprocServer32 key to v2.0.50727, as it would be the usual since it’s an assembly compiled against .NET 2.0.
  • If CLR 2.0 is not detected and CLR 4.0 is detected, set the Runtimeversion value to  v4.0.30319. This forces the assembly to use .NET 4.0 even if it was compiled against .NET 2.0:

ComInteropRegistration

Note: to register the assembly for COM Interop, the setup cannot use regasm.exe, not even the version of .NET 4.0 (C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe) because regasm.exe sets always the Runtimeversion value to the value of the CLR that the assembly was compiled against. Instead, the setup must create the registry entries for COM Interop directly (see the required entries here).

The second approach is to use a COM Shim, which I explained in Isolating .NET-based add-ins for the VBA editor with COM Shims. Since a COM Shim is a native C++ dll that loads the CLR to create a custom AppDomain and to load the assembly, it can decide which CLR version to load, depending on which CLRs are installed on the machine: if CLR 2.0 is installed, it loads it, and if CLR 2.0 is not installed but CLR 4.0 is installed, it loads CLR 4.0.

Email this to someoneTweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+

VS SDK, packages, add-ins, macros and more…