Category Archives: The strange case of…

The strange case of error 0x80070005 “Access Denied” using PEVerify.exe with an add-in referencing a Visual Studio assembly not in the GAC

It took me quite a lot of time to fix the problem of Error 0x80070002 “The system cannot find the file specified” using PEVerify.exe to verify add-in with referenced assemblies from Visual Studio not in the GAC. I found the problem yesterday Sunday afternoon when I was with my laptop at a Starbucks with no connection to Internet other than a very weak GPRS connection with my mobile phone, so I could only get on the web that the solution was to use the codeBase tag in a .config file. The file that I used was:

<?xml version="1.0" ?>
<configuration>
   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <dependentAssembly>
            <assemblyIdentity name="Microsoft.VisualStudio.Data.Interop" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
            <codeBase version="8.0.0.0" href="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE" />
         </dependentAssembly>
      </assemblyBinding>
   </runtime>
</configuration>

Using this file I got this other error:

0x80070005 “Access Denied”

rather than:

0x80070002 “The system cannot find the file specified”.

So today with more Internet connectivity I have been investigating the problem. The only reference to this problem was Debugging Assembly Loading Failures, where Suzzane Cook stated the obvious, to check file locks without shared-read access and ACLs. But this was not the problem.

The fusion log showed this:

LOG: GAC Lookup was unsuccessful.
LOG: Attempting download of new URL file:///C:/Program Files/Microsoft Visual Studio 8/Common7/IDE.
LOG: Assembly download was successful. Attempting setup of file: C:\Program Files\Microsoft Visual Studio 8\Common7\IDE
LOG: Entering run-from-source setup phase.
ERR: Error extracting manifest import from file (hr = 0x80070005).
ERR: Failed to complete setup of assembly (hr = 0x80070005). Probing terminated.

So I was clueless until I found the string “ERR: Error extracting manifest import from file” in a .rc file inside  www.koders.com, the site that has the source code of some open source portions of the .NET Framework CLR and tools. The string led me to the identifier ID_FUSLOG_MANIFEST_EXTRACT_FAILURE which in turn led me to the following code in the bindhelpers.cpp file:

HRESULT BindToSystem(IAssemblyName *pNameSystem,
   LPCWSTR pwzSystemDirectory,
   IUnknown *pUnk,
   IAssembly **ppAsmOut,
   IAssembly **ppNIAsmOut,
   IFusionBindLog **ppdbglog)
{
   HRESULT hr = S_OK;
   IAssembly *pAsm = NULL;
   CAssembly *pCAsm = NULL;
   CNativeImageAssembly *pAsmNI = NULL;
   DWORD dwSize = 0;
   IAssemblyManifestImport *pAsmImport = NULL;
   INativeImageEvaluate *pNIEva = NULL;
   WCHAR wzManifestFilePath[MAX_PATH];
   IAssemblyName *pName = NULL;
   static BOOL bCalled = FALSE;
   CDebugLog *pdbglog = NULL;
   BOOL bFoundInDevOverride = FALSE;

   MEMORY_REPORT_CONTEXT_SCOPE("FusionBindToSystem");

   if (!pNameSystem || !ppAsmOut || CAssemblyName::IsPartial(pNameSystem) || !CAssemblyName::IsStronglyNamed(pNameSystem) || !pwzSystemDirectory) {
      return E_INVALIDARG;
   }

   *ppAsmOut = NULL;

   // should only be called once.
   if (bCalled) {
      *ppAsmOut = g_pSystemAssembly;
      if (g_pSystemAssembly) {
         g_pSystemAssembly->AddRef();
         return S_OK;
      }
      else {
         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
      }
   }

   if (IsLoggingNeeded()) {
      CDebugLog::Create(NULL, pNameSystem, NULL, &pdbglog);
   }

   wzManifestFilePath[0] = L'';

   if (pUnk) {
      hr = pUnk->QueryInterface(IID_INativeImageEvaluate, (void **) &pNIEva);
      if (FAILED(hr)) {
         goto Exit;
      }
   }

   // temporary IAssemblyName for mscorlib
   g_pSystemAssemblyName = pNameSystem;
   g_pSystemAssemblyName->AddRef();

   // we are here because we cannot find the custom assembly,
   // or we are not asked for a custom assembly.

   if (!bFoundInDevOverride) {
      hr = CAssemblyName::CloneForBind(pNameSystem, &pName);
      if (FAILED(hr)) {
         goto Exit;
      }

      hr = CreateAssemblyFromCacheLookup(NULL, pName, ppAsmOut, NULL);
      if (FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
         goto Exit;
      }

      if (hr == S_OK) {
         DEBUGOUT(pdbglog, 1, ID_FUSLOG_CACHE_LOOKUP_SUCCESS);
      }
      else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)){
         DEBUGOUT(pdbglog, 1, ID_FUSLOG_ASSEMBLY_LOOKUP_FAILURE);
      }

      if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
         dwSize = MAX_PATH;
         hr = StringCchPrintf(wzManifestFilePath, MAX_PATH, L"%ws%ws.dll", pwzSystemDirectory, SYSTEM_ASSEMBLY_NAME);
         if (FAILED(hr)) {
            goto Exit;
         }

         DEBUGOUT1(pdbglog, 0, ID_FUSLOG_ATTEMPT_NEW_DOWNLOAD, wzManifestFilePath);
         hr = CreateAssemblyFromManifestFile(wzManifestFilePath, NULL, NULL, ppAsmOut);
         if (FAILED(hr)) {
            DEBUGOUT1(pdbglog, 1, ID_FUSLOG_MANIFEST_EXTRACT_FAILURE, hr);
            goto Exit;
         }
      }
   }

Exit:

   if (pdbglog) {
      pdbglog->SetResultCode(FUSION_BIND_LOG_CATEGORY_DEFAULT, hr);
      DUMPDEBUGLOG(pdbglog, g_dwLogLevel);
      DUMPDEBUGLOGNGEN(pdbglog, g_dwLogLevel);
      if (ppdbglog) {
         *ppdbglog = pdbglog;
         pdbglog->AddRef();
      }
   }

   SAFERELEASE(g_pSystemAssemblyName);

   if (SUCCEEDED(hr)) {
      pCAsm = static_cast (*ppAsmOut); // dynamic_cast
      pCAsm->SetIsSystemAssembly(TRUE);

      g_pSystemAssembly = *ppAsmOut;
      g_pSystemAssembly->AddRef();
      g_pSystemAssembly->GetAssemblyNameDef(&g_pSystemAssemblyName);
   }

   if (FAILED(hr)) {
      SAFERELEASE(*ppAsmOut);
   }

   SAFERELEASE(pdbglog);
   SAFERELEASE(pAsmImport);
   SAFERELEASE(pAsm);
   SAFERELEASE(pAsmNI);
   SAFERELEASE(pNIEva);
   SAFERELEASE(pName);

   bCalled = TRUE;
   return hr;
}

I have marked in bold the relevant code. It was not until I saw the name of the function CreateAssemblyFromManifestFile when I realized that I had to specify the full file name and not the folder of the assembly in the .config file. Once modified to the following it worked without errors:

<?xml version="1.0" ?>
<configuration>
   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <dependentAssembly>
            <assemblyIdentity name="Microsoft.VisualStudio.Data.Interop" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
            <codeBase version="8.0.0.0" href="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\Microsoft.VisualStudio.Data.Interop.dll" />
         </dependentAssembly>
      </assemblyBinding>
   </runtime>
</configuration>

The “access denied” error was misleading since it would be more useful to keep showing the “file not found” error.

Now, imagine the time that you could save diagnosing problems in extensions for Visual Studio if you could have access to the Visual Studio source code, even without being able to debug, just debugging with the brain as I did…

Those strange cases of error 8013XXXX…

From time to time I blog about strange cases of errors when loading add-ins for Visual Studio. At least three of them have been about errors with hexadecimal codes such as 8013XXXX:

The strange case of <Unknown Error> 8013150A loading a Visual Studio add-in
https://www.visualstudioextensibility.com/2009/03/24/the-strange-case-of-lt-unknown-error-gt-8013150a-loading-a-visual-studio-add-in

The strange case of <Unknown Error> 8013141A loading a Visual Studio add-in
https://www.visualstudioextensibility.com/2008/11/28/the-strange-case-of-lt-unknown-error-gt-8013141a-loading-a-visual-studio-add-in

The strange case of error 80131018 loading a Visual Studio add-in
https://www.visualstudioextensibility.com/2007/03/23/the-strange-case-of-error-80131018-loading-a-visual-studio-add-in

When those errors happen, the description that Visual Studio shows is “Unknown Error”. I’m sure that Visual Studio could do a better job getting the description of such errors, but meantime you have to Google them, which usually gets no much meaningful information. If you ever encounter such error that you don’t get much information about, here is what you can do:

1) Those errors are coded in the corerror.h file. You have an online copy of that file at the Koders web site www.koders.com: enter “file:corerror.h” (without quotes) as your search term to get the file.

2) You will notice that the file contains a lot of error definitions such as EMAKEHR(0x101A)

3) So, you really need to search for the last 4 hexadecimal digits of your error. For example for the error 8013141A you would get:

#define CORSEC_E_INVALID_STRONGNAME     EMAKEHR(0x141a)         // Strong name validation failed

and then you know that the error is related to an invalid strong name.

If you are curious, the EMAKEHR macro is defined as:

#define FACILITY_URT            0x13
#endif
#ifndef EMAKEHR
#define SMAKEHR(val)            MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_URT, val)
#define EMAKEHR(val)            MAKE_HRESULT(SEVERITY_ERROR, FACILITY_URT, val)

So you now know where the 0x13 digits (3th and 4th) of all those errors come from.

Hope this helps.

The strange case of Add Resource New Image menu empty when an add-in is loaded in Visual Studio

These days I have been playing a lot with resource images in managed satellite dlls and one thing that I was noticing sporadically was that the Add Resource New Image menu on the toolbar of the document window of a resource.resx file was empty, while it should have the typical “Bmp image”, “Png image”, etc. menu entries. Since I was playing with 32-bit bitmaps images with transparency in the alpha channel that cannot be created with Visual Studio, most of the time I was using a 3rd party bitmap editor and using the Add Resource Add Existing File… menu. But nonetheless, I was intrigued by the missing submenus in the New Image menu. Today that I finished my investigation of the managed satellite dlls, I decided to investigate the issue.

The first thing that I noticed was that the issue was not reported on the web, or maybe only mentioned once without a solution, which was strange because I could reproduce the problem on two computers on mine. I was clueless when I discovered by chance that the problem happened only when I had my MZ-Tools add-in loaded in Visual Studio on startup. After a few minutes of debugging I isolated the problem: it happened when the toolbar of the add-in was created, so I was able to create a minimal add-in to reproduce the problem and report it to Microsoft:

The Add ResourceNew Image menu empty when VS add-in creates a toolbar
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=470755

The problem happens with VS 2005 and 2008, but it is fixed in VS 2010 Beta 1, likely due to its new WPF-based commandbars.

The strange case of Visual Studio getting “out of memory”

I started programming in the old times of 16-bit MS-DOS, where you had only 640 KB of memory, and you had concepts like “expanded memory” and “extended memory” and memory bank switching to use more than 640 KB and then wonderful things like the Watcom C++ compiler that allowed you to create linear 32-bit MS-DOS programs to use all the available memory, not just 640 KB or 1 GB MB (oops!). When Windows (95) became a 32-bit OS and I learned at college about 32 bit pointers, it seemed that 4 GB of memory will be enough forever. Guess what? Some weeks ago I got “out of memory” errors from Visual Studio when trying some features of my MZ-Tools add-in that operate on every file of a solution. Specifically, the problem happened when iterating the code elements of the EnvDTE.ProjectItem.FileCodeModel of every file of the solution. My computer had 4 GB of RAM so I was surprised by this error (later I learned that the amount of RAM is irrelevant for most “out of memory” errors in modern virtual memory operating systems).

The first reference that I found on the web about this error was in the MSDN VSX forum:

ProjectItem.FileCodeModel causes memory fragmentation?
http://social.microsoft.com/Forums/en-US/vsx/thread/9d6c1d17-63c8-40c4-b6bc-e7f0ead65c3d

Notice that while MSFT people explained that this is due to COM objects in memory wrapped by .NET objects that are not released, the explanation does not solve the problem, neither the manipulation of the Garbage Collector (GC). I didn’t try using EnvDTE.Project.CodeModel because it didn’t meet my needs of “file by file” processing.

Then I found that mine was not the only extension for Visual Studio experiencing this problem. Resharper was also experiencing it and provided some tools to fix it. But it was non until I read this post when I learned about the cause and (kind of) solution for this problem:

Hacking Visual Studio to Use More Than 2Gigabytes of Memory
http://old.stevenharman.net/blog/archive/2008/04/29/hacking-visual-studio-to-use-more-than-2gigabytes-of-memory.aspx

So, the problem is not the lack of physical memory (RAM) or virtual memory, but the address space of 32-bit processes, which is limited to 4 GB and half of it is reserved for the OS (kernel mode) so the process has only 2 GB for it (user mode). And using the file code model of every file of a solution can cause Visual Studio to reach the limit of 2 GB of its user-mode address space. Using the /3GB option in the boot.ini file allows 3 GB of address space for the user mode part of the process and 1 GB for the kernel mode part, which can cause other problems because 1 GB is little these days for some video cards, etc.

Also, to take advantage of the 3 GB address space, a process must be “marked” to indicate the OS that it can use all those memory addresses. This is done with the LARGEADDRESSAWARE flag in the executable image that Visual Studio 2008 does have set, but Visual Studio 2005 doesn’t (although you can patch it with the editbin utility as explained in the link above).

In my case, using the /3GB option in boot.ini with VS 2008 “solved” the problem and using the Task Manager I saw devenv.exe passing the limit of 2 GB without causing the “OutOfMemory” exception. I say “solved” because this is not really a happy solution, it is not something that you can patch in your add-in, you need to show a message explaining the user to either use Windows 64-bit (which gives 32-bit processes the full 4 GB address space for user-mode) or to use the /3GB option with Windows 32-bit (that can cause other problems, in my case with two other applications).

So, 32-bit pointers and 4 GB are not enough these days… maybe with 64-bit pointers we’ll have these problems solved forever.

There is a bunch of very interesting resources on the web about all this, starting by the Raymond Chen’s series:

Summary of the recent spate of /3GB articles
http://blogs.msdn.com/oldnewthing/archive/2004/08/22/218527.aspx

and this one of today:

“Out Of Memory” Does Not Refer to Physical Memory
http://blogs.msdn.com/ericlippert/archive/2009/06/08/out-of-memory-does-not-refer-to-physical-memory.aspx

The strange case of Microsoft.mshtml assembly missing

I received the following bug report from a customer:

System.IO.FileNotFoundException: File or assembly name Microsoft.mshtml, or one of its dependencies, was not found.

My MZ-Tools add-in references this component. Actually it doesn’t use it  (it was for future HTML/ASP.NET support) but that doesn’t matter. AFAIK, that component was installed by every installation of Visual Studio, because it is used for the ASP.NET/HTML designers (which until VS 2008 were IE-based). How could be that the customer didn’t have it?

It turned out that the customer was using Visual Studio .NET 2003 and that he installed only Visual C++, neither VB.NET nor C#. It seems that Visual C++ 2003 doesn’t use that component, and as soon as he installed VB.NET, the component was installed…

BTW, it seems that each time that I think about dropping support for VS.NET 2003 and concentrating only in VS 2005, 2008 and 2010 with .NET 2.0, I receive an e-mail from someone that happens to use VS.NET 2003… definitely developers of commercial add-ins need to support old versions of Visual Studio for many years….

The strange case of 8013150A loading a Visual Studio add-in

Visual Studio add-ins are not very friendly when trying to auto-update them (at least as of Visual Studio 2008), so when deploying an add-in for internal use within an organization, one creative idea is to put the assembly dll in a shared network folder and register the add-in from there in each computer without copying it to the hard disks. You can do this putting the XML .AddIn file in that same folder and adding that folder to the list of locations that VS uses when searching for add-ins (Tools, Options menu, Environment, Add-in/Macros security). During the day you won’t be able to update the add-in assembly because it will be in use by the developers, but chances are that you will be able to do it at night (with some automated mechanism).

There is still a problem, though. As of .NET Framework 3.5 (without SP1) and below, by default assemblies in shared network folders lack FullTrust permission required to run add-ins, because shared folders belong to the LocalIntranet_Zone, which has a LocalIntranet permission set, not a FullTrust permission set. This was an incovenience not only for add-ins, but also for general .NET applications, and since configuring the .NET runtime security policy is not trivial, I guess that Microsoft got tired of support calls and complaints from developers and IT staffs and in SP1 of .NET Framework 3.5 they changed things to allow .NET assemblies to run with FullTrust permission from shared network folders (you can revert back this change if you want the old behavior).

Note: .NET Framework 3.5, 3.0 and 2.0 share the same CLR, so it’s CLR 2.0 what matters for security and add-ins, not the .NET Framework. 

So, the question I was wondering is: can then a .NET 2.0 add-in run from a shared network location without further security configuration changes? I did the test, and I got this nice COM error: 8013150A (not very helpful as usual). If I changed the permissions to FullTrust in that shared network location, it worked, so I have enhanced my article about troubleshooting with a mention to this error:

HOWTO: Troubleshooting Visual Studio and Office add-ins
http://www.mztools.com/articles/2007/MZ2007009.aspx

The only remaining thing to clarify is why the security policy change doesn’t work with add-ins. Well, it happens that this is by “design”, it doesn’t work with ClickOnce applications or applications that are designed to run on a host:

Running Intranet Applications in Full Trust
http://msdn.microsoft.com/en-us/library/cc713717.aspx

The strange case of ProjectItem.Open(EnvDTE.Constants.vsext_vk_Designer) trying to check-out a form file

Today I have solved a problem that was bugging me for months. I was aware of it but I hadn’t paid much attention to it (because I knew it was not a problem of my MZ-Tools add-in) until today, when I decided to take a closer look. The problem was the following: whenever I executed one of the features of MZ-Tools that reviews the designers of forms (such as the TabIndex review, the Access Keys review, etc.), of a solution under source control, the MDI form of the application became checked-out after the review and I had to undo the check-out by hand.

The line of code that caused the check-out was, of course:

ProjectItem.Open(EnvDTE.Constants.vsext_vk_Designer)

and I knew that the problem was not MZ-Tools because opening the designer by hand in the IDE resulted also in a silent check-out. I noticed too that if I disconnected the solution from source control, in this case Visual Studio prompted to edit the read-only file with a dialog with buttons “Edit In-memory”, “Make Writeable” and “Cancel”. So, Visual Studio was really feeling the need of edit a designer file when opening it. But what was causing it?

I have spent the whole morning prunning the project, the form and the code, with several oops-too-much-pruning-that-no-longer-reproduces-the-problem and finally I have a minimal reproducible pattern:

1) Create a new Windows Forms project with Form1.

2) Create a user control UserControl1 with this code:

Private Sub UserControl1_VisibleChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.VisibleChanged

   Me.Size = New Size(100, 100)

End Sub

3) Build the project so the usercontrol appears on the toolbox.

4) Add the usercontrol to the form and set its Dock property to Fill.

5) Build the project again

6) Close all windows

7) Close the solution

8) Using the Windows Explorer, set the read-only attribute of the Form1*.* files

9) Open the solution again

10) Try to open the Form1.vb file in Design view. You get a prompt to edit the read-only file.

I know it’s stupid to set the size of the usercontrol in the VisibleChanged event of a usercontrol that will be docked, but that was what the code was doing in the end …

So, the solutions are:

1) Stop doing stupid things or…

2) Change the code to the following, so it only executes at run-time:

Private Sub UserControl1_VisibleChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.VisibleChanged

   If Not Me.Site.DesignMode Then

      Me.Size = New Size(100, 100)

   End If

End Sub

Is there anything that an add-in can do when it encounters this situation? If VS shows a prompt, there is nothing that an add-in can do. But if VS does a silent check-out, or silently changes the read-only flag of files you can do something: get the read-only value before calling ProjectItem.Open(EnvDTE.Constants.vsext_vk_Designer), get it afterwards, compare them and it they are not the same you can inform the user that there is something wrong in the file or in the controls used in the file, and that the same problem would happen if he opens the designer by hand, so it’s not really add-in’s fault.

Update (March 11, 2009):

If the file is not read-only, then VS edits it behind the scenes and you get an unexpected prompt to save the changes when closing the solution.

Also, I have found other pattern that reproduces the problem without wrong code:

1) Create a Windows Form project in VS 2008

2) Add a ListBox control to Form1 and set its Dock property to Fill

3) Save the changes, close the windows, and close VS

4) Using Windows Explorer, set the read-only attributes of the three Form1*.* files

5) Open VS and load the solution

6) Try to open the Form1.vb file in Design view. You get a prompt to edit the read-only file.

The strange case of infinite recursion in an add-in severely crashing Visual Studio 2005 and 2008

Don’t you love when Visual Studio severely crashes? I really hate those moments because I am always afraid that if it is a problem of VS and not a problem in my code, I am in trouble…

Some time ago I explained The strange case of Visual Studio 2008 severely crashing loading an add-in that was caused by Visual Studio commandbars. Fortunately the cause of this problem and a workaround was known.

I also opened a bug report at Microsoft Connect due to Application of custom attribute crashes VS 2008 (VB) still with SP1 applied. This causes a crash in the VB 2008 compiler. While Microsoft closed the report as “not reproducible” I am glad that they have reopened it recently and they sent me some “dumping” tools to install on my computer and send them back more information to get a clue about the problem since it is not reproducible with a few lines of code. Hopefully this one can be fixed because it is preventing me to migrate to VS 2008.

Yesterday I got a new severe crash while debugging my MZ-Tools add-in. The crash was something like:

Problem signature
Problem Event Name: APPCRASH
Application Name: devenv.exe

Fault Module Name: kernel32.dll
Fault Module Version: 6.0.6001.18000
Fault Module Timestamp: 4791a76d
Exception Code: e053534f
Exception Offset: 000442eb

Searching the web I only found a similar issue but it didn’t explain the cause or fix. The line of code crashing in my code was just setting the value of a class field, which seemed quite innocent. On Windows Vista the dialog didn’t offer the chance of sending it to MS but today on a Windows XP machine the dialog allowed me to send the bug report. But more importantly, after more careful debugging I realized that my code was calling the method of a class that called another method, and then other, and then other, and 10 calls later it was calling again the original method, causing an circular dependency that eventually crashed Visual Studio. For example this code reproduces the problem:

public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
   switch (connectMode)
   {
      case ext_ConnectMode.ext_cm_Startup:
      case ext_ConnectMode.ext_cm_AfterStartup:
         f1();
         break;
   }
}
void f1()
{
   f2();
}
void f2()
{
   f1();
}

While the fault was in my code (I have already fixed it), I wondered if Visual Studio could show better information, such as an StackOverflowException or OutOfMemoryException or similar  (that would give me a hint about a circular dependency) rather than a crash in the kernel32.dll module. And guess what? I did a test and VS.NET 2003 shows a more friendly message with the StackOverflow exception indicating a too deep recursion. Somehow something was changed in VS 2005 and now Visual Studio behaves much worse crashing completely. So I have opened a bug report at Microsoft Connect (you are invited to vote to get it fixed):

Add-in causing stack overflow causes “Microsoft Visual Studio has encountered a problem and needs to close”
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=414301

It happens that I had seen this very same problem in my code a couple of years ago but I had totally forgotten that such crashes in VS are sometimes produced by circular dependencies of methods in the code. I hope this helps other people (and me) when searching on the web a crash in a VS add-in in
Exception Offset 000442eb of Kernel32.dll.

More on the The strange case of sporadic COMException 0x800A01A8 calling CommandBarButton.Delete from a Visual Studio add-in (solved)

Tired of the strange case of sporadic COMException 0x800A01A8 calling CommandBarButton.Delete from a Visual Studio add-in, but very annoying, I decided to take a closer look and solve it. First, I noticed that my MZ-Tools add-in only calls CommandBarButton.Delete for buttons that places on VS built-in toolbars. For buttons that places on its own toolbar, I don’t call CommandBarButton.Delete since I will destroy the toolbar anyway and that will remove its buttons (which is also faster). So, if your add-in doesn’t add buttons to built-in toolbars of VS chances are that you’ll never experience this COMException. Alas, my add-in adds buttons to VS built-in toolbars that must be removed. For these, I took the following approach:

  • Rather than calling CommandBarButton.Delete when the add-in is unloaded, and I remove the command calling Command.Delete (which also automatically deletes all CommandBarButtons created from that command) and I create it again.
  • As you may know, in general add-ins should not delete commands when unloaded because:
    • Command keyboard bindings are lost. To solve this, before deleting the command I get the command keyboard bindings (Command.Bindings) and I restore them when re-creating the command.
    • If the user has created a button from that command on some other toolbar of her choice (see “Addition of a button of the add-in to a toolbar not belonging to it” case in my article HOWTO: Handling buttons and toolbars user customizations from a Visual Studio add-in), that button will be lost when the add-in is unloaded. There is no solution to this case, but I can live with that. Hopefully my users don’t need to create buttons from my add-in on other toolbars (I strive for providing good button/toolbar customization inside the add-in).

The strange case of sporadic COMException 0x800A01A8 calling CommandBarButton.Delete from a Visual Studio add-in

I don’t have an answer to this strange case, but after 4+ years getting reports from users, I will write about it just in case someone can post a solution.

When using a temporary user interface (not a permanent one), add-ins typically add CommandBarButtons to commandbars using Command.AddControl when loaded. When the add-in is unloaded, it must remove its buttons calling CommandBarButton.Delete. You can see a sample in the following article of mine: HOWTO: Adding buttons, commandbars and toolbars to Visual Studio .NET from an add-in

The problem is that sometimes you get COMException 0x800A01A8 when calling CommandBarButton.Delete. There are some facts about this problem:

  • It happens very sporadically (I have received less than 20 user reports in all these years).
  • When I ask the users if the problem is reproducible, they always answer that no, that the add-in unloads fine next time.

So the occurrence is really really tiny and it is not a bug in the code of the add-in since it works fine almost always, but nonetheless I would like to erradicate it.

Searching the Web, it seems that this problem appears more commonly with add-ins for Office, but no forum seems to deal with Visual Studio add-ins or provides an answer.

A couple of years ago I posted in the MSDN Forum:

To Microsoft: Sporadic COM exceptions adding/removing commandbars and buttons from Visual Studio add-ins
http://social.microsoft.com/Forums/en-US/vsx/thread/8fff7d90-7867-4608-a8b6-ba9362d91411/

In that post I exposed three reproducible cases of COM Exceptions, but I mentioned that there were more COM exceptions. Reviewing the user reports in my e-mail system, I think that in fact there is only a non-reproducible COMException 0x800A01A8 and it happens always calling CommandBarButton.Delete.

Craig Skibo (former member of the Visual Studio extensibility team) answered to that post but since I posted 3 reproducible cases and 1 non-reproducible case the discussion was a bit messed and in the part that dealed with CommandBarButton.Delete he argued that buttons created with Command.AddControl shouldn’t be destroyed calling CommandBarButton.Delete, but IMHO he is wrong or his workarounds are not really workarounds.

So, at this point I don’t know it I should ignore that exception because I am not sure if the COM exception is caused because the button is not already there (it would be OK to ignore the exception) or if I would get a duplicated button the next time that the add-in is loaded (in this case I would like to notify the previous exception to know why I am getting a duplicated button).

Anyone is experiencing this problem? You can either post a comment or contact me by e-mail.

Finally, wouldn’t be nice to be able to debug the code of Microsoft.VisualStudio.CommandBars.dll when the exception is thrown? Or better yet, a managed IDE where the menus and toolbars are no longer based on Office COM-based commandbars?

Update (February 3, 2009). See More on the The strange case of sporadic COMException 0x800A01A8 calling CommandBarButton.Delete from a Visual Studio add-in (solved)