MobileMastering Windows Networking (WNet)

Mastering Windows Networking (WNet)

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

A Bit of Theory

Windows CE, being a little brother of its desktop counterpart, now exposes most PC communications capabilities. In many cases, they are just a subset of desktop versions, but still may be quite useful. In this article, we will discuss Windows Networking API (WNet).

Mobile applications may access a remote host’s file system and other resources via WNet. Your program can establish and terminate network connections, retrieve configuration information, and so forth. Similar to the desktop, WNet communicates to the outer world via CIFS—Common Internet File System redirector—a module, through which one computer can access another one. Network resources are named using Usiversal Naming Convention (UNC).

As noted above, Windows CE supports only some restricted subsets of WNet API. The main limitations are:

  • Microsoft Windows Network is the only network provider currently supported
  • No connections are restored during a warm boot. A persistent connection is stored in the Registry and then may be enumerated and retrieved by calling the WNetOpenEnum function with the dwScope parameter set to RESOURCE_REMEMBERED
  • You can’t use any drive letters to map network resources
  • No Mail slots or Named Pipes are supported
  • No LaN Manager functions are supported
  • Windows CE does not support the concept of a computer or device belonging to a specific network context
  • WNet API doesn’t communicate over ActiveSync connection

WNet API is compact enough and may be easily overviewed. The following table briefly describes its functions.

Function Description
Enumeration Functions
WNetOpenEnum Starts an enumeration of network resources or existing connections
WNetEnumResource Continues a network enumeration started by WNetOpenEnum
WNetCloseEnum Ends a network enumeration started by WNetOpenEnum
Connection-related Functions
WNetAddConnection3 Makes a connection to a network resource and can specify a local name for the resource
WNetCancelConnection2 Terminates an existing network connection
WNetConnectionDialog1 Displays a general browsing dialog box for connecting to a network
WNetDisconnectDialog Displays a dialog box listing all currently connected resources and permits the user to select which resources to disconnect
WNetDisconnectDialog1 Attempts to disconnect from a network; notifies the user of any errors
Network Information Functions
WNetGetConnection Retrieves the remote name of a network resource associated with a local name
WNetGetUniversalName Maps a local path for a network resource to a data structure containing the UNC-based name
WNetGetUser Retrieves user name used to establish a network connection

Keeping all this stuff in mind, you nevertheless can use WNet pretty effectively for different data transmission tasks. The basic idea here is that, once you know network resource name, you can treat this resource in terms of standard file I/O. The next sections will illustrate some common scenarios your applications may need to implement.

Foreword for MFC Applications

The recent implementation of MFC for Windows CE does not allow using WNet in MFC-based applications. This is a strange fact, but that’s how it is. To work around this issue, add the following lines to stdafx.h:

#undef _WINNETWK_
#include <winnetwk.h>

The origin of all this stuff comes from wce.h header files under the MFC Include folder, where you can find that many API are stored even though there are appropriate headers in the SDK folder. But all in all, WNet is easy to revive by such a simple trick.

Enumerating Domain Resources

The first sample we will discuss produces the list of domain network resources. It recursively surfs through domain entries and puts them into the listbox for simplicity. The basic points of this process are listed below:

  • Call WNetOpenEnum to open the enumeration
  • Sequentually call WNetEnumResource until it returns ERROR_NO_MORE_ITEMS
  • On each successful call of WNetEnumResource process, receive NETRESOURCE data
  • If the current node is of the RESOURCEUSAGE_CONTAINER type, you can use it as a starting point for nested enumeration
  • Close the enumeration handle by using a WNetCloseEnum call

So, please take a look at the following code snippet:

BOOL EnumerateDomainResources(LPNETRESOURCE lpnrStartFrom,
                              CListBox& ListBox)
{
   DWORD dwResult, dwResultEnum, i;
   LPNETRESOURCE lpNRBuffer;
   DWORD dwBufferSize = 16384;
   DWORD dwNumEntries = (DWORD)-1;
   HANDLE hEnum;
   CString sErr;

   dwResult = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
                           0, lpnrStartFrom, &hEnum);
   if(dwResult != NO_ERROR)
   {
      sErr.Format(L"WNetOpenEnum err = %d",dwResult);
      AfxMessageBox(sErr);
      return FALSE;
   }

   // allocate a buffer
   lpNRBuffer = (LPNETRESOURCE) new BYTE[dwBufferSize];

   // loop through all the stuff
   do
   {
      dwBufferSize = 16384;
      dwNumEntries = (DWORD)-1;
      // Get resources
      dwResultEnum = WNetEnumResource(hEnum, &dwNumEntries,
                                      lpNRBuffer, &dwBufferSize);
      if(dwResultEnum == NO_ERROR)
      {
         // loop through each of the entries
         for(i = 0; i < dwNumEntries; i++)
         {
            sErr.Format(L"%s",lpNRBuffer[i].lpRemoteName);
            ListBox.AddString(sErr);
            if( (lpNRBuffer[i].dwUsage & RESOURCEUSAGE_CONTAINER)
               == RESOURCEUSAGE_CONTAINER )
            {
               if( !EnumerateDomainResources(&lpNRBuffer[i],
                   ListBox) )
               {
                  AfxMessageBox(L"Enumeration Failed");
                  continue;
               }
            }
         }
      }
      else if(dwResultEnum != ERROR_NO_MORE_ITEMS)
      {
         sErr.Format(L"WNetEnumResource err = %d",dwResultEnum);
         AfxMessageBox(sErr);
         break;
      }
   }
   while(dwResultEnum != ERROR_NO_MORE_ITEMS);

   delete lpNRBuffer;

   dwResult = WNetCloseEnum(hEnum);
   if(dwResult != NO_ERROR)
   {
      sErr.Format(L"WNetCloseEnum err = %d",dwResult);
      AfxMessageBox(sErr);
      return FALSE;
   }

   return TRUE;
}

Let’s briefly overview this sample. As we have said before, a call to WNetOpenEnum opens a new enumeration. The first parameter, dwScope, controls what should be enumerated. The sample uses a RESOURCE_GLOBALNET value to get all resources on the network. RESOURCE_CONNECTED and RESOURCE_REMEMBERED will give us active connections and previously stored resources respectively. After enumeration has been initiated, the application sequentually calls WNetEnumResource to achieve the next resource data. You are free to do all required processing of this data depending on resource type and usage. The code snippet takes the simplest way—proceeds nested enumerations. This loop ends up when WNetEnumResource returns ERROR_NO_MORE_ITEMS. And finally, WNetCloseEnum releases the enumeration handle.

Establishing and Terminating Network Connections

WNet provides several fucntions to establish and to terminate a connection to a network resource. You can use either WNetConnectionDialog1 or WNetAddConnection3 to create a new connection. After a successful call, each one creates a new folder under the ‘Network’ directory on the device. Notice that the system does not re-connect to mapped resources after a warm reboot. Following are two code snippets to demonstrate the usage of each of these functions.

void CWNetSampleDlg::OnButtonAdd()
{
   UpdateData();

   NETRESOURCE nr;
   memset(&nr,0,sizeof(nr));
   nr.lpRemoteName = (LPTSTR)(LPCTSTR)m_sRemoteName;
   nr.dwType = RESOURCETYPE_DISK;

   CONNECTDLGSTRUCT c;
   memset(&c,0,sizeof(c));
   c.cbStructure = sizeof(CONNECTDLGSTRUCT);
   c.hwndOwner = m_hWnd;
   c.lpConnRes = &nr;

   DWORD dwRes = WNetConnectionDialog1(&c);
   CString sErr;
   sErr.Format(L"Error = %d",dwRes);
   AfxMessageBox(sErr);
}

void CWNetSampleDlg::OnButtonAdd3()
{
   UpdateData();

   NETRESOURCE nr;
   memset(&nr,0,sizeof(nr));
   nr.lpRemoteName = (LPTSTR)(LPCTSTR)m_sRemoteName;
   nr.lpLocalName  = L"MyShareTmp";
   nr.dwType       = RESOURCETYPE_DISK;

   DWORD dwRes = WNetAddConnection3(
      m_hWnd,&nr,
      NULL,
      NULL,
      CONNECT_UPDATE_PROFILE);

   CString sErr;
   sErr.Format(L"Error = %d",dwRes);
   AfxMessageBox(sErr);
}

WNetConnectionDialog1 shows a dialog to map the remote name to a local one. WNetAddConnection3 does the same job silently. Both of them may use a pre-defined NETRESOURCE entry to determine what to do. In addition, WNetAddConnection3 allows remembering this connection as persistent; in other words, it’ll be remembered after a warm reboot.

There are three functions your application may use to cancel the existing connection to network resources:

  • WNetDisconnectDialog
  • WNetDisconnectDialog1
  • WNetCancelConnection2

Each function above results in dropping the connection. WNetDisconnectDialog pops up a dialog with all existing connections. The user then can the select desired resource to get disconnected. WNetDisconnectDialog1 and WNetCancelConnection2 do their job silently. The return code indicates the result of the requested operation. Besides, the last two functions may fail depending on existing open files or jobs that use this connection. The user is informed about all errors that occurred. A simple example of this is shown below:

void CWNetSampleDlg::OnButtonDisc()
{
   DWORD dwRes = WNetDisconnectDialog(m_hWnd,0);
   CString sErr;
   sErr.Format(L"Error = %d",dwRes);
   AfxMessageBox(sErr);
}

void CWNetSampleDlg::OnButtonDisc1()
{
   DISCDLGSTRUCT d;
   d.cbStructure = sizeof(DISCDLGSTRUCT);
   memset(&d,0,sizeof(d));
   d.hwndOwner   = m_hWnd;
   d.lpLocalName = L"MyShare";
   DWORD dwRes   = WNetDisconnectDialog1(&d);
   CString sErr;
   sErr.Format(L"Error = %d",dwRes);
   AfxMessageBox(sErr);
}

void CWNetSampleDlg::OnButtonCancel2()
{
   DWORD dwRes = WNetCancelConnection2(L"MyShare",
                                       CONNECT_UPDATE_PROFILE,TRUE);
   CString sErr;
   sErr.Format(L"Error = %d",dwRes);
   AfxMessageBox(sErr);
}

Reading and Writing Data

And finally, the simplest thing: How can your application conduct I/O operations via WNet? The answer is quite primitive: by using standard file APIs. Once you have detected the UNC name of the network resource and have successfully established a connection to it, you can consider it a ‘file’ name. Thus, a common flow may look as follows:

  • Establish a connection to the network resource by calling WNetAddConnection3
  • Proceed with all desired read/write/copy/delete operations
  • Close the connection to the network resource

As a result, the application can handle all required I/O operations on remote hosts. The most obvious usage is printing on network printers. In this case, you can either open the selected resource and then use WriteFile to send data to it or simply call CopyFile. The following sample demonstrates it:

void CWNetSampleDlg::OnButtonPrint()
{
   UpdateData();
   HANDLE hFile = CreateFile(m_sRemoteName,GENERIC_WRITE,0,0,
                             OPEN_EXISTING,0,0);
   if ( hFile != INVALID_HANDLE_VALUE )
   {
      CFile f;
      if ( f.Open(L"Sample.txt",CFile::modeRead) )
      {
         CByteArray baFile;
         baFile.SetSize(f.GetLength() + 1);
         f.Read(baFile.GetData(),f.GetLength());
         f.Close();

         DWORD wb;
         WriteFile(hFile,baFile.GetData(),baFile.GetSize()
                   - 1,&wb,0);

         AfxMessageBox(L"Sent data to " + m_sRemoteName);
      }
      else
      {
         AfxMessageBox(L"Failed to open local file");
      }
      CloseHandle(hFile);
   }
   else
   {
      AfxMessageBox(L"Failed to open " + m_sRemoteName);
   }
}

void CWNetSampleDlg::OnButtonPrintCopy()
{
   UpdateData();
   CopyFile(L"Sample.txt",m_sRemoteName,FALSE);
}

This code snippet uses WNet to print on an HP LaserJet printer available somewhere on the network. It uses “Sample.txt” as a test PCL stream to print a cover page of the PCL manual. You may easily create your own printer data by printing some document and choosing the “Print to file” option.

Conclusion

As you have seen, WNet under Windows CE is really a simple but useful set of APIs. If your applications work in a networking environment and you want to use network resources, WNet will do its best.

Download

Download the accompanying code’s zip file here.

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers’ lives in the mobile jungles a little bit simpler.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories