Contents
Introduction
In this article, we will explore methods for enabling and disabling the wireless communications radios in your Windows Mobile device. We will first investigate methods that work for single radio types, and end with an unsupported method for managing the state of all radios.
Method |
Radio types supported |
Power Management API |
WiFi |
Telephony API |
Cellular (WWAN) |
BTH Util API |
Bluetooth |
OSSVCS |
WiFi, WWAN, Bluetooth |
As a side note, the attached demo applications use boost. I considered writing them using with only what comes with the Windows Mobile 6 SDK, but the boost libraries do such an incredible job of making the code more readable, exception-safe, and usable, that I decided to use them. If you're not using boost (and you really should!), the power-management concepts presented in this article will still apply.
Power Management API
The Power Management API allows our applications to monitor and alter the power state of any power managed device (i.e., a device whose driver advertises power management capabilities). Generally, this is limited to the WiFi 802.11 adapter which uses a power-managed NDIS miniport driver.
Changing the power state
The Power Management API provides us with two deceptively simple calls to determine the current state of the radio and to change the state: GetDevicePower()
and SetDevicePower()
. I call them "deceptively simple" because while two of their three parameters are straightforward, the first, pvDevice
, is not.
In order to set or get the power state of a device using the Power Management API, we must know its power class and name. The power class GUID for an NDIS device is, by default, {98C5250D-C29A-4985-AE5F-AFE5367E5006}. The name of the WiFi radio is somewhat less simple to get at.
Locating the WiFi radio
A number of APIs will yield the names of the attached network devices. We will use the IpHelperAPI
because unlike IOCTL_NDISUIO_QUERY_BINDING
, it works regardless of whether the radio is currently on or off.
DWORD buffer_size = 0;
if( ERROR_BUFFER_OVERFLOW == GetAdaptersInfo( NULL, &buffer_size ) )
{
std::vector< BYTE > adapter_buffer( buffer_size );
IP_ADAPTER_INFO* info =
reinterpret_cast< IP_ADAPTER_INFO* >( &adapter_buffer.front() );
DWORD res = GetAdaptersInfo( info, &buffer_size );
if( ERROR_SUCCESS == res )
{
}
}
Now we have a list of all the network adapters, but we need a method of determining which is a WiFi adapter. One good method is to ask it a question that only an 802.11 adapter could answer. For this example, we will query the current SSID using OID_802_11_SSID
.
There are four steps to querying an NDIS OID:
- Open a handle to
NDISUIO_DEVICE_NAME
.
- Build the
IOCTL_NDISUIO_QUERY_OID_VALUE
query structure.
- Issue the query through
DeviceIoControl()
.
- Retrieve the results from the query buffer.
First, we use CreateFile()
to open a handle to the NDIS protocol driver.
boost::shared_ptr< void > ndis( ::CreateFile( NDISUIO_DEVICE_NAME,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
INVALID_HANDLE_VALUE ),
&::CloseHandle );
if( INVALID_HANDLE_VALUE != ndis.get() )
{
}
Next, we prepare an NDIS_802_11_SSID
buffer to receive the SSID. We will provide each adapter name given to us using GetAdaptersInfo()
. If the WiFi adapter is already on, QueryOid()
will return ERROR_SUCCESS
. If not, it will fail and return ERROR_GEN_FAILURE
. An ERROR_INVALID_PARAMETER
error indicates we supplied the name of a non-802.11 adapter.
DWORD GetSSID( HANDLE ndis,
const std::wstring& adapter_name,
std::wstring* ssid = NULL )
{
std::vector< BYTE > ssid_buffer( sizeof( NDIS_802_11_SSID ) );
NDIS_802_11_SSID* ssid_int =
reinterpret_cast< NDIS_802_11_SSID* >( &ssid_buffer.front() );
ssid_int->SsidLength = NDIS_802_11_LENGTH_SSID - 1;
DWORD res = QueryOid( ndis,
adapter_name.c_str(),
OID_802_11_SSID,
&ssid_buffer );
if( ERROR_SUCCESS == res && NULL != ssid )
*ssid = reinterpret_cast< wchar_t* >( ssid_int->Ssid );
return res;
}
Third, we create an NDISUIO_QUERY_OID
buffer and supply it to DeviceIoControl
. The returned buffer should contain the SSID.
DWORD QueryOid( HANDLE ndis,
const wchar_t* device_name,
ULONG oid,
std::vector< BYTE >* buffer )
{
DWORD bytes_returned = 0;
std::vector< BYTE > query_buffer( sizeof( NDISUIO_QUERY_OID ) + buffer->size() );
NDISUIO_QUERY_OID* query =
reinterpret_cast< NDISUIO_QUERY_OID* >( &query_buffer.front() );
query->Oid = oid;
query->ptcDeviceName = const_cast< wchar_t* >( device_name );
BOOL res = ::DeviceIoControl( ndis,
IOCTL_NDISUIO_QUERY_OID_VALUE,
reinterpret_cast< LPVOID >( &query_buffer.front() ),
query_buffer.size(),
reinterpret_cast< LPVOID >( &query_buffer.front() ),
query_buffer.size(),
&bytes_returned,
NULL );
if( res )
{
std::copy( query->Data, query_buffer.end(), buffer->begin() );
return ERROR_SUCCESS;
}
return GetLastError();
}
Putting it all together, we can now determine which IP_ADAPTER_INFO
structure represents our WiFi adapter:
for( IP_ADAPTER_INFO* i = info; NULL != i; i = i->Next )
{
std::wstringstream adapter_name;
adapter_name << i->AdapterName;
DWORD res = GetSSID( ndis.get(), adapter_name.str() );
if( ERROR_SUCCESS == res || ERROR_GEN_FAILURE == res )
{
adapter_name.str().swap( *name );
return true;
}
}
To change the power state of the NDIS WLAN radio, we combine the NDIS power management GUID with the name of the WLAN radio and pass it to SetDevicePower()
. Power state D4
will turn the radio off, and D0
will turn it on.
void DoChangePowerState( const CEDEVICE_POWER_STATE& power_state )
{
std::wstring radio_name;
GetRadioName( &radio_name );
if( radio_name_.length() > 0 )
{
std::wstringstream power_command;
power_command << PMCLASS_NDIS_MINIPORT << L\\" << radio_name.c_str();
::SetDevicePower( const_cast< wchar_t* >( power_command.str().c_str() ),
POWER_NAME,
power_state );
}
}
void Enable()
{
DoChangePowerState( D0 );
}
void Disable()
{
DoChangePowerState( D4 );
}
Monitoring changes to the power state
In addition to changing the power state of the WLAN radio, the Power Manager API can be used to alert us when the power state changes. There are five steps to this process:
- Open a handle to the NDIS protocol driver.
- Create a system message queue.
- Request NDIS power notifications with
IOCTL_NDISUIO_REQUEST_NOTIFICATION
.
- Wait for NDIS power notification messages to arrive.
- Read the power notification message from the queue to determine if the radio is off or on.
First, we open the NDIS protocol driver handle exactly as we did earlier when retrieving the SSID:
boost::shared_ptr< void > ndis( ::CreateFile( NDISUIO_DEVICE_NAME,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
INVALID_HANDLE_VALUE ),
&::CloseHandle );
if( INVALID_HANDLE_VALUE != ndis.get() )
{
}
Next, we create a read-only message queue to listen for NDIS notifications:
MSGQUEUEOPTIONS options = { 0 };
options.dwSize = sizeof( MSGQUEUEOPTIONS );
options.cbMaxMessage = sizeof( NDISUIO_DEVICE_NOTIFICATION );
options.bReadAccess = TRUE;
options.dwFlags = MSGQUEUE_NOPRECOMMIT;
boost::shared_ptr< void > radio_power_queue( ::CreateMsgQueue( NULL, &options ),
&::CloseMsgQueue );
if( NULL != radio_power_queue.get() )
{
}
We use IOCTL_NDISUIO_REQUEST_NOTIFICATION
to request power state change notifications in our NDIS message queue. A struct
provides a handy exception-safe method of ensuring we properly cancel our notification request regardless of what may happen in the future.
struct NdisRequestNotifications : private boost::noncopyable
{
NdisRequestNotifications( HANDLE ndis,
HANDLE queue,
DWORD dwNotificationTypes )
: ndis_( ndis )
{
NDISUIO_REQUEST_NOTIFICATION radio_power_notifications = { 0 };
radio_power_notifications.hMsgQueue = queue;
radio_power_notifications.dwNotificationTypes = dwNotificationTypes;
::DeviceIoControl( ndis_,
IOCTL_NDISUIO_REQUEST_NOTIFICATION,
&radio_power_notifications,
sizeof( NDISUIO_REQUEST_NOTIFICATION ),
NULL,
0,
NULL,
NULL );
};
~NdisRequestNotifications()
{
::DeviceIoControl( ndis_,
IOCTL_NDISUIO_CANCEL_NOTIFICATION,
NULL,
NULL,
NULL,
0,
NULL,
NULL );
};
private:
HANDLE ndis_;
};
NdisRequestNotifications notifications( ndis.get(),
radio_power_queue.get(),
NDISUIO_NOTIFICATION_DEVICE_POWER_DOWN |
NDISUIO_NOTIFICATION_DEVICE_POWER_UP );
Now, we must wait for the message queue to alert us if a message is available. A second event handle can be used to cancel the thread read-loop at any time.
HANDLE stop_notification_;
HANDLE wait_objects[] = { radio_power_queue.get(), stop_notification_ };
size_t object_count = _countof( wait_objects );
while( ::WaitForMultipleObjects( object_count,
wait_objects,
FALSE,
INFINITE ) == WAIT_OBJECT_0 )
{
}
Finally, when we have received a message, we use the ReadMsgQueue()
function to extract the message from the queue. The power state of the radio will be stored in the dwNotificationType
parameter of NDISUIO_DEVICE_NOTIFICATION
.
NDISUIO_DEVICE_NOTIFICATION notification = { 0 };
DWORD bytes_read = 0;
DWORD notification_flags = 0;
if( ::ReadMsgQueue( radio_power_queue.get(),
¬ification,
sizeof( NDISUIO_DEVICE_NOTIFICATION ),
&bytes_read,
0,
¬ification_flags ) &&
bytes_read > 0 )
{
bool enabled =
( notification.dwNotificationType == NDISUIO_NOTIFICATION_DEVICE_POWER_UP );
}
Telephony API
The Microsoft Telephony Service Provider API (TAPI) will allow us to manage the power state of the Wireless Wide-Area Network (WWAN) radio. Because the functions we will use for this purpose are part of the Extended TAPI interface, which deals entirely with cellular radios, this method will not work for any other radio type.
Debugging Note
If the Microsoft Visual Studio 2008 debugger stops code execution before the TAPI cleanup functions have run, you may get an error like this:
It is irritating, but does not seem to have a negative impact on the Visual Studio session or on the mobile device.
Changing the power state
First, we must initialize the TAPI subsystem with lineInitializeEx()
. This not only gives us a handle object, but also tells us the number of TAPI devices in the system and the event HANDLE
TAPI signals when a message is available. In this example, we wrap the TAPI HLINEAPP
handle in a boost::shared_ptr<>
to manage the lifetime of the object.
class TapiLineApp
{
public:
TapiLineApp()
: line_app_( TapiLineApp::Create( &device_count_,
&event_,
&error_code_ ),
&::lineShutdown )
{
};
DWORD GetErrorCode() const { return error_code_; };
DWORD GetDeviceCount() const { return device_count_; };
HANDLE GetMessageEvent() const { return event_; };
operator HLINEAPP() const { return ( HLINEAPP )line_app_.get(); };
private:
static HLINEAPP Create( DWORD* device_count,
HANDLE* event_,
DWORD* error_code )
{
DWORD api_ver = TAPI_CURRENT_VERSION;
LINEINITIALIZEEXPARAMS init_params = { 0 };
init_params.dwTotalSize = sizeof( LINEINITIALIZEEXPARAMS );
init_params.dwOptions = LINEINITIALIZEEXOPTION_USEEVENT;
HLINEAPP line_app = NULL;
*error_code = ::lineInitializeEx( &line_app,
NULL,
NULL,
NULL,
device_count,
&api_ver,
&init_params );
if( *error_code == 0 )
{
*event_ = init_params.Handles.hEvent;
return line_app;
}
return NULL;
};
private:
DWORD device_count_;
DWORD error_code_;
HANDLE event_;
boost::shared_ptr< const HLINEAPP__ > line_app_;
};
Next, we use lineOpen()
to open a handle to the WWAN radio via the Cellular TAPI Service Provider (TSP). As in the previous example, we use a boost::shared_ptr<>
to manage the lifetime of the HLINE
handle.
class TapiLine
{
public:
TapiLine( const TapiLineApp& line_app,
DWORD line_id,
DWORD version,
DWORD privilege ) :
line_app_( line_app ),
line_( TapiLine::Create( line_app,
line_id,
version,
privilege,
&error_code_ ),
&::lineClose )
{
};
DWORD GetErrorCode() const { return error_code_; };
operator HLINE() const { return ( HLINE )line_.get(); };
private:
static HLINE Create( HLINEAPP line_app,
DWORD line_id,
DWORD version,
DWORD privilege,
DWORD* error_code )
{
HLINE line = NULL;
*error_code = ::lineOpen( line_app,
line_id,
&line,
version,
0,
NULL,
privilege,
NULL,
NULL );
if( *error_code == 0 )
{
return line;
}
return NULL;
};
private:
DWORD error_code_;
TapiLineApp line_app_;
boost::shared_ptr< const HLINE__ > line_;
};
Now, we are able to use lineGetEquipmentState()
to easily get the current state of the WWAN radio:
TapiLine line_;
bool IsRadioEnabled() const
{
DWORD equip_state = 0, radio_state = 0;
if( ::lineGetEquipmentState( line_, &equip_state, &radio_state ) == 0 )
{
return ( LINEEQUIPSTATE_MINIMUM != equip_state );
}
return false;
}
lineSetEquipmentState()
can be used to enable the WWAN radio:
TapiLine line_;
void Enable() const
{
if( ::lineSetEquipmentState( line_, LINEEQUIPSTATE_FULL ) == 0 )
{
::lineRegister( line_, LINEREGMODE_AUTOMATIC, NULL, LINEOPFORMAT_NONE );
}
}
We can even put it in to Flight Mode:
TapiLine line_;
void Disable() const
{
::lineSetEquipmentState( line_, LINEEQUIPSTATE_MINIMUM );
}
Monitoring changes to the power state
Listening for changes in the WWAN radio state is easy. We initialize TAPI just as we did earlier. Then, we use lineGetMessage()
to read messages from the TAPI message queue. When the Extended TAPI LINE_DEVSPECIFIC
message arrives with a LINE_EQUIPSTATECHANGE
value for dwParam2
, then we know the power state has changed. The new power state is stored in dwParam2
of the LINEMESSAGE
.
HANDLE stop_notification_;
TapiLineApp line_app_;
void MonitorRadioState()
{
HANDLE wait_objects[] = { line_app_.GetMessageEvent(),
stop_notification_ };
size_t object_count = _countof( wait_objects );
while( ::WaitForMultipleObjects( object_count,
wait_objects,
FALSE,
INFINITE ) == WAIT_OBJECT_0 )
{
LINEMESSAGE message = { 0 };
if( ::lineGetMessage( line_app_, &message, 0 ) == 0 )
{
switch( message.dwMessageID )
{
case LINE_DEVSPECIFIC:
if( LINE_EQUIPSTATECHANGE == message.dwParam1 )
{
bool enabled = ( message.dwParam2 != LINEEQUIPSTATE_MINIMUM );
}
break;
}
}
}
}
Bluetooth Application Development API
Of all the methods we will examine, the Bluetooth Application Development API provides us the simplest access to the power state of a radio.
Changing the power state
Only two commands are needed to check and change the power state of the Bluetooth radio: BthGetMode()
and BthSetMode()
. They don't require any supporting handle or structures so, as you will see, they are extremely easy to use.
Is the Bluetooth radio enabled?
bool IsRadioEnabled() const
{
DWORD mode = 0;
if( ::BthGetMode( &mode ) == ERROR_SUCCESS )
return ( BTH_POWER_OFF != mode );
return false;
}
Enable the Bluetooth radio:
void Enable() const { ::BthSetMode( BTH_DISCOVERABLE ); }
Disable the Bluetooth radio:
void Disable() const { ::BthSetMode( BTH_POWER_OFF ); }
Monitoring changes to the power state
The technique we will use to monitor the power state of the Bluetooth radio is very similar to the method we used earlier for monitoring the WiFi radio state.
- Use
CreateMsgQueue()
to open a message queue.
- Use
RequestBluetoothNotifications()
to request a notification be sent to our queue when the Bluetooth stack comes up or goes down.
- Wait for a new message on the queue or for the user to request a stop.
- When a new event is signaled, read the
BTEVENT
message. The power state is stored in the dwEventId
parameter.
HANDLE stop_notification_;
typedef boost::shared_ptr< void > SafeHandle;
void MonitorRadioState()
{
MSGQUEUEOPTIONS options = { 0 };
options.dwSize = sizeof( MSGQUEUEOPTIONS );
options.cbMaxMessage = sizeof( BTEVENT );
options.bReadAccess = TRUE;
options.dwFlags = MSGQUEUE_NOPRECOMMIT;
SafeHandle radio_power_queue( ::CreateMsgQueue( NULL, &options ),
&::CloseMsgQueue );
if( NULL != radio_power_queue.get() )
{
SafeHandle notifications(
::RequestBluetoothNotifications( BTE_CLASS_STACK, radio_power_queue.get() ),
&::StopBluetoothNotifications );
HANDLE wait_objects[] = { radio_power_queue.get(),
stop_notification_ };
size_t object_count = _countof( wait_objects );
while( ::WaitForMultipleObjects( object_count,
wait_objects,
FALSE,
INFINITE ) == WAIT_OBJECT_0 )
{
BTEVENT notification = { 0 };
DWORD bytes_read = 0;
DWORD notification_flags = 0;
if( ::ReadMsgQueue( radio_power_queue.get(),
¬ification,
sizeof( BTEVENT ),
&bytes_read,
0,
¬ification_flags ) &&
bytes_read > 0 )
{
bool enabled = ( notification.dwEventId == BTE_STACK_UP );
}
}
}
}
Wireless Device Power Management API (OSSVCS.dll)
The Wireless Power Management APIs provide functions that manage the power state of the wireless radios available on Windows Mobile devices. This includes the Bluetooth, Phone, and WiFi radios. It is commonly used in applications like the Wireless Manager (above image) that are created by the device OEM or Microsoft.
Note
Unfortunately, the Wireless Device Power Management API is not published by Microsoft in their public Windows Mobile SDK; it is part of the Platform Builder. If you don't have access to the Platform Builder, you will need to modify the demo project to dynamically link to ossvcs.dll. Its usage is sufficiently common that the wrlspwr.h interfaces can be easily found through a quick Internet search.
Changing the power state
The GetWirelessDevices()
function provides us with a linked list of RDD
structures defining the current state of each of the NDIS devices that support the Windows Embedded CE Power Management features (i.e., the Bluetooth, Cellcore, and WiFi devices). As this structure must be freed with a call to FreeDeviceList()
, we can use the boost::shared_ptr<>
to manage its lifetime.
class OssvcsRadioList
{
public:
typedef detail::OssvcsListIterator const_iterator;
OssvcsRadioList()
: radio_list_( OssvcsRadioList::Create(), &::FreeDeviceList )
{
};
explicit OssvcsRadioList( RDD* list )
: radio_list_( list, &::FreeDeviceList )
{
};
const_iterator begin() const
{
return const_iterator( radio_list_.get() );
};
const_iterator end() const
{
return const_iterator();
};
private:
static RDD* Create( DWORD flags = 0, HRESULT* error_code = NULL )
{
RDD* radio_list = NULL;
HRESULT hr = ::GetWirelessDevices( &radio_list, flags );
if( NULL != error_code )
*error_code = hr;
return radio_list;
};
boost::shared_ptr< RDD > radio_list_;
};
The RDD
linked-list structure can be easily adapted to work with standard library functions by using boost::iterator_adaptor<>
.
class OssvcsListIterator
: public boost::iterator_facade< OssvcsListIterator,
const RDD*,
boost::forward_traversal_tag,
const RDD* >
{
public:
OssvcsListIterator() : node_( NULL ) {};
explicit OssvcsListIterator( const RDD* p ) : node_( p ) {};
private:
friend class boost::iterator_core_access;
void increment() { node_ = node_->pNext; };
bool equal( OssvcsListIterator const& other ) const { return other.node_ == node_; };
const RDD* dereference() const { return node_; };
const RDD* node_;
};
Finally, the ChangeRadioState()
method can be used to turn a particular radio on or off.
namespace OssvcsRadioPower {
void Enable( const RDD* device )
{
::ChangeRadioState( const_cast< RDD* >( device ), 1, POWER_POST_SAVE );
};
void Disable( const RDD* device )
{
::ChangeRadioState( const_cast< RDD* >( device ), 0, POWER_POST_SAVE );
};
bool IsEnabled( const RDD* device )
{
return device->dwState != 0;
};
};
Now we're able to implement fantastic 1-liners using standard library functions like std::for_each()
:
OssvcsRadioList radio_list_;
std::for_each( radio_list_.begin(), radio_list_.end(), OssvcsRadioPower::Enable );
Monitoring changes to the power state
The OSSVCS API does include a provision for alerting a program of changes to the wireless radios: GetAndMonitorWirelessDevices()
. Unfortunately, it is quite broken.
- The monitor thread quits after 30 seconds even if no radio changes state.
- Regardless of the flags used, the list of devices returned by
GetAndMonitorWirelessDevices()
doesn't include the 802.11 adapter.
- Any process can signal the
WRLS_TERMINATE_EVENT
and cancel the monitor thread.
- Any process' call to
GetWirelessDevices()
will cancel the monitor thread.
- The callback is executed immediately after the
GetAndMonitorWirelessDevices()
call even if no device has yet changed state.
- The monitor thread exits when a radio changes state, but does not activate the callback first.
- Turning the Bluetooth or WWAN radios on or off does not trigger the callback; only the WLAN adapter can trigger it.
Most of these issues could be worked around, but issue 7 means that even with the workarounds, the API is only useful for monitoring the state of 802.11 WLAN devices, and we have simpler methods for doing that. If I'm mistaken in any of the above points or you have managed to use this method successfully, please add a comment describing your method! I would love to see it.
The solution is to use the same method as the Wireless Manager uses: the State and Notification Broker.
The Windows Mobile State and Notification Broker stores status information in specific Registry locations defined in snapi.h (mostly in [HKLM]\System\State). This allows us to use standard Registry queries to determine the current state of these devices, or use the Registry notification functions to be alerted of any change in state. Unfortunately, it cannot be used to modify the state of the radios, which is why we combine it with OSSVCS.
The RegistryNotifyCallback()
function requires us to define a callback function that will be alerted when a Registry value changes. This callback function must conform to the REGISTRYNOTIFYCALLBACK
definition. As with most Microsoft API functions, this callback allows us to pass a DWORD
parameter of our choosing. For convenience, our example will pass a this
pointer.
void RadioNotification::Callback( HREGNOTIFY hNotify,
DWORD dwUserData,
const PBYTE ,
const UINT )
{
RadioNotification* parent =
reinterpret_cast< RadioNotification* > ( dwUserData );
}
A small helper-function allows us to simplify linking a Registry value to our callback function.
HREGNOTIFY RadioNotification::LinkToCallback( DWORD bitmask,
HKEY root,
LPCTSTR path,
LPCTSTR value ) const
{
NOTIFICATIONCONDITION condition = { REG_CT_ANYCHANGE, bitmask, 0 };
HREGNOTIFY notification = 0;
::RegistryNotifyCallback( root,
path,
value,
&RadioNotification::Callback,
( DWORD )this,
&condition,
¬ification );
return notification;
}
Now, we can provide a method our application can use to start listening for changes in the state of the WiFi, Bluetooth, or Cellular radios. As always, the boost::shared_ptr<>
comes in handy for safely managing the lifetime of our handles.
typedef boost::shared_ptr< HREGNOTIFY__ > RegNotify;
RegNotify phone_notification_;
RegNotify wifi_notification_;
RegNotify bluetooth_notification_;
void RadioNotification::Start()
{
bluetooth_notification_.reset(
CreateCallback( SN_BLUETOOTHSTATEPOWERON_BITMASK,
SN_BLUETOOTHSTATEPOWERON_ROOT,
SN_BLUETOOTHSTATEPOWERON_PATH,
SN_BLUETOOTHSTATEPOWERON_VALUE ),
&::RegistryCloseNotification );
wifi_notification_.reset( CreateCallback( SN_WIFISTATEPOWERON_BITMASK,
SN_WIFISTATEPOWERON_ROOT,
SN_WIFISTATEPOWERON_PATH,
SN_WIFISTATEPOWERON_VALUE ),
&::RegistryCloseNotification );
phone_notification_.reset( CreateCallback( SN_PHONERADIOOFF_BITMASK,
SN_PHONERADIOOFF_ROOT,
SN_PHONERADIOOFF_PATH,
SN_PHONERADIOOFF_VALUE ),
&::RegistryCloseNotification );
}
Finally, we define a method to stop listening for changes in the radio state. This method need do nothing more than close the Registry notification handles.
void RadioNotification::Stop()
{
bluetooth_notification_.reset();
wifi_notification_.reset();
phone_notification_.reset();
}
We've examined every useful method I could think of to manage and monitor the power state of the wireless radios in a Windows Mobile device. If you use a different method or have a modification to one of the methods I described above, please describe it in a comment and I'll try to add it to the article.