"System.ComponentModel.Win32Exception: Class already exists
at System.Windows.Forms.WindowClass.RegisterClass()
at System.Windows.Forms.WindowClass.Create(String className, Int32
classStyle)
at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
at System.Windows.Forms.Control.CreateHandle()
at System.Windows.Forms.Form.CreateHandle()
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Form.ShowDialog(IWin32Window owner)
at System.Windows.Forms.Form.ShowDialog()
at WordTemplateProject1.OfficeCodeBehind.ThisDocument_New() in
D:\TestApps\WordTemplateProject1\ThisDocument.vb:line 132"
Has anyone experience this error or have suggestions on the casue?
Dialog being shown has two text boxes and two command buttons. No
processing is being performed in the form since it is just part of a demo.
TIA for any help or suggestions
The error is occuring because the Win32 version of RegisterClass is returning ERROR_CLASS_ALREADY_EXISTS. This error will occur when you try to
register a Window class and the class name string already exists in the atom table for that process. Since you are just showing a dialog, WinForms will be
creating native Win32 windows and wrapping them. Under normal circumstances, WinForms registers the window class once prior to creating the window
for the first time. Subsequent creations keep track of the fact that the window has been created and no attempt to register is made. On shutdown, WinForms
will unregister the class. This is the behavior for a stand-alone windows application--where the entry point for the application will call Application.Run.
Since you are getting this exception, my guess would be that WinForms isn't ever going through the normal Application shutdown sequencing. Probably
this is because you don't actually call Application.Run. This is crux of the problem with using WinForms in VSTO--without the notion of a main form, there are
definitely some issues that need to be worked around.
With respect to your particular problem, I'd need to know a bit more about what you were doing to give you a precise answer. However, here are some
thoughts. First, the solution might be as simple as keeping your dialog in global scope. As long as the dialog class exists, it will not try to re-register itself. It
may be the case that if you try to recreate an entirely new instance of the dialog that WinForms will try to register the same class name and will cause this
error. (I don't know whether this is the case, but it is worth consideration). Other possibilities might include showing the dialog by calling Application.Run, or
calling Application.Exit when the dialog goes away. I do not know for certain whether these are really the right approaches to take, but you may wish to
explore them. One consequence of Application.Run is that you will enter a modal message loop. If your form is modal, this is probably OK. Otherwise...
For certain you could accomplish the class unregistration yourself by getting the window handle (from the Handle property) and then calling the Win32
GetClassName (via PInvoke) to get the class name, followed by the Win32 UnRegisterClass function (again via PInvoke). You would need to be sure that
the Form instance was completely dead before you did this. This is probably the approach I would take if it were absolutely necessary. However, I would
really want to understand why I were hitting this problem prior to employing a workaround like this.
The other question in all of this is whether you are just seeing this exception in debug and what the impact is. It may be that this is actually handled & there
would be no problem with ignoring the exception. As sort of a side note, class registration is per process. It seems to me you can set VSTO up to not
launch a new application instance when you are debugging. If you have this setting turned on, it would certainly make you more vulnerable to this type of
problem.
If you can give us some more information on the circumstances where this error occurs, we may be able to give you a more precise answer. Your problem
isn't one that I've heard of before, so I would be interested to know what you might be doing differently. Hope this helps.
Sincerely,
Geoff Darst
Microsoft VSTO Tools
--
This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm
Note: For the benefit of the community-at-large, all responses to this message are best directed to the newsgroup/thread from which they originated.
--------------------
Hi Dave,
Yes, I see what is going on. The "problem" (and I use that word loosely as what you are doing is emminently reasonable) is that you are closing the
document. Since this will terminate the customization AppDomain, and since WinForms isn't getting cleaned up correctly, you end up with a situation where
the Window classes that got registered are orphaned. These classes will remain valid until you shut down Word.
The next document you load will create a new AppDomain with new WinForms instances that know nothing about the fact that there are already
"WinFormish" window classes registered. It turns out that the mechanism that WinForms uses to generate class names for Forms is not static. So
subsequent document / Form loads may result in class names being generated that are unique, but as soon as a non-unique name gets generated, the
exception gets thrown and you are dead in the water. It appears that problem is potentially going to bite anyone using WinForms in their VSTO application
who opens multiple documents (or closes and reopens the same document) in a single session of Office.
So unfortunately, the problem is as described in my previous post. At the moment I don't have a good answer for you. I just tested calling Application.Run
and Application.Exit, but there are problems associated with each approach. Yes, unregistering the window classes yourself would certainly be ugly, but it
would be a safe and effective way to get around this problem. As long as you can guarantee that the window is closed and won't be opened again, the
UnregisterClass call should be safe to call blindly. I agree that its not an ideal solution, but I believe it will enable your scenario if you need to get it working
quickly.
Meanwhile, I'm going to follow up with folks here to try to understand what can be done about this issue. I'll post back to this thread once I have some more
information on this. Dave, feel free to mail me directly if you have any more questions on this. Sorry I can't give you any better solutions at the moment.
It turns out my thinking regarding unregistering the class yourself won't work. WinForms registers at least one other hidden window with that class name, so
the UnregisterClass will fail as long as that window is open. I'll update this post once we understand the correct approach to this problem.
Sincerely,
Geoff Darst
Microsoft VSTO Support
C# example:
using System.Runtime.InteropServices;
internal class ExternDll
{
public const string User32 = "User32.dll";
public const string Kernel32 = "Kernel32.dll";
}
internal class SafeNativeMethods
{
[DllImport(ExternDll.Kernel32, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)]
public static extern int GetCurrentThreadId();
}
internal class UnsafeNativeMethods
{
[DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)]
public extern static bool EnumThreadWindows(int dwThreadId, NativeMethods.EnumThreadWindowsCallback lpfn, HandleRef lParam);
[DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)]
public static extern bool IsWindow(HandleRef hWnd);
}
internal class NativeMethods
{
public delegate bool EnumThreadWindowsCallback(IntPtr hWnd, IntPtr lParam);
public static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero);
}
internal class WinFormsHelper
{
public static void Cleanup()
{
UnsafeNativeMethods.EnumThreadWindows(SafeNativeMethods.GetCurrentThreadId(),
new NativeMethods.EnumThreadWindowsCallback(Callback),
NativeMethods.NullHandleRef);
}
private static bool Callback(IntPtr hWnd, IntPtr lparam)
{
Control c = Control.FromHandle(hWnd);
if (c != null)
{
c.Dispose();
}
return true;
}
}
protected void ThisDocument_Close()
{
// Ensure that Close is not called twice.
designModeClose = false;
WinFormsHelper.Cleanup();
}
VB example:
Imports System.Runtime.InteropServices
Public Class OfficeCodeBehind
Private Class SafeNativeMethods
Declare Auto Function GetCurrentThreadId Lib "Kernel32" () As Integer
End Class
Private Class UnsafeNativeMethods
Declare Auto Function EnumThreadWindows Lib "User32" (ByVal dwThreadId As Integer, ByVal lpfn As
NativeMethods.EnumThreadWindowsCallback, ByVal lParam As HandleRef) As Boolean
Declare Auto Function IsWindowHandle Lib "User32" (ByVal hWnd As HandleRef) As Boolean
End Class
Private Class NativeMethods
Public Delegate Function EnumThreadWindowsCallback(ByVal hwnd As IntPtr, ByVal lParam As IntPtr) As Boolean
Public Shared NullHandleRef As HandleRef = New HandleRef(New Object, IntPtr.Zero)
End Class
Private Class WinFormsHelper
Public Shared Sub Cleanup()
UnsafeNativeMethods.EnumThreadWindows(SafeNativeMethods.GetCurrentThreadId(), New NativeMethods.EnumThreadWindowsCallback
(AddressOf Callback), NativeMethods.NullHandleRef)
End Sub
Private Shared Function Callback(ByVal hWnd As IntPtr, ByVal lparam As IntPtr) As Boolean
Dim c As Control = Control.FromHandle(hWnd)
If Not (c Is Nothing) Then
c.Dispose()
End If
Callback = True
End Function
End Class
Private Sub ThisDocument_Close() Handles ThisDocument.Close
' Ensure that Close is not called twice.
designModeClose = False
WinFormsHelper.Cleanup()
End Sub