Windows CE

Sunday, April 19, 2009

Use of SHBrowseForFolder() in wince 6.0


Hi.. A another less documented API is MSDN is use of SHBrowseForFolder() in wince 6.0
Several people have trouble getting to use SHBrowseForFolder().
Here is the code snippet which will create a Folder selection dialog in Wince 6.0
   1:     TCHAR psz_result[MAX_PATH];
2: LPMALLOC p_malloc = 0;
   3:     LPITEMIDLIST pidl;
4: BROWSEINFO bi;
   5:  
6: #ifdef UNDER_CE
   7:     #   define SHGetMalloc MySHGetMalloc
   8:     #   define SHBrowseForFolder MySHBrowseForFolder
   9:     #   define SHGetPathFromIDList MySHGetPathFromIDList
  10:  
  11:  
12: HMODULE ceshell_dll = LoadLibrary( _T("ceshell") );
  13:         if( !ceshell_dll ) return;
  14:  
  15:         HRESULT (WINAPI *SHGetMalloc)(LPMALLOC *) =
  16:             (HRESULT (WINAPI *)(LPMALLOC *))
  17:             GetProcAddress( ceshell_dll, _T("SHGetMalloc") );
  18:         LPITEMIDLIST (WINAPI *SHBrowseForFolder)(LPBROWSEINFO) =
  19:             (LPITEMIDLIST (WINAPI *)(LPBROWSEINFO))
  20:             GetProcAddress( ceshell_dll, _T("SHBrowseForFolder") );
  21:         BOOL (WINAPI *SHGetPathFromIDList)(LPCITEMIDLIST, LPTSTR) =
  22:             (BOOL (WINAPI *)(LPCITEMIDLIST, LPTSTR))
  23:             GetProcAddress( ceshell_dll, _T("SHGetPathFromIDList") );
  24:  
  25:         if( !SHGetMalloc || !SHBrowseForFolder || !SHGetPathFromIDList )
  26:         {
  27:             _tprintf( _T( "couldn't load SHBrowseForFolder API") );
  28:             FreeLibrary( ceshell_dll );
  29:             return;
  30:         }
  31:     #endif
  32:  
  33:         if( !SUCCEEDED( SHGetMalloc(&p_malloc) ) ) goto error;
  34:  
  35:         memset( &bi, 0, sizeof(BROWSEINFO) );
36: bi.hwndOwner = NULL;
  37:         bi.lpszTitle = L"Select Folder to backup.";
  38:         bi.pszDisplayName = psz_result;
  39:         bi.ulFlags = BIF_EDITBOX;
  40:     #ifndef UNDER_CE
  41:         bi.ulFlags |= BIF_USENEWUI;
42: #endif
  43:  
  44:         if( (pidl = SHBrowseForFolder( &bi ) ) )
  45:             {
  46:                 SHGetPathFromIDList( pidl, psz_result );
  47:                 
48: p_malloc->Free( pidl );
  49:             }
  50:         _tprintf( _T( "%s"),psz_result );
  51:          error:
  52:  
  53:  
  54:  
  55:  
56: #ifdef UNDER_CE
  57:             FreeLibrary( ceshell_dll );
  58:         #endif

Saturday, April 11, 2009

Minimize / Hide Applications on startup

Hi Guys..
 In my last post I discussed about how to Add an MFC application into tray Icon. The sample application can be Minimized to tray icon, without even shown in task bar when HIDE menu Item is clicked. And again can be relaunched when SHOW menu in tray icon is clicked.
 But one problem is there. We can not start the Sample MFC application as a tray icon from the  startup.Even a call to  ShowWindow(SW_HIDE) from the  OnInitDialog() function, will not work.
Well here is a trick of doing this, by modifying the initinstance() function of the App class.
The Normal code for creating the dialog in InitInstance() looks like

CDialogBasedDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();


Edit this code as follows :

CDialogBasedDlg dlg;
if(dlg.Create( CDialogBasedDlg::IDD ))
 {
dlg.ShowWindow( SW_HIDE );
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.RunModalLoop();
}
And you are done.. Integrating it with previous sample application will result in a Dialog based application starting as a trayicon, Always Minimized and will not appear in System tray. ;))

Adding System Tray Icon in Wince

Adding Tray Icon in status area, Using Visual C++ (MFC), is not well documenetd in MSDN.
After many attempts, i have been able to add an ICON to the status area.
Lets see the steps required for this in a MFC Visual C++ Application.

Shell_NotifyIcon -- This function sends a message to the system to add, modify, or delete an icon from the taskbar status area.

WINSHELLAPI BOOL WINAPI Shell_NotifyIcon(
  DWORD dwMessage, 
  PNOTIFYICONDATA pnid 
);

The first parameter to Shell_NotifyIcon is either of NIM_ADD, NIM_DELETE, or NIM_MODIFY. The second parameter will be a pointer to NOTIFYICONDATA variable.
The NOTIFYICONDATA structure variable contains all the information regarding the icon to be placed on the status area.

For NOTIFYICONDATA structure check :

I have discussed few members here :

  • hWnd - Handle to the window that receives notification messages associated with an icon in the taskbar status area. 
In MFC applications it can be retrieved by calling this->GetSafeHWnd( ) from main dialog of the application in Dialog based application, and from CMainFrame class in SDI or MDI applications.
  • hIcon - Handle to the icon to be added, modified, or deleted. If the Icon is imported to the project, handle can be obtained by calling LoadIcon API.
  • uCallbackMessage - Application-defined message identifier. The system uses this identifier to send notifications to the window identified in hWnd. These notifications are sent when a mouse event occurs in the bounding rectangle of the icon, or when the icon is selected or activated with the keyboard.
The identifier can be defined a numeric value starting from WM_USER+1 to WM_USER+64k which is not previously assigned for other such identifiers. The application (Dialog based, SDI or MDI) receives ON_MESSAGE with first parameter as the identifier declared above.


Sample Application : 

1.Create a new Dialog Based Application using MFC wizard and name it as TrayIcon.

2.Open the file for CTrayIconDlg, the trayicondlg.cpp file and add the following at the end of preprocessor directives

define TRAYICON WM_USER+100
define ICONNUMBER 4001

3. From Resource View , insert an icon to the project and with id as ID_TRAYICON.
Also add a menu to the project and name it as ID_TRAYMENU. 
Using Menu Designer create a menu item status and add HIDE and SHOW menu in its drop-down menu 
Set the id for the menu item as ID_SHOW and ID_HIDE.
From the ClassWizard,add message handlers for each of them for COMMAND message.
The function that handles them will be OnShow() and OnHide(). 

4. Add a member function to CTrayIconDlg class that will react to the messages send by the system to the application regarding the status area icon.
Define it as  
void OnTrayIconMessage(WPARAM wParam, LPARAM lParam)

Edit the code of the function to add these 

POINT pt;
GetCursorPos(&pt);
// When Mouse Left button is clicked or Tapped in touch screen devices
if( (UINT)lParam == WM_LBUTTONDOWN)
{                                                                                     
CMenu menu;
menu.LoadMenu(ID_TRAYMENU);
CMenu *pContextMenu = menu.GetSubMenu(0);
pContextMenu->TrackPopupMenu( TPM_LEFTALIGN,
pt.x,pt.y,AfxGetMainWnd() );
}

5. Check for tag BEGIN_MESSAGE_MAP and END_MESSAGE_MAP and insert the following in between these two:

ON_MESSAGE(TRAYICON, OnTrayIconMessage) // Mapping the message identifier to the callback function

it should look like :

BEGIN_MESSAGE_MAP(CTrayIconDlg, CDialog)
//{{AFX_MSG_MAP(CTrayIconDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CLOSE()
ON_WM_DESTROY()
ON_MESSAGE(TRAYICON, OnTrayIconMessage) //message identifier
ON_COMMAND(ID_SHOW, OnShow) //Menu message identifier for SHOW
ON_COMMAND(ID_HIDE, OnHide) //Menu message identifier for HIDE
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

6. To add the icon when the application starts, place the following code to OnInitDialog function after the TODO tag

NOTIFYICONDATA nid;
nid.cbSize = sizeof(NOTIFYICONDATA); // Size of Structure
nid.hWnd=this->GetSafeHwnd(); // Application Handle
strcpy(nid.szTip, "My Tray Icon App");
nid.uCallbackMessage=TRAYICON; // message identifier - Callback function mapped to this
nid.uFlags=NIF_MESSAGE|NIF_ICON|NIF_TIP;
nid.hIcon=AfxGetApp( )->LoadIcon(ID_TRAYICON); // ICON to be displayed
nid.uID=ICONNUMBER;  
Shell_NotifyIcon(NIM_ADD,&nid); // NIM_ADD to add the icon

If yours is SDI or MDI application, add the above code to OnCreate function of CMainFrame class.

7. To delete the icon from status area when the application gets destroyed ,add a handler for WM_DESTROY message using ClassWizard..
 The function name will be OnDestroy.
Add the following code

NOTIFYICONDATA nid;
nid.cbSize=sizeof( NOTIFYICONDATA );
nid.hWnd=this->GetSafeHwnd();
nid.uID=ICONNUMBER;
Shell_NotifyIcon( NIM_DELETE , &nid ); // delete the Icon

8. To Show and Hide the Application when Menu Item SHOW and HIDE is clicked edit the following functions 

void CTrayIconDlg::OnStatusHide()
{
// TODO: Add your command handler code here
ShowWindow(SW_HIDE);
}

void CTrayIconDlg::OnStatusShow()
{
// TODO: Add your command handler code here
ShowWindow(SW_SHOWNORMAL);
}

In this way we can add and delete(Modify)the icon to the status area,and menu items can be displayed when clicked on tray icons.

Wednesday, April 8, 2009

device platform does not exist in your datastore

Hi,
I Recently downloaded a VS2005 Smart device Project from internet, and ended up with a Error Message while starting the Project.

---------------------------
Microsoft Visual Studio
---------------------------
Error retrieving information from user datastore. Platform not found.
---------------------------

The project could not be opened because it refers to a device platform that does not exist in your datastore.
---------------------------


Clicking OK or Cancel will result the same.Project will not be loaded.

Its clear from the message that the developer had saved or created the Project with some target device, which is not available in my development workstation.

Many of us has encountered with this problem. Now the USER is left with three choices.

  1. Install the platform SDK that supports the project you want to load and then change the target platform type to your target device.(Safest Option)
     2. Create a new sample Project with available SDK and import all the source code and references from the other project.
(Time Consuming but better than 1st option if you cant download SDK.)

     3. Last option, if you don't have time to download and install SDK, or don't wanna import the source code and resolve all the warning and errors.

open the .csproj file in notepad (Not .csproj.USER)
Locate the line 
<PlatformID>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</PlatformID>

and replace this to

<PlatformID>3C41C503-53EF-4c2a-8DD4-A8217CAD115E</PlatformID>
---- To open with Pocket PC 2003
or with
 <PlatformID>yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy</PlatformID>
   ---- from your existing projects for the target platform.

And the work is done.You can run the Project with your Target Device.
YAHHHHHOOOOOOOOO......
(Easiest trick, but its always safe to run the code in respective target )