Windows CE is designed to work with networks. When several Windows machines exist on a net, they can easily share disk drives and printers with one another. The Explorer provides an easy way for users to connect to these shared devices. The Windows CE API also gives you mechanisms to connect to these devices from within your applications. Windows sees the network as a tree. Any Windows network is divided into a series of domains, each of which contains a set of machines. Each machine can share zero or more drives, directories, or printers on the network. Windows CE supports a subset of the Win32 API WNet functions that can be used to maintain connections to network resources (such as folder and printer shares). Before using the WNet functions you must have a valid network connection through a Remote Access Services (RAS) or direct network connection using a network adapter (such as PCMCIA compatible NE2000). WNet functions cannot be used through an ActiveSync connection to a desktopPC. The Win32 API contains a set of functions that allow you to enumerate all the shares available throughout the network and then connect to any one of these shares. The network itself, its domains, and the machines in the domains are called containers. You open containers with the WNetOpenEnum function. A container can contain other containers (for example, domains contain machines), or it can contain actual drive and printer resources, called objects. You enumerate all the items in a container that is, you request a list of everything that a container holds using the WNetEnumResources function. Once you get down to the share level, you can connect to a drive with the WNetAddConnection2 function. This section shows you how to walk through the resource tree and also how to gather information about connected resources. Note you will need to include winnetwk.h to call the WNet functions. Enumerating Network ResourcesThe code shown in Listing 3.7 demonstrates how to walk recursively through all the resources available on your network. It starts with the network itself and opens every container it finds until it reaches actual drives and printers that each machine shares on the network. It is these drive and printer objects that receive connections. Listing 3.7 Lists all objects (shares and printers) on a network// NB: include winnetwk.h // This function handles WNet errors void ErrorHandler(DWORD dwErrorNum, LPTSTR s) { cout _T("Failure in: ") s _T(" ") GetLastError() endl; } // This function displays the information in a // NETRESOURCE structure void DisplayStruct(LPNETRESOURCE nr) { cout _T("Type: "); switch(nr->dwType) { case RESOURCETYPE_DISK: cout _T("Disk") endl; break; case RESOURCETYPE_PRINT: cout _T("Printer") endl; break; case RESOURCETYPE_ANY: cout _T("Any") endl; } cout _T("Display Type: "); switch(nr->dwDisplayType) { case RESOURCEDISPLAYTYPE_DOMAIN: cout _T("Domain") endl; break; case RESOURCEDISPLAYTYPE_GENERIC: cout _T("Generic") endl; break; case RESOURCEDISPLAYTYPE_SERVER: cout _T("Server") endl; break; case RESOURCEDISPLAYTYPE_SHARE: cout _T("Share") endl; } if(nr->lpLocalName) cout _T("Local Name: ") nr->lpLocalName endl; if(nr->lpRemoteName) cout _T("Remote Name: ") nr->lpRemoteName endl; if(nr->lpComment) cout _T("Comment: ") nr->lpComment endl; if(nr->lpProvider) cout _T("Provider: ") nr->lpProvider endl; cout endl; } // Recursive function to enumerate resources BOOL EnumerateResources(LPNETRESOURCE nrStartingPoint) { DWORD dwResult, dwResultEnum, i; LPNETRESOURCE lpNRBuffer; DWORD dwBufferSize = 16384; DWORD dwNumEntries = 0xFFFFFFFF; HANDLE hEnum; dwResult = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, nrStartingPoint, &hEnum); if(dwResult != NO_ERROR) { ErrorHandler(dwResult, _T("WNetOpenEnum")); return FALSE; } // allocate a buffer to hold resources lpNRBuffer = (LPNETRESOURCE) LocalAlloc(LPTR, dwBufferSize); // loop through all the elements in the container do { dwBufferSize = 16384; dwNumEntries = 0xFFFFFFFF; // Get resources dwResultEnum = WNetEnumResource(hEnum, &dwNumEntries, lpNRBuffer, &dwBufferSize); if(dwResultEnum == NO_ERROR) { // loop through each of the entries for(i = 0; i dwNumEntries; i++) { DisplayStruct(&lpNRBuffer[i]); // if container, recursively open it if(lpNRBuffer[i].dwUsage & RESOURCEUSAGE_CONTAINER) { if(!EnumerateResources( &lpNRBuffer[i])) cout _T("Enumeration Failed.") endl; } } } else if(dwResultEnum != ERROR_NO_MORE_ITEMS) { ErrorHandler(dwResultEnum, _T("WNetEnumResource")); break; } } while(dwResultEnum != ERROR_NO_MORE_ITEMS); // Clean up LocalFree(lpNRBuffer); dwResult = WNetCloseEnum(hEnum); if(dwResult != NO_ERROR) { ErrorHandler(dwResult, _T("WNetCloseEnum")); return FALSE; } return TRUE; } void Listing3_7() { // Start the recursion at the net level NETRESOURCE nr; TCHAR szContainer[MAX_PATH + 1]; if(!GetTextResponse( _T("Enter Container to list:"), szContainer, MAX_PATH)) return; memset(&nr, 0, sizeof(nr)); nr.lpRemoteName = szContainer; nr.dwUsage = RESOURCEUSAGE_CONTAINER; EnumerateResources(&nr); } The program in Listing 3.7 starts in its Listing3_7 function by prompting the user for the container (either a domain or a server). It passes this container to the EnumerateResources function, which recursively traverses the container. The EnumerateResources function calls WNetOpenEnum.
The WNetOpenEnum function opens a container, returning a handle to that container so that you can enumerate its contents. The Resource parameter specifies the container that you want to open. The Scope, Type, and Usage parameters specify the type of objects that will be enumerated by the WNetEnumResources function. Initially, the WNetOpenEnum function receives the container specified by the user for its resource. Once the container is open, Listing 3.7 enters a loop that calls WNetEnumResources to get all the objects inside the container.
The WNetEnumResources function accepts the handle returned by WNetOpenEnum, the number of entries desired (or 0xFFFFFFFF if you want them all), a buffer to place the entries into (allocated by LocalAlloc; see Chapter 12 for details), and the size of the buffer (the documentation specifies that 16K is a reasonable value). In the buffer the function returns an array of NETRESOURCE structures that contains information about each entry in the container. typedef struct _NETRESOURCE { DWORD dwScope; DWORD dwType; DWORD dwDisplayType; DWORD dwUsage; LPTSTR lpLocalName; LPTSTR lpRemoteName; LPTSTR lpComment; LPTSTR lpProvider; } NETRESOURCE; Much useful information is contained in a NETRESOURCE structure. The DisplayStruct function near the top of Listing 3.7 displays most of this information. The Scope field tells the status of an enumeration.
If connected or remembered, the enumeration must be a device, either a printer or a drive, and the LocalName field contains the local name of the device. An enumeration marked as USAGE_GLOBALNET gives more information about itself in the Usage field, which can have one of the following values:
In either case, the RemoteName field contains the name used to connect to or open the enumeration. The Type field tells whether a connectable object is a disk or a printer.
The DisplayType field tells how to display the object. This field is used in Windows' connection dialogs to determine the icon placed next to each item.
The NETRESOURCE structure also contains the comment and the name of the provider. Following the call to WNetEnumResources, Listing 3.7 loops through all the NETRESOURCE structures in the buffer. First it displays each record's contents. Then it inspects each record to decide whether or not it is a container. If it is a container, the EnumerateResources function recursively calls itself so that it can open and display the container. If it is not a container, it is a drive or a printer and a connection can be formed to it. Once the code has examined all the entries in the buffer, it cleans up and returns. The first time that you call WNetEnumResources for any container it should return the error code NO_ERROR, as well as a buffer full of entries. However, there is no guarantee that the function was able to place all the entries for a given container into the buffer on the first call. Therefore, you should call it repeatedly until it returns ERROR_NO_MORE_ITEMS. This is the reason for the do . . . while loop in the code. If something goes wrong, the ErrorHandler function seen in Listing 3.7 handles any WNet error. In cases where the network provider reports an error, the ErrorHandler function calls the GetLastError function to obtain error information.
|