Skip to content

net

Detecting ASP.NET

I've been wondering for a while how you could reliably tell if you are currently running under ASP.NET. This is really only of interest to be because of the ThreadSingleton vs. Static Singleton issue. The best way I've found so far is:

bool isASP_NET = ( System.Web.HttpContext.Current == null )?false:true;

Kind of annoying, because you have to reference System.Web in your Project, which you wouldn't otherwise.

I looked through most of classes that come out of the System and mscorlib assemblies but couldn't find anything good and reliable (i.e. didn't want to use AppDomain and see if the config file was called web.config.. Sounds like an accident just waiting to happen.) Still, there's got to be a better way.

Building custom configuration section handlers

Been playing around with creating my own configuration section handlers. Call me crazy, but i just have a distaste for storing my hierarchical application configurations in some kind of artificially derived flat Key/Value pair scheme.

I see a lot of applications doing something like:

<AppSettings>
  <add key="MyApplication.SomeValue" value="foo" />
  <add key="MyApplication.DB.Host" value="localhost" />
  <add key="MyApplication.DB.Password" value="secret" />
</AppSettings>

when it would be so much cleaner to do:

<my_application>
  <some_value>foo</some_value>
  <db>
    <host>localhost</host>
    <password>secret</password>
  </db>
</my_application>

The answer to this is System.Configuration.IConfigurationSectionHandler and then you stuff the result in a singleton. A couple of pitfalls with this approach are:

Application vs. ASP.NET: The singleton needs to determine whether it's running as an application or under ASP.NET, so it can decide whether it needs to be a thread singleton or can just be a static variable. Easiest solution here is to allow a way to manually initialize the singleton with a flag. Then you can use Begin_Request in global.asax to tell it to use a thread singleton and default to a static variable otherwise

Allowing for config relocation like AppSettings: AppSettings lets you externalize your name/value pairs via . This can easily be handled in System.Configuration.IConfigurationSectionHandler except that you need to look at the path to see if it's relative and then you need to figure out how to get that. The best way to determine the directory that's the same between Console, WinForms and ASP.NET is using AppDomain.BaseDirectory and just appending the relative path to it.

CPAN for .NET

Maybe I just don't have my feelers in the .NET community like I did in the perl community. Actually, know I don't... Anyway, I think a CPAN for .NET would be a fantastic community to build. http://csharp-source.net/ looks like a nice site, but it's project oriented. And otherwise, I haven't really found any good aggregators for C#. Sure, there are tons of components, but you gotta google each one.

I'm thinking of a site that's more tightly focused on components. Maybe have a common prefix namespace, then authors would sign up for sub namespaces for their components. You'd have dev and release versions, where a release component projects would have to include nant, ndoc and nunit and have the componnent strongly named. Users could then report back to say whether they were able to build on their system, or even add automated buid/test farms.

Users could then have a certain expectation of what a component from this community archive is like. You'd also have place to start looking for new components. Any time i started something in perl, i first hit search.cpan.org first to see if there was something that already did what i needed or provided stepping stones. And i wouldn't get back a list of projects that build some of the pieces i want but tightly integrate them into a project that otherwise doesn't meet my needs.

It would also be a place that keeps you up to date with the development, instead of having to navigate each developers site, hoping it goes you some piece of information on whether the project is alive. You would have RSS feeds, for project status, etc.

So if I just happen upon some free time... Hold on, gotta catch my breath... that one was funny... erm... Anyway, I'd love to build this, but i just don't know when. Maybe i should float the idea on one of the mono dev lists?

Managed Direct3D and World Transformations

I spent some time this weekend playing with Managed DirectX. And I finally figured out a "bug" I've been having whenever i tried to do Direct3D transformations:

Every time I've tried to animate objects, they all seemed to end up moving in lock step. It's got to do with the fact that everything gets moved through world transformations. And every example i found would always go "ok, we'll create an object, and then you do this to move it". Wow, so simple. None of the examples ever did "here are two objects and they move independently". The next step always was "and here is a full fledged game, enjoy going through the undocumented code".

Well, so what happens if you just duplicate the code for the second object and try to move it with a different transformation? Basically, both objects are in lock step. Why? Not entirely sure, of the exact mechanics, but basically the way to fix is to to transform the world back to the origin as the last step. I still don't really understand the mechanics of it all, but that fixes it. So, until i find a better explanation of what happens with all the transformations at the render stage, I'm just happy to go with this.

Realistically, if I was going to go for any serious 3D programming, I'd skip all this base code I'm playing with and just code against Axiom. No point re-inventing the wheel, at least for the level of stuff I'm ever likely to get into.

Heisenberg and Properties

There are two reasons that I'm using log4net with increasing, rather than decreasing frequency, despite the excellent debugger in VS.NET:

  1. When doing multithreaded programming, it lets me more easily see what threads are doing in parallel
  2. Heisenberg's Uncertainty Principle as it it relates to Properties

Let me expand on 2)... The Uncertainly Principle, simply put, states that the act of observation changes the observed. Now, he was talking about electrons, but when using the debugger in VS.NET this very principle can really come to bite you. The problem is that Properties do not have to be mere member variable accessors. They can have all sorts of logic attached. You may consider it bad form to have the act of observation (read: the getter) enact some sort of write behavior, and that is a position worth arguing. But even a simple "lazy load" put inside a getter can alter the behavior of the object if executed outside the normal flow.

And that's precisely what happens when you start stepping into the Debugger. Unlike Methods, Properties are shown by default and therefore cause execution of the getter. So if you have a problem with some internal value not being properly set when you run your program, but it being set, if you step through it, look at your Properties.

Now, how does log4net help this? It's still going to cause the getter to fire! Well, in some circumstances yes, but in many others, the reason you are getting this behavior is because the debugger fires all getters, not just the ones you want to look at, likely causing the problem via some part of code you really don't want to be firing off at the time of observation.

Partial Exposure

Quite by accident, I just came across a useful syntax feature of using internal classes and interfaces: Partially exposing an internal class outside the containing assembly.

The basic pattern is this:

  • Create your partially exposed class as internal
  • Create an interface for the pieces of the class you want publicly visible and have the class implement the interface
  • Create a public factory that returns your internal class cast to the interface.

I thought this would cause a compilation or at least runtime error, since your factory returns an internal class. But it doesn't. It returns an object implementing the public interface. Since the underlying class is internal to its assembly, it can never be cast back to its originating type. The public members not exposed by the interface become virtually private. The nice thing is, that they are still public to classes that reside in the same assembly.

What is still funky is that you can take that object and call GetType and it will tell you the name of the internal type. You just can access that type.

Playing with this, i realized that the debugger can see all this quite nicely. It was then that i realized, that really, nothing is ever functionally internal or private. It's all just semantics. With Reflection, you can poke around any object as much as you want.

Invoke vs. BeginInvoke

Been using the DebugLog code a bunch but kept having the occasional lock-up at shutdown. So i went back to the whole Invoke business. Problem is that while Invoke does make sure our update code runs on the proper thread, it is also synchronous, which means that if you are asking the DebugLog worker to Join and it is just then calling Invoke, you've got yourself a deadlock. The thread asking for the Join is now blocked, but it's also the thread responsible for the handling the Invoke. Solution:_Should've used BeginInvoke, since it does the call _asynchronously, avoiding the the Deadlock issue.

As I was typing this, suddenly the room shook as if a truck had hit the house. Turns out there was a 5.6 Earthquake near Anza, CA, about 60 miles away.

New InSimLib w/ source

InSimLib is now at version 0.5b. I consider it feature complete and have excersized it with a number of little Apps i've written, so I'm relatively confident it's ready for use. If someone comes across any problem, just drop me a line at insimlib at claassen.net.

Binary only Dll w/ Windows.Forms tester is here.

Source for the Lib is here

And NDocs are still here

Revised DebugLog window

Ran into problems with the debuglog window code i posted. Basically, since the log watcher thread is not the form's thread, you can run into deadlock. So i created a fully self-contained floating window, that properly manages the log writing using Invoke(). For more detail on the whole Form thread vs. other threads issue, check out this Code Project article.

Update 6/12/05: Fixed another deadlock issue with the Debug window. See this post for details. The linked source was updated.

Log4Net ScrollingTextBox

I use log4net for most of my apps. I did similar things in all the languages i've worked with. I just like having a way to verbosely spew information into the ether so that i could use for analysing code when something gest wonky without having to step through the code or go uncommenting loads of Console.Writeline() calls.

On unix, i just always have a terminal open for tailing the log files and while i often do the same under Windows, using a Cygwin bash shell, i've started putting debug windows into my Windows.Forms apps, just because it's nice and convenient. To make this work, you simply use the log4net MemoryAppender. For display purposed, I use a TextBox, but do some legwork to limit the size of the text logged and to make sure it stays scrolled at the bottom.

public class FormX : System.Windows.Forms.Form
{
    #region Static Members ####################################################
    private static readonly ILog log = LogManager.GetLogger(typeof(FormX));
    #endregion

    #region Members Variables #################################################
    private bool logWatching = true;
    private log4net.Appender.MemoryAppender logger;
    private Thread logWatcher;
    /// <summary>
    /// The TextBox for our logging messages
    /// </summary>
    private System.Windows.Forms.TextBox mLog;
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components = null;
    #endregion

    #region Constructors #################################################
    public FormX()
    {
        //
        // Required for Windows Form Designer support
        //
        InitializeComponent();

        this.Closing += new CancelEventHandler(FormX_Closing);
        logger = new log4net.Appender.MemoryAppender();

        // Could use a fancier Configurator if you don't want to catch every message
        log4net.Config.BasicConfigurator.Configure(logger);

        // Since there are no events to catch on logging, we dedicate
        // a thread to watching for logging events
        logWatcher = new Thread(new ThreadStart(LogWatcher));
        logWatcher.Start();
    }
    #endregion

    // [...]

    private void FormX_Closing(object sender, CancelEventArgs e)
    {
        // Gotta stop our logging thread
        logWatching = false;
        logWatcher.Join();
    }

    private void LogWatcher()
    {
        // we loop until the Form is closed
        while(logWatching)
        {
            LoggingEvent[] events = logger.Events;
            if( events != null && events.Length > 0 )
            {
                // if there are events, we clear them from the logger,
                // since we're done with them
                logger.Clear();
                foreach( LoggingEvent ev in events )
                {
                    StringBuilder builder;
                    // the line we want to log
                    string line = ev.LoggerName + ": " + ev.RenderedMessage+"\\r\\n";
                    // don't want to grow this log indefinetly, so limit to 100 lines
                    if( mLog.Lines.Length > 99 )
                    {
                        builder = new StringBuilder(mLog.Text);
                        // strip out a nice chunk from the beginning
                        builder.Remove(0,mLog.Text.IndexOf('\\r',3000)+2);
                        builder.Append(line);
                        mLog.Clear();
                        // using AppendText since that makes sure the TextBox stays
                        // scrolled at the bottom
                        mLog.AppendText(builder.ToString());
                    }
                    else
                    {
                        mLog.AppendText(line);
                    }
                }
            }
            // nap for a while, don't need the events on the millisecond.
            Thread.Sleep(500);
        }
    }
}