Command-Line Arguments and File Association via InstallForge

Posted in Code Tutorial by walkingTarget on June 26, 2012 1 Comment

If you’re going to start from scratch like I did, go ahead and create a new project for testing out command-line arguments.  File Association will use your program’s ability to parse command-line arguments, but other than that they’re really separate topics.

Now that you’ve got your project created, you need to add some code so that your program will read the command-line arguments passed to it. The following code snippet is something I wrote as an example for this tutorial. It’s very basic – it just iterates through all the command-line args and writes them to console, opens the file specified by the command-line args, and then dumps the files text contents to the console.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestAssociation
{
    class Program
    {
        static void Main(string[] args)
        {
            //Dump the command-line arguments to the console window
            Console.WriteLine("Command-Line Arguments");
            for (int i = 0; i < args.Length; i++)
                Console.WriteLine("{0}. {1}", i, args[i]);

            //Do a little redundant checking of expected command-line args
            if (args.Length >= 1 && !String.IsNullOrEmpty(args[0]))
            {
                //Output text from file sent by command-line args
                Console.WriteLine("Output text in {0}", args[0]);
                System.IO.StreamReader sr = new System.IO.StreamReader(args[0]);
                Console.WriteLine(sr.ReadToEnd());

                sr.Close();
                sr.Dispose();
            }
        }
    }
}

Be sure to save and build your project before continuing. If you try to run the program now, you won’t see anything special because aren’t any command-line args to parse yet.

Next, lets create an installer for our program. For this tutorial I used InstallForge. I’m not overly impressed with its capabilities, but this was a good chance to learn and experiment with another tool.

When you run InstallForge, you’re greeted by the first in a series of pages that allow configuration of your installer. Here’s a screenshot of the values I used for my little program.

Pretty basic and totally meaningless for the end goal of File Association, so enter whatever you want here.

The next page you want to use is the Files page under Setup, which is on InstallForge’s left toolbar.  Here, I just added my executable file, FileAssociation.exe from the bin\Release folder of my project.

You can also change the default installation path, if you so desire, but again, this choice is meaningless and won’t affect the following steps.

The next page to focus on is the Registry Page under the System group.  On the registry page, click the “Add…” button.  Here you’re prompted to enter the registry change you want to make.  Take a look at the settings I chose, and then I’ll explain them.

Since we’re creating a file association, the root key we want is HKEY_CLASSES_ROOT.  Our Sub key will follow the pattern {ext}\shell\open\command where {ext} is the extension you want to associate with your program (ex: .faf).  I used .faf in this tutorial because, at least on my computer, it was an extension that wasn’t already associated with a program.

Under Value data, fill in <InstallPath>\{ProgramName} “%1″.  This probably requires some explanation.  The <InstallPath> portion is quite literal.  <InstallPath> is a Setup Constant understood by InstallForge to be the path the user has chosen to install your program.  Since we can’t predict where they will install our program, we use the constant to ensure that our registry key matches their selection.  The “%1″ section is also meant to be taken literally.  “%1″ will be the command-line argument to our program.  Windows will help us out by automatically replacing the “%1″ with whichever associated file was opened.  Lastly, the  {ProgramName}.exe will be whatever you named your program.  I named mined TestAssociation.exe – take a look at the whole window below.

Click Ok to add your registry changes.  A screenshot of my changes follows…

Finally, click the build button (which I’ve highlighted in yellow) to build your installer.  Once the program is installed, find an associated file and open it to test your association.

I hope I haven’t been to brief in this tutorial – I may attach the project and source code at a later time.

Good luck and happy coding!

ProLiant N40L and NAS Upgrade

Posted in Uncategorized by walkingTarget on March 1, 2012 No Comments yet

My server was a piece of crap.  It had a large CPU cooler and an overpowered video card.  Its two optical drives spun up, flashed their pretty lights, but couldn’t read a disc.  Basically, it was old, over-sized, and noisy.  And it had to go.

My requirements for a replacement were pretty simple.  It had to be quiet and relatively small.  After a year with my old server, all of its other disadvantages took a backseat to those two characteristics.  Extra points were awarded for having nice features like RAID, lots of hard drive slots, and low power consumption.  All I had to do now was start looking.

I first stumbled across the HP ProLiant Ultra Microserver on Slickdeals.  It was recently on sale and I had missed it.  However, it was mentioned that it was on-sale often, and upon looking up the details it became the baseline for all my future searches.  When it went on sale again at PC Connection for $179, and I jumped at the opportunity.

I’ll spare you the unboxing video, but suffice it to say that it’s put together pretty well.  The door opens to your hard drives, and the top of the case slides to reveal an additional 5.25″ slot.  That gives you up support for up to five drives, if you buy a 3.5″ to 5.25″ adapter (I did).  I store the OS drive in the 5.25″ slot and all of my data in the easy access slots behind the door.

Here’s a side-by-side comparison of my old, full-size ATX tower.  Check out the big differences in size. (Small? Check.)

Cable management is really nice too, as you can see in the comparison photos.  While it wasn’t that much of a hassle with my old rig, the ProLiant pretty much avoids cable management altogether.  Everything is already run where it needs to be (except for the 5.25″).  To connect hard drives, just pop out their chassis and mount them.  When you lock them back in, they connect to the interfaces in the back of the hard drive cradle.

Everything about it is organized.  They even include a star wrench and mounting screws in the door for you to use.

Bottom line, I’m really enjoying my new machine.  I’ve got it running Ubuntu Server 11.10 and it makes a nice, quiet addition to my home network.

RDC#

Posted in Uncategorized by walkingTarget on December 31, 2011 No Comments yet

Working lately on a wrapper for Window’s Remote Differential Compression. The wrapper is written using Interop in C#. After writing much of the interfaces and coclasses, I don’t get very far using the code before running into a memory exception. I run into the same exception using an imported type library, which I had difficulty obtaining initially. My inexperience with interop makes it difficult to know how to fix the issues I’m having, but I’m going to keep at it and hopefully release something here for others to use.

UPDATE: Very important note to remember when manually defining your COM interfaces in C# – “they must declare the interface member functions in the order that the methods appear in the COM interface”. Thank you, MSDN. After following this step everything fell into place and I’m back on my way. Although, since this is the case, I kinda wish they wouldn’t sort everything on MSDN alphabetically. At the very least, to avoid situations such as these for COM. I’ll post some code later.

WordPress Switch

Posted in Site News by walkingTarget on December 31, 2011 No Comments yet

Excuse the new and basic appearance of the the blog. I’d been playing some with this behind the scenes, but I hadn’t looked at it for a while. Perhaps pulling back the curtain will encourage me to improve the theme.

I coded all of the original blog’s interface and schema as an exercise. I didn’t really have the time to commit to improving and maintaining a component that only my site would use. WordPress is also a well-tested blog system, and available with numerous plugins. As a result, it made too much sense to switch. If you’d like to see the old blog, you can at http://www.walkingtarget.com/old_blog.

Create a Thumbnail Provider with C# using IExtractImage

Posted in Uncategorized by walkingTarget on February 21, 2011 1 Comment

As far back as Windows 2000, Microsoft has offered interfaces for developers to extend the behavior of the Windows shell. Implementing these interfaces delivers a variety of functions for new and old file types within Windows Explorer – context menus, icons, custom tooltips, etc. In this article, we’ll cover a bit of the topic and use C# to implement a Thumbnail Handler for a custom file type in Windows XP.

Maybe you’re wondering, “Why Windows XP in 2011?” First of all, Windows XP is still operated by the majority of Windows users. Also, Windows Vista and Windows 7 introduce a new thumbnail handler interface that is not compatible with Windows XP. Fortunately, the Thumbnail Handler interface available for Windows XP is supported on newer versions of Windows.

The interface I’m talking about is IExtractImage. As you can see from the MSDN article, there isn’t a whole of information on this. The Internet ether is also lacking on examples of how to implement this interface in C#. However, I had a project at the office that didn’t lend itself to an unmanaged language. So despite the shortcomings and with the help of the PInvoke.net and a number of articles, I cobbled together a working thumbnail handler from IExtractImage in C# (we’ll discuss the limitations at the end of this article).

Lets start by creating a new project in whatever flavor of Visual Studio you prefer. Create a new Class Library project, give it a name and click OK. Open the Project Properties and click the “Assembly Information” button. Check the “Make assembly COM-Visible” checkbox (I noticed later that I forgot to do this the first time, so it may not matter). I also changed the target framework of the project to .NET 2.0.

Now that our project is set up, add a new code file named IExtractImage.cs. This will contain all the definitions of the COM interfaces and objects we’ll be using in our implementation. The code listed here should be reusable enough to work as the base data for any IExtractImage project, so just copy and paste the following into your new file:

using System;
using System.Text;
using System.Runtime.InteropServices;

namespace IExtractImage_Example
{
    public enum IEIFLAG
    {
        ASYNC = 0x0001, // ask the extractor if it supports ASYNC extract (free threaded)
        CACHE = 0x0002, // returned from the extractor if it does NOT cache the thumbnail
        ASPECT = 0x0004, // passed to the extractor to beg it to render to the aspect ratio of the supplied rect
        OFFLINE = 0x0008, // if the extractor shouldn't hit the net to get any content neede for the rendering
        GLEAM = 0x0010, // does the image have a gleam ? this will be returned if it does
        SCREEN = 0x0020, // render as if for the screen (this is exlusive with IEIFLAG_ASPECT )
        ORIGSIZE = 0x0040, // render to the approx size passed, but crop if neccessary
        NOSTAMP = 0x0080, // returned from the extractor if it does NOT want an icon stamp on the thumbnail
        NOBORDER = 0x0100, // returned from the extractor if it does NOT want an a border around the thumbnail
        QUALITY = 0x0200 // passed to the Extract method to indicate that a slower, higher quality image is desired, re-compute the thumbnail
    }

    /// <summary>
    /// The SIZE structure specifies the width and height of a rectangle.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct SIZE
    {
        /// <summary>
        /// Specifies the rectangle's width. The units depend on which function uses this.
        /// </summary>
        public int cx;

        /// <summary>
        /// Specifies the rectangle's height. The units depend on which function uses this.
        /// </summary>
        public int cy;

        /// <summary>
        /// Simple constructor for SIZE structs.
        /// </summary>
        /// <param name="cx">The initial width of the SIZE structure.</param>
        /// <param name="cy">The initial height of the SIZE structure.</param>
        public SIZE(int cx, int cy)
        {
            this.cx = cx;
            this.cy = cy;
        }
    }

    /// <summary>
    /// Exposes methods that request a thumbnail image from a Shell folder.
    /// </summary>
    [ComImportAttribute()]
    [GuidAttribute("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    interface IExtractImage
    {
        /// <summary>
        /// Gets a path to the image that is to be extracted.
        /// </summary>
        /// <param name="pszPathBuffer">The buffer used to return the path description. This value identifies the image so you can avoid loading the same one more than once.</param>
        /// <param name="cch">The size of pszPathBuffer in characters.</param>
        /// <param name="pdwPriority">Not used.</param>
        /// <param name="prgSize">A pointer to a SIZE structure with the desired width and height of the image. Must not be NULL.</param>
        /// <param name="dwRecClrDepth">The recommended color depth in units of bits per pixel. Must not be NULL.</param>
        /// <param name="pdwFlags">Flags that specify how the image is to be handled.</param>
        [PreserveSig]
        long GetLocation(out StringBuilder pszPathBuffer, int cch, ref int pdwPriority, ref SIZE prgSize, int dwRecClrDepth, ref int pdwFlags);

        /// <summary>
        /// Requests an image from an object, such as an item in a Shell folder.
        /// </summary>
        /// <param name="phBmpThumbnail">The buffer to hold the bitmapped image.</param>
        [PreserveSig]
        long Extract(out IntPtr phBmpThumbnail);
    }
}

Next, we’re going to create the class that will implement our interfaces. If you took a look at the MSDN article for IExtractImage, you’ll remember we also need to implement the IPersistFile interface. We also need to generate a unique GUID to identify our class in COM. If you’re using Visual Studio, a GUID generator should be in the Tools menu. If not, www.guidgenerator.com does a perfectly fine job. Here is the sample code I cooked up as an example of what your implementation might look like:

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Drawing;

namespace IExtractImage_Example
{
    [ComVisible(true), ClassInterface(ClassInterfaceType.None)]
    [ProgId("IExtractImage_Example.ExtractImage"), Guid("7CA3151C-2F5C-11E0-B2B8-B039E0D72085")]
    public class ExtractImage : IExtractImage, IPersistFile
    {
        #region ExtractImage Private Fields

        private Size m_size = Size.Empty;
        private string m_filename = String.Empty;

        #endregion

        private const long S_OK = 0x00000000L;
        private const long E_PENDING = 0x8000000AL;

        #region IExtractImage Members
        public long GetLocation(out StringBuilder pszPathBuffer, int cch, ref int pdwPriority, ref SIZE prgSize, int dwRecClrDepth, ref int pdwFlags)
        {
            pszPathBuffer = new StringBuilder();
            pszPathBuffer.Append(m_filename);
            m_size = new Size(prgSize.cx, prgSize.cy);

            if (((IEIFLAG)pdwFlags & IEIFLAG.ASYNC) != 0)
                return E_PENDING;

            return S_OK;
        }

        public long Extract(out IntPtr phBmpThumbnail)
        {
            Bitmap bmp = new Bitmap(m_size.Width, m_size.Height);

            using (Graphics g = Graphics.FromImage(bmp))
            {
                using (Pen p = new Pen(Color.Black))
                {
                    g.Clear(Color.White);
                    g.DrawLine(p, 0, 0, m_size.Width, m_size.Height);
                }
            }
            phBmpThumbnail = bmp.GetHbitmap();

            return S_OK;
        }
        #endregion

        #region IPersistFile Members
        public void GetClassID(out Guid pClassID)
        {
            throw new NotImplementedException();
        }

        public void GetCurFile(out string ppszFileName)
        {
            throw new NotImplementedException();
        }

        public int IsDirty()
        {
            throw new NotImplementedException();
        }

        public void Load(string pszFileName, int dwMode)
        {
            m_filename = pszFileName;
        }

        public void Save(string pszFileName, bool fRemember)
        {
            throw new NotImplementedException();
        }

        public void SaveCompleted(string pszFileName)
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}

Once you’ve got your class written, build your project and it should be ready to be registered. Since we’re using a .NET, we’re going to have to use Regasm to register our .NET assembly for COM Interop. You can do this using the regasm.exe tool that comes with Visual Studio, or you can create an installer that does the work for you. I opened a Visual Studio Command Prompt and ran regasm /codebase /regfile to generate the information I needed to create an NSIS installer that does all the work.

Now that COM is ready to accept access our assembly there’s just a little more left to do. Even though COM can get at our implementation, we still haven’t told Windows that there’s a new Thumbnail Handler in town. To do this, we are going to edit registry again. Under HKEY_CLASSES_ROOT, we’re going to add or change the key for a file extension and add the interface’s GUID underneath the shellex its key. You saw the GUID earlier, actually, in the interface definition in IExtractImage.cs. To say it graphically, we need to create a key like this:

HKEY_CLASSES_ROOT
|----<File Extension>
    |-----shellex
         |-----{BB2E617C-0920-11D1-9A0B-00C04FC2D6C1}
              |-----(Default) = {<Our Class Guid>}

As soon as you make this change, thumbnails should start showing up once you open a folder containing your custom file type. If not, then the the source of the problem may be difficult to find. While debugging my work, I used the Task Manager to kill explorer.exe when things went wrong. Just launch a new instance of explorer.exe right after that. This will also ensure that the new extension is loaded by the shell, just to be safe. One problem that you might run into if you’re using a 64-bit version of Windows, is that your bitmap handle is 32-bit. Cast this to a 64-bit pointer before you assign it to phBmpImage. I found some fellow agonizing developers at this site.

Finally, lets talk about the problems with writing this extension in managed code. First and foremost, you’ll be annoyed when your extensions DLL file gets locked because it’s in use by Windows Explorer. This is because the project is written using managed code. Unlike an unmanaged project, managed code uses managed memory which is handled by the non-deterministic Garbage Collector. Its way of marshaling memory means that you don’t know when your class will be unloaded. An unmanaged Thumbnail Provider may be more difficult to write, but it will be unloaded as soon as its reference count reaches zero.

Another problem I ran into was caused by the extension using one of our custom libraries to generate the thumbnail. I included a seperate copy of this second library with the installer of the shell extension so that we could update the application’s copy without worrying about the file being locked. However, this seems to cause problems when we use common File Dialogs in the application because the extension’s version of the library would override the application’s version when it got loaded. Unfortunately, the only solution I’ve come up with so far is to rename the extension’s version of the library, and this isn’t a very maintainable design decision.

Well, that conclude this tutorial. I hope I helped you a little with this rather confusing topic. I also hope I didn’t hurt too much with my ignorance of the topic. I’ll be the first to admit I’m not an expert in this field yet. If you discover any problems with my implementation, please let me know in the comments. Likewise, I’ll be sure to let you know of any developments in this project that I make in the future.

Happy coding!

And now for something completely different

Posted in Uncategorized by walkingTarget on March 16, 2010 No Comments yet

New site feature: RSS!

I know you’re all thinking, “RSS wasn’t even in the list of features you promised us last time!” Well, consider this free feature a bonus! I won’t comment too much about the project; using PHP to generate the XML code for RSS is pretty straightforward. If you do peek at the XML source however, you’ll notice that the formatting gets a little sloppy. For now, I just have to suffer that this is the way it’s stored in MySQL. I’ll also save you 15mins of debug and mention that the first line, <?xml version=”1.0″?> needs to be echoed by PHP or you will get an error. Enjoy the new feature!

Hey, you didn’t expect me to promise updates and not come through, did you? You did?

Jerks.

Project Updates

Posted in Uncategorized by walkingTarget on February 2, 2010 No Comments yet

Did a little work this morning on WarGames’ code base and got file transfer via TCP Sockets working. I sent a 2,360,144 byte file locally (I’m still waiting for a friend to test it remotely) and got the same thing on the other end. To summarize the process, the sender sends the file’s size in bytes, then loops through successive file fread()s and socket send()s to send the file. On the other end, the receiver gets the file size as a reference, then socket recv()s and file fwrite()s to output the file to disk. When sender reaches EOF, it’s done. When the reciever gets the number of expected bytes, it is done.

The impact on WarGames is significant since no game can start until every player possesses the game data of every other player’s army. One thing to consider is multi-threading. You might already be running your network in a thread so that other jobs can continue – rendering, events processing, audio, etc. However, because we’re stuck in a while loop as we recv() and send() files, no other networking actions can occur during transfer. The less obtrusive alternative is give file transfer its own thread and allow basic networking functions to continue unobstructed. This isn’t to say that you should make a thread for each file, however. For each client I would build a file manifest and spawn a thread that transfers all the files they need. As far as the manifest goes, basically they send what they’ve got, the server checks it and starts sending them what they need. The manifest would contain file names, sizes and a checksum to ensure consistency. This also effectively leads to the integration of a progress bar that users can watch as they download.

I’ve also made some bug fixes to the website that I recently discovered. If I actually had any visitors, they would have discovered them first. I’ve also upgraded the server with the dying (yet still more reliable than its predecessor) PSU from my desktop computer. Powering up on the first press of the button is fantastic. =)

Wargonizer

Posted in Uncategorized by walkingTarget on September 19, 2009 No Comments yet

Wargonizer is a campaign organization tool for war games that I wrote for fun in college. I created the name as a combintaion of “War” + “Organizer” and because I thought it was silly. I will have to post pictures later because I am using my laptop in Hawaii at the moment and I believe the latest code is on my desktop computer at home.

Feature List

  • User registration
  • User management and privilege levels
  • Administrative Panel
  • Complete CRUD management for Territory Maps
  • Interactive territory creation for Campaign Maps

Future Developments

  • Movement phase integration
  • Integration with WarGames