Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Class already exists

166 views
Skip to first unread message

dave

unread,
Jan 5, 2004, 11:44:09 AM1/5/04
to
I am using Visual Studio Tools for Office with Word 2003 on XP Pro.
In the code behing page, I open a dialog window for information. This
window opens and closes just fine but randomly I get an error paerforming
the form.showdialog whcih returns the following :

"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

Geoff Darst(MSFT)

unread,
Jan 7, 2004, 2:06:52 PM1/7/04
to
>From: "dave" <dheize...@hotmail.com>
>Newsgroups: microsoft.public.vsnet.vstools.office
>Subject: Class already exists
>Date: Mon, 5 Jan 2004 10:44:09 -0600
>Organization: Posted via Supernews, http://www.supernews.com
>Message-ID: <vvj59jg...@corp.supernews.com>
>X-Priority: 3
>X-MSMail-Priority: Normal
>X-Newsreader: Microsoft Outlook Express 6.00.2800.1158
>X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1165
>X-Complaints-To: ab...@supernews.com
>Lines: 27
>Path: cpmsftngxa07.phx.gbl!cpmsftngxa06.phx.gbl!TK2MSFTNGP08.phx.gbl!newsfeed00.sul.t-online.de!t-online.de!newsfeed.icl.net!newsfeed.fjserv.net!
proxad.net!news.tele.dk!news.tele.dk!small.news.tele.dk!news.maxwell.syr.edu!sn-xit-03!sn-xit-04!sn-xit-01!sn-post-01!supernews.com!corp.supernews.com!
not-for-mail
>Xref: cpmsftngxa07.phx.gbl microsoft.public.vsnet.vstools.office:1476
>X-Tomcat-NG: microsoft.public.vsnet.vstools.office
Hi Dave,

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.
--------------------


dave

unread,
Jan 7, 2004, 11:43:40 PM1/7/04
to
Geoff,
 
Thanks for the detailed response.  The error occurs when running in VS or just when running Word alone.  I tried moving the form reference to be global in the class.  It appeared to take longer before the error occurred, but it still occurred.  Coding to unregistered the class just sounds like a real hack to me and I would be hard pressed to defend using such a work-around even if I was inclined to do so.
 
I am doing a relatively simple thing.  Using the Word project template, I added a form to the project to display a modal dialog if a new document event occurred to select information I will eventually write a web service to provide (this is still a demo).  If the user cancels the dialog, I close the document and exit.  The only code in the dialog is in the event on the OK button which closes the form. 
 
A second form is used in the case of the user is opening the template itself.  When this occurs, the second form provides some configuration options.  This form always opens fine and it use does not seem to be related to the crash in the new.  Since this form is not modal and is left open during the edit of the template, it is closed when the document close event fires.  The close event saves the configuration options.
 
I have deleted the rest of the processing which handles bookmarks and parsing XML into a set of entity objects used in replacing the bookmarks to limit the possibility of the application code causing the issues.
 
The only pattern of note I can determine, is the error does not occur until I have cancelled the search dialog.  It may be the next time I open  New document, or it might be 10 times.  But this is not truly verifiable since it appears to be so random.
 
This template will be used repetitively by the user so it is very possible they will open a new document 100 times from this template before moving on to a different task.
 
Below are bits of code from the event handlers to demonstrate what is occurring.
 
I appreciate your taking time to look at this issue.  I am totally at a loss since it seems so simple.
 
Thanks,
Dave
 
' Called when a new document is created from the template.
' Not called when the document is opened subsequently.
Private Sub ThisDocument_New() Handles ThisDocument.New
    Dim searchForm As CorrSearch
    Try
        searchForm = New CorrSearch
        If searchForm.ShowDialog = DialogResult.Cancel Then
            searchForm.Dispose()
            searchForm = Nothing
            ThisDocument.Close()
            Exit Sub
        End If
        'retrieve properties of search form here and call webservice
        searchForm.Dispose()
        searchForm = Nothing
    Catch ex As Exception
        Debug.WriteLine(ex.ToString)
        Windows.Forms.MessageBox.Show(ex.Message, "Error loading new document.")
    End Try
End Sub
 
' Called when documents that were created from the template are opened,
' and when the template is opened for editing.
' Not called when a new document is created from the template.
Private Sub ThisDocument_Open() Handles ThisDocument.Open
    Dim xmldoc As New Xml.XmlDocument
    Try
        ' Check if the ThisDocument is a template and not a document.
        If ThisDocument.Name.EndsWith(".dot") Then
            m_MappingDialog = New BookmarkMapper
            m_MappingDialog.Show()
        End If
    Catch ex As Exception
        Debug.WriteLine(ex.ToString)
        Windows.Forms.MessageBox.Show(ex.Message, "Error loading document template.")
    End Try
End Sub
 
' Called when documents that were created from the template are closed,
' and when the template is closed.
Private Sub ThisDocument_Close() Handles ThisDocument.Close
    Dim newXmlMap As String
    Try
        If Not m_MappingDialog Is Nothing Then
            m_MappingDialog.Close()
            m_MappingDialog.Dispose()
            m_MappingDialog = Nothing
        End If
    Catch ex As Exception
        Debug.WriteLine(ex.ToString)
        Windows.Forms.MessageBox.Show(ex.Message, "Error saving document template.")
    Finally
        ' Ensure that Close is not called twice.
        designModeClose = False
    End Try
End Sub
 
 

Geoff Darst(MSFT)

unread,
Jan 8, 2004, 4:43:31 PM1/8/04
to
>From: "dave" <dheize...@hotmail.com>
>Newsgroups: microsoft.public.vsnet.vstools.office
>Subject: Re: Class already exists
>Date: Wed, 7 Jan 2004 22:43:40 -0600

>Organization: Posted via Supernews, http://www.supernews.com
>Message-ID: <vvpo6qr...@corp.supernews.com>
>References: <vvj59jg...@corp.supernews.com> <DbDFtFV1...@cpmsftngxa07.phx.gbl>
>MIME-Version: 1.0
>Content-Type: multipart/alternative;
> boundary="----=_NextPart_000_000F_01C3D56F.B5448A80"

>X-Priority: 3
>X-MSMail-Priority: Normal
>X-Newsreader: Microsoft Outlook Express 6.00.2800.1158
>X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1165
>X-Complaints-To: ab...@supernews.com
>Lines: 407
>Path: cpmsftngxa07.phx.gbl!cpmsftngxa06.phx.gbl!TK2MSFTNGP08.phx.gbl!newsfeed00.sul.t-online.de!t-online.de!news-spur1.maxwell.syr.edu!
news.maxwell.syr.edu!in.100proofnews.com!tdsnet-transit!newspeer.tds.net!sn-xit-02!sn-xit-01!sn-post-02!sn-post-01!supernews.com!corp.supernews.com!
not-for-mail
>Xref: cpmsftngxa07.phx.gbl microsoft.public.vsnet.vstools.office:1486
>X-Tomcat-NG: microsoft.public.vsnet.vstools.office

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.

Geoff Darst(MSFT)

unread,
Jan 9, 2004, 3:38:22 PM1/9/04
to
>X-Tomcat-ID: 733631488
>References: <vvj59jg...@corp.supernews.com> <DbDFtFV1...@cpmsftngxa07.phx.gbl> <vvpo6qr...@corp.supernews.com>
>MIME-Version: 1.0
>Content-Type: text/plain
>Content-Transfer-Encoding: 7bit
>From: geo...@microsoft.com (Geoff Darst(MSFT))
>Organization: Microsoft
>Date: Thu, 08 Jan 2004 21:43:31 GMT

>Subject: Re: Class already exists
>X-Tomcat-NG: microsoft.public.vsnet.vstools.office
>Message-ID: <5UAY2Bj1...@cpmsftngxa07.phx.gbl>
>Newsgroups: microsoft.public.vsnet.vstools.office
>Lines: 199
>Path: cpmsftngxa07.phx.gbl
>Xref: cpmsftngxa07.phx.gbl microsoft.public.vsnet.vstools.office:1489
>NNTP-Posting-Host: TOMCATIMPORT1 10.201.218.122

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

Geoff Darst(MSFT)

unread,
Feb 10, 2004, 6:02:09 PM2/10/04
to
For anyone encountering the "Class already exists" exception it is possible that the following code may resolve the problem. This code defines a class
called WinForms helper which exposes a single method called cleanup. Cleanup walks all of the windows on the thread and checks to see if they are
WinForms controls. If they are, the Dispose method is called on them. WinFormsHelper.Cleanup should be called in the This_Document_Close handler.
The remaining code is just PInvoke noise. By explicitly walking the thread and disposing any controls out there, this code should ensure that the
UnregisterClass call that WinForms will make later will actually succeed.

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

0 new messages