Hosting Control Panel Applets using C#/C++

Accessing an Applet Library

So let's begin by initializing an applet library and finding out how many applets are inside. The following snippet from the AppletLibrary class demonstrates how this is achieved:

public void Initialize()
{
        if (this.CPlApplet(AppletMessages.Initialize, IntPtr.Zero, IntPtr.Zero) == (int)BOOL.TRUE)
        {
                int count = this.CPlApplet(AppletMessages.GetCount, IntPtr.Zero, IntPtr.Zero);
       
//                                System.Diagnostics.Trace.WriteLine(string.Format("{0} applets found in '{1}'", count, _path));                           
                                 
                  for(int i = 0; i < count; i++)
                {
                          Applet applet = new Applet(this, i);
                          System.Diagnostics.Trace.WriteLine(applet.ToString());
                          _applets.Add(applet);
                }                       
        }

Take note of the class hierarchy here. AppletLibrary contains Applets. One to many. The AppletLibrary contains a property that exposes an ArrayList of Applet objects. I purposefully used ArrayList as I do not want the reader getting confused by any custom collection classes. Believe me, if this were production code, that would be a strongly typed collection class either by implementing ICollection and others, or by inheriting from CollectionBase . That again is outside the scope of this article, so try and stay focused on the problems at hand, and not with my coding style or means for enumerating sub objects.

Now that we know how many applets are actually inside an applet library, we need to extract the information from the applet so we can display its name, a short description, and an icon for the applet. What good would this do us if we couldn't show it to the users like Windows does, right? Now, take a look a the AppletLibrary class. This is where the remainder of our discussion lies. As I stated before, the applets will pass us information back in structures. If the applet is using static resources, we will have to extract them. So let's see how this is accomplished by yet another snippet of code, and talk about the caveats. This is the fun stuff I think, pulling of some pointers and marshalling!

Here I will demonstrate a small scenario that uses an unsafe code block, and another similar safe means to achieve the same ends using P/Invoke that does not require marking the code as unsafe. Keep in mind, our goal is to stay in the managed world as much as possible, to allow us to reap the benefits of garbage collection and type safety. There are plenty of days left to track down bugs because it was cool to pin pointers using the fixed statement and cast byte* around like they were Mountain Dew cans. Yeah I can do it, but it kinda defeats the purpose of a clean type safe managed language, so I avoid it if at all possible. There are probably a lot of cocky guys around who want to do it just to be cool, but believe me I was one of those guys, till deadlines hit and CEOs are asking when my app is going to stop crashing at random times. Pointers are cool, not using pointers is cooler. Trust me when I say life in a managed world is good, very good.

The first block that uses unsafe code actually requires the unsafe statement simple to demonstrate an alternative to the sizeof() function we're all so used to. The sizeof() is a pretty standard means for determining the size of a structure in bytes. Unfortunately, it must be used in unsafe code. Its alternative is Marshal.SizeOf which does not require unsafe code statements. Here, have a look for yourself. This code is going to call the applet and ask for its information, and then use the pointers returned to cast into our managed structures.

public void Inquire()
{
        unsafe
        {
                _info = new CPLINFO();
                _infoPtr = Marshal.AllocHGlobal(sizeof(CPLINFO));
                Marshal.StructureToPtr(_info, _infoPtr, true);
                if (!base.IsNullPtr(_infoPtr))
                {
                          _appletLibrary.CPlApplet(AppletMessages.Inquire, new IntPtr(_appletIndex), _infoPtr);
                          _info = (CPLINFO)Marshal.PtrToStructure(_infoPtr, typeof(CPLINFO));
                         
                          if (!this.IsUsingDynamicResources)
                          {
                                  this.ExtractNameFromResources();
                                  this.ExtractDescriptionFromResources();
                                  this.ExtractIconFromResources();
                          }
                          else
                          {
                                  this.NewInquire();
                          }
                }
        }
}

public void NewInquire()
{
//      unsafe
//      {
                _dynInfo = new NEWCPLINFO();
                _dynInfo.Size = Marshal.SizeOf(_dynInfo);
                _dynInfoPtr = Marshal.AllocHGlobal(_dynInfo.Size);
                Marshal.StructureToPtr(_dynInfo, _dynInfoPtr, true);
                if (!base.IsNullPtr(_dynInfoPtr))
                {
                          _appletLibrary.CPlApplet(AppletMessages.NewInquire, new IntPtr(_appletIndex), _dynInfoPtr);
                          _dynInfo = (NEWCPLINFO)Marshal.PtrToStructure(_dynInfoPtr, typeof(NEWCPLINFO));

                          _smallImage = Bitmap.FromHicon(_dynInfo.hIcon);
                          _largeImage = Bitmap.FromHicon(_dynInfo.hIcon);                                 
                          _name = _dynInfo.NameCharArray.ToString();                                   
                          _description = _dynInfo.InfoCharArray.ToString();
                }
//      }
}

To get back the structure from the pointer returned by the applet, we first have to allocate memory on the stack for the structure. This can be achieved by calling Marshal.AllocHGlobal . Keep in mind that anytime we allocate memory on the stack, we have to free that memory back up otherwise we have yet another crappy app with a memory leak. That's just no good for anyone because the stack is a finite resource shared by everyone. You run out of stack memory and well, better start thinking about rebooting and explaining why your programs run like a fat man in a marathon. They start out strong enough, but end up taking a taxi across the finish line. That's just no way to be. Because of the memory allocation occurring, all of my classes inherit from DisposableObject . That is just my simple wrapper for implementing IDisposable and putting some wrappers around pointer checks. So look to the overrides in the AppletLibrary and Applet classes to see resources being disposed properly by overriding the abstract method in the DisposableObject class. Once we have the information back out of the applets, we're free to display it and wait for the user to open an applet. You can programmatically open an applet by calling the Open method on the Applet class. Here is the code for that:

public void Open()
{
        IntPtr userData = (this.IsUsingDynamicResources ? _info.AppletDefinedData : _dynInfo.AppletDefinedData);
        int result = _appletLibrary.CPlApplet(AppletMessages.DoubleClick, new IntPtr(_appletIndex), userData);
        if (result != 0)
        {
                System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception();
                System.Diagnostics.Trace.WriteLine(e);
        }
}

Notice that we are passing a pointer back to the applet. Each applet can define data in a pointer that we must pass back to when we open and close the applet. If the result of sending the CPL_DBLCLK method returns 0, then everything went OK according to MSDN. However, this call blocks until the applet's dialog closes, and I've seen cases where it fails, by result being non-zero, even after the applet shows its dialog. I am currently trying to figure this out, but the docs aren't much help. I have noticed that certain applets always seem to fail according to the result of this call, even though they appear to work correctly. I've tried to catch and look at the exception, but most times it's not much help. Try it out and see what your results are. Put a break point on the trace and launch an applet. I'm really quite curious to what the deal with my results are.

Again I'm hoping someone can pick up the ball and help me figure this out here. I've cleared a lot of pretty tricky stuff I think, I just hate to get stumped this far along with the project to give up and say we just got lucky. On a side note, the Display Properties applet quit working on my system. I don't know why. I was working in this code base, but then I changed the window handle around. Look at the docs and source to see, if you don't understand what I'm saying, then you probably can't help me, so no worries k? LOL. Like I said, this is supposed to be a learning experience for all of us, I'm learning too. I have never seen any code to do what I'm trying to do before online, so feel free to tear me up if you want. Go find another example if you can, I'd love to see it! Seriously, this might have been easier to write having something to turn to other than the cryptic header files and MSDN docs to help me out.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Perl - The only language that looks the same before and after RSA encryption.” - Keith Bostic