ILoggable

A place to keep my thoughts on programming

 Subscribe

geekblog
[at]
claassen [dot] net

Powered by Blogger

Friday, May 27, 2005

A single binary

I started to wonder last night whether you could statically link C# code so I'd get just a single EXE. In my quest I came across ilMerge. I guess it's not "statically linking", but it does let you combine multiple assemblies into a single dll or exe.

So now, you can distribute a single binary even though you may be using a bunch of DLLs to build it. Pretty neat.

Tuesday, May 10, 2005

Thread Singleton

There are a couple of classes in most projects that end up being singletons. The two most adept for this paradigm are Configuration and Database Handle Pooling. Both get used by many objects, both should only be instantiated once for efficiency and data integrity and we don't want to have to pass either of them around all the time. Voila, perfect candidate for singleton's.

When i started working with ASP.NET, however I ran into a problem with this, since ASP.NET persists over many requests and is multithreaded. For configuration that's user specific, that could be a problem. For transactional Database handles it's definitely a problem. Simplest way is to put it into the HttpContext.Current.Items collection. But now your code is tied to ASP.NET. You could write wrappers that test the environment and wrap the objects in singleton or HttpContext as appropriate, but that still leaves the singleton being shared across threads, should you write other multi-threaded apps.

I finally found a solution (sorry, i can't give credit because it's been a while and I can't find the original article) to this in the form of a Thread Singleton. This pattern uses the System.Runtime.Remoting.Messaging.CallContext to store our singleton. I've used it so many times now, that i figured i'd better write it down here so i don't have to dig through old code every time i want to re-use it:

using System;
using System.Runtime.Remoting.Messaging;

namespace Claassen.Util
{
    public class ThreadSingleton
    {
        /// <summary>
        /// Some unique string that identifies this class.. I just use the
        /// Namespace qualified name
        /// </summary>
        const string SINGLETON_ID = "Claassen.Util.ThreadSingleton";

        /// <summary>
        /// Accessor/Factory for the ThreadSingleton
        /// </summary>
        private static ThreadSingleton Current
        {
            get
            {
                // pull the object from the CallContext
                ThreadSingleton threadSingleton = (ThreadSingleton)CallContext.GetData(SINGLETON_ID);
                if( threadSingleton == null ) 
                {
                    // if there was nothing in the CallContext, create and add it
                    threadSingleton = new ThreadSingleton();
                    CallContext.SetData(SINGLETON_ID,threadSingleton);
                }
                return threadSingleton;
            }
        }

        // Class code would follow
    }
}

Wednesday, May 04, 2005

Mouse Clicks and LFS

Wrote a little program that launches LFS, clicks the appropriate buttons and sends strings to connect to a server defined by IP (it's called local in LFS for some reason), puts the player in the game and starts the game.

All works nicely, but man, is it ever an ugly hack. Hard-coding screen coordinates, putting in Thread.Sleep(n) so the UI can catch up, etc. But at least there is a way to automate the client.

Back to coding: Click this

Having run into a number of things I cannot accomplish with InSim in LFS, I've decided to venture down the ugly route of simulating the mouse. Not that horrible. InSim was never really designed for client automation.

I haven't tested this on LFS yet, just Calc.exe, but I finally found a way to do mouse clicks in another application from .NET.

The first route i went down was the SendMessage from user32.dll. Tried many different ways of getting the windowhandle and all that stuff, but no click ever materialized. Also found this method weird. Why do i have to tell it what window i'm clicking when i give it the cursor position. It's likely i just never figured out the proper syntax, but the docs i found certainly didn't aid me much.

Then i found mouse_event also from user32.dll. This one just sent a click event for the current location of the mouse. Just the way i would have expected. And it works!

Here's the sample code:

const int WM_LBUTTONDOWN = 0x0201;
const int WM_LBUTTONUP = 0x0202;
const int SC_MAXIMIZE = 0xF030;
const UInt32 MouseEventLeftDown = 0x0002;
const UInt32 MouseEventLeftUp = 0x0004;

[DllImport("user32.dll", EntryPoint="SendMessage", CharSet=CharSet.Auto)]
public static extern void SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

[DllImport("user32.dll")]
static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);

public static void Test()
{
    Process myProc = new Process();
    myProc.StartInfo.FileName = @"calc.exe";
    myProc.Start();

    // maximizing so we know where the button is (cheap ass hack for testing)
    SendMessage(myProc.MainWindowHandle,WM_SYSCOMMAND,SC_MAXIMIZE,0);

    Point p = new Point(150,150); // the 6 button
    Cursor.Position = p;
    Thread.Sleep(1000);

    if (myProc.WaitForInputIdle(3000))
    {
        // This one works !!!
        mouse_event(MouseEventLeftDown,0,0,0,new System.UIntPtr());
        mouse_event(MouseEventLeftUp,0,0,0,new System.UIntPtr());

        // This one doesn't do anything :(
        SendMessage(myProc.MainWindowHandle,WM_LBUTTONDOWN, Cursor.Position.X, Cursor.Position.Y);
        SendMessage(myProc.MainWindowHandle,WM_LBUTTONDOWN, Cursor.Position.X, Cursor.Position.Y);
    }
}

Tuesday, May 03, 2005

Track your Head

Ok, more geek, than programming.

Been spending a lot of time with LFS S2 lately. The new Alpha is out and with my tweaked setup for the GTT is finally drivable. I can even drift it a fair amount. Fantastic game!

Well, i took advantage of the discount that LFS offered on the TrackIR Pro. It looks like a camera, and it is one, but for the infrared spectrum. It tracks your heads movements and adjusts your POV in-game. It uses a progressive exageration of your head movement, so only a couple of degrees left of right and you are looking behind yourself.

Tried it out with LFS and the extra immersion is intense. It comes completely natural to you. You just move your head and you are looking in that direction and the exageration doesn't seem out of place. Makes driving a little funky, since you are no longer driving in the direction you are looking, necessarily. Physics feedback be nice, right about now :) Still, i think i'll adjust. Went for a lap with some AI opponents and being able to look left and right and see your opponents going into a turn was a tremendous help. This device kicks ass!