A quick peak at the Applet Viewer Window displaying the applets in large icon mode.
Now viewing in details view. Notice the name and description displayed for each applet.
And finally notice an applet that has just been launched by double clicking its icon. This demonstrates the applets are really being hosted by the applet engine.
Assumptions
This article assumes a basic understanding of writing unmanaged C/C++ dynamic link libraries and exporting functions from those libraries. Also basic understanding of using P/Invoke to access unmanaged libraries will benefit the reader, but I will attempt to explain as much as possible.
Abstract
The purpose of this article is to discuss several problems a developer might face when attempting to mix unmanaged and managed code. A problem that is all too often encountered when attempting to interact with the current Windows API from a managed language such as C# or VB.NET. In this article, I will discuss the problems I faced and how I solved the problems using a combination of managed and unmanaged code, C# and VC++ respectively. Here is a brief overview of what you can learn by reading this article:
- Calling unmanaged function pointers
- Dynamically loading unmanaged libraries at runtime
- Acquiring function pointers to unmanaged functions
- Translating C/C++ structures and data types to CLR compliant code
- Finding alternatives to unsafe code for the purposes of using
sizeof
- Allocating/deallocating memory on the stack
- Understanding how applets can be manipulated programmatically
- Extracting strings and icons from embedded resources located in external unmanaged libraries.
Background
So let�s begin by discussing why I decided to write this article and code. Being the curious coder I am, I am always intrigued by the underlying implementation of features in the Windows operating system. Control panel applets have always been a somewhat uncovered topic for some reason, yeah there is documentation on them in the MSDN library, but rarely any good working examples. Let along how they actually work. Before I set out to write this article, I already had a pretty good understanding of how applets are written, having written several as a professional developer in the past. However, it wasn�t until I stepped into shell development that I became completely curious just how Windows pulled off these wonderfully useful creatures.
As days went by developing my shell, I came across many methods for actually launching control panel applets from code. Most of the implementations involved hard coding references to rundll32.exe to call the Control_RunDLL
function with various arguments to launch off control panel applets. This always bothered me, because this was far from dynamic, you had to know about the applets ahead of time, at least somewhat to figure out how to launch them. I decided I wanted a means to enumerate and host the applets just like Windows.
So having said the why, let�s discuss what an applet actually is. By the way, all of the information presented here is my personal dissection of the documentation found in the MSDN library. Control panel applets are nothing special, simply .dlls built with a special extension, .cpl, and placed into the Windows system directory. If you were to attempt to write one, you would have to choose a language that creates unmanaged DLLs and allows for exporting of unmanaged functions. Something that C# and VB just don�t do, so look to C++ or Delphi to pull this off. It�s not all that hard, but it�s beyond the scope of the article.
Now that we know what an applet is, an unmanaged DLL compiled with a .cpl extension, let's look at how they work. Digging into the documentation, you will discover the CplApplet
function. It is the sole function an applet must export from its library to interface with Windows. The function looks like this:
LONG CPlApplet(HWND hWnd, UINT uMsg, LPARAM lParam1, LPARAM lParam2);
The function is very similar to the WndProc
functions behind all windows. All communication to applets occurs through this function. You do not need to understand anything more about this function for now. If you are interested, do a search for CPlApplet
in the MSDN library and you will have the pleasure of translating this wonderful function just as the rest of us were forced to do.
Ok, so let�s review what we know about applets. This will be the foundation of how we discover and communicate with all applets:
- An unmanaged Windows dynamic link library
- Uses the .cpl extension instead of the standard .dll extension
- Exports a single function called
CPlApplet
- All applets should be located in the Windows System directory
Problem #1: Finding and loading unmanaged DLLs dynamically
Just when life is looking easy, and you are saying how hard can this be, in walks the first problem. How do we call this function from unmanaged code? As you know, you can call unmanaged functions from DLLs using System.Runtime.InteropServices.DllImportAttribute
, the problem lies in the fact that you have to know the name of the library while coding. So how do we load unmanaged DLLs on the fly and call that unmanaged function if we can�t define its entry point ahead of time.
The answer lies in several functions. LoadLibrary
, FreeLibrary
, and GetProcAddress
. We will use LoadLibrary
to load the applet�s .cpl file (a.k.a. nothing more than a .dll) by its filename, and use GetProcAddress
to get an unmanaged function pointer to the CplApplet
function. FreeLibrary
will be used to release the DLL once we are finished using it. This is a standard practice, and any of you who have done dynamic function pointers can probably skip ahead just a bit. However, I can remember when this was a magic bag of voodoo magic, and needed a little explanation.
Let�s look at how we can do this. First we will need to search the Windows System directory for all files ending with the .cpl extension. This is very easy to do using the methods and classes in the System.IO
namespace. Here is the method that will do the grunt work of discovering these on the fly. Let�s take a look. But first let me break down the classes that we will be working with, the classes I have created to make the magic happen. Very briefly they are�
AppletEngine
AppletLibrary
Applet
The AppletEngine
class contains the following method that will allow us to find the applet libraries.
public FileInfo[] FindAppletLibraries(string path)
{
DirectoryInfo di = new DirectoryInfo(path);
if (di != null)
{
return di.GetFiles("*.cpl");
}
return new FileInfo[] {};
}
This will allow us to be returned an array of FileInfo
objects that contain information about the files that fit our search. This is all pretty standard stuff and shouldn�t cause any questions as of yet. If it does, refer to the docs on MSDN or my source code and I�m sure the lights will come on quickly.
Now that we have discovered the files that end with .cpl, we will assume them to be all applet libraries. Let�s look at how we can use LoadLibrary
and GetProcAddress
to load them and get that function pointer so we can communicate with the applets. We simply need to loop through the FileInfo
objects and call LoadLibrary
on the filename to load the DLL, and assuming that succeeds, we can call GetProcAddress
to return a function pointer to the CplApplet
function. Here is a snippet from the AppletLibrary
constructor that implements this algorithm.
public AppletLibrary(string path, IntPtr hWndCpl)
{
_path = path;
_hWndCpl = hWndCpl;
_applets = new ArrayList();
if (!System.IO.File.Exists(path))
throw new System.IO.FileNotFoundException
("No applet could be found in the specified path.", path);
_library = LoadLibrary(path);
if (base.IsNullPtr(_library))
throw new Exception("Failed to load the library '" + _path + "'");
_appletProc = GetProcAddress(_library, "CPlApplet");
if (base.IsNullPtr(_appletProc))
throw new Exception("Failed to load CPlApplet proc for the library '"
+ _path + "'");
this.Initialize();
}
Let�s discuss just what this code snippet is doing. First off, it will try and call LoadLibrary
on the path to the file, this should be something like C:\Windows\System\SomeApplet.cpl. The method will return an IntPtr
which is a handle to the library. Look at the MSDN docs for more info on this, I�d rather let the creators explain it. If the function succeeds, the IntPtr
will be something other than IntPtr.Zero
. Once we have a handle to the library, we can call GetProcAddress
with the handle and the name of the function to get yet another IntPtr
which is an unmanaged function pointer.
Problem #2: Calling unmanaged function pointers from managed code
Here now we are faced with a rather tricky problem. How do you call an unmanaged function pointer from managed code? At first glance the answer seems simple, we use delegates. However correct that solution may seem, I have not been able to uncover a means of creating a delegate in managed code to an unmanaged function pointer. Several methods in the Marshal
class look promising, namely GetUnmanagedThunkForManagedMethodPtr
. Here I will admit defeat because I cannot for the life of me figure out how to work this method. The docs are no help, and I simply got tired of racking my brain to figure it out. I am hoping that someone will read this article and come up with a solution for what I am about to do next. Part of this article, by the way, I am assigning to the rest of you interop gurus to help me figure that method out. I�m certain it can be done, it�s just not worth my time to waste any more time trying to figure it out. If someone does, please let me know!
Enter our own trickery. I decided the easiest way to do this would be to create a small unmanaged C++ DLL that could call it for us. Calling function pointers in C++ is as easy as declaring integers to the rest of the managed world. So breaking open a Win32 project and setting its project type to dynamic link library, I created a DLL to do this work for me. I called it AppletProxy.dll for lack of a better term, because it will act as a proxy between our managed code in C# and the unmanaged function exported by the applet. I am not going to cover how to create unmanaged DLLs here, that is also beyond the scope of the article. If you are really interested, the source code should provide you with a very simple example to learn from, and as always, I�m around for questioning if you get stuck. Here is what the unmanaged function looks like that will be the key to calling the unmanaged function pointers for us.
LONG APIENTRY ForwardCallToApplet(APPLET_PROC pAppletProc,
HWND hwndCpl, UINT msg, LPARAM lParam1, LPARAM lParam2)
{
if (pAppletProc != NULL)
return pAppletProc(hwndCpl, msg, lParam1, lParam2);
return 0L;
}
Ok, I know a lot of you are looking at this and thinking, what in the heck is this guy doing? I don�t understand the syntax of this stuff, and what�s with all the funky data types. I�m hoping that�s not the case, because if it is, you should really go open MSDN and look up the data types. They are all readily available in the docs.
This method will accept an unmanaged function pointer and call the method it points to and return us the result.
Now that we have our proxy function to call the unmanaged function pointer, you might be wondering how we call that from our managed code. We simple use P/Invoke and define the entry point just like any other API. Here is how to do just that.
[DllImport("AppletProxy")]
public static extern int ForwardCallToApplet(IntPtr appletProc,
IntPtr hWndCpl, AppletMessages message, IntPtr lParam1, IntPtr lParam2);
One of the problems I encountered when I started trying to call API functions were the difference in data types. I had a real problem trying to figure out what an HWND
or LPARAM
translated to in managed code. Here is a quick reference for you newbies that will help you out when trying to translate functions from C/C++ to managed code.
HWND
, HANDLE
, HINSTANCE
, HICON
, any Pointer type maps to System.IntPtr
DWORD
, LONG
, BOOL
map to System.Int32
or int
in C#
LPSTR
, LPTSTR
, LPCTSTR
map to System.String
and System.Text.StringBuilder
- Use
System.String
most times, but if the API requires a pre-initialized buffer of any length, then use a System.Text.StringBuilder
I hope this helps, because I know for a while I was constantly heading off to the C header files to find the underlying definitions and then doing some research on MSDN to figure out what the data type was supposed to be declared as in managed code. The main thing to remember in my opinion is that if it starts with "H", it�s most likely a handle of some sort which maps nicely to System.IntPtr
. The specific implementations of course may vary from time to time, but as a general guideline these have worked out just fine.
Problem #3: What does the applet expect?
Now that we have covered a few tricky concepts, let�s move back into some functionality discussions. At this point, we can load any applet we want, and call the CplApplet
function to communicate with the applet. But what exactly are we going to pass to this function to get the results we want. There are several things that you�ll notice Windows presents for applets, by looking at the Windows Control Panel in Windows Explorer. There are icons, and text for all the applets. How does it get those? Answer: By calling the CplApplet
function with specific messages and getting specific results back.
Applets are designed (or should be designed) to provide a name and description as well as an icon to be displayed for the user. Let�s look at how we can recreate this functionality and talk to our applets. A quick look at the docs on MSDN and we find a set of messages and several structures that are used to communicate with the applet. Another problem is coming, but it�s not nearly as rough as any previous ones, but if you didn�t know what to do, it could be really a world ending problem. Don�t worry I will show you how to deal with it, but let�s see what these messages and structures look like first.
To communicate with the applet we will send a message, and optionally a pointer to a structure to receive information back from the applet. The structures are where our last hurdle occurs. Here are the definitions in managed code:
[StructLayout(LayoutKind.Sequential)]
public struct CPLINFO
{
public int IconResourceId;
public int NameResourceId;
public int InformationResourceId;
public IntPtr AppletDefinedData;
public override string ToString()
{
return string.Format(
"IconResourceId: {0},
NameResourceId: {1},
InformationResourceId: {2},
AppletDefinedData: {3}",
IconResourceId.ToString(),
NameResourceId.ToString(),
InformationResourceId.ToString(),
AppletDefinedData.ToInt32().ToString("X"));
}
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct NEWCPLINFO
{
public int Size;
public int Flags;
public int HelpContext;
public IntPtr AppletDefinedData;
public IntPtr hIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
public string NameCharArray;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]
public string InfoCharArray;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
public string HelpFileCharArray;
}
There are really two kickers here. The first is in the CPLINFO
structure. Once returned to us, it contains the integer IDs of resources in the applet�s resource file that contain either the string for a name or description, or an icon. A quick glance at the docs, I realized we could extract this information using LoadString
and LoadImage
. However, it states clearly that you should use the MAKEINTRESOURCE
macro on the resource ID before passing it to the LoadString
or LoadImage
functions. I dug through the header files and discovered a really nasty looking conversion. I won�t even bring it up, because I think the Windows developers did it just to screw with the rest of the world, a.k.a. anyone not programming in C/C++! It�s funknasty and I don�t know how to convert it to C#, believe me I tried. Here is what it looks like in the header file:
#define IS_INTRESOURCE(_r) (((ULONG_PTR)(_r) >> 16) == 0)
#define MAKEINTRESOURCEA(i) (LPSTR)((ULONG_PTR)((WORD)(i)))
#define MAKEINTRESOURCEW(i) (LPWSTR)((ULONG_PTR)((WORD)(i)))
#ifdef UNICODE
#define MAKEINTRESOURCE MAKEINTRESOURCEW
#else
#define MAKEINTRESOURCE MAKEINTRESOURCEA
#endif
Now if any of you can translate that to C#, again please clue me in. I�d love to know. I consider myself pretty good at translating, but again I may have just been up too late or had too much Mountain Dew to operate at the level required to translate this. So like my previous solution to the function pointers, I went back to my C DLL and made another wrapper function so that I could just use the real deal and be done with it. Here is what the wrapper functions look like:
HICON APIENTRY LoadAppletIcon(HINSTANCE hInstance, int resId)
{
return ::LoadIcon(hInstance, MAKEINTRESOURCE(resId));
}
HANDLE APIENTRY LoadAppletImage(HINSTANCE hInstance,
int resId, int width, int height)
{
return ::LoadImage(hInstance, MAKEINTRESOURCE(resId),
IMAGE_ICON, width, height, LR_DEFAULTCOLOR);
}
Ok, now that we can load the strings and icons from the resources of the applet, we hit the last of our hurdles. This probably caused me more trouble than any of them, but ended up being the easiest to solve once I understood how to tackle the problem. Like I stated before, the structures above will be used to pass information back to us when we call into the applet. The CPLINFO
structure is really straight forward, but the NEWCPLINFO
structure is kinda different. Some applets expose dynamic information, based on some changing resource for example. Something like wi-fi or some network or disk resource the description or icon might need to be changed. So we have to refresh the information each time using the NEWCPLINFO
structure. As I started translating the structure to C# I discovered the following definition in the docs. A fixed length char[]
of predefined length. As you may or may not know you cannot pre-initialize public fields in managed structures. And I didn�t understand how to get my structure to map to the real one because of this limitation.
typedef struct tagNEWCPLINFO {
DWORD dwSize;
DWORD dwFlags;
DWORD dwHelpContext;
LONG_PTR lpData;
HICON hIcon;
TCHAR szName[32];
TCHAR szInfo[64];
TCHAR szHelpFile[128];
} NEWCPLINFO, *LPNEWCPLINFO;
Take the szName
field for our example. How do we define a fixed length array of characters in our structure? The answer lies in the MarshalAs
attribute. We can define our field as a string and let the P/Invoke services marshal the data as in a specific format. This is what we can do to get the desired marshalling to occur. We will define our field to be marshaled as an array of null terminated characters with a fixed array size.
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
public string NameCharArray;
A small side note is in the string conversions. Do we use ANSI or Unicode? For the most part Unicode is the data type of choice for strings, but because we don�t know ahead of time the real struct
uses the TCHAR
type which will map to the appropriate type when compiled based on the various #define
s in the C/C++ header files. We don�t have that luxury, but we do have a solution. We will apply the StructLayout
attribute to our structures to define sequential layout of the fields, which will keep the fields in the order we specify, (the CLR tends to optimize structure fields to what it feels is optimal, which can really hose us when dealing with pointers and unmanaged code, so we need to tell it that it needs to leave our fields in the order we define) and also the character set to use for any strings it encounters. This is accomplished by the following:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct NEWCPLINFO
Having piddled around with the various character sets, I discovered the strings are displayed correctly using the ANSI charset on my Windows XP pro box on down to my 98 test machine. I don�t know if this is the right choice, hopefully someone will step up and put a final yes or no to this question. Again I�m completely open for suggestions as this was a learning experience for me as well. I hope to have all of my questions answered and sleep comfortable in my bed at some point in the next 30 years, but that�s only going to happen when all of my Windows API questions have been answered. And at the rate I find questions, I�ll either have to get a job at Microsoft or steal some of the source to get at the answers. I�m quite frankly open for both at this point. So many hours of my life have been wasted trying to understand the minds of the guys that made those APIs, but again I digress.
Ok, we�re nearing the completion of our translations and definitions, so let�s look at how to communicate with the applets. The applets expect a certain order of messages. The first call to CPlApplet
should initialize the applet and let it know we are loading. The second call will tell us how many applets are actually contained in the .cpl file. This is a one to many relationship as one DLL could have an unlimited number of applets contained inside. The third and fourth calls will inquire the information from the applet. After getting the information we need, we can ask the applet to show its UI by sending it a double click message (don�t stress this is normal, and I�ve made an enumeration for the messages. I renamed them to fit my .NET likings but if you are interested, find the cpl.h header file and look at the CPL_INIT
, CPL_GETCOUNT
, CPL_INQUIRE
, CPL_NEWINQUIRE
, CPL_DBLCLK
, CPL_CLOSE
, CPL_STOP
messages for details). Here is the message enumeration that I translated from the MSDN docs:
public enum AppletMessages
{
Initialize = 1,
GetCount = 2,
Inquire = 3,
Select = 4,
DoubleClick = 5,
Stop = 6,
Exit = 7,
NewInquire = 8
}
There is a lot of stuff I�m skipping because it�s kinda dull. I know you all are interested in seeing something running. But if you are curious, read through the comments in my code and the docs in MSDN to see the exact steps the applets are expecting.
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);
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 Applet
s. 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()
{
_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.
Time to go download the code, and perhaps toss me a vote so I know how to improve for the next article
Well it�s time that you downloaded the source and demo projects if you haven�t done so already. Nothing feels quite as good as running a project and seeing some results. So go have fun playing with the project and stepping around through some interesting coding ideas. I hope this helps some of you overcome some similar hurdles. I had a lot of fun writing the code and almost as much fun trying to explain it in this article. This is my official first posting online, probably will have many more, just gotta find the time. I�ve held back for sooooo long on posting from lack of time, not lack of ideas or knowledge. Look for more posts in the future.
Also, this being my first article, if you don't mind, give me some feedback, and possibly a vote. Have some kindness on my writing abilities as an author of articles, because as you may know I write code, not books. So chances are high that the article reads poorly, but hopefully the code is solid! :)
What we learned
Let�s review a few interesting concepts that we�ve covered:
- Calling unmanaged function pointers
- Dynamically loading unmanaged libraries at runtime
- Acquiring function pointers to unmanaged functions
- Translating C/C++ structures and data types to CLR compliant code
- Finding alternatives to unsafe code for the purposes of using
sizeof
- Allocating/deallocating memory on the stack
- Understanding how applets can be manipulated programmatically
- Extracting strings and icons from embedded resources located in external unmanaged libraries
Things left, and known issues
- Thread calls to Open and Close, or make Asynchronous wrappers
- Figure out why certain applets return false after they close
- Find out where in the heck the Admin Tools Applets are located or how they are inserted into the control panel namespace (I think it�s a Shell Extension, just haven�t found it yet)
- Figure out how to work
Marshal.GetUnmanagedThunkForManagedMethodPtr
- Figure out how to convert
MAKEINTRESOURCE
to C#
History
Version 1.0.0