Friday, September 30, 2005

Writing a Home Screen plugin

Writing a Home Screen plugin

In my last blog I promised you guys a blog about Home Screen plugin development. Well Jason on Virtueoffice wrote a pretty good Home Screen tutorial for you already, and I feel no need to repeat what he already have written. Check out his series of home screen tutorials Home Screen Plugin Tutorial - Part 1: The Basics.

Now there were many things that Jason just showed the code for, whilst not explaining what the code actually does. So I think that perhaps I could shed some light on those parts that was a little bit hard to understand.

COM

If you don’t know what COM is yet, then you have saved your self loads of headaches. It is true, almost everyone hates COM, but it is here and we have learned to accept it. So what do we do?

We start out by fetching our self a brand new GUID. What is a GUID you may ask? Well GUID is short for Global Unique Identifier. What is that then? It is a ship load of numbers that end up being a unique id. Ok, so we have this unique id stored in our brand new COM DLL, and we ship that DLL to a device and when it is there it will have to be registered in Windows. The registration progress goes something like this. Windows loads your DLL and executes the DllRegisterServer() method that you declared in your code. If you for some reason want to unregister your DLL then Windows would execute DllUnregisterServer() in your code. Basically all these two methods actually do is writing/deleting some information about your DLL in the registry so that Windows has some background information about them.

This is not a bad or unnecessary thing, because your GUID will be written together with the path to the DLL and other interesting stuff. This means that if you for some reason are about to use a COM object in your code, you could just referee to the GUID and let Windows take care of finding it for you. Now, if that was the only smart thing about COM then we might as well just use the good‘ol way, giving the frikkin path and roll with it. But oh no, this is where the magic starts to sparkling and go all wild. Because you would referee to the GUID instead of the path to the DLL, thus making these calls go thru some sort of COM DLL manager in the OS that acts like much like a proxy, the DLL only needs to be loaded once. Now all applications that are using this COM DLL are using the very same copy of the DLL in RAM rather then keeping their own copy of the DLL. This also allows the applications to communicate with each other thru the COM DLL memory space. I think that another intension was to save some RAM too.

Now that you understand that, you could probably muster up the imagination of one thing that gives us a shit load of security holes in Windows. You could also imagine what would happen to the three applications that depend on a certain COM DLL, if it was updated by its developer and the old GUID was kept from the old version for some reason. You are probably right, (let’s take a wild guess) many of these applications would start behaving very strange.

So how do they work? Well a COM DLL implements something called a Class Factory or an implementation of interface IClassFactory to be sure. This is a class that hands out new instances of your main class (in the Jason Home Screen example it would be the PluginBase class), keeps track of how many objects that is out there and other cool stuff that a factory needs to do. Now that we have this factory available, Windows could just go there and ask for another COM Object and our factory would produce it for Windows. Now Windows may ask if it could unload our COM DLL from RAM, then our factory manager could say something like “Oh no, that wouldn’t do! I still have objects out there!” or it could just return FALSE. This entire infrastructure is cool and all, but yes it presents a lot of potential security risks and memory leaks if the COM DLL coder doesn’t do his job well and write a lot of code that is just boring code that have to be written. And that is probably one reason why many coders don’t like ‘em.

Now you see that I haven’t even touched actual code regarding COM and I have written too much about the subject just to cover up some background info for you. I suggest that you sit down with the source from the Home Screen plugin and check it out on your own with this background info fresh on your mind.

The Plugin

Your actual plugin class for the Home Screen need to implement two interfaces, IHomePlugin and IPersistStream.

IHomePlugin gives you four important methods that you need to implement in your class. Initialize(), GetHeight(), GetSelectability() and OnEvent().

IPersistStream gives you four other important methods that you need to implement. IsDirty(), Load(), Save() and GetSizeMax(). Where I think that you will use only Load() and Save().

Now let’s look at the scenario where you have a finished Home Screen plugin and you have added it to your myhome.home.xml file with the <plugin> tag. You go into your SmartPhone settings and chose Home screen and chose your custom XML and you return back to your home screen to see what it looks like. What will happen in your code now?

  1. The home.exe process will read the custom XML, and find your <plugin> tag with the GUID of your COM DLL attached to it. So it will ask Windows for an object of this type.

  2. Windows will load the DLL (if it wasn’t loaded already) into RAM and initialize the Class Factory in the DLL. Then Windows will ask the newly born factory for a new Object, which Windows hopefully receives and then pass it on to the home.exe process.

  3. The home.exe will then take the new object and execute the Initialize() method implemented from the IHomePlugin interface, and pass everything that is within the <plugin> and </plugin> tags of your custom myhome.home.xml file.

  4. Now it’s up to your plugin to parse whatever it needs out of that XML node and store it in local variables.

  5. The home.exe will call the Save() method that is implemented with the IPersistStream interface, and pass a IStream object with it.

  6. Your plugin have to store the values of all the variables that it loaded with the Initialize() method on this stream using the IStream interface method Write(). This is a piece of cake. But why would I need to do that you may ask?

  7. Your plugin object (note the ‘plugin object’ referees to PluginBase class instance only, not the factory or any other code that might be instantiated in the DLL) will be destroyed by the home.exe process.

  8. The home.exe process will ask Windows for a new object, which Windows will retrieve from the factory of course.

  9. The home.exe will execute Load() implemented from the IStream interface in the new object and pass another IStream object from which you can read your earlier Written variables.

  10. Your plugin must read the values that were stored in the Save() method and return them to their rightful home.

  11. The home.exe process will execute the GetHeight() method and you need to return the height your plugin intends to have on the screen (in pixels).

  12. The home.exe process will execute the GetSelectability() method and you are supposed to return a TRUE or a FALSE weather your plugin should be selectable or not.

  13. From now and on the home.exe process OnEvent() method will be called whenever something interesting is happening to your plugin.

Could some one explain to me why step 6 to 10 is necessary? I’ve read explanations that it would solve eventual memory leaks that could occur in the initialization process of a plugin, but I can’t figure out the logic in that. Because once the DLL is loaded, it will not be unloaded, so eventual memory leaks will not be released after all. Or is there something in COM architecture that I am missing here?

The OnEvent

The OnEvent() method is executed whenever your plugin needs to do something in particular.

If your plugin is selected and the user presses a direction key, the PE_KEYDOWN message is sent with the OnEvent() method. Now you need to figure out what key was pressed and take action accordingly. If your plugin is selected and the user presses the action key (on most SmartPhone devices it would be the joystick, not in any direction but strait down into the device) you will receive the PE_ACTION message. Whenever your plugin is invalidated you will receive a PE_PAINT. And so it goes on. It’s not that hard to figure out what they all do.

One good thing about the Home Screen plugins, is that they doesn’t suffer from the same update problematics that the Today Screen does. In my MobiDock plugin I use the same code for rendering the Today Screen as the Home Screen. Actually I reuse all code that is not Today Screen API specific in the Home Screen plugin. But the Today Screen flickered, which the Home Screen did not even when I wasn’t using the double buffer technique. I think this is because each plugin is painted on the actual Home Screen window, whilst a Today Screen plugin has it’s own window that is on top of the Today Screen window.

To sum everything up

Once you’ve got code that handles all the COM specific functionality up and running, I think that the Home Screen API is nicer then the Today Screen API, with the IHomePlugin interface and all. But in the end, it’s all ruined by the COM thingy. I don’t know if you’re even supposed to be able to load more the one instance of each plugin. Or if your supposed to be able communicate with your plugin from another process using the COM techniques available? If so I think it’s an atom bomb to kill a bee solution.

Anyway, next blog will probably be about CAB files on your smart devices. How you can write a smart setup.dll that is executed by the CAB installer and other neat stuff.

I started writing this because I couldn’t sleep. Now I think I will drop dead when I reach my bed.

Thursday, September 29, 2005

Writing a Today Screen plugin for Pocket PC

Writing a Today Screen plugin for Pocket PC

I'm one of two developers working with MobiDock which is a site developed for Windows Mobile devices. Anyway, our latest MobiDock-add-on was a Today Screen plugin for Pocket PC. It sounded as a fun project when I started working on it. It was some time since I last wrote any C++, but I thought "what the heck" (or something similarly ignorant in Swedish).

First I found out the examples on MSDN, which was OK. And later I found out that there were issues that were not covered by the MSDN articles. So I will try and focus on those.

What you do is that you download the example, lemme see if I can find it... .. yeah here it is Today the Pocket PC, Tomorrow the World!. That article gives you all code you need to begin your fine career as a Today Screen Plugin writer (awesome title there). Fine, now you've got it.

DllMain

DllMain() is the entry point for your DLL. It receives two messages, DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH. DO NOT IGNORE THESE! I know that it is perfectly possible to write a Today plugin not even declaring DllMain(), that however is highly NOT the recommended way to do it. DLL_PROCESS_ATTACH is called whenever a process wants to create an instance of our DLL. DLL_PROCESS_DETACH is called whenever the process is finished with it. Hence we should allocate whatever resources or whatever in the ATTACH message and then release those resources whenever the DETACH message is received.

I got this one tip from a site where one guy recommends that you unregistered your window class before you try to register it, or else your plugin would be invisible in some circumstances. He also states that he doesn’t know why this strange behaviour occurs, only that this is a solution to it. Well the bug occurs because of bad coding of course. He should have registered the window class in DllMain when the Attach message was received and then unregistered it in the Detach message and he wouldn’t have to solve it that way. More about pragmatic programming in the next section :D.

InitializeCustomItem

InitializeCustomItem is the second entry point. This one is special for Today items only. This is where we set in motion whatever resources we allocated in the DllMain. So DllMain is executed sending the Attach message first, then the InitializeCustomItem is called.

Why don’t we skip the DllMain and allocate all our resources in InitializeCustomItem and then release whatever resources in WM_TODAYCUSTOM_CLEARCACHE? Well the answer to that is actually whole other topic for code design (I urge all coders to read The Pragmatic Programmer). Let’s just say that it is good practice to keep allocation and deallocation of resources in the same method or at least level in code, it simplifies the search for eventual memory leaks.

WinProc

What will happen whenever we’ve set our window up correctly is that the first Window message that your message handler (WndProc) will receive a WM_TODAYCUSTOM_QUERYREFRESHCACHE message. This gives you a TODAYLISTITEM-pointer in the WPARAM. This struct has a member called cyp which you set to the intended height of your plugin. Now you return TRUE, telling the caller that you actually want to change the size of your plugin (The caller is the parent window, the today screen). Returning FALSE will tell the caller that nothings changed. Now remember that this message will be sent to your plugin every once in a while, and every time you return a TRUE your window will be invalidated, hence it will also receive a WM_PAINT message. This is an unwanted behaviour because it will force your plugin to re-render when it is not needed, and that might cause flickering behaviour or worst case it will slow down the whole machine or at least the today screen.

However, the WM_TODAYCUSTOM_QUERYREFRESHCACHE message is clearly useful. Let’s say that we are writing a today plugin that displays the current battery status. Every once in a while we need to check whatever battery status we’ve got in our machine. The WM_TODAYCUSTOM_QUERYREFRESHCACHE would be the perfect place to retrieve such data from the system and store it in local variables. Now that we have updated our internal variables we would of course want the display to reflect those changes so we could return a TRUE from the WinProc.

If we return a TRUE from the WinProc then we should absolutely check that our cyp were correct first, that it reflected the intended height of our plugin. A TRUE response from the WinProc will also result in an invalidation of the whole window. We might just want to update a small portion of it, perhaps something that displays the percentage in text or a small icon or, whatever. So instead we will create a RECT that holds the coordinates of what we want to update and just do a InvalidateRect() on that portion of our window. And then we return FALSE from the WinProc.

WM_PAINT

This is the message we all have been waiting for :). Basically you do whatever you usually do when you do your GDI Window painting. But there are some things worth putting down in this blog. First you might want your plugin to have transparent background. The above MSDN code was written for Pocket PC 2000 which utilized only solid background, thus doesn’t cover the transparency feature implemented since the Pocket PC 2002 version. How do we do it?

TODAYDRAWWATERMARKINFO dwi;

dwi.hwnd = hWnd;
dwi.hdc = hdc;
dwi.rc.left = ps->rcPaint.left;
dwi.rc.top = ps->rcPaint.top;
dwi.rc.right = ps->rcPaint.right;
dwi.rc.bottom = ps->rcPaint.bottom;

SendMessage(GetParent(hWnd), TODAYM_DRAWWATERMARK, 0, (LPARAM)&dwi);

That’s about it, we send a message to the today window, handing the RECT to be updated and our window handle and our device context (hdc).

The flicker drives me nuts!

Now that you’ve got your plugin up and running you may notice that it flickers each time you make a move on the today screen, and I do not mean only when your own plugin got focus but also when you step thru the other today items. How do we solve this? The only solution I ever came up with was to actually use an off screen DC.
How do we get an off screen DC you may ask? There is something called a Memory DC, they’re quite easy to use. What you do basically is just CreateCompatibleDC() and you pass on your current DC (the hdc you get when you BeginPaint()). This gives you a compatible device context, but it isnt associated with a bitmap that can handle more then black and white (which I find strange). So you’ve gotta create one your selfe. That is quite simple too, just CreateCompatibleBitmap(), pass on the hdc and the size of your window in a RECT and you have a nice bitmap. Then you just associate the new bitmap with your DC with SelectObject(). Here is some code to help you out.

HDC hdcOffscreen = CreateCompatibleDC(hdc);

HBITMAP hbOffscreen = CreateCompatibleBitmap(hdc,
windowSize.right - windowSize.left,
windowSize.bottom - windowSize.top);

HBITMAP hbOffscreen_Old = (HBITMAP) SelectObject(hdcOffscreen, hbOffscreen);

Now, promis me that you release theise resources when you are finnished with em =)

SelectObject(hdcOffscreen, hbOffscreen_Old);
DeleteObject(hbOffscreen);
DeleteDC(hdcOffscreen);

I suggest that you create the offscreen resources only once, and then you reuse it for all your WM_PAINT messages. This speeds up the painting a lot. Remember, if you do that, then you also must reinitialize them whenever you change the size of your plugin window. Also you must remember to release the resources whenever your plugin is unloaded, you can do that when the WM_TODAYCUSTOM_CLEARCACHE message arrives. I went as far as creating a DC of the background as well, it’s not necessary, it takes up some extra memory but results in a faster WM_PAINT.

I have probably forgotten stuff that I was intending to include. Yeah, there is one more thing that I use extensively. It’s quite a hassle to debug your today screen item. So I created a small app that updates the “Enabled” registry value for my plugin, simply setting it to 0 if it was 1 and the other way around. Then I do a:

SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0xF2, 0)

This message will force the Today screen to update it self, hence loading/unloading your plugin. Now if you keep that “toggle”-application as a part of your solution and set it as the “Startup project”, your plugin will load/unload whenever you press F5.

Next blog will probably be about Home screen plugins :D