Windows Networking Support
The WNet API is a provider-independent interface that allows Windows applications to access network resources without regard for the network implementation. The Windows CE version of the WNet API has fewer functions but provides the basics so that a Windows CE application can gain access to shared network resources, such as disks and printers. The WNet API is implemented by a "redirector" DLL that translates the WNet functions into network commands for a specific network protocol.By default, the only network supported by the WNet API is Windows Networking. Support for even this network is limited by the fact that redirector files that implement Windows Networking aren't bundled with some Windows CE devices. For the WNet API to work, the redirector DLLs must be installed in the \windows directory. In addition, the network control panel, also a supplementary component on some systems, must be used to configure the network card so that it can access the network. If the redirector DLLs aren't installed, or an error occurs when you're configuring or initializing the network adapter, the WNet functions return the error code ERROR_NO_NETWORK.
WNet Functions
As with other areas in Windows CE, the WNet implementation under Windows CE is a subset of the same API on the desktop, but support is provided for the critical functions, while the overlapping and obsolete functions are eliminated. For example, the standard WNet API contains four different and overlapping WNetAddConnection functions, while Windows CE supports only one, WNetAddConnection3.
Conventions of UNC
Network drives can be accessed in one of two ways. The first method is to explicitly name the resource using the Universal Naming Convention (UNC) naming syntax, which is a combination of the name of the server and the shared resource. An example of this is \\BIGSRVR\DRVC, where the server name is BIGSRVR and the resource on the server is named DRVC. The leading double backslashes immediately indicate that the name is a UNC name. Directories and filenames can be included in the UNC name, as in \\bigsrvr\drvc\dir2\file1.ext. Notice that I changed case in the two names. That doesn't matter because UNC paths are case insensitive.As long as the WNet redirector is installed, you can use UNC names wherever you use standard filenames in the Windows CE API. You'll have problems, though, with some programs, which might not understand UNC syntax.
Mapping a Remote Drive
To get around applications that don't understand UNC names, you can map a network drive to a local name. When a network drive is mapped on a Windows CE system, the remote drive appears as a folder in the \network folder in the object store. The \network folder isn't a standard folder; in fact, in early versions of Windows CE, it didn't even show up in the Explorer. (For current systems, the visibility of the \network folder depends on a registry setting that's usually enabled.) Instead it's a placeholder name by which the local names of the mapped network drives can be addressed. For example, the network drive \\BigSrvr\DrvC could be mapped to the local name JoeBob. Files and directories on \\BigSrvr\DrvC would appear under the folder \network\joebob. The local name can't be represented as a drive letter, such as G, since Windows CE doesn't support drive letters.
I mentioned that the \network folder is a virtual folder; this needs further explanation. If you use the FindFirstFile/FindNextFile process to enumerate the directories in the root directory, the \network directory might not be enumerated. However, FindFirstFile/FindNextFile enumerates the mapped resources contained in the \network folder. So if the search string is \*.* to enumerate the root directory, the \network folder might not be enumerated, but if you use \network\*.* as the search string, any mapped drives will be enumerated.In Windows CE, the visibility of the \network folder is controlled by a registry setting. The \network folder is visible if the DWORD value RegisterFSRoot under the key [HKEY_LOCAL_MACHINE]\comm\redir exists and is set to a nonzero value. Deleting this value or setting it to 0 hides the \network folder.The most direct way to map a remote resource is to call this function:
DWORD WNetAddConnection3 (HWND hwndOwner, LPNETRESOURCE lpNetResource,
LPTSTR lpPassword, LPTSTR lpUserName,
DWORD dwFlags);
The first parameter is a handle to a window that owns any network support dialogs that might need to be displayed to complete the connection. The window handle can be NULL if you don't want to specify an owner window. This effectively turns the WNetAddConnection3 function into the WNetAddConnection2 function supported under other versions of Windows.The second parameter, lpNetResource, should point to a NETRESOURCE structure that defines the remote resource being connected. The structure is defined as
typedef struct _NETRESOURCE {
DWORD dwScope;
DWORD dwType;
DWORD dwDisplayType;
DWORD dwUsage;
LPTSTR lpLocalName;
LPTSTR lpRemoteName;
LPTSTR lpComment;
LPTSTR lpProvider;
} NETRESOURCE;
Most of these fields aren't used for the WNetAddConnection3 function and should be set to 0. All you need to do is specify the UNC name of the remote resource in a string pointed to by lpRemoteName and the local name in a string pointed to by lpLocalName. The local name is limited to 99 characters in length. The other fields in this structure are used by the WNet enumeration functions that I'll describe shortly.
You use the next two parameters in WNetAddConnection3, lpPassword and lpUserName, when requesting access from the server to the remote device. If you don't specify a user name and Windows CE can't find user information for network access already defined in the registry, the system displays a dialog box requesting the user name and password. Finally, the dwFlags parameter can be either 0 or the flag CONNECT_UPDATE_PROFILE. When this flag is set, the connection is dubbed persistent. Windows CE stores the connection data for persistent connections in the registry. Unlike other versions of Windows, Windows CE doesn't restore persistent connections when the user logs on. Instead, the local name to remote name mapping is tracked only in the registry. If the local folder is later accessed after the original connection was dropped, a reconnection is automatically attempted when the local folder is accessed.If the call to WNetAddConnection3 is successful, it returns NO_ERROR. Unlike most Win32 functions, WNetAddConnection3 returns an error code in the return value if an error occurs. This is a nod to compatibility that stretches back to the Windows 3.1 days. You can also call GetLastError to return the error information. As an aside, the function WNetGetLastError is supported under Windows CE, but it's just an alias for GetLastError, so you can call that function if compatibility with other platforms is important.The other function you can use under Windows CE to connect a remote resource is WNetConnectionDialog1. This function presents a dialog box to the user requesting the remote and local names for the connection. The function is prototyped as
DWORD WNetConnectionDialog1 (LPCONNECTDLGSTRUCT lpConnectDlgStruc);
The one parameter is a pointer to a CONNECTDLGSTRUCT structure defined as the following:
typedef struct {
DWORD cbStructure;
HWND hwndOwner;
LPNETRESOURCE lpConnRes;
DWORD dwFlags;
DWORD dwDevNum;
} CONNECTDLGSTRUCT;
The first field in the structure is the size field and must be set with the size of the CONNECTDLGSTRUCT structure before you call WNetConnectionDialog1. The hwndOwner field should be filled with the handle of the owner window for the dialog box. The lpConnRes field should point to a NETRESOURCE structure. This structure should be filled with zeros except for the lpRemoteName field, which may be filled to specify the default remote name in the dialog. You can leave the lpRemoteName field 0 if you don't want to specify a suggested remote path.
The dwFlags field can either be 0 or be set to the flag CONNDLG_RO_PATH. When this flag is specified, the user can't change the remote name field in the dialog box. Of course, this requirement means that the lpRemoteName field in the NETRESOURCE structure must contain a valid remote name. Windows CE ignores the dwDevNum field in the CONNECTDLGSTRUCT structure.When the function is called, it displays a dialog box that allows the user to specify a local name and, if not invoked with the CONNDLG_RO_PATH flag, the remote name as well. If the user taps on the OK button, Windows attempts to make the connection specified. The connection, if successful, is recorded as a persistent connection in the registry.If the connection is successful, the function returns NO_ERROR. If the user presses the Cancel button in the dialog box, the function returns -1. Other return codes indicate errors processing the function.
Disconnecting a Remote Resource
You can choose from three ways to disconnect a connected resource. The first method is to delete the connection with this function:
DWORD WNetCancelConnection2 (LPTSTR lpName, DWORD dwFlags,
BOOL fForce);
The lpName parameter points to either the local name or the remote network name of the connection you want to remove. The dwFlags parameter should be set to 0 or CONNECT_UPDATE_PROFILE. If CONNECT_UPDATE_PROFILE is set, the entry in the registry that references the connection is removed; otherwise, the call won't change that information. Finally, the fForce parameter indicates whether the system should continue with the disconnect, even if there are open files or print jobs on the remote device. If the function is successful, it returns NO_ERROR.You can prompt the user to specify a network resource to delete using this function:
DWORD WNetDisconnectDialog (HWND hwnd, DWORD dwType);
This function brings up a system-provided dialog box that lists all connections currently defined. The user can select one from the list and tap on the OK button to disconnect that resource. The two parameters for this function are a handle to the window that owns the dialog box and dwType, which is supposed to define the type of resources—printer (RESOURCETYPE_PRINT) or disk (RESOURCETYPE_DISK)—enumerated in the dialog box. However, some systems ignore this parameter and enumerate both disk and print devices. This dialog, displayed by WNetDisconnectDialog, is actually implemented by the network driver. So it's up to each OEM to get this dialog to work correctly.A more specific method to disconnect a network resource is to call
DWORD WNetDisconnectDialog1 (LPDISCDLGSTRUCT lpDiscDlgStruc);
This function is misleadingly named in that it won't display a dialog box if all the parameters in DISCDLGSTRUCT are correct and point to a resource not currently being used. The dialog part of this function appears when the resource is being used.DISCDLGSTRUCT is defined as
typedef struct {
DWORD cbStructure;
HWND hwndOwner;
LPTSTR lpLocalName;
LPTSTR lpRemoteName;
DWORD dwFlags;
} DISCDLGSTRUCT;
As usual, the cbStructure field should be set to the size of the structure. The hwndOwner field should be set to the window that owns any dialog box displayed. The lpLocalName and lpRemoteName fields should be set to the local and remote names of the resource that's to be disconnected. Under current implementations, lpLocalName is optional, while the lpRemoteName field must be set for the function to work correctly. The dwFlags parameter can be either 0 or DISC_NO_FORCE. If this flag is set and the network resource is currently being used, the system simply fails the function. Otherwise, a dialog appears asking the user if he or she wants to disconnect the resource even though the resource is being used. Under the current implementations, the DISC_NO_FORCE flag is ignored.
Enumerating Network Resources
It's all very well and good to connect to a network resource, but it helps if you know what resources are available to connect to. Windows CE supports three WNet functions used to enumerate network resources: WNetOpenEnum, WNetEnumResource, and WNetCloseEnum. The process is similar to enumerating files with FileFindFirst, FileFindNext, and FileFindClose.To start the process of enumerating network resources, first call the function
DWORD WNetOpenEnum (DWORD dwScope, DWORD dwType, DWORD dwUsage,
LPNETRESOURCE lpNetResource,
LPHANDLE lphEnum);
The first parameter, dwScope, specifies the scope of the enumeration. It can be one of the following flags:
RESOURCE_CONNECTEDEnumerate the connected resources.
RESOURCE_REMEMBEREDEnumerate the persistent network connections.
RESOURCE_GLOBALNETEnumerate all resources on the network.
The first two flags, RESOURCE_CONNECTED and RESOURCE_REMEMBERED, simply enumerate the resources already connected on your machine. The difference is that RESOURCE_CONNECTED returns the network resources that are connected at the time of the call, while RESOURCE_REMEMBERED returns those that are persistent regardless of whether they're currently connected. When either of these flags is used, the dwUsage parameter is ignored and the lpNetResource parameters must be NULL.The third flag, RESOURCE_GLOBALNET, allows you to enumerate resources—such as servers, shared drives, or printers out on the network—that aren't connected. The dwType parameter specifies what you're attempting to enumerate—shared disks (RESOURCETYPE_DISK), shared printers (RESOURCETYPE_PRINT), or both (RESOURCETYPE_ANY).You use the third and fourth parameters only if the dwScope parameter is set to RESOURCE_GLOBALNET. The dwUsage parameter specifies the usage of the resource and can be 0 to enumerate any resource, RESOURCEUSAGE_CONNECTABLE to enumerate only connectable resources, or RESOURCEUSAGE_CONTAINER to enumerate only containers such as servers.If the dwScope parameter is set to RESOURCE_GLOBALNET, the fourth parameter, lpNetResource, must point to a NETRESOURCE structure; otherwise, the parameter must be NULL. The NETRESOURCE structure should be initialized to specify the starting point on the network for the enumeration. The starting point is specified by a UNC name in the lpRemoteName field of NETRESOURCE. The dwUsage field of the NETRESOURCE structure must be set to RESOURCETYPE_CONTAINER. For example, to enumerate the shared resources on the server BIGSERV, the lpRemoteName field would point to the string \\BIGSERV. To enumerate all servers in a domain, lpRemoteName should simply specify the domain name. For the domain EntireNet, the lpRemoteName field should point to the string EntireNet. Because Windows CE doesn't allow you to pass a NULL into lpRemoteName when you use the RESOURCE_GLOBALNET flag, you can't enumerate all resources in the network namespace as you can under Windows XP. This restriction exists because Windows CE doesn't support the concept of a Windows CE device belonging to a specific network context.
The final parameter of WNetOpenEnum, lphEnum, is a pointer to an enumeration handle that will be passed to the other functions in the enumeration process. WNetOpenEnum returns a value of NO_ERROR if successful. If the function isn't successful, you can call GetLastError to query the extended error information.Once you have successfully started the enumeration process, you actually query data by calling this function:
DWORD WNetEnumResource (HANDLE hEnum, LPDWORD lpcCount,
LPVOID lpBuffer,
LPDWORD lpBufferSize);
The function takes the handle returned by WNetOpenEnum as its first parameter. The second parameter is a pointer to a variable that should be initialized with the number of resources you want to enumerate in each call to WNetEnumResource. You can specify -1in this variable if you want WNetEnumResource to return the data for as many resources as will fit in the return buffer specified by the lpBuffer parameter. The final parameter is a pointer to a DWORD that should be initialized with the size of the buffer pointed to by lpBuffer. If the buffer is too small to hold the data for even one resource, WNetEnumResource sets this variable to the required size for the buffer.The information about the shared resources returned by data is returned in the form of an array of NETRESOURCE structures. While this is the same structure I described when I talked about the WNetAddConnection3 function, I'll list the elements of the structure here again for convenience:
typedef struct _NETRESOURCE {
DWORD dwScope;
DWORD dwType;
DWORD dwDisplayType;
DWORD dwUsage;
LPTSTR lpLocalName;
LPTSTR lpRemoteName;
LPTSTR lpComment;
LPTSTR lpProvider;
} NETRESOURCE;
The interesting fields in the context of enumeration start with the dwType field, which indicates the type of resource that was enumerated. The value can be RESOURCETYPE_DISK or RESOURCETYPE_PRINT. The dwDisplayType field provides even more information about the resource, demarcating domains (RESOURCEDISPLAYTYPE_DOMAIN) from servers (RESOURCEDISPLAYTYPE_SERVER) and from shared disks and printers (RESOURCEDISPLAYTYPE_SHARE). A fourth flag, RESOURCEDISPLAYTYPE_GENERIC, is returned if the display type doesn't matter.
The lpLocalName field points to a string containing the local name of the resource if the resource is currently connected or is a persistent connection. The lpRemoteName field points to the UNC name of the resource. The lpComment field contains the comment line describing the resource that's provided by some servers.WNetEnumResource either returns NO_ERROR, indicating that the function passed (but you need to call it again to enumerate more resources), or ERROR_NO_MORE_ITEMS, indicating that you have enumerated all resources matching the specification passed in WNetOpenEnum. With any other return code, you should call GetLastError to further diagnose the problem.You have few strategies when enumerating the network resources. You can specify a huge buffer and pass -1in the variable pointed to by lpcCount, telling WNetEnumResource to return as much information as possible in one shot. Or you can specify a smaller buffer and ask for only one or two resources for each call to WNetEnumResource. The one caveat on the small buffer approach is that the strings that contain the local and remote names are also placed in the specified buffer. The name pointers inside the NETRESOURCE structure then point to those strings. This means that you can't specify the size of the buffer to be exactly the size of the NETRESOURCE structure and expect to get any data back. A third possibility is to call WNetEnumResource twice, the first time with the lpBuffer parameter 0, and have Windows CE tell you the size necessary for the buffer. Then you allocate the buffer and call WNetEnumResource again to actually query the data. However you use WNetEnumResource, you'll need to check the return code to see whether it needs to be called again to enumerate more resources.When you have enumerated all the resources, you must make one final call to the function:
DWORD WNetCloseEnum (HANDLE hEnum);
The only parameter to this function is the enumeration handle first returned by WNetOpenEnum. This function cleans up the system resources used by the enumeration process.Following is a short routine that uses the enumeration functions to query the network for available resources. You pass to a function a UNC name to use as the root of the search. The function returns a buffer of zero-delimited strings that designate the local name, if any, and the UNC name of each shared resource found.
// Helper routine
int AddToList (LPTSTR *pPtr, INT *pnListSize, LPTSTR pszStr) {
INT nLen = lstrlen (pszStr) + 1;
if (*pnListSize < nLen) return -1;
lstrcpy (*pPtr, pszStr);
*pPtr += nLen;
*pnListSize -= nLen;
return 0;
}
//----------------------------------------------------------------------
// EnumNetDisks - Produces a list of shared disks on a network
//
int EnumNetDisks (LPTSTR pszRoot, LPTSTR pszNetList, int nNetSize){
INT i = 0, rc, nBuffSize = 1024;
DWORD dwCnt, dwSize;
HANDLE hEnum;
NETRESOURCE nr;
LPNETRESOURCE pnr;
PBYTE pPtr, pNew;
// Allocate buffer for enumeration data.
pPtr = (PBYTE) LocalAlloc (LPTR, nBuffSize);
if (!pPtr)
return -1;
// Initialize specification for search root.
memset (&nr, 0, sizeof (nr));
nr.lpRemoteName = pszRoot;
nr.dwUsage = RESOURCEUSAGE_CONTAINER;
// Start enumeration.
rc = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &nr,
&hEnum);
if (rc != NO_ERROR)
return -1;
// Enumerate one item per loop.
do {
dwCnt = 1;
dwSize = nBuffSize;
rc = WNetEnumResource (hEnum, &dwCnt, pPtr, &dwSize);
// Process returned data.
if (rc == NO_ERROR) {
pnr = (NETRESOURCE *)pPtr;
if (pnr->lpRemoteName)
rc = AddToList (&pszNetList, &nNetSize,
pnr->lpRemoteName);
// If our buffer was too small, try again.
} else if (rc == ERROR_MORE_DATA) {
pNew = LocalReAlloc (pPtr, dwSize, LMEM_MOVEABLE);
if (pNew) {
pPtr = pNew;
nBuffSize = LocalSize (pPtr);
rc = 0;
}
}
} while (rc == 0);
// If the loop was successful, add extra zero to list.
if (rc == ERROR_NO_MORE_ITEMS) {
rc = AddToList (&pszNetList, &nNetSize, TEXT ("));
rc = 0;
}
// Clean up.
WNetCloseEnum (hEnum);
LocalFree (pPtr);
return rc;
}
While the enumeration functions work well for querying what's available on the net, you can use another strategy for determining the current connected resources. At the simplest level, you can use FileFindFirst and FileFindNext to enumerate the locally connected network disks by searching the folders in the \network directory. Once you have the local name, a few functions are available to you for querying just what that local name is connected to.
Querying Connections and Resources
The folders in the \network directory represent the local names of network-shared disks that are persistently connected to network resources. To determine which of the folders are currently connected, you can use the function
DWORD WNetGetConnection (LPCTSTR lpLocalName,
LPTSTR lpRemoteName,
LPDWORD lpnLength);
WNetGetConnection returns the UNC name of the network resource associated with a local device or folder. The lpLocalName parameter is filled with the local name of a shared folder or printer. The lpRemoteName parameter should point to a buffer that can receive the UNC name for the device. The lpnLength parameter points to a DWORD value that initially contains the length in characters of the remote name buffer. If the buffer is too small to receive the name, the length value is loaded with the number of characters required to hold the UNC name.
One feature (or problem, depending on how you look at it) of WNetGetConnection is that it fails unless the local folder or device has a current connection to the remote shared device. This allows us an easy way to determine which local folders are currently connected and which are just placeholders for persistent connections that aren't currently connected.Sometimes you need to transfer a filename from one system to another and you need a common format for the filename that would be understood by both systems. The WNetGetUniversalName function translates a filename that contains a local network name into one using the UNC name of the connected resource. The prototype for WNetGetUniversalName is the following:
DWORD WNetGetUniversalName (LPCTSTR lpLocalPath, DWORD dwInfoLevel,
LPVOID lpBuffer, LPDWORD lpBufferSize);
Like WNetGetConnection, this function returns a UNC name for a local name. There are two main differences between WNetGetConnection and WNetGetUniversalName. First, WNetGetUniversalName works even if the remote resource isn't currently connected. Second, you can pass a complete filename to WNetGetUniversalName instead of simply the local name of the shared resource, which is all that is accepted by WNetGetConnection.WNetGetUniversalName returns the remote information in two different formats. If the dwInfoLevel parameter is set to UNIVERSAL_NAME_INFO_LEVEL, the buffer pointed to by lpBuffer is loaded with the following structure:
typedef struct _UNIVERSAL_NAME_INFO {
LPTSTR lpUniversalName;
} UNIVERSAL_NAME_INFO;
The only field in the structure is a pointer to the UNC name for the shared resource. The string is returned in the buffer immediately following the structure. So if a server \\BigServ\DriveC was attached as LocC and you pass WNetGetUniversalName the filename \Network\LocC\Win32\Filename.ext, the function returns the UNC name \\BigServ\DriveC\win32\filename.ext.If the dwInfoLevel parameter is set to REMOTE_NAME_INFO_LEVEL, the buffer is filled with the following structure:
typedef struct _REMOTE_NAME_INFO
LPTSTR lpUniversalName;
LPTSTR lpConnectionName;
LPTSTR lpRemainingPath;
} REMOTE_NAME_INFO;
This structure not only returns the UNC name but also parses the UNC name into the share name and the remaining path. So, using the same filename as in the previous example, \network\LocC\win32\filename.ext, the REMOTE_NAME_INFO fields would point to the following strings:
lpUniversalName: | \\BigServ\DriveC\win32\filename.ext |
lpConnectionName: | \\BigServ\DriveC |
lpRemainingPath: | \win32\filename.ext |
One more thing: you don't have to prefix the local share name with \network. In the preceding example, the filename \LocC\Win32\filename.ext would have produced the same results.One final WNet function supported by Windows CE is
DWORD WNetGetUser (LPCTSTR lpName, LPTSTR lpUserName,
LPDWORD lpnLength);
This function returns the name the system used to connect to the remote resource. WNetGetUser is passed the local name of the shared resource and returns the user name the system used when connecting to the remote resource in the buffer pointed to by lpUserName. The lpnLength parameter should point to a variable that contains the size of the buffer. If the buffer isn't big enough to contain the user name, the variable pointed to by lpnLength is filled with the required size for the buffer.
The ListNet Example Program
ListNet is a short program that lists the persistent network connections on a Windows CE machine. The program's window is a dialog box with three controls: a list box that displays the network connections, a Connect button that lets you add a new persistent connection, and a Disconnect button that lets you delete one of the connections. Double-clicking on a connection in the list box opens an Explorer window to display the contents of that network resource. Figure 13-1 shows the ListNet window, while Listing 13-1 shows the ListNet source code.

Figure 13-1: The ListNet window containing a few network folders
Listing 13-1: The ListNet source
ListNet.rc
//======================================================================
// Resource file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include "windows.h"
#include "ListNet.h" // Program-specific stuff
//----------------------------------------------------------------------
// Icons and bitmaps
//
ID_ICON ICON "ListNet.ico" // Program icon
//----------------------------------------------------------------------
// Main window dialog template
//
ListNet DIALOG discardable 10, 10, 120, 65
STYLE WS_OVERLAPPED | WS_VISIBLE | WS_CAPTION | WS_SYSMENU |
DS_CENTER | DS_MODALFRAME
CAPTION "ListNet"
BEGIN
LISTBOX IDD_NETLIST, 2, 2, 116, 46,
WS_TABSTOP | WS_VSCROLL |
LBS_NOINTEGRALHEIGHT | LBS_USETABSTOPS
PUSHBUTTON "&Connect...", IDD_CNCT, 2, 50, 55, 12, WS_TABSTOP
PUSHBUTTON "&Disconnect...",
IDD_DCNCT, 61, 50, 55, 12, WS_TABSTOP
END
ListNet.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD { // Structure associates
UINT Code; // menu IDs with a
LRESULT (*Fxn)(HWND, WORD, HWND, WORD); // function.
};
//----------------------------------------------------------------------
// Generic defines used by application
#define ID_ICON 1
#define IDD_NETLIST 100 // Control IDs
#define IDD_CNCT 101
#define IDD_DCNCT 102
//----------------------------------------------------------------------
// Function prototypes
//
INT RefreshLocalNetDrives (HWND hWnd);
int CheckErrorCode (HWND hWnd, int rc, LPTSTR lpText);
// Dialog window procedure
BOOL CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
// Dialog window Message handlers
BOOL DoCommandMain (HWND, UINT, WPARAM, LPARAM);
// Command functions
LPARAM DoMainCommandExit (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandViewDrive (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandMapDrive (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandFreeDrive (HWND, WORD, HWND, WORD);
ListNet.cpp
//======================================================================
// ListNet - A network demo application for Windows CE
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include <winnetwk.h> // Network includes
#include "ListNet.h" // Program-specific stuff
#if defined(WIN32_PLATFORM_PSPC)
#include <aygshell.h> // Add Pocket PC includes.
#pragma comment( lib, "aygshell" ) // Link Pocket PC lib for menu bar.
#endif
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("ListNet");
HINSTANCE hInst; // Program instance handle
BOOL fFirst = TRUE;
// Command Message dispatch for MainWindowProc
const struct decodeCMD MainCommandItems[] = {
IDOK, DoMainCommandExit,
IDCANCEL, DoMainCommandExit,
IDD_NETLIST, DoMainCommandViewDrive,
IDD_CNCT, DoMainCommandMapDrive,
IDD_DCNCT, DoMainCommandFreeDrive,
};
//======================================================================
//
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
// Save program instance handle in global variable.
hInst = hInstance;
// Create main window.
DialogBox (hInst, szAppName, NULL, MainWndProc);
return 0;
}
//======================================================================
// Message handling procedures for main window
//----------------------------------------------------------------------
// MainWndProc - Callback function for application window
//
BOOL CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
INT i;
// With only two messages, do it the old-fashioned way.
switch (wMsg) {
case WM_INITDIALOG:
#if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
{
SHINITDLGINFO di;
SHMENUBARINFO mbi; // For Pocket PC, create
memset(&mbi, 0, sizeof(SHMENUBARINFO)); // menu bar so that we
mbi.cbSize = sizeof(SHMENUBARINFO); // have a sip button.
mbi.hwndParent = hWnd;
mbi.dwFlags = SHCMBF_EMPTYBAR;
SHCreateMenuBar(&mbi);
di.dwMask = SHIDIM_FLAGS;
di.hDlg = hWnd;
di.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLG;
SHInitDialog (&di);
}
#endif
i = 75;
SendDlgItemMessage (hWnd, IDD_NETLIST, LB_SETTABSTOPS, 1,
(LPARAM)&i);
RefreshLocalNetDrives (hWnd);
break;
case WM_COMMAND:
return DoCommandMain (hWnd, wMsg, wParam, lParam);
}
return FALSE;
}
//----------------------------------------------------------------------
// DoCommandMain - Process WM_COMMAND message for window.
//
BOOL DoCommandMain (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam){
WORD idItem, wNotifyCode;
HWND hwndCtl;
INT i;
// Parse the parameters.
idItem = (WORD) LOWORD (wParam);
wNotifyCode = (WORD) HIWORD (wParam);
hwndCtl = (HWND) lParam;
// Call routine to handle control message.
for (i = 0; i < dim(MainCommandItems); i++) {
if (idItem == MainCommandItems[i].Code) {
(*MainCommandItems[i].Fxn)(hWnd, idItem, hwndCtl,
wNotifyCode);
return TRUE;
}
}
return FALSE;
}
//======================================================================
// Command handler routines
//----------------------------------------------------------------------
// DoMainCommandExit - Process Program Exit command.
//
LPARAM DoMainCommandExit (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
EndDialog (hWnd, 0);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandViewDrive - Process list box double clicks.
//
LPARAM DoMainCommandViewDrive (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
TCHAR szCmdLine[128], szFolder[MAX_PATH];
PROCESS_INFORMATION pi;
HCURSOR hOld;
INT i, rc, nLen;
// We're only interested in list box double-clicks.
if (wNotifyCode != LBN_DBLCLK)
return 0;
i = SendMessage (hwndCtl, LB_GETCURSEL, 0, 0);
if (i == LB_ERR) return 0;
nLen = SendMessage (hwndCtl, LB_GETTEXT, i, (LPARAM)szFolder);
if (nLen == LB_ERR)
return 0;
// Trim off description of share.
for (i = 0; i < nLen; i++)
if (szFolder[i] == TEXT ('\t'))
break;
szFolder[i] = TEXT ('\0');
hOld = SetCursor (LoadCursor (NULL, IDC_WAIT));
lstrcpy (szCmdLine, TEXT ("\\network\\"));
lstrcat (szCmdLine, szFolder);
rc = CreateProcess (TEXT ("Explorer"), szCmdLine, NULL, NULL,
FALSE, 0, NULL, NULL, NULL, &pi);
if (rc) {
CloseHandle (pi.hProcess);
CloseHandle (pi.hThread);
}
SetCursor (hOld);
return TRUE;
}
//----------------------------------------------------------------------
// DoMainCommandMapDrive - Process map network drive command.
//
LPARAM DoMainCommandMapDrive (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
DWORD rc;
CONNECTDLGSTRUCT cds;
NETRESOURCE nr;
TCHAR szRmt[256];
memset (&nr, 0, sizeof (nr));
nr.dwType = RESOURCETYPE_DISK;
memset (szRmt, 0, sizeof (szRmt));
cds.cbStructure = sizeof (cds);
cds.hwndOwner = hWnd;
cds.lpConnRes = &nr;
cds.dwFlags = CONNDLG_PERSIST;
// Display dialog box.
rc = WNetConnectionDialog1 (&cds);
if (rc == NO_ERROR)
RefreshLocalNetDrives (hWnd);
else
CheckErrorCode (hWnd, rc, TEXT ("WNetConnectionDialog1"));
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandFreeDrive - Process disconnect network drive command.
//
LPARAM DoMainCommandFreeDrive (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
int rc = WNetDisconnectDialog (hWnd, RESOURCETYPE_DISK);
if (rc == NO_ERROR)
RefreshLocalNetDrives (hWnd);
else
CheckErrorCode (hWnd, rc, TEXT ("WnetDisconnectDialog"));
return 0;
}
//======================================================================
// Network browsing functions
//----------------------------------------------------------------------
// EnumerateLocalNetDrives - Add an item to the list view control.
//
INT RefreshLocalNetDrives (HWND hWnd) {
HWND hwndCtl = GetDlgItem (hWnd, IDD_NETLIST);
INT rc, nBuffSize = 1024;
DWORD dwCnt, dwSize;
HANDLE hEnum;
LPNETRESOURCE pnr;
NETRESOURCE nr;
PBYTE pPtr, pNew;
TCHAR szText[256];
SendMessage (hwndCtl, LB_RESETCONTENT, 0, 0);
// Allocate buffer for enumeration data.
pPtr = (PBYTE) LocalAlloc (LPTR, nBuffSize);
if (!pPtr)
return -1;
// Initialize specification for search root.
memset (&nr, 0, sizeof (nr));
lstrcpy (szText, TEXT ("\\sjdev"));
nr.lpRemoteName = szText;
nr.dwUsage = RESOURCEUSAGE_CONTAINER;
// Start enumeration.
rc = WNetOpenEnum (RESOURCE_REMEMBERED, RESOURCETYPE_ANY, 0, 0,
&hEnum);
if (rc != NO_ERROR) return -1;
// Enumerate one item per loop.
do {
dwCnt = 1;
dwSize = nBuffSize;
rc = WNetEnumResource (hEnum, &dwCnt, pPtr, &dwSize);
pnr = (NETRESOURCE *)pPtr;
lstrcpy (szText, pnr->lpLocalName);
// Process returned data.
if (rc == NO_ERROR) {
switch (pnr->dwType) {
case RESOURCETYPE_ANY:
lstrcat (szText, TEXT ("\t Share"));
break;
case RESOURCETYPE_PRINT:
lstrcat (szText, TEXT ("\t Printer"));
break;
case RESOURCETYPE_DISK:
lstrcat (szText, TEXT ("\t Disk"));
break;
}
SendMessage (hwndCtl, LB_ADDSTRING, 0, (LPARAM)szText);
// If our buffer was too small, try again.
} else if (rc == ERROR_MORE_DATA) {
pNew = (PBYTE)LocalReAlloc (pPtr, dwSize, LMEM_MOVEABLE);
if (pNew) {
pPtr = pNew;
nBuffSize = LocalSize (pPtr);
rc = 0;
} else
break;
}
} while (rc == 0);
// Clean up.
WNetCloseEnum (hEnum);
LocalFree (pPtr);
return 0;
}
//----------------------------------------------------------------------
// CheckErrorCode - Print error messages as necessary.
//
int CheckErrorCode (HWND hWnd, int rc, LPTSTR lpText) {
TCHAR szTxt[128];
// If good or dialog canceled, just return.
if ((rc == NO_ERROR) || (rc == -1))
return rc;
if (rc == ERROR_NO_NETWORK)
lstrcpy (szTxt, TEXT ("No network detected."));
else
wsprintf (szTxt, TEXT ("%s failed rc = %d"), lpText, rc);
MessageBox (hWnd, szTxt, szAppName, MB_OK);
return rc;
}
The heart of the networking code is at the end of ListNet, in the routine RefreshLocalNetDrives. This routine uses the WNet enumerate functions to determine the persistent network resources mapped to the system. Network connections and disconnections are accomplished with calls to WNetConnectionDialog1 and WNetDisconnectDialog respectively. You open an Explorer window containing the shared network disk by launching Explorer.exe with a command line that's the path of the folder to open.This chapter has given you a basic introduction to some of the networking features of Windows CE. Next on our plate is networking from a different angle: peer-to-peer communication. In Chapter 14, we look at how a Windows CE device can communicate with another Windows CE device using Infrared and Bluetooth communication. Let's take a look.