Monday, May 19, 2008
We've published a new article on the OpenNETCF Community Site titled "Native vs. Managed Code: GDI Performance"
 
 
In it, I look at the performance differences between native and managed code making GDI calls.
 
In case you missed them, our other recently published articles include:
 
- Performance Implications of Crossing the P/Invoke Boundary
- An Introduction to WCF for Device Developers
- Getting a Millisecond-Resolution DateTime under Windows CE
- Using GDI+ on Windows Mobile
- Sharing Windows Mobile Ink with the Desktop
- OpenNETCF Mobile Ink Library for Windows Mobile 6
- Improving Data Access Performance with Data Caching
- Developing Connected Smart Device Applications with sqlClient
- Debugging Without ActiveSync
- Image Manipulation in Windows Mobile 5.0
- Don't Fear the Garbage Collector
 
All of our articles are available online at:
http://community.OpenNETCF.com/articles
 
5/19/2008 12:27:16 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Tuesday, October 30, 2007
OpenNETCF is proud to announce that we've released Padarn - an ASP.NET Web Server designed to run under the limited resource environment of Windows CE.  For more information on Padarn, visit our web site.

10/30/2007 12:51:03 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2]  | 
 Tuesday, September 04, 2007

Today we were working on some final testing of a new app and I was irritated by the About Form's behavior of generice CE devices.  When we went to display the form, we'd get the ugly "expanding rectangle" animation of the Form opening, and even worse, when we closed it it would animate the close, then animate the re-opeing of the main form.

Unfortunately the Compact Framework give us absolutely nothing for controlling this behavior (hello?  Windows Mobile isn't the only platform the CF is used on....).  So we have to work around it.

Add the following to your form (or a utility class is what we actually used, as we called this from multiple places):

private const int WS_EX_NOANIMATION = 0x04000000;
private const int GWL_EX_STYLE = -20;

[DllImport("Coredll.dll", SetLastError=true)]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

[DllImport("coredll.dll", SetLastError=true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

internal static void StopAnimation(Form form)
{
    int style = GetWindowLong(form.Handle, GWL_EX_STYLE);
    style |= WS_EX_NOANIMATION;
    SetWindowLong(form.Handle, GWL_EX_STYLE, style);
}

Then simply call StopAnimation in your Form's constructor immediately after InitializeComponent is called:

public Form1()
{
    InitializeComponent();
    StopAnimation(this);
}

We'll probably add this to the next release of the Smart Device Framework.

9/4/2007 4:41:52 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Thursday, July 05, 2007

Not surprisingly, our initial release of the OpenTimeCE library (a utility to allow developers to port code using the time_t, mktime, etc APIs to Windows CE) has some bugs in it.  First releases are rarely bug free.  What was really nice was that these fixes were purely community driven.  We'd received some "reports" in the past that there were bugs, but finally today someone (thanks Ben Murdoch) sent in a fully patched source file, so we rolled it into the code base and re-released it. It just goes to show that an open-source mindset and a Microsoft product *can* indeed not just co-exist, but provide something beneficial.

 

7/5/2007 4:43:21 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [1]  | 
 Tuesday, June 26, 2007

Like most developers, I have tons of small code samples and snippets lying around.  I'm starting to go through them in order to post what might be useful to others.

Here's one that I wrote about 5 years ago when I had a customer complaining about the speed of parsing an XML document using MSXML.  Of course this test simply confirmed that yes, MSXML is molasses-in-January slow and that if raw speed is what you want, then something like Expat is a better route to go.  However the test is a reasonably good sample of how you might use the DOMDocument from a C++ application.

As always, the code is an as-is sample.  uSe it at your own risk.

#include <windows.h>
#include "objbase.h"
#include "oleauto.h"
#include "MSXML.h"
#include "ATLBASE.h"
#import "msxml.dll" named_guids raw_interfaces_only

TCHAR *xmlfile = _T("\\test.xml");
using namespace MSXML;

void GetCurrentDirectory(TCHAR *szDirectory);


int WINAPI WinMain(    HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR lpCmdLine,
                    int nCmdShow)
{
    MSXML::IXMLDOMDocument      *iXMLDoc          = NULL;
    HRESULT                     hr;
    VARIANT_BOOL                b;
    int                         et                = 0;
    HANDLE                      hFile             = NULL;
    DWORD                       wsize, dwhigh     = 0;
    TCHAR                       filename[MAX_PATH];
    MEMORYSTATUS                ms;
    BYTE                        *buffer           = NULL;
    TCHAR                       *wbuffer          = NULL;
    DWORD                       dwRead            = 0;
    DWORD                       toalloc           = 0;
    int                         i;
    CComBSTR                    *bstr;

    _tprintf(_T("Starting XML Benchmark...\n"));
    _tprintf(_T("Creating DOMDocument..."));

    // start COM
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

    // create a DOMDocument
    hr = CoCreateInstance (MSXML::CLSID_DOMDocument, NULL,
                 CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
                 MSXML::IID_IXMLDOMDocument,(LPVOID *)&iXMLDoc);

    // check for success
    if(!iXMLDoc)
    {
        _tprintf(_T("failed (hr = %i)\n"), hr);
        goto exit;
    }

    _tprintf(_T("ok\n"));

    // synchronous operation
    iXMLDoc->put_async(VARIANT_FALSE);

    // get the data file - pull from the same directory as our app
    GetCurrentDirectory(filename);
    _tcscat(filename, xmlfile);
    _tprintf(_T("Opening XML file (%s)..."), filename);

    hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);

    if(hFile == INVALID_HANDLE_VALUE)
    {
        _tprintf(_T("failed (err = %i)\n"), GetLastError());
        goto exit;
    }

    // determine the file size
    dwsize = GetFileSize(hFile, &dwhigh);

    _tprintf(_T("ok\n"));

    // check available memory
    ms.dwLength = sizeof(MEMORYSTATUS);
    GlobalMemoryStatus(&ms);

    // either 1/3 available memory or file size, whichever is less
    toalloc = (dwsize > (ms.dwAvailPhys / 3)) ? (ms.dwAvailPhys / 3) : dwsize;

    _tprintf(_T("Allocating %i bytes..."), toalloc);
    
    //alocate buffers. data is ANSI, loadXML requires Unicode
    buffer = (BYTE *)LocalAlloc(LPTR, toalloc);
    wbuffer = (TCHAR *)LocalAlloc(LPTR, toalloc * sizeof(TCHAR) + 1);

    // check for success
    if((buffer == NULL) || (wbuffer == NULL))
    {
        _tprintf(_T("failed (err = %i)\n"), GetLastError());
        goto exit;
    }

    _tprintf(_T("ok\nReading %i bytes..."), toalloc);
    
    // fill the RAM buffer with data
    ReadFile(hFile, &buffer[0], toalloc, &dwRead, NULL);

    // lazy man's ANSI to Unicode conversion
    // this is NOT recommended for production code, don't cut and paste!!
    for(i = 0 ; i < (int)toalloc ; i++)
    {
        wbuffer[i] = buffer[i];
    }
    wbuffer[i] = '\0';
    bstr = new CComBSTR(wbuffer);

    _tprintf(_T("ok\nLoading %i bytes into DOMDocument..."), toalloc);

    // get a start time
    et = GetTickCount();

    // load the xml
    hr = iXMLDoc->loadXML(*bstr, &b);

    // determine ET
    et = GetTickCount() - et;

    // check for load failure
    if(!b)
    {
        _tprintf(_T("failed (hr = %i)\n"), hr);
        _tprintf(_T("GetLastError = %i\n"), GetLastError());
        goto exit;
    }

    _tprintf(_T("done.\n\nET = %i ms\n\n"), et);

exit:
    // release the DOMDocument
    iXMLDoc->Release();

    // release the file
    CloseHandle(hFile);

    // release COM
    CoUninitialize();

    _tprintf(_T("Exiting XML Benchmark in 5 seconds...\n"));
    
    // free buffers
    if(buffer)
        LocalFree(buffer);
    
    if(wbuffer)
        LocalFree(wbuffer);

    // wait to allow display to persist for a while
    Sleep(5000);

    return 0;
}

void GetCurrentDirectory(TCHAR *szDirectory)
{
    TCHAR *p, *p2;

    // get current directory
    GetModuleFileName(NULL, szDirectory, MAX_PATH);

    // trim off the exe name
    p = _tcsstr(szDirectory, _T("\\"));

    if(p == NULL)
    {
        // no slash found - return root?
        _tcscpy(szDirectory, _T("\\"));
        
        return;
    }

    while( p != NULL)
    {
        p2 = p + 1;
        p = _tcsstr(p2, _T("\\"));
    }

    // null terminate to crop
    p2--;
    *p2 = '\0';

    return;
}

6/26/2007 4:16:45 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Friday, June 22, 2007

A newsgroup post today reminded me of a simple app I wrote a few years back that enumerates all Fonts on a CE device, then searches a specific location (an inserted storage card in this case), adds any fonts found, then re-enumerates all the existing fonts.

Here's the code, in its entirety:

#include <windows.h>

#define FONT_PATH    (_T("\\Storage Card"))

void FindFonts(TCHAR *location, TCHAR *type);
int CALLBACK EnumFontFamProc(ENUMLOGFONT *lpelf, TEXTMETRIC *lpntm,
                             int FontType, LPARAM lParam);

int WINAPI WinMain(
HINSTANCE hInst,
HINSTANCE hInstPrev,
LPTSTR lpszCmdLine,
int nCmdShow)
{
    HDC    pDC = GetDC(NULL);
    //
    // Enumerate all the fonts
    //
    RETAILMSG(TRUE, (_T("\r\n------------------------------\r\n")));
    RETAILMSG(TRUE, (_T("Available Fonts on this device\r\n------------------------------\r\n")));
    EnumFontFamilies(pDC, NULL, (FONTENUMPROC)EnumFontFamProc, NULL);
    RETAILMSG(TRUE, (_T("------------------------------\r\n\n")));

    RETAILMSG(TRUE, (_T("Looking for fonts on Storage Card...\r\n")));


    // TrueType file (.ttf)
    FindFonts(FONT_PATH, _T("TTF"));
    // TrueType resource file (.fot)
    FindFonts(FONT_PATH, _T("FOT"));
    // TrueType collection file (.ttc)
    FindFonts(FONT_PATH, _T("TTC"));
    // raw bitmap font file (.fnt)
    FindFonts(FONT_PATH, _T("FNT"));
    // raster resource file (.fon)
    FindFonts(FONT_PATH, _T("FON"));


    RETAILMSG(TRUE, (_T("\r\n------------------------------\r\n")));
    RETAILMSG(TRUE, (_T("Available Fonts on this device\r\n------------------------------\r\n")));
    EnumFontFamilies(pDC, NULL, (FONTENUMPROC)EnumFontFamProc, NULL);
    RETAILMSG(TRUE, (_T("------------------------------\r\n\n")));

    ReleaseDC(NULL, pDC);

    return 0;
}

void FindFonts(TCHAR *location, TCHAR *type)
{
    TCHAR fontpath[MAX_PATH];
    TCHAR findpath[MAX_PATH];

    WIN32_FIND_DATA fd;

    _stprintf(findpath, _T("%s\\*.%s"), location, type);

    HANDLE hFind = FindFirstFile(findpath, &fd);
    
    if(hFind != INVALID_HANDLE_VALUE)
    {
        RETAILMSG(TRUE, (_T(" found %s\r\n"), fd.cFileName));
        _stprintf(fontpath, _T("\\Storage Card\\%s"), fd.cFileName);

        AddFontResource(fontpath);

        while(FindNextFile(hFind, &fd))
        {
            RETAILMSG(TRUE, (_T(" found %s\r\n"), fd.cFileName));
            _stprintf(fontpath, _T("\\Storage Card\\%s"), fd.cFileName);
            AddFontResource(fontpath);
        }
    }
    FindClose(hFind);
}

int CALLBACK EnumFontFamProc(ENUMLOGFONT *lpelf, TEXTMETRIC *lpntm,
                             int FontType, LPARAM lParam)
{
    RETAILMSG(TRUE, (_T("\t%s\r\n"), lpelf->elfFullName));

    return 1;
}

6/22/2007 1:50:03 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Friday, April 06, 2007

If you've done much development on non-Windows Mobile CE devices I'm sure your painfully aware of the pain in the ass process of getting Visual Studio 2005 to actually attach to the device.  Lately I've been doing nothing but non-WM development and deviced to automate and solidify the process, plus document it while I was at it.

We've got a (native) tool called CEDbgSetup that you run on your device (x86 or ARMv4I supported) and then you set up Studio once and only once, then you can debug every time using wired Ethernet, 802.11, USB RNDIS (and likely any other transport that uses TCP/IP) and without ActiveSync.

The tool supports auto-launching from \Windows\Startup or by setting up an LaunchXX entry in HKLM\Init in the device registry, or just manual click.  It should work headless too, though I've not specifically tested it.

It works with native or managed apps.

The tool, along with full source can be downloaded here (607k zip).

A white paper on how to set up Visual Studio 2005 can be viewed here (144k PDF).

Feedback is appreciated, but keep in mind that its free and completely unsupported (read: don't ask me for support).

4/6/2007 5:49:54 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2]  | 
 Thursday, March 01, 2007

I've been writing apps in mostly C# for several years now.  Of course I've also been doing drivers and kernel work in C or C++, and that has only improved my ability to write, debug and understand managed code in ways that pure managed developers will never really get.  Hell, I pretty much believe that if your managed code doesn't require allowing unsafe code then it could probably be done better.

Well for a few months now I've been neck-deep in a C++ application.  It was started using MFC, which tends to give me a rash just by saying the letters, but it was a customer decision not mine.  At any rate, I was doing some work with a RAS class where I needed any number of consumers to be able to be notified of any changes in the RAS dial status (dialing, connecting, authenticating, etc.).

Imagine a C# class for RAS that will expose events for when the dial staus changes.  THe first thing you'd need is a delegate, right?

In C# you'd have this (assuming DialStatus is an enum):

delegate void DialStatusChange(DialStatus dialStatus);

Well the c++ I wrote (without even thinking about C#) looked eerily familiar:

typedef void(*DIAL_STATUS_DELEGATE)(DialStatus newStatus);

In C# you'd use the += or -= operators for adding an event handler to a classes event, so we get spoiled.  In VB, you call AddHandler with the event name and then the address of the handler.  Well my C++ had a private vector of function pointers (which the CF maintains internally):

std::vector <DIAL_STATUS_DELEGATE> ConnectionManager::m_statusCallbackList;

And then I added methods for adding and removing handlers to the class:

 void ConnectionManager::StatusAddHandler(DIAL_STATUS_DELEGATE callback)
 {
   m_statusCallbackList.push_back(callback);
 }
 
 void ConnectionManager::StatusRemoveHandler(DIAL_STATUS_DELEGATE callback)
 {
   std::vector<DIAL_STATUS_DELEGATE>::iterator iterator;
 
   for(iterator = m_statusCallbackList.begin() ; iterator != m_statusCallbackList.end() ; iterator++)
   {
     if((*iterator) == callback)
     {
        m_statusCallbackList.erase(iterator);
        break;
     }
   }
 }

And how about usage?  In C#, our event-exposing class would have a defined event:

 event DialStatusChange OnStatusChange;

and then when we want to raise the event (assume it's multicast) from our app, we'd do something like this:

 if (OnStatusChange != null)
 {
     foreach (DialStatusChange dsc in OnStatusChange.GetInvocationList())
     {
         dsc(newStatus);
     }
 }

Well my C++ didn't need the event declaration, but raising the "event" (which is simply calling a function pointer callback) looked like this:

 std::vector<DIAL_STATUS_DELEGATE>::iterator iterator;
 
 recheck2:
 for(iterator = m_statusCallbackList.begin() ; iterator != m_statusCallbackList.end() ; iterator++)
 {
   if(IsBadCodePtr((FARPROC)(*iterator)))
   {
      // invalid callback found (someone hooked us then died without unhooking)
      m_statusCallbackList.erase(iterator);
      goto recheck2;
   }
      (*iterator)(status);
 }

Now this was just off the top of my head, so maybe there are improvements that could be made, but it kind of surprised me how I'd taken the concepts I really learned in C# and translated them back to my C++ code.  So yes, writing C# can make you a better C++ developer.

3/1/2007 12:02:32 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Thursday, February 22, 2007

For all those naysayers who complain that we don't do enough free stuff for the community, here's another contribution:

We're now hosting the latest zlib source for Windows CE and a managed wrapper for it.  Click the bitmap or here for more info.

2/22/2007 11:49:30 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Wednesday, February 07, 2007

Some days I really want to strangle Microsoft engineers.

 

For the past couple days I've been working on a project where I want a CE device to dial in to an XP desktop. In a perfect world, I'd just use RAS, create an entry, and go.  Well this isn't a perfect world.

 

Spent several hours yesterday trying different chipsets and PCs to finally determine that my device modem seems to be not-so-good and it won't negotiate at the default 56kbps that the chipset uses (than you Alex Feinman for the help).

 

The workaround is to manually slow it down with an AT command.  Using terminal on the device I just type it in and boom - it's set.  Next step was to try to repro that simple operation in code.  This is where the fun begins. Well this extra dialing data is set via the RasSetEntryProperties API in the lpb parameter.  The parameter docs say "Pointer to a buffer that contains device-specific configuration information. This is opaque TAPI device configuration information." 

 

It gives a reference to the TAPI lineGetDevConfig API for getting that data.  So we look over there and fine that it's a VARSTRING parameter.  Simple enough eh?  I fill in a VARSTRING with the command and send it in.  RasSetEntryProperties returns a success.

 

For fun I then open the connection via the UI's Network Connections.  Click on the "Configure" button and the shell completely locks up.  That can't be good.

 

So I figure, I'll see what the UI generates and use that to figure out what I screwed up.  I create a connection entry manually and add the info into the dialog.  Now clicking "Configure" works fine, so it's back to the code.

 

I call RasGetEntryProperties on the manually created entry to see what it looks like.  I see my data's in there, but it certainly isn't aligned as a VARSTRING and I have no idea what the "header" data is.  The length reported is also *way* bigger than my data.

 

  0x001291C0  30 00 00 00 78 00 00 00 10 01 00 00 00 4b 00 00  0...x........K..

  0x001291D0  00 00 08 00 00 00 61 00 74 00 2b 00 6d 00 73 00  ......a.t.+.m.s.

  0x001291E0  3d 00 76 00 33 00 32 00 2c 00 2c 00 31 00 34 00  =.v.3.2.,.,.1.4.

  0x001291F0  34 00 30 00 30 00 2c 00 32 00 38 00 38 00 30 00  4.0.0.,.2.8.8.0.

  0x00129200  30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0...............

  etc.

 

Alright, so what's going on here?  Off to the Platform Builder source (and this is why every developer should get the PB eval and install it - the source is gold) to see how the default UI is doing it.  A little GREP on the dialog text and I find that the UI handling this is, not surprisingly NETUI.

 

So after a long while tracing through convoluted code (Source Insight is an invaluable tool here) it appears that you're supposed to call lineConfigDialogEdit and have TAPI give a dialog to fill this structure.  Well what if you don't want user interaction (like in my case where the user would have no idea what to enter)?  Or even more fun, what if it's a headless device?

 

Ok, so time to regroup.  Let's search all the PB source for anything calling lineSetDevConfig to see if there's something to base my work on.  Turns out that there are a few samples, but most of them just do a lineGetDevConfig, save that data somewhere, then send it back in - they don't actually modify it.

 

One exception is the sample RASENTRY app.  It calls lineGetDevConfig, and then appears to alter that config not through lineSetDevConfig but instead by calling lineDevSpecific.  It uses this to change the baud rate.  It's not exactly what I need (I need to prevent the modem from negotiating a v.90 or v.92 connection) but a start, right?

 

So a little work and I ended up with this (well more than this, but it's the heart of it):

 

            result = lineGetDevConfig(tapiId, config, DEV_CLASS_COMM_DATAMODEM);

            ucd.dwCommand = UNIMDM_CMD_CHG_DEVCFG;

            ucd.lpszDeviceClass = DEV_CLASS_COMM_DATAMODEM;

            ucd.lpDevConfig = config;

            ucd.dwOption = UNIMDM_OPT_CFGBLOB;

            ucd.dwValue = (DWORD)MY_CUSTOM_DIALUP_STRING;

            result = lineDevSpecific(hLine, 0, NULL, &ucd, sizeof(ucd));

 

I run it and all APIs return success.  Hooray!  Right?  Open the connection with the UI on the device and the "Extra Settings" box is still blank.  At this point I have to chalk it up to a huge WTF in the Microsoft code.

 

At this point I have no option but to go the route that the UNIMODEM dialer went (again in PB source) and set it in the registry at HKEY_LOCAL_MACHINE\Drivers\Unimodem\Init.  Some may argue that this is easier and I should have gone that route in the first place, but that just doesn't sit well with me and not just because it affects all Unimodem connections on the device. 

 

I strive to understand what is going on when I'm writing code because it not only makes it easier to debug aberrant behavior but it also helps make me a better developer all around. I hate black-box programming - that is programming where I don't fully understand how something works from end to end.  Somehow TAPI gets that data into the blob returned by RasGetEntryProperties, but I'll be damned if I can figure it out. 

 

The actual blob is created or copied to  HKCU\Comm\RasBook\<connection name>\DevCfg and it appears to be an extension of HKLM\Drivers\Unimodem\DevConfig, but they are slightly different and what those differences mean is totally undocumented.

2/7/2007 3:18:21 PM (Eastern Standard Time, UTC-05:00)  #    Comments [1]  | 
 Tuesday, January 02, 2007

Sue Loh posted a really good blog entry on what seems to confuse many people - what exactlyis an SDK.  While her entry is really targeted toward the native developer, it's also quite applicable to a managed developer as well.

1/2/2007 8:04:38 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Tuesday, December 19, 2006

Over the years I've noticed that questions always come in groups.  When I worked at Applied Data Systems, when a support question came in we always went the extra mile in writing up an answer because invariably we'd get at least 2 more questions on the same topic within a week.

I still see the same thing today.  Last week a fried who is pretty new to C development asked me about returning a string from a function and I went through and explained the hows and whys.  Then today I see the same question in a newsgroup.  Rather than just provide a short answer, I figure I might as well head off the repeats that are sure to come up in the coming weeks by providing a reasonable blog entry on it so I can just post a link.

So, how do we return a string from a C or C++ function anyway?  Well the short answer is "you don't."  You can, but it's rare that you ever should.  The two exceptions that come to mind are if you have an accessor that returns a globally allocated value, like a constant, or if the return is simply a copy or internal location of a parameter to the same function (strstr is an example).  Let's look at the reason why this is the case.

Let's create a method to get a string:

TCHAR *GetString()
{
}

Now the question here is memory ownership.  For the function to return some string value, a buffer must be allocated to hold that value.  So let's add that:

TCHAR *GetString()
{
    TCHAR *value = (TCHAR*)LocalAlloc(LPTR, MAX_PATH);
    _tcscpy(value, _T("Hello memory leak"));
    return value;
}

So now we allocate a buffer and return it.  This will work, fine but the caller then must know that after they use the string they must call LocalFree or they'll have a memory leak.  Now if you're tempted to say "yes, but I'll remember that" or "yes, but it's in an internal library that I'll use LocalFree in and it will never change" then you haven't been developing long.  Rule #1 is that code will *always* be changed, or copied into another project.  Rule #2 is that it is almost always someone else who will do it. Even if you're the one that does it, believe me, you won't remember this 2 years down the road until you've burned 2 weeks trying to find the memory leak that a high-profile customer is complaining about and that management has made priority 1 for the entire team.  It happens.  Do not be tempted to do this.  Ever.

Ok, so we all agree that returning a string is bad (nod your head - yes you agree).  So how do we do it?  Well we know that to prevent a leak, the caller needs to do the allocation, so can't we just pass in the buffer as a pointer?  How about this?

void GetString(TCHAR *value)
{
    _tcscpy(value, _T("Hello overrun"));
}

The value I used should be a clue as to what the problem here is.  Let's look at a use case.

TCHAR *myValue = (TCHAR*)LocalAlloc(LPTR, MAX_PATH);
GetString(myValue);

Will this work?  Sure, in this exact case it will.  But what if GetString's value is larger than MAX_PATH?  What if the caller allocated a smaller buffer, or didn't allocate one at all?  Well you'll get a buffer overrun.  If you're lucky this will manifest as a first chance exception or something that blows up spectacularly and is easy to find.  If you're not lucky (and if you're against a tight deadline, you won't be) it will cause unexpected and non-reproducible behavior that is a real bitch to debug (on a Palm I've seen this cause execution to jump right to another application with no warning - try debugging that). 

So this could be even worse than our original FUBAR (if you're unfamiliar I'll let you look it up) code.  So what's the right way to do this?  Well a good indication is how the Win32 APIs work.  Look at something like RegQueryValue.  It takes a buffer pointer and a size. And if the function is really nice, it will tell me how big the buffer should be if the caller has it too small.  Let's look at an example.

BOOL ReturnString3(TCHAR *myString, DWORD *size)
{
    DWORD requiredSize = 0;
    TCHAR *buffer = _T("Hello nice function");

    // determine how big the buffer must be
    requiredSize = _tcslen(buffer);

    if(IsBadWritePtr(myString, requiredSize)
    {
        *size = requiredSize;
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    _tcscpy(myString, buffer);
    *size = _tcslen(myString);
    return TRUE;
}

Here you see that we first determine how big the buffer should be and then we check the incoming buffer against that number.  If it fails, we copy the required size to the size parameter, set a meaningful error, and return FALSE so the caller knows we failed (we can't force them to read the return, but if they don't that's just poor practice).

If the buffer is big enough, we copy in the value, set the actual size (in case they want it for something) and return TRUE.

A use case would look like this:

TCHAR *getString = NULL;
DWORD size = 0;

// this is supposed to fail - it gets the size
ReturnString3(getString, &size);

// now allocate a buffer
getString = LocalAlloc(LPTR, size);

// and get the data
BOOL success = ReturnString3(getString, &size);

So now you've seen the good, the bad and the ugly on how to return a string froma C function.  I have no illusions that me posting this will stop people from asking - even if it ends up as the number 1 result for Google.  Let's face it, you have to know enough to do the search for it to be of any use.  But hopefully, those who are in the industry because they like to learn and don't like to write crap code can either find it or be pointed to it and make good use of it.

12/19/2006 9:58:42 AM (Eastern Standard Time, UTC-05:00)  #    Comments [2]  | 
 Monday, December 11, 2006

Over the last week of so I've noticed several questions in the newsgroups about how to synchronize a CE device's time with a time server.  Once again, it's not something I'd done, but I knew there were servers out there that provide the time publicly, so it couldn't bee too tough.

First stop was to see if Wikipedia gave a simple explanation.  Useful, but not exactly what we need - we want to know exactly what the server expects, and what exactly it returns.  It does give us the RFCs.

So a little looking at RFCs and we see that RFC 2030 is very applicable, and gives us all the info we need about the protocol.

A little more looking with Google found a public server domain name from NTP.org that actually rotates through public NTP servers, so we don't have to worry about one being up or not.

Armed with nothing but the second two links I wrote a little code.  This could be far more robust, with fractional seconds. mode, stratum, precision info and all that, but I just wanted to get a reasonable time - so to the second is all I was after.

public DateTime GetNTPTime()
{
   // 0x1B == 0b11011 == NTP version 3, client - see RFC 2030
   byte[] ntpPacket = new byte[] { 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

   IPAddress[] addressList = Dns.GetHostEntry("pool.ntp.org").AddressList;

   if (addressList.Length == 0)
   {
      // error
      return DateTime.MinValue;
   }

   IPEndPoint ep = new IPEndPoint(addressList[0], 123);
   UdpClient client = new UdpClient();
   client.Connect(ep);
   client.Send(ntpPacket, ntpPacket.Length);
   byte[] data = client.Receive(ref ep);

   // receive date data is at offset 32
   // Data is 64 bits - first 32 is seconds - we'll toss the fraction of a second
   // it is not in an endian order, so we must rearrange
   byte[] endianSeconds = new byte[4];
   endianSeconds[0] = (byte)(data[32 + 3] & (byte)0x7F); // turn off MSB (some servers set it)
   endianSeconds[1] = data[32 + 2];
   endianSeconds[2] = data[32 + 1];
   endianSeconds[3] = data[32 + 0];
   uint seconds = BitConverter.ToUInt32(endianSeconds, 0);

   return (new DateTime(1900, 1, 1)).AddSeconds(seconds);
}

12/11/2006 10:59:10 AM (Eastern Standard Time, UTC-05:00)  #    Comments [4]  | 
 Thursday, December 07, 2006

I was doing a search for some info just now and came across a really well done article describing drivers in Windows CE.  Check it out.

12/7/2006 6:17:45 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Saturday, February 18, 2006

I'm trying to debug an error in some managed code related to timezones when a P/Invoke is throwing a native access violation exception.  To make sure it's not something with the platform itself I decided to do the same in native code first to ensure that it's not some platform limitation or bug.  Surprisingly I didn't readily find any sample code for doing it, so here's my contribution to the community at large.  It uses dynamic loading (and it doesn't make sure that the function loads succeed, so you might want to