Monday, July 30, 2012

WinCE 5.0 debuggin again!

Why am I making this post in 2012? We'll I have a new work now and my first task today was to look at the USB driver of a WinCE 5.0 based system. So for refresher's I'm posting key areas of WinCE debugging.

1.) The bootloader and startup stuffs- check this is you want to know how it's done in WinCE 5.0 -> http://msdn.microsoft.com/en-us/library/aa446905.aspx

2.) Adding a device driver to a catalogue -> http://msdn.microsoft.com/en-us/library/cc440212.aspx

3.) Build a retail image instead of debug then later make it a ship build. Check out the difference of these logs here -> http://blogs.msdn.com/b/ce_base/archive/2006/12/18/debug-messages-and-debug-zones-in-windows-ce.aspx

4.) IMGNOKITL=0 . Don't forget KITL. http://msdn.microsoft.com/en-us/library/ms901796.aspx

Tuesday, July 10, 2012

[CE] Making Logfiles and More

I'm sharing a few stuff today that could help a lot with capturing logs and messages. This is very useful if the system has complicated logging mechanism that it's more convenient to improvise one yourself.

Here's one scenario. Most big WinCE/Mobile system does not build debug images because this consume a lot of memory. But by default a lot of drivers have their debugging enabled only for DEBUG build. So what I do is define this on a specific file I want to debug:

#define DEBUGMSG(cond,printf_exp)   \
   ((void)((cond|1)?(NKDbgPrintfW printf_exp),1:0))


What it does is convert all debug messages to print even in retail build. You just need to define the debug zones used by that module.

Ex.
#define ZONE_WARNING 1

Another way for better monitoring of functions and code lines is to add function names and line numbers to your RETAILMSG. Ex.

// declaration
#define DUMMY_DBG(fmt,p1,p2,p3)     RETAILMSG(1, (TEXT("[File : %s][fn : %s ][line : %d]") TEXT(fmt), TEXT(__FILE__),TEXT(__FUNCTION__), __LINE__, p1, p2, p3)) // Marlon : debugging
    RETAILMSG(1,(TEXT("Marlon Comment: Entered: %s\r\n"), TEXT(__FUNCTION__))); // Marlon
// usage
  DUMMY_DBG("entry",0,0,0); //Marlon : debugging

  DUMMY_DBG("exit",0,0,0); //Marlon : debugging



Another useful way in debugging especially with early driver development if to check which drivers are currently running. So here's what you can do:

#include
#include "bldver.h"

void EnumerateRunningDrivers( void )
{
#if CE_MAJOR_VER>4

                HANDLE hDriver;
                DeviceSearchType DeviceSearch = DeviceSearchByLegacyName;
                DEVMGR_DEVICE_INFORMATION DeviceInfo;

                DeviceInfo.dwSize = sizeof( DEVMGR_DEVICE_INFORMATION );
              
                hDriver = FindFirstDevice(DeviceSearch, L"*", &DeviceInfo);
              
                if( hDriver != INVALID_HANDLE_VALUE )
                {
                                RETAILMSG( 1, (TEXT("Legacy     Key      Name   BUS\n")));
                                do
                                {
                                                RETAILMSG( 1, (TEXT("%s %s %s %s\n"),
                                                                DeviceInfo.szLegacyName,
                                                                DeviceInfo.szDeviceKey,
                                                                DeviceInfo.szDeviceName,
                                                                DeviceInfo.szBusName ));

                                } while( FindNextDevice( hDriver, &DeviceInfo ) );
                                FindClose( hDriver );
                }
#else
                RETAILMSG( 1, (TEXT("Cannot find drivers Windows CE Version doesn't support function\n")));
#endif
}



Sunday, July 8, 2012

[iOS] Multiplayer Card Game

Lately, I've been working on iPhone apps too. Thanks to Apple's initiative, common developer can now earn for those late night coding sweats with a simple upload to app store and have their apps available to billions of people worldwide and not some smart ass who'll easily criticize someone's app because he envy his skills. LOL!. And now Windows and Android are doing the same easier stuff. I myself bought a book 2 years ago on iOS development but never really had a chance to upload my own apps till this year. LOL!

Anyway, one of the sites that really jumpstart me was http://www.raywenderlich.com/. A bunch of nice guys are sharing some awesome tutorials on this site led by Ray himself. One of the tutorials I'm waiting for them to finish publishing is about a multiplayer card game. Anyway here's 2 parts of their planned 7-part tutorial.

Part 1:
http://www.raywenderlich.com/12735/how-to-make-a-simple-playing-card-game-with-multiplayer-and-bluetooth-part-1

Part 2:
http://www.raywenderlich.com/12865/how-to-make-a-simple-playing-card-game-with-multiplayer-and-bluetooth-part-2

Friday, July 6, 2012

CE: Sample Camera Dialog App

Here's a sample code for running the camera on a Windows CE/Mobile device

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.WindowsMobile.Forms;
using System.Runtime.InteropServices;
using System.IO;

namespace CamTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void LaunchCam_Click(object sender, EventArgs e)
        {
            string filename;
            CameraCaptureDialog cameraCapture = new CameraCaptureDialog();

            try
            {
                cameraCapture.Owner = null;
                string strAppDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
                cameraCapture.InitialDirectory = strAppDir;
                string fileName = "picture1";
                cameraCapture.DefaultFileName = fileName + ".jpg";
                cameraCapture.Title = "CamTest";
                cameraCapture.Resolution = new System.Drawing.Size(400, 640);
                cameraCapture.Mode = CameraCaptureMode.Still;
                cameraCapture.StillQuality = CameraCaptureStillQuality.Normal;
                cameraCapture.VideoTimeLimit = new TimeSpan(0, 0, 0);


                if (DialogResult.OK == cameraCapture.ShowDialog())
                {
                    System.Diagnostics.Debug.WriteLine(cameraCapture.FileName);
                    if (!File.Exists(cameraCapture.FileName))
                    {
                        throw new Exception("Error saving the photo");
                    }
                    filename = cameraCapture.FileName;
                    return;
                }
            }
            catch (Exception ex)
            {

                throw ex;
            }
            finally
            {

                if (cameraCapture != null)
                {
                    cameraCapture.Dispose();
                    cameraCapture = null;
                }
            }
            return;

        }
    }
}

Thursday, July 5, 2012

CE: Controlling Perfman

    HWND topWindow;
    TCHAR szBuf[50] = {0};
    static bool fLogIsActive = FALSE;
    TCHAR newName[255] = {0};
    FILE *tempFile;

    topWindow = GetForegroundWindow();
    if(topWindow != NULL){
        GetWindowText(topWindow, szBuf, sizeof(szBuf)-1);
        if(wcscmp(szBuf, L"CeLog/Profiler")){
            MessageBox(NULL,L"WRONG WINDOW!!!\nLaunch Perfman first and go to Celog/Profile tab",L"BattLevelPlus", 0);           
        }else{
           
            if(fLogIsActive == FALSE){
                //Check perfman log file and rename it. Then, delete it.
                tempFile = fopen("\\My Documents\\Perfman.clg","r");
                if(tempFile){
                    swprintf(newName,L"\\My Documents\\Perfman%d.clg",logCount);
                    MoveFile(gPerfmanLogDefName,newName);
                    fclose(tempFile);
                }
               
                //Wait 5 seconds
                Sleep(5000);

                mouse_event(MOUSEEVENTF_MOVE+MOUSEEVENTF_ABSOLUTE, BUTTON_X, BUTTON_Y, 0, 0);
                mouse_event(MOUSEEVENTF_ABSOLUTE+MOUSEEVENTF_LEFTDOWN, BUTTON_X, BUTTON_Y, 0, 0);
                mouse_event(MOUSEEVENTF_ABSOLUTE+MOUSEEVENTF_LEFTUP, BUTTON_X, BUTTON_Y, 0, 0);
                fLogIsActive = TRUE;
            }else{
                mouse_event(MOUSEEVENTF_MOVE+MOUSEEVENTF_ABSOLUTE, BUTTON_X, BUTTON_Y, 0, 0);
                mouse_event(MOUSEEVENTF_ABSOLUTE+MOUSEEVENTF_LEFTDOWN, BUTTON_X, BUTTON_Y, 0, 0);
                mouse_event(MOUSEEVENTF_ABSOLUTE+MOUSEEVENTF_LEFTUP, BUTTON_X, BUTTON_Y, 0, 0);
                fLogIsActive = FALSE;       
            }
        }
    }

=================================================================

What does this code do? Well this is how you can control Profiler tool for CE devices known as Perfman. This tool logs a .clg file in which after you use "Readlog" (see: http://msdn.microsoft.com/en-us/library/ms905162.aspx), you can view how the system performs over a period of time. Running Perfman for more than 10 minutes with dump a very big log file which could be very difficult to convert or open. So what you can do is to collect this log as maybe 5 minute intervals.



This function does not access any API except for mouse event because I don't know which API exposes control to Perfman. So what I did is to have Perfman on the foreground window all the time while I'm running this. It forces the mouse to run an event that emulates touching the Start/Stop button of Perfman while it sees the app as the top window. It will also rename the perfman.clg log file so that previous log files can be differentiated.


CE: Memory Monitoring

Memory leaks can be a pain in the ass. But memory leaks that only shows up after a long period of testing would kill all the excitement in your development. Well this happened in my work (yes those times that I've been away from this blog). In memory of that experience I'll share here a few tricks that worked for me.  

I'll start with  MEMORYSTATUS, this is defined in MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366772%28v=vs.85%29.aspx

You can use this to gather system specific memory status.Here's a snippet of what I did:
===================================================
                // Log Memory Status at this time
                file = fopen(gMemLogFilename, "a");
                if(file)
                {
                    fprintf(file, "\nLog %8d:", logCount);
                 
                    // Get Memory details
                    GlobalMemoryStatus(&memstatus);
                    fprintf(file, "%8d:", memstatus.dwMemoryLoad);
                    fprintf(file, "%8d:", memstatus.dwTotalPhys/1024);
                    fprintf(file, "%8d:", memstatus.dwAvailPhys/1024);
                    fprintf(file, "%8d:", memstatus.dwTotalPageFile/1024);
                    fprintf(file, "%8d:", memstatus.dwAvailPageFile/1024);
                    fprintf(file, "%8d:", memstatus.dwTotalVirtual/1024);
                    fprintf(file, "%8d:", memstatus.dwAvailVirtual/1024);
                   
                    // Get physical disk space
                    GetDiskFreeSpaceEx(_T("\\Flash File Store"),&notused,&totalBytes,&freeBytes);
                    fprintf(file, "%8d:", totalBytes.QuadPart/1024);
                    fprintf(file, "%8d:", freeBytes.QuadPart/1024);

                    GetDiskFreeSpaceEx(_T("\\My Device"),&notused,&totalBytes,&freeBytes);
                    fprintf(file, "%8d:", totalBytes.QuadPart/1024);
                    fprintf(file, "%8d:", freeBytes.QuadPart/1024);

                    fclose(file);
                }

I separate the values with colon(:) so that I can export the log to excel and trace how the memory consumption performs over time. I put this inside a loop that checks the memory at specific intervals. This a very useful tool if you do stress testing (those tests that need more than a day to finish...i.e. hopper, battery, etc.)

=======================================================================
If your not contented with system level monitoring. You can even dig to which process is taking most of your memory. Use PROCVMINFO. This structure is not documented in MSDN but should work for all WinCE/WinMobile platform. Here's how to use it.

 DWORD total = 0;
 PROCVMINFO vmi = { 0 };
 HANDLE procID = 0;

     for( int idx = 0; idx < 33; ++idx )
    {
        memset(&vmi, 0, sizeof(PROCVMINFO));
        if( ::CeGetProcVMInfo( idx, sizeof( PROCVMINFO ), &vmi ) )
        {
            procID = GetProcessIDFromIndex(idx);
           
            AddProcessInfoToList((DWORD)procID, vmi.cbRwMemUsed/1024);//You can use this function for as data collector
            total += vmi.cbRwMemUsed;
        }

    }

======================================================================
For system that suddenly become slow over time, I would also suggest to the percentage of the system process being idle. Here's how to do it:

    DWORD dwStartTick, dwIdleSt, dwStopTick,dwIdleEd;

    dwStartTick = GetTickCount();
    dwIdleSt = GetIdleTime();
    //  Insert a call to the Sleep(sleep_time) function to allow idle time
    //  to accrue. An example of an appropriate sleep time is 1000 ms.
    Sleep(1000);
    dwStopTick = GetTickCount();
    dwIdleEd = GetIdleTime();

    return ((100*(dwIdleEd - dwIdleSt)) / (dwStopTick - dwStartTick));

System Debuggig for WinCE (ToolHelp and more)

With my first post after like a quarter of a decade, I'll go in depth to trick and a box of tools that'll help you debug CE if you don't what really is going on.

Well in MSDN there is this box of tools known as ToolHelp Library it has a lot of functions that would help you debug devices and gather system data like module info, process info, and memory stuff. Yes I know you can do this with Remote Tools in Platform builder. But for Remote Tools to work you have to connect that device via KITL. So what if you have an issue that occurs on only in 2 out of 10 units? Well the best way for that to work is to make a standalone application that you can run 10 devices and collects what you need.

These functions can be found here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686832%28v=vs.85%29.aspx

This is documented in MSDN, so you guys can check the definitions there. What I'll share are tricks how to use these functions.

1.) Make sure your project has include these headers
#include
#include


These files are normally declared in PUBLIC\COMMON\OAK\INC. So make sure you point your project to include this directory to your build.
2.) Make sure your project include PUBLIC\COMMON\OAK\LIB\ARMV4I\RETAIL\toolhelp.lib in your additional dependencies.
3.) I'll share a code snippet here from an app I created to monitor my system. Some variable declaration were omitted so you may fill in the blanks yourself. :)

====================================================================
Here's a sample for gathering all the running processes
 // Get the list of running processes
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS|TH32CS_SNAPNOHEAPS,NULL);
PROCESSENTRY32 Tmp;
               
if (hSnapShot != INVALID_HANDLE_VALUE)
{
   Tmp.dwSize = sizeof(PROCESSENTRY32);
     if (Process32First(hSnapShot,&Tmp))
     {           
        do {
                 AddProcessToList(&Tmp); // This can be your function that write the processes to and array or logs the process info immediately.
        }while(Process32Next(hSnapShot,&Tmp));
     }

     CloseToolhelp32Snapshot(hSnapShot);

  }

==================================================================

Here's a sample how I try to create a list of processes and log them to a file.

    bool fFound = FALSE;
    DWORD index = 0;


    while(index < gProcessCount)
    {
        if(gProcessList[index].id == proc->th32ProcessID)
        {
            memcpy(gProcessList[index].name,proc->szExeFile,sizeof(TCHAR) * MAX_PATH);
            fFound = TRUE;
        }
        index++;
    }
    if(fFound == FALSE)
    {
      
        gProcessList[index].id = proc->th32ProcessID;
        memcpy(gProcessList[index].name,proc->szExeFile,sizeof(TCHAR) * MAX_PATH);
        // New entry so we increment
        gProcessCount = index + 1;
    }

============================================================
I created my own list structure to like this.

struct List
{
    DWORD id;
    DWORD value;
    bool gotData;
    TCHAR name[MAX_PATH];
};
struct List gProcessList[100] = {0};
=============================================================
Here's how I log this to a file:

    DWORD index = 0;
    FILE *fProcLog;
    // Open log file
    fProcLog = fopen(gProcLogFilename, "a");
    if(fProcLog){
        // print log number
        fprintf(fProcLog, "\nLog %8d:", logCount);
        while(index < gProcessCount)
        {
            // Print module name
            fwprintf(fProcLog,L"%s:%08X:%8d:",gProcessList[index].name,gProcessList[index].id,(DWORD)gProcessList[index].gotData * 100);

            gProcessList[index].gotData = FALSE;
            index++;
        }
    }
    fclose(fProcLog);

I'm back!!!! After like forever!!! LOL

Wow! It's been over 2 years since I last shared stuff here. Well, I got busy with this called "Life". But anyway, I'm back!!! Yey!!!

What better is that I've been doing a lot of Windows Mobile so as iOS stuff. I got a few apps released and several tools I will share.

I'll start it with my next post.