Microsoft KB Archive/822828

= PRB: &quot;Application Object Error ASP 0197 : 80004005&quot; Error Message When .NET Components Perform the Agility Test for Application Scope Objects =

Article ID: 822828

Article Last Modified on 12/3/2007

-

APPLIES TO


 * Microsoft Active Server Pages 4.0
 * Microsoft Internet Information Services 5.0
 * Microsoft Internet Information Services 5.1
 * Microsoft Internet Information Services 6.0

-



SYMPTOMS
By default, Microsoft .NET Components fail the agility test that Active Server Pages (ASP) performs for application scope objects. By default, the Component Object Model (COM) callable wrapper (CCW) object that is used for COM interoperability implements the IMarshal interface, and its implementation is similar to the free-threaded marshaler (FTM) implementation. However, because the unmarshal class that is returned by the IMarshal::GetUnmarshalClass method is not the class identifier (CLSID) of the FTM (its value is {3F281000-E95A-11d2-886B-00C04F869F04}), the agility test fails and you receive the following error message:

Application object error 'ASP 0197 : 80004005'

Disallowed object use /LM/W3SVC/1/Root/SRQ030513600956/global.asa, line 7

Cannot add object with apartment model behavior to the application intrinsic object.



CAUSE
ASP performs an agility test for COM objects that are assigned to the application scope objects in Microsoft Internet Information Server 5.x or later. The purpose of the test is to make sure that the component is efficient enough to perform well at application scope. The agility test looks for COM objects where the ThreadingModel registry entry is Both and then checks that the component uses the FTM for inter-apartment and intra-process marshaling. The test explicitly checks that the unmarshaling class that is returned by IMarshal::GetUnmarshalClass is equal to the CLSID (its value is {0000033A-0000-0000-C000-000000000046}) of the unmarshaling class of the FTM.



RESOLUTION
Microsoft .NET components can be used at application (and session) scope by explicitly implementing the IMarshal interface. Typically, this would be done by aggregating the FTM. For .NET components, you must use containment. Containment is also known as delegation. Modify the Class1 class as shown in the following code sample: using System; using System.Runtime.InteropServices; using System.Diagnostics;

namespace Q822828 {   [    ComImport, Guid(&quot;00000003-0000-0000-c000-000000000046&quot;), InterfaceType(ComInterfaceType.InterfaceIsIUnknown) ]   interface IMarshal {       Guid GetUnmarshalClass([In] ref Guid iid,            [MarshalAs(UnmanagedType.Interface)] Object pvInterface,            int dwDestContext, IntPtr pvDestContext, int mshflags); uint GetMarshalSizeMax([In] ref Guid iid,            [MarshalAs(UnmanagedType.Interface)] Object pvInterface,            int dwDestContext, IntPtr pvDestContext, int mshflags); void MarshalInterface(UCOMIStream pstm, [In] ref Guid iid,            [MarshalAs(UnmanagedType.Interface)] Object pvInterface,            int dwDestContext, IntPtr pvDestContext, int mshflags); [return:MarshalAs(UnmanagedType.Interface)] Object UnmarshalInterface(UCOMIStream pstm, [In] ref Guid iid); void ReleaseMarshalData(UCOMIStream pstm); void DisconnectObject(int dwReserved); }

public class Class1: IMarshal {       [DllImport(&quot;ole32.dll&quot;, PreserveSig=false)] static extern void CoCreateFreeThreadedMarshaler(           [MarshalAs(UnmanagedType.IUnknown)] object punkOuter,            [MarshalAs(UnmanagedType.IUnknown)] out object ppunkMarshaler);

public Class1 {           try {               CoCreateFreeThreadedMarshaler(null, out m_pFTM); }           catch(Exception e)            { LogEvent(&quot;CoCreateFreeThreadedMarshaler failed&quot;); LogEvent(e.Message); }       }

public void LogEvent(string sMessage) {           if(!EventLog.SourceExists(&quot;Class1&quot;)) {               EventLog.CreateEventSource(&quot;Class1&quot;, &quot;Application&quot;); //Console.WriteLine(&quot;CreatingEventSource&quot;); }           EventLog myLog = new EventLog; myLog.Source = &quot;FTM Test&quot;; myLog.WriteEntry(sMessage); }       // IMarshal implementation public Guid GetUnmarshalClass([In] ref Guid iid,           [MarshalAs(UnmanagedType.Interface)] Object pvInterface,            int dwDestContext, IntPtr pvDestContext, int mshflags) {           LogEvent(&quot;GetUnmarshalClass&quot;); IMarshal m = (IMarshal) m_pFTM; return m.GetUnmarshalClass(ref iid, pvInterface, dwDestContext, pvDestContext, mshflags); }

public uint GetMarshalSizeMax([In] ref Guid iid,            [MarshalAs(UnmanagedType.Interface)] Object pvInterface,            int dwDestContext, IntPtr pvDestContext, int mshflags) {           LogEvent(&quot;GetMarshalSizeMax&quot;); IMarshal m = (IMarshal) m_pFTM; return m.GetMarshalSizeMax(ref iid, pvInterface, dwDestContext, pvDestContext, mshflags); }

public void MarshalInterface(UCOMIStream pstm, [In] ref Guid iid,            [MarshalAs(UnmanagedType.Interface)] Object pvInterface,            int dwDestContext, IntPtr pvDestContext, int mshflags) {           LogEvent(&quot;MarshalInterface&quot;); IMarshal m = (IMarshal) m_pFTM; m.MarshalInterface(pstm, ref iid, pvInterface, dwDestContext, pvDestContext, mshflags); // Safety Net Marshal.ReleaseComObject(pstm); }

[return:MarshalAs(UnmanagedType.Interface)] public Object UnmarshalInterface(UCOMIStream pstm, [In] ref Guid iid) {           LogEvent(&quot;UnmarshalInterface&quot;); IMarshal m = (IMarshal) m_pFTM; Object o = m.UnmarshalInterface(pstm, ref iid); Marshal.ReleaseComObject(pstm); return o;       }

public void ReleaseMarshalData(UCOMIStream pstm) {           LogEvent(&quot;ReleaseMarshalData&quot;); IMarshal m = (IMarshal) m_pFTM; m.ReleaseMarshalData(pstm); Marshal.ReleaseComObject(pstm); }

public void DisconnectObject(int dwReserved) {           LogEvent(&quot;DisconnectObject&quot;); IMarshal m = (IMarshal) m_pFTM; m.DisconnectObject(dwReserved); }

object m_pFTM = null; } } Note
 * Maintenance is easier if you implement IMarshal in a common base class and then derive all the .NET components that are to be used at application scope from that class.
 * Standard .NET Security applies to this component. Make sure that both the process identity and the impersonated user have sufficient permissions to run the code.



STATUS
This behavior is by design.



Steps to Reproduce the Behavior
 Start Microsoft Visual Studio .NET. On the File menu, point to New, and then click Project. Click Visual C# Projects under Project Types, and then click Class Library under Template. In the Name text box, type Q822828, and then click OK. In Solution Explorer, right-click the project, and then click Properties. In the Property Pages dialog box, expand Configuration Properties, and then click Build. Set the Register for COM Interop property to True, and then click OK. Press CTRL+ALT+B to build the solution.</li>  Paste the following code in Notepad: <%@ LANGUAGE = &quot;VBScript&quot; %> <%Set Application(&quot;Test&quot;) = CreateObject(&quot;Q822828.Class1&quot;)%> </li> Save the file as Test.asp in the ..\Inetpub\wwwroot folder.</li> In Microsoft Internet Explorer, visit the Test.asp page (use http://localhost/test.asp). You may receive the error message that is described in the &quot;Symptoms&quot; section of this article.</li></ol>

<div class="references_section">