Mobile Development - 'Support Side Story'

Broadcasting technical support to Windows Mobile\CE Application Developers to help realizing their potential

Broadcasting technical support to Windows Mobile\CE Application Developers to help realizing their potential

March, 2008

  • Mobile Development - 'Support Side Story'

    Supporting Kiosk-Applications on Windows Mobile (Technically achievable vs. supported)

    • 8 Comments

    - Why unsupported

    - Supported examples

    - System Center Mobile Device Manager 2008

    Every now and then we receive requests from ISV Application Developers regarding how to restrict users' access to the device functionality by writing a KIOSK application: many times it happens that Application Developers are asked to develop such a solution *AFTER* the final customer chose and bought all the devices to be used on the field, and many times they happen to be Windows Mobile-based, not CE. You're lucky if all the devices share the same manufacturer and model...

    This brings me talking about supporting developers with problems when writing a kiosk-mode application. The Microsoft Windows *Mobile* platforms do not support a "Kiosk" type solution. It is designed from a PDA background, where the user is in control of the device rather than any one user application. There is always going to be some UI event that will cause the kiosk mode to be broken out of. This is true of Windows Mobile 2003, 5.0 and 6, and for both Pocket PC or Smartphone.

     

    When a request similar to that comes from a device-manufacturer or at early stage of the "project", we usually suggest using Windows CE instead of Windows Mobile. This may involve working with a hardware vendor to get a custom embedded device that meet your needs. While the Windows Mobile platforms are not designed for Kiosk-type applications, Windows CE product can be used to build custom operating systems for embedded devices of all types. Windows CE is fully customizable, so a hardware vendor can tailor it specifically to support Kiosk usage.

     

    Marcus Perryman, Mike Hall and Rabi Satter blogged about this:

    So the main problem an application developer can face is that the UI will always have some Windows Messages that he\she might potentially have not trapped (see for example the “Run” dialog when tapping the clock together with the Action button of some Pocket PCs...).

    I'm not stating that it's not technically achievable: there are even some 3rd party solutions on the market that demonstrate this and there are external consultants available for such matter. However, since it's conceptually something the platform was not designed\tested\documented for, then it's something Microsoft can't provide support about.

    Having said that and setting the right expectations, in some special cases it might be that the contextual problem the developer is asking support about can be seen from a different perspective, without considering that the final application will be a kiosk-one. For example, it might be that (1) the developer needs support with a full-screen application, which is a documented topic in the Windows Mobile's SDKs and therefore it's completely supported. Or, it might be that (2) the developer needs help on having an application running at system startup. Or, (3) the developer might need help on knowing how to soft-reset the device (when the application is unexpectedly closed, for example). This is NOT the case when a developer wants for example to remove phone-UI on a phone-powered Windows Mobile device; or this is NOT the case when a developer wants to remove the OS taskbar altogether and\or subclass it; and so on... In such cases, even if the request came from an OEM we couldn't help because its platform would probably not pass the Windows Mobile Certification Logo Test. Again, technically achievable doesn't mean supported...

    So, let's keep in mind the 3 "supported" examples above:

    (1) the developer needs support with a full-screen application, which is a documented topic in the Windows Mobile's SDKs and therefore it's completely supported.

    Sometimes it's not sufficient on NETCF applications to have:

    //imagine the following in Form's ctor, after InitiliazeComponents
    this.FormBorderStyle = FormBorderStyle.None;
    this.WindowState = FormWindowState.Maximized;
    this.ControlBox = false;
    this.MinimizeBox = false;
     
    and sometimes it's not sufficient on native applications to invoke SHFullScreen, even if you set SetForegroundWindow before and MoveWindow afterwards:
    RECT  rc;
    SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
    SetForegroundWindow(hWnd); //just to make sure main window it's foreground
    if (SHFullScreen(hWnd, SHFS_HIDETASKBAR | SHFS_HIDESTARTICON | SHFS_HIDESIPBUTTON))
    {
        MoveWindow(hWnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, TRUE);
    }
     
    It might not be sufficient for many reasons, depending on the actual scenario:

    a- for example you want the hardware buttons to continue working (such as volume control)

    b- for example you want to remove the taskbar but maintain SIP button and therefore you need to show SIP Options dialog correctly

    c- for example you have many forms and when switching among them user can see the taskbar flickering (and this undesired effect is dependent on the complexness of the form that is going to be shown, expressed in terms of numbers of child controls)

    d- for example in your code you show a dialog at some point (MessageBox.Show() or frm2.ShowDialog()) and don't want the taskbar or the menubar or the SIP to appear while the dialog is shown

    e- for example you want to intercept pressing hardware buttons

    - ...

    Considering NETCF applications (but since I'm going to talk about P/Invoke some Windowing API the same applies to native applications) the best approach I've ever found (and in the past I tried with many combinations of MoveWindow, ShowWindow, SetWindowLong, etc...), which addresses "specifications" a, b, c of the above in one shot is the one recommended by Rabi Satter in the post I mentioned above (Kiosk Pattern).

    !WARNING! Remember in any case that before closing the application you must unhide the taskbar and you must consider that if your application closes unexpectedly nothing will know to unhide the taskbar again. So you can think for example of a"shell"-like application that checks if yours is running and if not launch it again, similarly to Hopper's "FocusApp" sample provided with WM6's SDK (or checks if your process is not running through OpenProcess and if so unhide the taskbar, for example):

    //
    // FocusApp.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include <windows.h>
    
    #define ONE_SECOND      1000
    #define TEN_SECONDS     (10 * ONE_SECOND)
    #define TWENTY_SECONDS  (2 * TEN_SECONDS)
    #define ONE_MINUTE      (3 * TWENTY_SECONDS)
    #define FIVE_MINUTES    (5 * ONE_MINUTE)
    #define FIFTEEN_MINUTES (3 * FIVE_MINUTES)
     
    // Adjust the following to suit your needs
    #define SLEEP_TIMEOUT TEN_SECONDS
    TCHAR *g_pszAppName = TEXT("\\windows\\wmPlayer.exe"); 
     
    //------------------------------------------------------------------------------
    int _tmain(int argc, _TCHAR* argv[])
    {
        TCHAR tszTmp[MAX_PATH];
        PROCESS_INFORMATION piProcInfo; 
     
        while(TRUE)
        {
            wsprintf(tszTmp, TEXT(" ... Relaunching: %s"), g_pszAppName);
            OutputDebugString(tszTmp);
            if(! CreateProcess(g_pszAppName, NULL, NULL, NULL, FALSE, 0, NULL, 
                    NULL, NULL, &piProcInfo))
            {
                goto Error;
            }
     
            // Adjust this value to suit your needs above
            Sleep(SLEEP_TIMEOUT);
        }
    Error: 
        // Error condition - should never get here.
        wsprintf(tszTmp, TEXT("ERROR: could not launch %s, last error: %d"), 
            g_pszAppName, GetLastError());
        OutputDebugString(tszTmp);
        return(TRUE);
    }
     

    For point d above the best approach I found (but there might be others, depending on the actual scenario) is to override OnActivate and OnDeactivate events. Note that OnDeactivate is raised also after closing the form, which is the point where you want to unhide the taskbar and restore usual UI. Finally, for point e you can basically use Microsoft.WindowsCE.Forms.HardwareButton class' AssociatedControl property.

    The code might look like the following (provided 'as is', without error-checking - moreover I wanted to maintain Rabi Satter's methods as they were, so the code might be better accommodated):

    public Form1()
    {
        InitializeComponent();
    
        this.FormBorderStyle = FormBorderStyle.None;
        this.WindowState = FormWindowState.Maximized;
        this.ControlBox = false;
        this.MinimizeBox = false;
    
        Util.HideTaskbar();
    }
    
    //Deactivate is raised when:
    //  - a Messagebox is shown
    //  - another form is shown
    //  - SIP Options dialog is shown
    //  - the application is closed
    //SHFullScreen is successful only if it receives the handle of the foreground window
    protected override void OnDeactivate(EventArgs e)
    {
        //if the application is closing then you don't want to remove menu and
        //you want to remove associations with hardware buttons
        if (!closing) 
        {
            this.Menu = null;
            IntPtr handle = Util.GetForegroundWindow();
            Util.SHFullScreen(handle, Util.SHFS.HIDESTARTICON | Util.SHFS.HIDESIPBUTTON );
            Util.UnhideTaskbar();
        }
        base.OnDeactivate(e);
    }
    
    protected override void OnActivated(EventArgs e)
    {
        this.Menu = this.mainMenu1;
        Util.SetForegroundWindow(this.Handle);
        Util.SHFullScreen(this.Handle, Util.SHFS.HIDESTARTICON | Util.SHFS.SHOWSIPBUTTON);
        Util.HideTaskbar();
    
        base.OnActivated(e);
    }
    
    
    //this is required if you want to completely restore the taskbar (with the Start button as well)
    //when exiting the application
    private void Form1_Closed(object sender, EventArgs e)
    {
        IntPtr handle = Util.GetForegroundWindow();
        Util.SHFullScreen(handle, Util.SHFS.SHOWSIPBUTTON | Util.SHFS.SHOWSTARTICON | Util.SHFS.SHOWTASKBAR);
        Util.UnhideTaskbar();
        
        hardwareButton1.AssociatedControl = null;
        hardwareButton2.AssociatedControl = null;
        hardwareButton3.AssociatedControl = null;
        hardwareButton4.AssociatedControl = null;
        hardwareButton5.AssociatedControl = null;
        hardwareButton6.AssociatedControl = null;
    }
    
    
    //this is needed when you want to exit the application (Closing->Closed->Deactivate)
    private bool closing = false;
    private void Form1_Closing(object sender, CancelEventArgs e)
    {
        closing = true;
    }
    
    
    private void Form1_Load(object sender, EventArgs e)
    {
        hardwareButton1.AssociatedControl = this;
        hardwareButton1.HardwareKey = HardwareKeys.ApplicationKey1;
        hardwareButton2.AssociatedControl = this;
        hardwareButton2.HardwareKey = HardwareKeys.ApplicationKey2;
        hardwareButton3.AssociatedControl = this;
        hardwareButton3.HardwareKey = HardwareKeys.ApplicationKey3;
        hardwareButton4.AssociatedControl = this;
        hardwareButton4.HardwareKey = HardwareKeys.ApplicationKey4;
        hardwareButton5.AssociatedControl = this;
        hardwareButton5.HardwareKey = HardwareKeys.ApplicationKey5;
        hardwareButton6.AssociatedControl = this;
        hardwareButton6.HardwareKey = HardwareKeys.ApplicationKey6;
    }
    
    private void Form1_KeyDown(object sender, KeyEventArgs e)
    {
        switch ((HardwareKeys)e.KeyCode)
        {
            case HardwareKeys.ApplicationKey1:
                MessageBox.Show("Hardware Key 1");
                break;
            case HardwareKeys.ApplicationKey2:
                MessageBox.Show("Hardware Key 2");
                break;
            case HardwareKeys.ApplicationKey3:
                MessageBox.Show("Hardware Key 3");
                break;
            case HardwareKeys.ApplicationKey4:
                MessageBox.Show("Hardware Key 4");
                break;
            case HardwareKeys.ApplicationKey5:
                MessageBox.Show("Hardware Key 5");
                break;
            case HardwareKeys.ApplicationKey6:
                MessageBox.Show("Hardware Key 6");
                break;
        }
    }
     

    where Util class is:

    //see Rabi Satter's http://www.satter.org/2007/04/kiosk_pattern.html
    public class Util
    {
        public static int TaskbarHeight = Screen.PrimaryScreen.Bounds.Height - Screen.PrimaryScreen.WorkingArea.Height;
    
        public static void HideTaskbar()
        {
            IntPtr handle;
            try
            {
                // Find the handle to the Start Bar
                handle = FindWindow("HHTaskBar", null);
    
                // If the handle is found then hide the start bar
                if (handle != IntPtr.Zero)
                {
                    // Hide the start bar
                    SetWindowPos(handle, 0, 0, 0, 0, 0, SWP.SWP_HIDEWINDOW);
                }
            }
            catch
            {
                MessageBox.Show("Could not hide Start Bar.");
            }
        }
    
        public static void UnhideTaskbar()
        {
            IntPtr handle;
            try
            {
                // Find the handle to the Start Bar
                handle = FindWindow("HHTaskBar", null);
    
                // If the handle is found then show the start bar
                if (handle != IntPtr.Zero)
                {
                    // Show the start bar
                    SetWindowPos(handle, 0, 0, 0, Screen.PrimaryScreen.Bounds.Width, TaskbarHeight, SWP.SWP_SHOWWINDOW);
                }
            }
            catch
            {
                MessageBox.Show("Could not show Start Bar.");
            }
        } 
    
        
        [DllImport("aygshell.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SHFullScreen(IntPtr hwndRequester, SHFS dwState);
    
        [DllImport("coredll.dll", SetLastError = true)]
        public static extern IntPtr FindWindow(string _ClassName, string _WindowName);
    
        [DllImport("coredll.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetWindowPos(IntPtr hwnd, int hwnd2, int x, int y, int cx, int cy, SWP uFlags);
    
        [DllImport("coredll.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool MoveWindow(IntPtr hWnd, int x, int y, int cx, int cy, bool repaint);
    
        [DllImport("coredll.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);
    
        [DllImport("coredll.dll", SetLastError = true)]
        public static extern IntPtr GetForegroundWindow();
    
        [Flags()]
        public enum SHFS
        {
            SHOWTASKBAR = 0x0001,
            HIDETASKBAR = 0x0002,
            SHOWSIPBUTTON = 0x0004,
            HIDESIPBUTTON = 0x0008,
            SHOWSTARTICON = 0x0010,
            HIDESTARTICON = 0x0020,
        }
    
        [Flags()]
        public enum SWP
        {
            SWP_ASYNCWINDOWPOS = 0x4000,
            SWP_DEFERERASE = 0x2000,
            SWP_DRAWFRAME = 0x0020,
            SWP_FRAMECHANGED = 0x0020,
            SWP_HIDEWINDOW = 0x0080,
            SWP_NOACTIVATE = 0x0010,
            SWP_NOCOPYBITS = 0x0100,
            SWP_NOMOVE = 0x0002,
            SWP_NOOWNERZORDER = 0x0200,
            SWP_NOREDRAW = 0x0008,
            SWP_NOREPOSITION = 0x0200,
            SWP_NOSENDCHANGING = 0x0400,
            SWP_NOSIZE = 0x0001,
            SWP_NOZORDER = 0x0004,
            SWP_SHOWWINDOW = 0x0040
        }
    }
    (2) the developer needs help on having an application running at system startup.

    This is a matter of setting some registry keys under HKLM\Init as documented in Configuring the Process Boot Phase and add a call to SystemStarted() in the application. Or, an alternative approach was proposed by Mike Hall here.

    (3) the developer might need help on knowing how to soft-reset the device (when the application is unexpectedly closed, for example).

    The article How to: Reset the Device contains NETCF  sample code.

    The upcoming "Microsoft System Center Mobile Device Manager 2008" (second quarter of 2008) will give the ability to IT administrators to control the devices connected through Exchange by using Active Directory\Group-Policies. Some of the group policies are related to prevent users to modify certain device-settings and to install\run only some specified applications. From Product Reference Guide: "[...] Mobile Device Manager allows IT administrators to either “enable” or “disable” specific applications or sets of applications. Disabled applications cannot be installed on a managed Windows Mobile device. System Center Mobile Device Manager can also set a list of enabled applications, which are the only applications that a user can install on a device". Another interesting introductive reading is the Technical Overview White Paper.

  • Mobile Development - 'Support Side Story'

    x.inf contains DirIDs, which are not supported

    • 7 Comments

    Since looking around over the web I didn't find any reference to this particular case, I think it's worth writing it here. I tracked down this issue recently together with a developer that wanted to package "many" files within a single .CAB (1000+). He was using a quite old version of CabWiz.exe, launched through an application he wrote. So, as the initial troubleshooting step, I tried to reproduce the issue with the CabWiz.exe shipped with Visual Studio 2008: same problem.

    Above all, this error can arise from CabWiz (which is internally invoked by VS2008 when creating Smart Device CAB Projects) also if you're using '%' in registry keys, as my friend Chris reported in his How to avoid hardcoding file paths within CAB file registry settings, in order to add registry values that refer to the location of files installed as part of the application.

    So I thought: ok, he's hitting the known limit of 998 files that could be packaged in a CAB... Wrong: the problem happened also when using much less files, say 300... I'm still talking about C:\Program Files\Microsoft Visual Studio 9.0\SmartDevices\SDK\SDKTools\CabWiz.exe, version 4.0.4332.0.

    After running some tests, I reached a magic number: 262. With 262 files VS2008 could create a Smart Device CAB successfully, with 263 files it gave error "x.inf contains DirIDs, which are not supported". At this point I noticed that in the .INF file that VS2008 creates and gives to CabWiz.exe, it wrote an entry under [DestinationDirs] for each added file, even if the target folder on the device is the same, as it was in my case. By manually editing the INF (using some applications that automated lines-creation...), I could successfully create a CAB for 263+ files, specifying only one entry under [DestinationDirs]. So, another limit I wasn't aware of is: VS2008's CabWiz can parse up to 262 different entries under [DestinationDirs].

    Now: Windows Mobile 6 SDKs ship with a newer version of CabWiz.exe, v4.5.5102.0 and, very interestingly, it overcame not only the limitation about 998 files, but also the one about 262 entries under [DestinationDirs]. So the "simple" solution in this case was to download one of the 2 Windows Mobile 6 SDKs and use that CabWiz.exe (C:\Program Files\Windows Mobile 6 SDK\Tools\CabWiz).

    Playing with files under C:\Program Files\Microsoft Visual Studio 9.0 is not recommended (and probably "not supported"), however for testing purposes I replaced C:\Program Files\Microsoft Visual Studio 9.0\SmartDevices\SDK\SDKTools\CabWiz.exe and CabWiz.ddf with the ones under C:\Program Files\Windows Mobile 6 SDK\Tools\CabWiz, and now my VS2008 can continue creating one entry under [DestinationDirs] for each file, independently if the target folder will the be same, but now it successfully creates the CAB. I imagine there are still some limits, but this may be a topic of another post... Smile

    Cheers,

    ~raffaele

  • Mobile Development - 'Support Side Story'

    GPS Programming Tips for Windows Mobile - Part 1

    • 3 Comments

    - Introducing GPS Intermediate Driver

     

    Scenario: jogging in a park with a friend. You wouldn't like to carry on a Pocket PC with you, even if with integrated GPS Antenna (ok, you may prefer it...). So I chose a Smartphone in a pocket and the Bluetooth GPS Antenna in the other. Some runners prefer to have empty pockets or none at all, but I considered it a good deal.

     

    Windows Mobile 5.0 introduced the GPS Intermediate Driver: "The GPS Intermediate Driver provides a very simple-to-use API for providing shared access to GPS data". By P/Invoking the stub gpsapi.dll, you can interact with the GPS Intermediate Driver below (gpsid.dll). Few APIs are needed:

    #region PInvokes to gpsapi.dll
    [DllImport("gpsapi.dll")]
    static extern IntPtr GPSOpenDevice(IntPtr hNewLocationData, IntPtr hDeviceStateChange, string szDeviceName, int dwFlags);
    
    [DllImport("gpsapi.dll")]
    static extern int  GPSCloseDevice(IntPtr hGPSDevice);
    
    [DllImport("gpsapi.dll")]
    static extern int  GPSGetPosition(IntPtr hGPSDevice, IntPtr pGPSPosition, int dwMaximumAge, int dwFlags);
    
    [DllImport("gpsapi.dll")]
    static extern int  GPSGetDeviceState(IntPtr pGPSDevice);
    #endregion

    There's even 2 handy code samples available in the SDK (native and managed), that have everything you need to start with, together with relevant and helpful MSDN Documentation. An important detail to know about GPS Intermediate Driver is that on Smartphones you won't find the control applet, the one that would allow different applications to access the driver at the same time without interfering with each other, as reported in Setting up GPS on Windows Mobile 5 by Jason Fuller, thus having made the GPS Intermediate Driver unusable on Smartphones, in the sense that only one application can access the underlying GPS device at a time.

    However, Windows Mobile 6 SDKs come with a pretty solution to this limitation: "GpsSettings.exe enables Smartphone users to set up the GPS intermediate driver. It works the same as the built-in GPS settings applet on Pocket PC." (C:\Program Files\Windows Mobile 6 SDK\Tools\GPS\Settings.exe). Windows Mobile 6 SDKs come also with a Fake GPS, so that developers can emulate receiving "data using the GPS APIs even if there is no GPS receiver on the device. The GPS data is read from NMEA .txt files that get deployed to \Program Files\FakeGPS\GPSFiles when FakeGPS.CAB is installed on the device" (C:\Program Files\Windows Mobile 6 SDK\Tools\GPS\FakeGPS.CAB).

    A developer can work with GPS APIs if the GPS Antenna is integrated on the device and also if it's connected by other means, such as Bluetooth for example, as long as it can be considered a virtual COM port, which is set through the GPS settings applet.

    However, if you want to play with NMEA sentences on your own, you can retrieve the streaming data from the antenna and interpret it, and raise events so that upper layers (typically the UI, for example). Before leveraging on the GPSID, I exactly wanted to play with NMEA sentences (once in my life!), so that I could later better appreciate the GPSID... more on the next post.

    Cheers,

    ~raffaele

  • Mobile Development - 'Support Side Story'

    Configuring Devices through XML Provisioning from the Desktop

    • 2 Comments

    Many developers know (or should know) the bedrock principle of software development: "KNOW THY USER, FOR HE IS NOT THEE" [cit. from David S. Platt, President of Rolling Thunder Computing]. So if you know that device users are not as geek as you and it might even be that they won't be completely confident about copying a CAB and launch it from within the File Explorer on the device, then you may consider the following scenario. Imagine you want to let a Windows Mobile-based device user configure a device without asking him\her to copy a CAB to the device, but instead you want to leverage on the ActiveSync's or WMDC's "Add/Remove Programs" semi-automatic feature: so, we're talking about a sort of a desktop ConfigureMyDevice.exe (or .bat, or .cmd), which internally launches CeAppMgr.exe (still on the desktop), which the user can simply double-click on, and the next time he\she cradles the device it's automatically deployed and installed.

    Well, when a developer asked for support about that, I didn't know the post provided the Windows Mobile Dev Team Injecting Provisioning XML into a cab using VS 2005, and we came out with a slightly different approach, yet satisfying in our case and still based on the /postxml option provided by CabWiz.exe. The limitation of the VS2005 and VS2008 in this case is that Smart Device CAB projects allow developer to target only the FileOperation and the Registry CSPs (Configuration Service Provider).

    You may say at this point that if your goal is to "simply" configure a cradled device connected through ActiveSync\WMDC you can also programmatically launch RapiConfig.exe instead of relying on ActiveSync\WMDC's "Add/Remove Programs" (in any case, note that using "Add/Remove Programs" does NOT require a partnership with the cradled device, so you wouldn't ask so to device users), but remember that RAPI may be disallowed or restricted on the target device (more on this possibly on a later post) and then there might be some issues with redistributing RapiConfig.exe as it is. If I recall correctly, developers are not allowed to redistribute RapiConfig.exe and if you really need to let users have it and don't want to ask them to download\install one of the Windows Mobile SDKs, you can let them download the much smaller Microsoft Exchange Server ActiveSync Certificate-Based Authentication Tool (123 KB), which contains an edition of RapiConfig.exe as well.

    The steps  that were satisfactory in our case were:

    0. Create a Smart Device CAB project through VS2005

    1. Through the File Editor add it a dummy txt file, whose contents are for example (but really, it can be anything):
    ==============================================================================
    THIS IS SAMPLE CODE AND IS ENTIRELY UNSUPPORTED. THIS CODE AND INFORMATION
    IS PROVIDED "AS-IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
    INCLUDING BUT NOT LIMITED TO AN IMPLIED WARRANTY OF MERCHANTABILITY AND/OR
    FITNESS FOR A PARTICULAR PURPOSE.
    ==============================================================================

    This will show how the FileOperation CSP will be added the resulting _setup.xml file. Adding a "dummy" file was a required step, because otherwise Visual Studio complains stating that "You tried to build a setup project but did not include any files.  You must specify at least one file before continuing.".

    2. Through the Registry Editor, add a key: this will show how the Registry CSP will be added the resulting _setup.xml file. Contrarily to the file, this is not strictly required.

    3. You can't add other CSPs by using VS2005's editors within the Smart Device CAB project: if you want to add other XML you have to launch CabWiz.exe manually, and using /postxml command-line option. See below.

    4. Starting from the INF file created in the steps above, copy the SmartDeviceCab1.INF file in a folder (let’s name the folder C:\test and the file test.inf). You should edit the .INF file in 2 points:
    - AppName="SmartDeviceCab1" (you may want to change the name that will appear on the device “smartdevicecab1 was installed successfully…”)
    - Under [SourceDisksNames], modify to the current directory, i.e.
    1=,"Common1",,"C:\test"

    5. Create a XML file with the provisioning you want to be added to the _setup.xml: please note that you must NOT add <wap-provisioningdoc>. For example, the usual test one:

    <characteristic type="BrowserFavorite"> 
      <characteristic type="Southridge Video Store"> 
        <parm name="URL" value="<http://www.southridgevideo.com/>"/> 
        <parm name="Order" value="0"/> 
      </characteristic> 
    </characteristic> 

    and name it _setupToAppend.xml.

    6. now you should launch CabWiz.exe, and in order to make things reproducible you save it in a .bat or .cmd: < "C:\Program Files\Microsoft Visual Studio 8\SmartDevices\SDK\SDKTools\CabWiz.exe" "C:\test\test.inf" /postxml _setupToAppend.xml /nouninstall >
    (NOTE the /postxml option)
    This will create the CAB file: you should open it to verify if the resulting contained _setup.xml is what you were looking for and if it contains the 2 .000 and .001 files based on the "dummy" txt file.

    7. Now that we have the CAB, you should use the sample2.INI file - in my case I used the following (you might want to change the cab file name):
    [CEAppManager]
    Version = 1.0
    Component = test

    [test]
    Description = Sample2
    CabFiles = test.CAB

    8. Finally, you should cradle the device (or you can do it later) and launch the CeAppMgr.exe by specifying the .ini: < "C:\Program Files\Microsoft ActiveSync\CEAPPMGR.EXE" "C:\test\sample2.ini" > (you don't need to create an .exe, but a .bat or a .cmd would suffice).

  • Mobile Development - 'Support Side Story'

    GPS Programming Tips for Windows Mobile - Presentation

    • 1 Comments

    Do you love SportsDo? I do. Smile "SportsDo is a GPS sports tracking system for your mobile phone which enables you to record your sporting activities while broadcasting live tracking stats to friends and family via the SportsDo web portal." [cit.] SportsDo was even mentioned by Bill Gates at MEDC2005 in Las Vegas (I was there!) during the launch of Windows Mobile 5.0, as an example of mobile application that exploits some cool capabilities of the platform.

    So I asked myself: what about developing (for my own and some friends' fun) a sort of very limited clone of it? Well, I'm in process of it at the moment... I reached a beta1 version: it works but I realized I need to review some architectural choices to make it more usable... more on this later. And I would really have loved a unique blog\web-page that could have helped me with some basic tips.

    That's why I'm going to write a series of posts based on the subject "GPS Programming Tips for Windows Mobile", hoping that others may benefit from my (admittedly limited!) experience.

    Cheers,

    ~raffaele

  • Mobile Development - 'Support Side Story'

    GOTCHA!

    • 0 Comments

    At the end someone is really reading my posts... good to know!! Open-mouthedThanks Chris so much for your kind words! Raffael’s new blog - tips for Windows Mobile ISVs from a Microsoft Support Engineer

     

    So... Stay tuned!

    ~raffaele

     

    P.S. I can't promise a quick answer (if any Angel), but should you have any topic you'd like me to discuss about, pls feel free to add a comment!

  • Mobile Development - 'Support Side Story'

    "Socket Unknown Error" on Windows Mobile

    • 0 Comments

    Have you ever got a "Socket Unknown Error" on Windows Mobile 5.0 and 6 devices, while the exact same code worked with no issues on Windows Mobile 2003? Well, it took some time to understand where the problem lied so I think this post may add some value. Basically we understood that WM5.0 and 6 require that AddressFamily is specified when creating a new socket, because since WM5 both IPv4 and IPv6 are supported. So, the following managed code worked with WM2003, but returned the unspecified "Socket Unknown Error":

    Socket clientSocket = new Socket(AddressFamily.Unspecified, SocketType.Stream, ProtocolType.Tcp);
    The same happened with native code, so the problem was not with NETCF's Socket class:
    #include "stdafx.h"
    #include <windows.h>
    #include <commctrl.h>
    #include <winsock2.h>
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        WSADATA wsaData;
        SOCKET conn_socket = INVALID_SOCKET;
        int retval = 0;
    
        // Load Winsock
        if ((retval = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
        {
            fprintf(stderr,"WSAStartup failed with error %d\n",retval);
            WSACleanup();
            return -1;
        }
    
        //test conn   
        //Socket clientSocket = new Socket(AddressFamily.Unspecified, SocketType.Stream, ProtocolType.Tcp);
        conn_socket = socket(0, SOCK_STREAM, IPPROTO_TCP);
        if (conn_socket == INVALID_SOCKET)
        {
            fprintf(stderr, "socket failed: %d\n", WSAGetLastError());
            //goto cleanup; //...
        }
    
        return 0;
    }
     

    As I said the point is that since WM5 there is IPv4 and IPv6 and you have to specify which version the socket should be created for: for IPv4 you should use AF_INET as the address family specification while for IPv6 you should use AF_INET6. Therefore, specifying the address family resolved it:

    Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    Native:

    //IPPROTO_TCP is documented for backward compatibility with Winsock 1.1. 
    //SOCK_STREAM always uses TCP for the Internet address family
    conn_socket = socket(AF_INET, SOCK_STREAM, 0); 
Page 1 of 1 (7 items)