PRB: "Application Object Error ASP 0197 : 80004005" Error Message When .NET Components Perform the Agility Test for Application Scope Objects (822828)



The information in this article applies to:

  • Microsoft Active Server Pages
  • Microsoft Internet Information Services 5.0
  • Microsoft Internet Information Services version 5.1
  • Microsoft Internet Information Services version 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("00000003-0000-0000-c000-000000000046"),
    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("ole32.dll", 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("CoCreateFreeThreadedMarshaler failed"); 
                LogEvent(e.Message);
            }
        }

        public void LogEvent(string sMessage)
        {
            if(!EventLog.SourceExists("Class1"))
            {
                EventLog.CreateEventSource("Class1", "Application");
                //Console.WriteLine("CreatingEventSource");
            }
            
            EventLog myLog = new EventLog();
            myLog.Source = "FTM Test";
            myLog.WriteEntry(sMessage);
        }
        // IMarshal implementation
        public Guid GetUnmarshalClass([In] ref Guid iid,
            [MarshalAs(UnmanagedType.Interface)] Object pvInterface,
            int dwDestContext, IntPtr pvDestContext, int mshflags)
        {
            LogEvent("GetUnmarshalClass");
            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("GetMarshalSizeMax");
            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("MarshalInterface");
            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("UnmarshalInterface");
            IMarshal m = (IMarshal) m_pFTM;
            Object o =  m.UnmarshalInterface(pstm, ref iid);
            Marshal.ReleaseComObject(pstm);
            return o;
        }

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

        public void DisconnectObject(int dwReserved)
        {
            LogEvent("DisconnectObject");
            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.

MORE INFORMATION

Steps to Reproduce the Behavior

  1. Start Microsoft Visual Studio .NET.
  2. On the File menu, point to New, and then click Project.
  3. 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.
  4. In Solution Explorer, right-click the project, and then click Properties.
  5. 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.
  6. Press CTRL+ALT+B to build the solution.
  7. Paste the following code in Notepad:
    <%@ LANGUAGE = "VBScript" %>
    <%Set Application("Test") = CreateObject("Q822828.Class1")%>
  8. Save the file as Test.asp in the ..\Inetpub\wwwroot folder.
  9. 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 "Symptoms" section of this article.

REFERENCES

For more information about improving ASP application performance, visit the following Microsoft Developer Network (MSDN) Web site:

Modification Type:MajorLast Reviewed:5/10/2006
Keywords:kbprb kbCOMInterop KB822828 kbAudDeveloper