|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
ContentsIntroductionThere are a lot of examples on how to use a Web Service from the managed world, but sometimes, having the whole runtime installed is an unneeded luxury. If you want something light, this article will give you a taste of how this can be done. For the past few years, Web Service development has been one of my main areas of interest. I have been designing and developing all kinds of Web Services as well as client applications that consume Web Services. I have used different environments/languages to build web based clients as well as standalone desktop applications. One thing I didn't try before is to build a Web Service client application on a mobile device. This is one of the reasons why I have decided to build a mobile application for this article. I also wanted to have something a little bit more exciting than a stock ticker app or Amazon product search app that will use the capabilities of the device it will be hosted on. Fortunately, I have just received a new GPS enabled SmartPhone, MOTO Q 9h. Having a GPS on my phone was the second reason for a mobile application example, because I have a good web service to complete the picture, which is described below. BackgroundThe demo application was tested on the MOTO Q 9h SmartPhone. Screen size on that phone is 320x240. The demo application is not orientation and/or size aware. It uses s static 320x240 bitmap for background image. However, it would not be hard to make it orientation/size aware. Using the codeMake sure you have read the article and has everything that is needed. Download, unzip, compile, deploy, and enjoy! I have also included a pre-built CAB file. ATL, WTLI will be using the ATL library which has built-in support for Web Services. For the UI, I will be using one of my favorites - Windows Template Library. WTL is a lightweight extension to ATL. WTL provides template based wrappers for Windows controls and various Win32 APIs. WTL can be downloaded here. This article uses WTL 8.0, which integrates very well with Visual Studio 2005. After downloading and installing WTL, please run setup80.js from the AppWizMobile folder. That script will install the Application Wizard for Visual Studio 2005.
ApplicationThe following section provides a step by step instruction on how to create the application project and add a Web Service reference. Creating the project using the WTL WizardLet's go ahead and start Visual Studio 2005. If you have installed WTL and registered (by running setup80.js) the WTL Application Wizard, you will have a new "WTL Mobile Application Wizard" template under the Visual C++/WTL Project types category.
Name your project, select the location, and click "OK". I have named my project GPSDemographics. The WTL Mobile Application Wizard dialog has four pages: 1. General Overview Page
2. Platforms Page
3. Application Type Page
4. User Interface Features Page
Project settingsSince we are using WTL, we have to let Visual Studio know where the header files are located. There are several ways of doing that, which I am sure you are aware of. But just in case, I will show you how to do that using the project properties dialog. Open the project properties window. Project -> Properties. Please add the path to your WTL include folder for the following properties:
Web ServiceI will be using CDYNE's Demographics Web Service. This service provides socio-economic data for a given address.
The Web Service has 15 different methods. I will be using the
Adding Web ReferenceMake sure your Solution Explorer is open.
If everything is right, you will see the Web Service page. The name of the service in this case is DemographixQuery.
At this point, you should be able to compile the project. You might receive an error that looks like this:
This error is due to a name conflict. In order to fix it, add the following to the stdafx.h file just before #ifdef lstrlenW
#undef lstrlenW
#endif
GPSThere are several ways of working with the GPS hardware. In this article, I will be using the GPS Intermediate Driver API. The main benefit of using the GPS Intermediate Driver is that it provides an intermediate layer that hides the complexity and implementation details of the actual GPS device. Applications that use the GPS Intermediate Driver architecture should work with any GPS hardware. There are two modes to working with the GPS hardware under the GPS Intermediate Driver:
I will be using Parsed mode. The API consists of four functions:
When you are done with the driver, close it by calling CodePretty simple, right? OK, let's get to the actual implementation. In my demo application, I have a wrapper class for the GPS API - #pragma once
//IGPSSink.h
#include <gpsapi.h>
interface IGPSSink
{
virtual HRESULT SetGPSPosition( GPS_POSITION gps_Position ) = 0;
virtual HRESULT SetGPSDeviceInfo( GPS_DEVICE gps_Device ) = 0;
};
#pragma once
//GPSDevice.h
#include <GPSApi.h>
#include "IGPSSink.h"
class CGPSDevice
{
private:
//Singleton instance
static CGPSDevice * s_pInstance;
//Device handle
HANDLE m_hGPS_Device;
//Event for location data updates
HANDLE m_hNewLocationData;
//Event for device state changes
HANDLE m_hDeviceStateChange;
//Thread's handle and id
HANDLE m_hThread;
DWORD m_dwThreadID;
//Exit event
HANDLE m_hExitThread;
//Pointer to sink interface
IGPSSink * m_pSink;
private:
//Our wrapper is singleton make constructor private
CGPSDevice(void);
HRESULT StartThread();
HRESULT StopThread();
static CGPSDevice * Instance();
static DWORD WINAPI GPSThreadProc(__opt LPVOID lpParameter);
public:
~CGPSDevice(void);
static HRESULT TurnOn(IGPSSink * pSink);
static HRESULT TurnOff();
};
//GPSDevice.cpp
#include "StdAfx.h"
#include "GPSDevice.h"
CGPSDevice * CGPSDevice::s_pInstance = NULL;
#define MAX_WAIT 5000
#define MAX_AGE 3000
#define GPS_CONTROLLER_EVENT_COUNT 3
CGPSDevice::CGPSDevice(void)
{
m_pSink = NULL;
m_hGPS_Device = NULL;
m_hNewLocationData = NULL;
m_hDeviceStateChange = NULL;
m_hExitThread = NULL;
}
CGPSDevice::~CGPSDevice(void)
{
}
CGPSDevice * CGPSDevice::Instance()
{
if( CGPSDevice::s_pInstance == NULL )
{
s_pInstance = new CGPSDevice();
}
return CGPSDevice::s_pInstance;
}
DWORD WINAPI CGPSDevice::GPSThreadProc(__opt LPVOID lpParameter)
{
DWORD dwRet = 0;
GPS_POSITION gps_Position = {0};
GPS_DEVICE gps_Device = {0};
CGPSDevice * pDevice = (CGPSDevice*) lpParameter;
HANDLE gpsHandles[GPS_CONTROLLER_EVENT_COUNT] =
{ pDevice->m_hNewLocationData,
pDevice->m_hDeviceStateChange,
pDevice->m_hExitThread
};
gps_Position.dwSize = sizeof(gps_Position);
gps_Position.dwVersion = GPS_VERSION_1;
gps_Device.dwVersion = GPS_VERSION_1;
gps_Device.dwSize = sizeof(gps_Device);
do
{
dwRet = WaitForMultipleObjects(
GPS_CONTROLLER_EVENT_COUNT,
gpsHandles, FALSE, INFINITE);
if (dwRet == WAIT_OBJECT_0)
{
dwRet = GPSGetPosition(
pDevice->m_hGPS_Device,
&gps_Position, MAX_AGE, 0);
if (ERROR_SUCCESS != dwRet)
continue;
else
pDevice->m_pSink->SetGPSPosition(gps_Position);
}
else if (dwRet == WAIT_OBJECT_0 + 1)
{
dwRet = GPSGetDeviceState(&gps_Device);
if (ERROR_SUCCESS != dwRet)
continue;
else
pDevice->m_pSink->SetGPSDeviceInfo(gps_Device);
}
else if (dwRet == WAIT_OBJECT_0 + 2)
break;
else
ASSERT(0);
} while( TRUE );
return 0;
}
HRESULT CGPSDevice::StartThread()
{
HRESULT hr = E_FAIL;
DWORD dwRet = 0;
m_hNewLocationData = CreateEvent(NULL, FALSE, FALSE, NULL);
if ( m_hNewLocationData )
{
m_hDeviceStateChange = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hDeviceStateChange)
{
m_hExitThread = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hExitThread)
{
m_hThread = ::CreateThread(NULL, NULL,
GPSThreadProc, this, NULL, &m_dwThreadID);
if ( m_hThread )
hr = S_OK;
}
}
}
if( FAILED(hr) )
{
dwRet = GetLastError();
hr = HRESULT_FROM_WIN32(dwRet);
}
if (FAILED(hr))
{
if (m_hNewLocationData)
{
CloseHandle(m_hNewLocationData);
m_hNewLocationData = NULL;
}
if (m_hDeviceStateChange)
{
CloseHandle(m_hDeviceStateChange);
m_hDeviceStateChange = NULL;
}
if (m_hExitThread)
{
CloseHandle(m_hExitThread);
m_hExitThread = NULL;
}
}
return hr;
}
HRESULT CGPSDevice::StopThread()
{
HRESULT hr = E_FAIL;
DWORD dwRet = 0;
if( SetEvent(m_hExitThread) )
{
dwRet = WaitForSingleObject(m_hThread, MAX_WAIT);
if(WAIT_OBJECT_0 == dwRet)
hr = S_OK;
}
if( FAILED(hr) )
{
dwRet = GetLastError();
hr = HRESULT_FROM_WIN32(dwRet);
}
if (m_hNewLocationData)
{
CloseHandle(m_hNewLocationData);
m_hNewLocationData = NULL;
}
if (m_hDeviceStateChange)
{
CloseHandle(m_hDeviceStateChange);
m_hDeviceStateChange = NULL;
}
if (m_hExitThread)
{
CloseHandle(m_hExitThread);
m_hExitThread = NULL;
}
if (m_hThread)
{
CloseHandle(m_hThread);
m_hThread = NULL;
m_dwThreadID = 0;
}
return hr;
}
HRESULT CGPSDevice::TurnOn(IGPSSink * pSink)
{
if( !pSink )
return E_INVALIDARG;
CGPSDevice * pDevice = Instance();
//We already have a device opened
if( pDevice->m_hGPS_Device )
return E_UNEXPECTED;
if( pDevice->m_pSink != NULL )
return E_UNEXPECTED;
pDevice->m_pSink = pSink;
HRESULT hr = pDevice->StartThread();
if( SUCCEEDED(hr) )
{
pDevice->m_hGPS_Device = GPSOpenDevice(
pDevice->m_hNewLocationData,
pDevice->m_hDeviceStateChange,
NULL, NULL);
if( pDevice->m_hGPS_Device )
hr = S_OK;
else
hr = HRESULT_FROM_WIN32(GetLastError());
}
if( FAILED(hr) )
TurnOff();
return hr;
}
HRESULT CGPSDevice::TurnOff()
{
CGPSDevice * pDevice = Instance();
if( !pDevice->m_hGPS_Device )
return E_UNEXPECTED;
HRESULT hr = pDevice->StopThread();
pDevice->m_pSink = NULL;
DWORD dwRet = GPSCloseDevice(pDevice->m_hGPS_Device);
pDevice->m_hGPS_Device = NULL;
if( SUCCEEDED(hr) )
{
if( ERROR_SUCCESS != dwRet )
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
As I mentioned before, the view class implements //DemographicsView.h class CDemographicsView : public CWindowImpl<cdemographicsview />, public IGPSSink { ... HRESULT SetGPSPosition(GPS_POSITION gps_Position); HRESULT SetGPSDeviceInfo(GPS_DEVICE gps_Device); ... } The most interesting method is //DemographicsView.cpp
HRESULT CDemographicsView::SetGPSPosition(GPS_POSITION gps_Position)
{
::EnterCriticalSection(&m_cs);
m_gpsPosition = gps_Position;
//We will update only if we have data
if( m_gpsPosition.dwSatelliteCount != 0 )
{
double dist = (m_oldLat - gps_Position.dblLatitude) *
(m_oldLat - gps_Position.dblLatitude) +
(m_oldLong - gps_Position.dblLongitude) *
(m_oldLong - gps_Position.dblLongitude);
if( dist > DIST_THRESHOLD )
{
m_oldLat = gps_Position.dblLatitude;
m_oldLong = gps_Position.dblLongitude;
//This is the web service class that
//has been generated by Visual Studio
DemographixQuery::CDemographixQuery webService;
//Invoke the method we are interested in.
HRESULT hr = webService.GetLocationInformationByLatitudeLongitude(
m_gpsPosition.dblLatitude,
m_gpsPosition.dblLongitude,
CComBSTR(DEMO_LICENSE),
&m_info);
}
//Update the view
Invalidate();
}
::LeaveCriticalSection(&m_cs);
return S_OK;
}
HRESULT CDemographicsView::SetGPSDeviceInfo(GPS_DEVICE gps_Device)
{
::EnterCriticalSection(&m_cs);
m_gpsDevice = gps_Device;
Invalidate();
::LeaveCriticalSection(&m_cs);
return S_OK;
}
Resources
History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||