
Establishing a Connection
With the native method entry points, enumerations, and structure all defined, you are now ready to use the Connection Manager to establish a connection. The good news is that the hard work is done; using the Connection Manager is quite easy. Establishing the actual connection involves two steps: determining the network destination identifier and making the connection request.
Determining a Network Destination Identifier
The first step to establishing a connection is to identify whether your application needs to connect to the Internet or your Work network. Each network destination has a specific GUID. The GUID for the Internet is 436EF144-B4FB-4863-A041-8F905A62C572. The GUID for your Work network is A1182988-0D73-439e-87AD-2A5B369F808B.
Although the GUIDs for each of the network destinations are well known, most applications do not use the GUIDs directly. Instead you can use the ConnMgrMapURL function to determine the appropriate network GUID.
The Connection Manager encapsulates the logic to determine which network a specific URL requires. So rather than hard-code a specific network GUID in your application, you simply pass the destination URL to the ConnMgrMapURL function and the function returns the appropriate GUID.
Note: |
|---|
|
The Connection Manager determines whether a URL requires the Internet or the Work network (intranet) based on whether the destination address contains periods in the name. Addresses that contain periods are considered to be on the Internet, addresses that do not contain periods are considered to be on the Work network.
If your organization uses periods for intranet addresses, you will need to add those address to the Work URL exception list. To add URLs to the Work URL exception list, select Start on the device, choose Settings, select the Connections tab, select the Connections icon, select the Advanced tab, and then select the Exceptions button.
|
The following code shows an example of using the ConnMgrMapURL function to determine the destination GUIDs for different URLs.
string url1 = "http://msdn2.microsoft.com/en-us/netframework/bb495180.aspx";
string url2 = "http://hedgydev02/test/default.aspx";
Guid destination1 = Guid.Empty;
Guid destination2 = Guid.Empty;
ConnMgrMapURL(url1, ref destination1, 0);
ConnMgrMapURL(url2, ref destination2, 0);
// Write to debug window
Debug.WriteLine(url1);
Debug.WriteLine(destination1.ToString());
Debug.WriteLine("");
Debug.WriteLine(url2);
Debug.WriteLine(destination2.ToString());
In the sample, there are two URLs. The first URL points to a location on Microsoft's MSDN® Web site, which is of course on the Internet. The other URL points to one of the computers on the internal network and therefore is located on the Work network. Figure 1 shows the information that appears in the Visual Studio Output window when you run this code. As you can see, the Microsoft URL returns the Internet GUID (436EF144-B4FB-4863-A041-8F905A62C572) whereas the URL pointing the internal network returns the Work GUID (A1182988-0D73-439e-87AD-2A5B369F808B).
Figure 1. The destination network identifiers for an Internet URL and a Work URL
Thanks to the ConnMgrMapURL function, this may be the last time you ever have to see those GUIDs. In the future, you can just let the ConnMgrMapURL function figure out the GUIDs for you.
Making the Connection
From this point, making the connection simply requires you to create an instance of the ConnMgrConnectionInfo class with the appropriate connection criteria, and call the appropriate Connection Manager function.
When you make the connection request you can use either the ConnMgrEstablishConnectionSync function, which blocks until the connection process completes or you can use the ConnMgrEstablishConnection function, which returns immediately and establishes the connection without requiring the calling thread to wait for the connection process to complete. If your application is running in the background or if you are establishing the connection on a background thread, the ConnMgrEstablishConnectionSync function is easy to work with because you know as soon as the function returns whether you have successfully established a connection. The following code sample demonstrates using the ConnMgrEstablishConnectionSync function to establish a connection.
IntPtr _connectionHandle = IntPtr.Zero; // Class-level field
const int _syncConnectTimeout = 60000; // 60 seconds
void DoConnect(string url)
{
Guid networkGuid = Guid.Empty;
ConnMgrStatus status = ConnMgrStatus.Unknown;
ConnMgrMapURL(url, ref networkGuid, 0);
ConnMgrConnectionInfo info = new ConnMgrConnectionInfo(networkGuid, ConnMgrPriority.HighPriorityBackground);
ConnMgrEstablishConnectionSync(info, ref _connectionHandle, _syncConnectTimeOut, ref status);
if (status == ConnMgrStatus.Connected)
Debug.WriteLine("Connect Succeeded");
else
Debug.WriteLine("Connect failed: " + status.ToString());
}
Notice that the preceding code passes a timeout value to the ConnMgrEstablishConnectionSync function. You should provide a substantial value for the timeout because establishing connections, especially in places with poor network coverage, can sometimes take many tens of seconds. If the function returns because the timeout expires, as opposed to because of a connection error, the status value is ConnMgrStatus.WaitingConnection. When you receive the ConnMgrStatus.WaitingConnection status value, chances are that the connection could have succeeded given enough time; therefore, you should try to make the connection using a longer timeout value.
To test the preceding connection code, use the Windows Mobile 6 Professional Device Emulator and Cellular Emulator. The Cellular Emulator acts as a virtual cellular radio for the Device Emulator providing the Device Emulator with General Radio Packet Service (GPRS) connectivity. To get started, you must first connect the Device Emulator to the Cellular Emulator by following the instructions in the Cellular Emulator section of the Developer's Guide to the Arm Emulator.
You also need to define GPRS connections for the emulator. This is the same process you follow to define a GPRS connection on a physical device to connect to your service provider's network. Follow these steps to define the GPRS Internet connection.
-
Within the Device Emulator select Start, and then select Settings.
-
On the Settings screen, select the Connections tab.
-
Select the Connections icon.
-
On the Connections screen, select Add a new modem connection immediately below the heading My ISP.
-
Name the connection GPRS Internet. The name has no affect on the connection behavior.
-
Select Cellular (GPRS) from the Select a modem drop-down list.
-
Select Next.
-
Leave the Access point name field blank and select Next.
-
Leave all of the fields blank (User name, Password, Domain) and select Finish.
You now have a GPRS connection defined for the Internet. To define a GPRS connection for your Work network, follow these same steps except this time in step 4 select Add a new modem connection immediately under My Work Network, and in step 5 name the connection GPRS Work.
Note: |
|---|
|
The reason that you are able to leave the Access point and login information blank is because you are connecting through the Cellular Emulator. On an actual device, you would need to provide the information for these fields as defined by your service provider.
|
When you run the preceding code, you should notice that after a few seconds the Cellular Emulator shows activity on one of the Network Channels as shown in Figure 2.
Figure 2. The Cellular Emulator with an active GPRS connection
A few seconds after the Cellular Emulator shows activity, the ConnMgrEstablishConnectionSync function should return. You always want to check the returned status value to verify that the connection is successful. A successful connection returns a status of ConnMgrStatus.Connected.
Note: |
|---|
|
To close the Cellular Emulator's connection, on the Network tab of the Cellular Emulator, click the Disconnect GPRS button.
|
Because the ConnMgrEstablishConnectionSync function blocks until the connection process is complete, it is not appropriate for connections that your application initiates on the main thread of a user-oriented application. Using the ConnMgrEstablishConnectionSync function in this way will cause your application user interface to freeze until the connection request completes. To avoid freezing the application user interface, use the ConnMgrEstablishConnection function. The ConnMgrEstablishConnection function does not wait for the connection process to complete but rather initiates the connection request then returns immediately. As the following code shows, calling the ConnMgrEstablishConnection function is very much like calling the ConnMgrEstablishConnectionSync function.
Guid networkGuid = Guid.Empty;
ConnMgrMapURL(url, ref networkGuid, 0);
ConnMgrConnectionInfo info = new ConnMgrConnectionInfo(networkGuid);
ConnMgrEstablishConnection(info, ref _connectionHandle);
The call to the ConnMgrEstablishConnection function in the preceding code initiates the connection process but returns before the Connection Manager actually establishes the connection. To determine when the connection completes, you can use the SystemState class in conjunction with the ConnMgrConnectionStatus function.
The following code shows how to set up the SystemState class to notify your application when the number of active connections changes.
SystemState _connectionsCount;
private void Form1_Load(object sender, EventArgs e)
{
_connectionsCount = new SystemState(SystemProperty.ConnectionsCount);
_connectionsCount.Changed += new ChangeEventHandler(ConnectionsCount_Changed);
}
In the preceding code, the program constructs the _connectionsCount instance and associates it with the ConnectionsCount state value. By then associating the ConnectionsCount_Changed method with the _connectionsCount.Changed event, the _connectionsCount instance will call the ConnectionsCount_Changed method anytime the number of active connections changes.
Once you know that the number of active connections has changed, you need to determine if the change is caused by the connection you requested. The following code demonstrates using the ConnMgrConnectionStatus function to determine if that change is caused by successfully establishing the connection you requested in your earlier call to the ConnMgrEstablishConnection function.
void ConnectionsCount_Changed(object sender, ChangeEventArgs args)
{
ConnMgrStatus status = ConnMgrStatus.Unknown;
ConnMgrConnectionStatus(_connectionHandle, ref status);
if (status == ConnMgrStatus.Connected)
{
// Connection Established
}
}
As you can see in the preceding code, if the connection status is ConnMgrStatus.Connected, you know your connection is ready and you can begin using it. One thing to keep in mind is that the system calls the ConnectionsCount_Changed method on the main application thread; therefore, you do not want to perform any long-running tasks from within this method. Instead you should perform the task either on a background thread or by using an asynchronous method such as the SqlCeReplication class' BeginSynchronize method.
You can test this code using the Device Emulator and Cellular Emulator just as you did earlier when testing the ConnMgrEstablishConnectionSync function.
Note: |
|---|
|
A number of options exist for determining the current status of a connection. You can use the ConnMgrConnectionStatus function to check the status of the connection at any time, which you may find useful for verifying that the connection has not encountered unexpected problems. You can also create the MessageWindow-derived class and set the ConnMgrConnectionInfo class' hWnd field to the MessageWindow-derived class' window handle; in this case the Connection Manager will send a message to the MessageWindow-derived class each time the connection status changes. You can also use the ConnMgrRegisterForStatusNotification function.
|
Releasing the Connection
The process of releasing a connection is pretty straightforward. For the most part, you simply the call the ConnMgrReleaseConnection function passing the connection handle returned from either the ConnMgrEstablishConnectionSync function or the ConnMgrEstablishConnection function. The ConnMgrReleaseConnection function signals the Connection Manager that your application is done with the connection but the function does not necessarily close it. To understand when connections are actually closed, you need to understand the affect of the ConnMgrReleaseConnection function's cache parameter and how the Connection Manager manages connection caching.
Caching the Connection
As you know, establishing a connection can be time consuming. To avoid the overhead of re-establishing the same connection repeatedly, the Connection Manager may maintain a connection even after the last application has released the connection. This is known as connection caching. Applications can influence the length of time the Connection Manager caches a connection through the cache parameter of the ConnMgrReleaseConnection function but the Connection Manager has ultimate authority. Table 4 shows the list of possible cache parameter values and their meaning.
Table 4. ConnMgrReleaseConnection cache parameter values
|
Parameter Value
|
Description
|
|---|
|
0
|
Do not cache the connection
|
|
1
|
Cache the connection for the default amount of time
|
|
Greater than 1
|
Cache the connection for this number of seconds
|
As shown in Table 4, you can request that the Connection Manager close the connection immediately by passing a cache parameter value of 0. To request that the Connection Manager cache the connection for the default period of time, pass a cache parameter value of 1 as shown in the following code sample.
// Release the connection and cache it for the default time period.
ConnMgrReleaseConnection(_connectionHandle, 1);
The default connection cache times are defined as named values in the registry under the HKEY_LOCAL_MACHINE\Comm\ConnMgr\Planner\Settings registry key. The default cache time for a connection depends on whether the connection is created as an exclusive connection. The CacheTime named registry value defines the default cache time for non-exclusive connections. The CacheTime named registry value is set to 600 seconds (10 minutes) on most devices. For exclusive connections, the VPNCacheTime named registry value defines the cache time, which is 60 seconds (1 minute) on most devices.
Note: |
|---|
|
An exclusive connection is a connection that cannot be used by any application other than the application that creates the connection. You create an exclusive connection by setting the ConnMgrConnectionInfo.bExclusive field to a non-zero value. In most cases, you can make most efficient use of device resources by allowing connections to be shared (that is, non-exclusive).
|
Prior to Windows Mobile 5.0, an application could only specify whether or not a connection was cached. With Windows Mobile 5.0 and continuing with Windows Mobile 6, you can specify the amount of time you would like the Connection Manager to cache the connection. You do this by simply passing the desired number of seconds to cache the connection rather than passing a 0 or a 1 to the cache parameter. For example, the following code shows how to release the connection and request that the Connection Manager cache the connection for 5 minutes (300 seconds).
// Release the connection and cache it for 5 minutes.
ConnMgrReleaseConnection(_connectionHandle, 300);
You should always remember that the value that your application passes to the ConnMgrReleaseConnection function's cache parameter is simply a request. The Connection Manager may choose to ignore that request. Most commonly when the Connection Manager overrides your caching request, the Connection Manager keeps the connection cached longer than requested. One case where this happens is when the device has a constant connection, such as when connected to the network via Ethernet cable or when the device is cradled and is using desktop pass-through networking through ActiveSync. In these types of connections, the actual network connection is established physically through a network cable or by a cradled device; they remain connected until physically disconnected. The Connection Manager manages these connections but does not actually open or close them.
Another case where the Connection Manager may cache the connection longer than your application requests is when there is no cost to maintaining the connection. The most common example is a GPRS connection. GPRS radios do not consume power except when actually transferring data, and in most cases mobile operators bill GPRS connections based on data transfer volume not the amount of time connected.
You can see the Connection Manager's caching behavior of GPRS connections with the Cellular Emulator. To do so, verify that the Device Emulator and Cellular Emulator are still properly connected. If the Cellular Emulator still has an active connection, close the connection by clicking Disconnect GPRS on the Cellular Emulator's Network tab. Now run the following code.
Guid networkGuid = Guid.Empty;
ConnMgrStatus status = ConnMgrStatus.Unknown;
ConnMgrMapURL(url, ref networkGuid, 0);
ConnMgrConnectionInfo info = new ConnMgrConnectionInfo(networkGuid, ConnMgrPriority.HighPriorityBackground);
Debug.WriteLine ("Attempting Sync Connect");
ConnMgrEstablishConnectionSync(info, ref _connectionHandle, _syncConnectTimeOut, ref status);
if (status == ConnMgrStatus.Connected)
ConnMgrReleaseConnection(_connectionHandle, 0);
else
Debug.WriteLine("Connection failed: " + status.ToString());
The preceding code is the code from the earlier ConnMgrEstablishConnectionSync function example code with a call to the ConnMgrReleaseConnection function added at the end. Notice that the call to the ConnMgrReleaseConnection function passes 0 to the cache parameter requesting that the Connection Manager immediately close the connection.
When you run the code, you will notice that the Cellular Emulator shows that the connection becomes active just as it did previously. Notice though that the connection never terminates; the value in the Time(s) column keeps incrementing. The connection remains established but it is not transferring any data, and on a real device it does not consume any power in this state. The Connection Manager is overriding the request to not cache the connection but doing so does not consume any significant resources.
This type of connection is sometimes called a suspend/resume connection indicating that when not in use, the connection can remain connected but go into an efficient suspended state. This GPRS connection may remain connected indefinitely but will not be active until the Connection Manager returns the connection to an application and the application uses it.