all groups > dotnet interop > september 2004 >
You're in the

dotnet interop

group:

"Class doesn't support Automation" error


"Class doesn't support Automation" error rob NO[at]SPAM roblocher.com
9/28/2004 12:00:35 PM
dotnet interop:
Greetings all,

I'm having a very challenging problem. We have a VB.NET middle-tier
assembly (.DLL) made up of several classes. The code needs to run
both periodically and on-demand, so we decided to use some very
simple SQL Server jobs which run VBScript code to call the assembly.
Other classes in the DLL are called by an ASP.NET application.

The assembly includes two relatively simple classes derived from the
same base class, which I will call Alpha and Bravo. There are two
SQL Server jobs, one for each class; each job calls its class'
constructor and then invokes the class' Run() method, like so:

Dim oAgent
Set oAgent = CreateObject("BrokenDLL.Alpha")
oAgent.TraceLevel = 4
oAgent.Run
Set oAgent = Nothing

The problem is that one job succeeds and one job fails, but ONLY ON
THE PRODUCTION SERVER; it works fine on my machine. The production
server is running Windows 2000 Advanced Server; I have Windows 2000
Professional.

I know that the code never reaches the Run() method in the failing
job from examining trace data.

In order to better figure out what was going on, I translated each
job's VBScript code into a JScript file to be launched by the Windows
Script Host. (JScript has better error handling than VBScript as I
recall.) One script ran successfully and one failed, as before.
This time I got an error message: "Class doesn't support Automation",
which apparently is VB/VBScript error 430.

I am surprised by the "Class doesn't support Automation" error
because the two classes are so similar; it makes no sense to me that
one class works and the other doesn't. As I mentioned, the two
classes are derived from the same base class, and in fact they both
share the same Run() method inherited from the base class. The
constructors are very similar:

Sub New()
Dim eDataType As utilities.DataType = DataType.Alpha
Dim oJob As New Job(eDataType, client.Job.JobType.FileQueuer)
MyBase.m_oJob = oJob
MyBase.m_User = _
New Authentication().GetSystemAccount(eDataType)
MyBase.m_Queue = New AlphaQueue(MyBase.m_User)
End Sub


Sub New()
Dim eDataType As utilities.DataType = DataType.Bravo
Dim oJob As New Job(eDataType, client.Job.JobType.FileQueuer)
MyBase.m_oJob = oJob
MyBase.m_User = _
New Authentication().GetSystemAccount(eDataType)
MyBase.m_Queue = New BravoQueue(MyBase.m_User)
End Sub

Originally there were no ClassInterface attributes declared for any
of the classes. After doing some reading on how the interop layer
works, I tried explicitly setting a ClassInterface attribute to
ClassInterfaceType.AutoDispatch, which is the default; that did not
fix the problem. I did "regasm /tlb" to generate a type library, and
then used the OLE View tool to look at the IDL, and the interface for
the interface class "Bravo" did have the "oleautomation" attribute.
Strange...

I have tried all the other remedies I could think of to resolve the
problem. I have of course unregistered and re-registered the DLL
with REGASM. I have rebooted the server. I have checked file and
SQL Server permissions, although the fact that one job succeeds
should indicate that that effort was a waste of time. I made sure
that the SQLSERVERAGENT service and the World Wide Web Publishing
Service were stopped when the DLL was installed.

It occurred to me early on that the registry entries for the COM
wrapper might be corrupt. The relevant registry keys follow. By the
way, there are other InprocServer32 keys pointing to older versions
of the same DLL which had been previously installed at the same
location; I understand that these registry keys aren't removed by
"regasm /unregister" by design, so that side-by-side deployment can
work. As I see it this shouldn't be a problem because the key with
the ProgID points to the key corresponding to the correct version,
and the COM object is instantiated by ProgID.

I have been wrestling with this problem for a day and a half, and I
don't know what else to check. Any help would be MUCH appreciated!

- Rob


--------------------------------

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\BrokenDLL.Bravo]
@="BrokenDLL.Bravo"

[HKEY_CLASSES_ROOT\BrokenDLL.Bravo\CLSID]
@="{E270E1DF-AC77-326D-9A92-5A3F33C773EE}"

--------------------------------

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\CLSID\{E270E1DF-AC77-326D-9A92-5A3F33C773EE}]
@="BrokenDLL.Bravo"

[HKEY_CLASSES_ROOT\CLSID\{E270E1DF-AC77-326D-9A92-5A3F33C773EE}\Implemented
Categories]

[HKEY_CLASSES_ROOT\CLSID\{E270E1DF-AC77-326D-9A92-5A3F33C773EE}\Implemented
Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}]

[HKEY_CLASSES_ROOT\CLSID\{E270E1DF-AC77-326D-9A92-5A3F33C773EE}\InprocServer32]
@="C:\\WINNT\\system32\\mscoree.dll"
"ThreadingModel"="Both"
"Class"="BrokenDLL.Bravo"
"Assembly"="BrokenDLL, Version=1.4.7.0, Culture=neutral,
PublicKeyToken=0a02e62e1ce3d396"
"RuntimeVersion"="v1.0.3705"
"CodeBase"="file:///C:/Inetpub/wwwroot/WebApp/bin/BrokenDLL.DLL"

[HKEY_CLASSES_ROOT\CLSID\{E270E1DF-AC77-326D-9A92-5A3F33C773EE}\ProgId]
@="BrokenDLL.Bravo"

RE: "Class doesn't support Automation" error yhhuang NO[at]SPAM online.microsoft.com
10/5/2004 2:15:44 AM
Hello Rob,

We have got your request. John and I are contacting our support team and
will have one support engineer look into this issue. The issue is
complicated and so we may need some more time to troubleshoot it. But
please rest assured that we will return here as soon as possible.

Thanks very much for your patience.

Best regards,
Yanhong Huang
Microsoft Community Support

Get Secure! ¨C www.microsoft.com/security
Register to Access MSDN Managed Newsgroups!
-http://support.microsoft.com/default.aspx?scid=/servicedesks/msdn/nospam.as
p&SD=msdn

This posting is provided "AS IS" with no warranties, and confers no rights.
RE: "Class doesn't support Automation" error Rob Locher
10/6/2004 10:47:03 AM
(Although my reported email address is different, I am the author of the
original post that started this thread.)

I believe I have solved the problem -- the developer who created the install
utility (not me) made it so RegAsm.exe was manually copied to the target
system. This copy of RegAsm.exe was used to register the assembly instead of
the version installed as part of the .NET framework. The registration was
being done in a batch file; I just assumed that RegAsm.exe was along the
path, and had no idea until today that it was being copied.

If I have learned anything from this experience, it is that it is possible
to be too close to a problem. I didn't find the problem until I had backed
away from it for a couple days.

I would like to offer my hearty thanks and congratulations to all the folks
from Microsoft that helped me diagnose this problem.

Sincerely,
- Rob




[quoted text, click to view]
RE: "Class doesn't support Automation" error Rob Locher
10/8/2004 12:39:06 PM
Alas, I was mistaken. My problem still exists. I am open to any and all
suggestions!

- Rob



[quoted text, click to view]
RE: "Class doesn't support Automation" error Rob Locher
10/15/2004 2:25:06 PM
Rob,

Thank you very much for the reply. I'm delighted to have another head
looking into this problem besides mine. Incidentally you can "speak C++ and
COM" as far as I'm concerned; my background is in C++ and I've taken a COM
class. Of course I am a bit rusty in COM because as soon as I learned the
basics I got a job doing .NET, and haven't used COM much since. But then
again, to keep busy I've been working on an out-of-process COM server written
in C++ that acts as a custom interop assembly. That's had its problems too,
but at least I've been learning something dealing with those difficulties.

So anyway, I have tested your theory very carefully. First I made a Regmon
log as you suggested. Then I searched the registry for all the HKCR\CLSID
entries that had the problem ProgId ("BrokenDLL.Bravo" in my example). For
each such entry, I copied the CLSID. Then I searched for each such entry in
the Regmon log. The only Regmon log entry I found was for the CLSID
corresponding to the current version of the assembly.

Next I unregistered the assembly using Regasm.exe /unregister. I then
searched the registry for the ProgId string, and deleted every key that
contained it. (The only keys that contained the ProgId string were the
HKCR\CLSID keys corresponding to previous versions of the assembly.) Then I
used Regasm.exe to reregister the assembly, and tried again. The very same
"Class doesn't support Automation" error was the result, unfortunately.

Do you have any other suggestions for me to try? In addition to continuing
work on my custom interop assembly, I'm thinking about making a test C++ app
that manually accesses the target assembly via the IDispatch interface of the
interop layer. Maybe that will provide some more information -- if not, it
will at least be a good learning exercise for me.

- Rob Locher




[quoted text, click to view]

RE: "Class doesn't support Automation" error robmaush NO[at]SPAM online.microsoft.com
10/15/2004 5:19:44 PM
Hi Rob,

I was looking into the problem description you posted. Thanks for
providing good details. That really helps with this kind of scenario.

The fact that this works in one environment (your dev box) and fails in
another (production) suggests to me that the problem is in the registration
of the component and not any problem with your code. In fact, I think you
already hit upon the likely cause:

[quoted text, click to view]

If you uninstall a component by unregistering it, and these registry
entries are left around, then COM still expects to be able to use them. In
your case, you mentioned that the previous versions of the DLL had been
installed in the same location. This provides conflicting information to
COM. To resolve this, I would suggest manually searching the registry for
each of these leftovers from previous builds and manually deleting them.
Of course, be carefull when working with the registry. As they say in all
those KB articles:

IMPORTANT: ... Before you modify the registry, make sure to back it up and
make sure that
you understand how to restore the registry if a problem occurs. For
information about how to
back up, restore, and edit the registry, click the following article number
to view the article in
the Microsoft Knowledge Base:

256986 Description of the Microsoft Windows registry
http://support.microsoft.com/?id=256986


If you wanted to investigate this further, you might see that COM is
picking one of those older CLSIDs or IIDs by examining a RegMon log. You
can get RegMon from www.sysinternals.com. Start a log, reproduce the
problem, then stop the log, and look for entries related to the broken
component. I think you'll find the cause will be an orphaned registry
entry.


Also, as you pointed out, COM Interop supports side-by-side versioning for
NET assemblies, but for COM to be able to find the correct DLL, we also
have to obey COM's registration requirements, and essentially, if COM gets
a ProgID ("BrokenDLL.Bravo") it then looks in the registry to find a CLSID,
and it will try to use the first one that it finds, but if that doesn't
point to a valid DLL (the correct interfaces are implemented, vtables
match, IIDs (Interface ID GUIDs) match, etc.) then you will get some kind
of error, depending upon the environment(error 430 or "Automation Error" in
scripting clients, is equivalent to E_NOINTERFACE in other languages).

Error Result : 0x80004002 ( -2147467262 )
ID Defined as : E_NOINTERFACE
Message Text : No such interface supported


Another thing you can do is take the COM versioning matter into your own
hands by using the System.Runtime.InteropServices.GuidAttribute to specify
a fixed GUID for your class, interface, and assembly. These become the COM
CLSID, IID, and Type Library LibID respectively. If you're using VB.NET,
the ComClassAttribute makes this even easier.

Once you do this, you as the developer are now responsible for ensuring
that v.next of your COM Interop Assembly is compatible at a binary level
with earlier versions, or clients will break.

I hope that gives you the information you need to resolve this problem.
Messing about in the registry can be tricky, so you may want to open a
troubleshooting case with PSS, especially if you want confirmation of the
root cause.

Cheers,
Rob

Rob Maushardt
Microsoft COM Team


This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
[quoted text, click to view]
<t1ycEFoqEHA.3468@cpmsftngxa06.phx.gbl>
<44555826-88C1-4835-B7D0-C1FE29304CE8@microsoft.com>
[quoted text, click to view]
rights.
RE: "Class doesn't support Automation" error robmaush NO[at]SPAM online.microsoft.com
10/29/2004 1:36:50 AM
Hi Rob,

I like your idea of taking COM Interop out of the picture momentarily by
testing directly from a small test C++ App. That would certainly let us
know on which side of the fence this issue falls.

It would also be useful to confirm that this fails on a different machine.
You may have already tested that, but if not, it's useful to know if this
is specific to the one machine where it fails, or if it fails on everything
but your dev box.

Another thing you can try is comparing a RegMon log from your working and
failing machines. There will be lots of differences (lots of background
noise from other applications running at the same time) but you can
experiment with the filtres in RegMon. It's kind of like the
needle-in-the-haystack approach, but if there's any difference in the
registry, you should find it by comparative analysis. (Make sure you
reboot and start RegMon before the first failure or success to ensure that
everything gets into the log on the first access).

Unfortunately, if those are not fruitful, the only other way I can think to
isolate this is to debug into the Automation/COM APIs and COM Interop calls.

If you are familiar with using the Debugging Tools for Windows from
http://www.microsoft.com/whdc/ddk/debugging/, you can attach the debugger
to the application and enter "sxe e0434f4d" to enable the CLR exception
debugging. Then "g" to tell the debugger to go. COM Interop should throw
a CLR Exception which would then be converted into the E_NOINTERFACE
HRESULT that gets returned to the COM side, which is then converted into
the "Error 430" or "Class doesn't support Automation" message by the
scripting language. Hopefully that will help discover what is missing in
the failing case. If you're not familiar with the Debugging Tools for
Windows, then I would recommend opening a PSS case to debug this further
because it can be very non-trivial :-)

I hope that helps.

Cheers,
Rob

Rob Maushardt
Microsoft COM Team


This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
[quoted text, click to view]
<t1ycEFoqEHA.3468@cpmsftngxa06.phx.gbl>
<44555826-88C1-4835-B7D0-C1FE29304CE8@microsoft.com>
<4805D3CE-34EE-4984-9C5E-E1976C67B9E6@microsoft.com>
<Q7JpustsEHA.3288@cpmsftngxa10.phx.gbl>
[quoted text, click to view]
AddThis Social Bookmark Button