Daily stuff not frequently posted a software developer finds out or rants about.

Monday, July 31, 2006

Mini Windows framework available

Having been asked on the Lazarus forums about how I did implement the WinAPI-only non-OO application as OO classes for use in my own applications, I decided to make a development snapshot of these units available. The files are all GPL'ed, and there's a limited documentation.
This miniature framework is neither complete nor indented to replace the LCL or compete with other frameworks, it's merely a fun project for myself, and to get something to run while the LCL for WinCE has still some downsides.

Monday, July 24, 2006

Transparent controls for WinAPI applications

If you look at Windows CE applications, quite a few use a bitmap as the background. I guess this has to do with how phones need to be colorful and multi-channel noisy ;) Anyway, since the space is limited, it may make sense sometimes to put a logo on the background instead of wasting precious space.

As a first step, you'll need to add a bitmap to your projects resource files, and give it a simple name, like BACKGROUND. To use this bitmap as your forms standard background, you'll have to use the WM_CREATE message of your main message procedure:

var FHDCBis: THandle; // you'll need this again!

function WndMessageProc(hWnd: HWND; Msg: UINT; wParam: wPARAM; lParam: LPARAM): LongInt; stdcall;
// ...
var hDC, hBmp, hOldBmp: THandle;
// ...
begin
   // ...
   case Msg of
      WM_CREATE: begin
         hDC := GetDC(hwnd);
         hBmp := LoadImage(hInstance, 'BACKGROUND',
          IMAGE_BITMAP, 0, 0, 0);
         FHDCBis := CreateCompatibleDC(hDC);
         hOldBmp := SelectObject(FHDCBis, hBmp);
         ReleaseDC(hwnd, hDC);
      end;


This gets the device context for the form (GetDC), loads the bitmap from the files resources (LoadImage), and puts it (SelectObject) on a newly created (CreateCompatibleDC) device context.

      WM_ERASEBKGND: begin
         // This fills the background with
         // the contents of the background image
         GetClientRect(hwnd, @r);
         BitBlt(wParam, r.Left, r.Top, r.Right-r.Left,
          r.Bottom-r.Top, FHDCBis, 0, 0, SRCCOPY);
         Result := 1;
         Exit;
      end;


Now that you have a background available, you need to put it on the form. To do this, we override the message used for erasing the background, WM_ERASEBKGND, by getting the dimensions of the form (or control), and copying the background into it using BitBlt. Here you can also see why FHDCBis needs to be global - since it's needed in others run of WndMessageProc than just the one that called WM_CREATE.

      WM_CTLCOLORSTATIC: begin
         if (lParam=FLabelHandle)
         or (lParam=FGroupHandle)
         or (lParam=FGroupLabelHandle)
         then begin
            SetBkMode(wParam, TRANSPARENT);
            GetClientRect(lParam, @rClient);
            GetWindowRect(lParam, @r);
            MapWindowPoints(HWND_DESKTOP, hwnd, @r, 2);
            BitBlt(wParam, 0, 0,
             rClient.right, rClient.bottom, FHDCBis,
             r.left, r.top, SRCCOPY);
            hBrush := GetStockObject(NULL_BRUSH);
            Result := hBrush;
            Exit;
         end;
      end;


This handler is also needed in the message procedures of any controls that you use as a container (containing other controls inside them). SetBkMode is to make sure our nice background isn't filled before drawing, then the forms and controls rectangles are used in MapWindowPoints to get a background brush that has the right coordinates for the control (otherwise, the background of each control would be the same as the main background beginning from the top left corner). Finally, we draw this using BitBlt again and return an empty brush (see GetStockObject) so that Windows won't overpaint it.

      WM_DESTROY: begin
         DeleteDC(FHDCBis);
      end;


Don't forget to free this device context on WM_DESTROY (DeleteDC).

// ...
end;
// ...
end;


That's it for today! There's more to it - group box captions are not properly drawn this way, and a few things may be improved for handling rotating displays, but that's left for a future blog entry, which will then also update the example file. Credits for this hint go to kidpaddle2, who posted the idea on the developpez.com forum.

Sunday, July 23, 2006

Bounty for Symbian RTL

Cellphones are an ambivalent thing. I don't really like using them anyway, but if I use one, it has got to be near to a PDA - because I want to develop software for it to give it some use for myself. Now I'm a fan of Symbian phones, but while I can use the C++ compilers for Symbian, I'm not a fan of them. I really like Pascal, and Pascal is available for Windows CE, but I somehow don't really like Windows CE smartphones.

Now FreePascal already supports ARM processors - the Windows CE target makes use of that. Most of my existing projects are Pascal, and I hate to continue to update a C++ Symbian version, but I don't really have time to do the work to add Symbian support to FreePascal. A discussion on the Lazarus forums led me to the idea to post a bounty on the Lazarus Wiki.

I'll pay $1000 (USD) to the person who delivers (to me and the FreePascal/Lazarus team) a RTL and the necessary steps to compile Symbian applications using FreePascal (using a sample app similar to my NoFrameworkAppDemo for Windows CE); my preferred Symbian widgetset would be UIQ (both 2 and 3); Series 60, 80 and/or 90 would make nice additions to negiotate for more bounty.

If you're interested, take a look at the Lazarus bounty page, then contact me at bounty -AT- ccrdu.de .

Thursday, July 20, 2006

Making sure an application runs only in one instance

Especially on Windows Mobile 5 smartphones, it's difficult to handle an application that starts a new instance of itself each time it is started. You can't really easy get back to already open application windows, and you can't even easily close them.

But then, you most often don't really need multiple instances of an application running, since their mostly fullscreen anyway and another instance gives you no advantages. Now there are a bunch of methods to prevent that, for example creating a Mutex. If it gets created, you can continue, if it fails because the Mutex already exists, you can terminate the application.

Since Windows CE applications are centered around one main window usually, we'll make it even easier. We can use FindWindow if we know the class name or title of the window (or both) of your application (using my simple no-framework example, you can find the class name as FClassName, while using the LCL, it's probably the name of your main forms class). If this window is found, there's already another instance running, which you'll probably want to bring to the foreground.

var hExisting: THandle;
// ...
begin // main loop
   hExisting := FindWindow(sMainWindowClassName, nil);
   if hExisting<>0 then begin
      SetForegroundWindow(hExisting);
      Halt(1);
   end;
// ...
end.


This code should be at the very beginning of your application of course, since it needs to run before your main window is created (otherwise it would always quit). The disadvantage of this code would be that your class name needs to be quite unique, because depending on window titles alone wouldn't be too safe.

Other methods would be:
Method 2 (Mutex): Use CreateMutex to create a system-wide named mutex, then check if the return value is ERROR_ALREADY_EXISTS, in which case there's already another instance of the application running (that previously succeeded in creating the mutex). The disadvantage is that you've got to make sure you release it using ReleaseMutex, no matter how your application terminates. Also, when you have the mutex, you still don't have the handle of the window to bring to the foreground.

Method 3 (Message Ping): You could broadcast your own custom message through the system, using PostMessage with HWND_BROADCAST and a new message you created using RegisterWindowMessage. Then you would need to catch that as well, and reply with another similar message. The disadvantage of this method is that you need to give your application some time to receive the echo for your ping. At least the echo will contain the handle of the window of the other running instance to bring to the foreground.
With Win32, you may want to at least think about using messages to pass along parameters (for files to be opened) to the running instance using WM_COPYDATA.

Method 4 (Process list): you could enumerate all running processes to see if another process with your filename, and path if available, is running (which you can either believe or do a MD5 check to be sure it's not someone just using the same filename). If you find more than one instance, you'll have to switch to the one that is not you (doesn't have the current processes process id). For getting a processes main window handle, which you need to bring to the foreground, you can use messages or FindWindow again for example.

As you can see, all methods have their up- and downsides, but the FindWindow one is probably the easiest one if you have some kind of main window you can depend on.

Saturday, July 08, 2006

Windows applications without a framework (and not even object orientation if not wanted)

When starting to write an application for Windows CE, I noticed a few things in the LCL (Lazarus Component Library) that were not yet perfect for modern CE devices, like the missing proper fullscreen mode: up to Windows CE 2.0, the start bar was at the bottom, afterwards it was moved to the top, but out of compatibility, developers have to tell the application that they want it the modern style (and if I remember FreePascal for arm-wince correctly, it creates code for Windows CE 3.0 and upper only anyway). So I had to add that code. And then there was the thing about the close button not working on first click, the window being movable and all that stuff.

So I went to the drawing board and started to create everything nearly like in the old Turbo Pascal for Windows days, using a message procedure and creating everything by hand using CreateWindowEx. For more details regarding the controls, including special Windows CE attributes (this example works on Windows 32 as well otherwise), take a look into the Windows Mobile SDK (or even better than that link, into the downloaded version, which you'll probably need to install for the emulator to be able to install anyway).

The results are very lightweight applications that don't even need object-oriented programming like shown in my simple demonstration application, available here; though I would recommend to capsulate the window and control creation at least, and for larger projects, classes do really make sense for creating code with a structure that's way more easy to read. Showing my whole framework unit that does this would have made the basics a bit more difficult to understand though ;)

Monday, July 03, 2006

Closing LCL windows on first click on WinCE

The LCL for Windows CE is still quite uncomplete, and since I created this blog to post a few workarounds for Windows CE development, here is a hint I received myself on the Lazarus support forums:

In the OnClose event of the form, use formname.Close;.

Yes, that's all :)

Sunday, July 02, 2006

File archives on Windows CE (and all other platforms probably)

Network traffic on a Windows CE device is usually either slow (Bluetooth) or expensive (GPRS, UMTS), so it makes sense to compress and data on it's way to or from the device.

The data I had in mind were program updates, and seeing that on Windows CE the .cab file format is used even for installers, I first thought using this method provided by Microsoft would be the best and smallest method. But sadly, I couldn't find a cabinet.dll on the actual device after having tried to port to existing cab libraries and writing my own interface for that dynamic link library.

So naturally, I looked at what FreePascal and Lazarus had to offer - there were examples for bzip2 and gzip, for example. I couldn't really get both to work though, so I came back to something I tried earlier, but discarded then because of a 32K limit problem: TCompressionStream and TDecompressionStream, which use the paszlib package.

The 32K problem was just a temporary one, and testing showed these classes are compatible between Win32 and WinCE, so I wrote a small unit, ceZStreams.pas, which includes two small functions to compress and extract multiple files into/from one archive. It still needs some error handling (checking the MD5s previously stored) as well as standard features (storing and restoring file dates, paths, listing only instead of extracting, etc.), but it comes with a very simple directory structure that makes it easy to be enhanced without loosing backward compatibility.

Simple HTTP communication with FreePascal on Windows CE

One of the topics I came to when writing my first real Windows CE application was how to deal with network communications. From Delphi, I knew Indy, and from Lazarus, I knew Synapse. And even though there exists a port of Indy for FreePascal which was said to work with Windows CE, my experiences with Indy weren't so good, and Synapse explicitely tells us that it is not for Windows CE. So what can be done?

Thanks to my job, I've seen quite a lot of binary code of lightweight applications doing network communications. They all use the thing called Windows Internet, or short WinINet. This is available on both Win32 and WinCE, so I could test properly on the development machine first. Here are the necessary steps:

I've bundled all this into a small class for HTTP download handling, and having been asked in the Lazarus forums to publish it as an example, I've uploaded it as ceWinINet.pas.

Saturday, July 01, 2006

Using Lazarus to compile FreePascal applications for Windows CE devices on ARM processors

I already mentioned in my introduction post that I've switched to using FreePascal and Lazarus for most of my projects; and since I'm also developing for mobile devices, I was happy to see the development of an arm-wince part for those. Since this is quite new and I had some problems following the wiki entries for FPCs Windows CE port and Lazarus' Windows CE interface, I decided to add my own steps to my users page on the same wiki.

Part of those steps was to create some kind of mechanism to automate the steps to get a working arm-wince environment, since I was and still am playing around with constantly getting new versions. At first I though about a Lazarus app that downloads and installs everything, but in the end, it got a simple batch file, because that's what batch for. This webspace and blog is partly for offering files like lazarus-armwince.bat. I already got ideas to improve it: the arm files probably belong into a subfolder of the Lazarus folder for example, and instead of using the pp substructure from the original tutorial I based my own one on, the source could/should be downloaded into the fpcsrc substructure, user interaction for Subversion release would be nice, along with some other things.

Anyway, stay tuned for more topics and files here; examples on how to do archive compression/decompression as well as network communication from a WinCE device will follow soon.