4
votes

I'm trying to add code to the application_start event of a 3rd party ASP.NET web application for which I don't have the source code. I'm doing this by inheriting from the vendor's Global class. Here's the code (note "new" keyword and the two calls to the base class versions of the events. This is because the base class methods aren't abstract, virtual or overrides and I don't have the source code to change that):

public class CustomGlobal : VendorNamespace.Global
{
    new protected void Application_Start(object sender, EventArgs e)
    {
        var logName = "SPPCustom";
        if (!System.Diagnostics.EventLog.SourceExists(logName))
        System.Diagnostics.EventLog.CreateEventSource(logName, "Application");
        var text = "Hello from Application Start!";
        System.Diagnostics.EventLog.WriteEntry(logName, "Application started!");
        File.WriteAllText(@"c:\ApplicationStart.txt", text);
        Debug.WriteLine(text);
        base.Application_Start(sender, e);
    }
    new protected void Session_Start(object sender, EventArgs e)
    {
        var logName = "SPPCustom";
        if (!System.Diagnostics.EventLog.SourceExists(logName))
            System.Diagnostics.EventLog.CreateEventSource(logName, "Application");
        System.Diagnostics.EventLog.WriteEntry(logName, "Session started!");
        base.Session_Start(sender, e);
    }
}

I'm referencing my code in the global.asax file for the site like so:

<%@ Application Codebehind="Global.asax.cs" Inherits="MyNamespace.CustomGlobal" Language="C#" %>

The Application_Start code doesn't execute but the Session_Start code does. I can write events and write out text files from Session_Start but Application_Start, nadda.

Does anyone know what's going on here?

Edit:

Here's the code now after implementing Jan's suggestion: public class CustomGlobal : HttpApplication { private readonly Global _global; private readonly MethodInfo _appStartInfo; private readonly MethodInfo _sessionStartInfo;

    public CustomGlobal()
    {
        _global = new Global();
        _appStartInfo = typeof(Global).GetMethod("Application_Start", BindingFlags.Instance | BindingFlags.NonPublic);
        _sessionStartInfo = typeof(Global).GetMethod("Session_Start", BindingFlags.Instance | BindingFlags.NonPublic);
    }

    protected void Application_Start(object sender, EventArgs e)
    {

        var logName = "SPPCustom";
        if (!System.Diagnostics.EventLog.SourceExists(logName))
            System.Diagnostics.EventLog.CreateEventSource(logName, "Application");
        var text = "Hello Patient Portal from Application Start!";
        System.Diagnostics.EventLog.WriteEntry(logName, "Application started!");
        File.WriteAllText(@"c:\PatientPortalApplicationStart.txt", text);
        Debug.WriteLine(text);
        //_sxaGlobal.ApplicationStart(sender, e);
        _appStartInfo.Invoke(_global, new[] {sender, e});
    }
}

Now it throws the following error:

[NullReferenceException: Object reference not set to an instance of an object.]

Global.Application_Start(Object sender, EventArgs e) +28

[TargetInvocationException: Exception has been thrown by the target of an invocation.] System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner) +0 System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner) +72 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) +251 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +28 System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) +19 CustomGlobal.Application_Start(Object sender, EventArgs e) +231

[HttpException (0x80004005): Exception has been thrown by the target of an invocation.] System.Web.HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(HttpContext context, HttpApplication app) +9239341 System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +131 System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +194 System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +339 System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +253

[HttpException (0x80004005): Exception has been thrown by the target of an invocation.] System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +9157968 System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +97 System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +256

1
And one more thing, I am restarting the web site in IIS, making a request for an Asp.net page before checking the event log to see whether Application_Start() executed.Sean
How are you determining that the Application_Start isn't running, if you just put a breakpoint in it and start debugging it will not be hit because the debugger attaches after the application has started.Ben Robinson
I'm determining it based on the fact that the message isn't written to the event log above. Also, the File.WriteAllText() call doesn't write the file. However, both of these function calls work inside of Session_Start. I have not tried debugging it.Sean

1 Answers

1
votes

It's inheritance problem. When new keyword is used then calling CustomGlobal.Application_Start() will execute your code, but calling ((HttpApplication) CustomGlobal).Application_Start() will execute vendor's code.

Solution could be pattern with private member

public class CustomGlobal : HttpApplication
{
    private readonly VendorGlobal _global;
    private readonly MethodInfo _appStartInfo;

    public CustomGlobal()
    {
        _global = new VendorGlobal();
        _appStartInfo = typeof(VendorGlobal).GetMethod("Application_Start", BindingFlags.Instance | BindingFlags.NonPublic);
    }

    protected void Application_Start(object sender, EventArgs e)
    {
        _appStartInfo.Invoke(_global, new[] {sender, e});
        // your custom code
    }