This week we are going to start exploring the peer to peer (p2p) in Windows Vista.  Before we get into any code an overview of the p2p APIs available in Windows Vista  is in order.

PNRP (Peer Name Resolution Protocol) is like server-less DNS.  It allows clients to register secured and unsecured peer names that can be resolved over the internet.  Currently large data centers are required to host services that PNRP has the potential to replace.  PNRP gives people the ability to discover a friend and see their presence online and then play a game or work on a project together.

PNM (People Near Me) allows for dynamic discovery and invitation of computers running Windows Vista on the same subnet or Ad-hoc wireless.  This means you can play games with people in the airport, swap photos with your friends or collaborate with colleagues. For example Windows Meeting Space uses PNM.

Peer Naming API allows email providers to tie PNRP names to email addresses.  Then any program that uses the WinSock APIs can resolve registered email addressesThis means you can literally ping a friend or put their email address where you would have to put an IP address in a game.

Graphing is a mesh networking API.  It allows groups of people to share information and collaborateA game could create a mesh of online players so that it could do server-less match making. Or an evangelism team could collaborate on a document without having to upload the latest copy to a server.

Grouping is the security component of Graphing.  It allows for a group to be administrated, so that membership and permissions can be restricted.  Think of Grouping and Graphing as a database in the cloud.

Peer Channel is the only managed API in the P2P API suite.  It allows for mesh replication instead of mesh collaboration.

Application Invite (AppInvite) allows a user to invite another user to use a collaboration application.  Any application can register with the invite API and when a user sends an invite to use that application the application will be launched by Windows Vista.  Windows Meeting Space is an example of an application that uses AppInvite.

Contacts API allows the creation and administration of p2p contacts.

Serverless Presence:  Gets contact presence information.

Now that we know what peer to peer offers let’s look at an example of how to use one of these APIs. A few weeks ago I did a video to explain why developers and end users should care about peer to peer.  As a part of that video there was an explanation of how PNRP works. Luckily calling the PNRP API doesn’t require understanding how the resolution works.

We will start our p2p adventure off with the People Near Me API.  Most of the p2p API is Win32 and doesn’t lend its self well to being called through interop from managed code.  So I will be writing all the examples in C++\CLI 2005 which will allow me to expose the APIs through a nice managed wrapper, which will make calling the API possible from .NET.

Our PNM sample application will just request enumerate the people on our local subnets and print their nick name, IP address and endpoint name to the console.  After creating a new C++ CLR console application in Visual Studio we need to add the Windows SDK path to our additional includes and our additional dependencies fields in our project. 

On my machine header files are here "C:\Program Files\Microsoft SDKs\Windows\v6.0\lib" and libs are here "C:\Program Files\Microsoft SDKs\Windows\v6.0\include". NOTE: these paths will be deferent on builds other then Windows Vista RC1 and are subject to where you installed the Windows SDK.

We also need to add three libraries: p2p.lib, p2pgraph.lib, Ws2_32.lib

The first two libraries contain all the peer to peer definitions and the last is WinSock2 and is needed when we convert an IP address to a string later in the program. The last thing we need to do is a #include for “p2p.h”

Here is the whole program listing. 

// PNMSample.cpp : main project file.

 

#include "stdafx.h"

#include "p2p.h"

 

#define MAX_PEERNAME_LENGTH 256

 

using namespace System;

using namespace System::Runtime::InteropServices;

 

int main(array<System::String ^> ^args)

{

      HPEERENUM hPeerEnum;

      HRESULT hr;

     

      // Startup Collaboration.

      hr = PeerCollabStartup(PEER_COLLAB_VERSION);

     

      // Find out who is in our local subnet.

      hr = PeerCollabEnumPeopleNearMe(&hPeerEnum);

 

      ULONG Count = 0;

 

      // Get the number of people in our subnet returned to hPeerEnum; an enumeration.

      hr = PeerGetItemCount(hPeerEnum, &Count);

 

      // Create a place to hold the array of items.

      PVOID *items = NULL;

     

      // Fetch the items from the enumeration.

      hr = PeerGetNextItem(hPeerEnum, &Count, &items);     

 

      // Loop through the people near us.

      for(int i = 0; i < Count; i++)

      {

            // Cast each item to the PEER_PEOPLE_NEAR_ME structure.

            PEER_PEOPLE_NEAR_ME *item = (PEER_PEOPLE_NEAR_ME*)(items[i]);          

 

            // Get the people near me nick name for this item.

            PWSTR szNickName = item->pwzNickName;

 

            // Marshal our native char array to a managed string and write it to the console.

            System::String ^nickName = gcnew System::String(szNickName);

            Console::WriteLine(L"Nick Name:{0}", nickName);

 

            // Get the peer end point information out of our item.

            PEER_ENDPOINT endpoint = item->endpoint;

 

            System::String ^endpointName = gcnew System::String(endpoint.pwzEndpointName);

            Console::WriteLine(L"End Point Name:{0}", endpointName);

     

            // Get the peer address structure from the end point.

            PEER_ADDRESS pa =  endpoint.address;

 

            // Get the IPv6 address.

            if(endpoint.address.sin6.sin6_family == AF_INET6)

            {

                  WCHAR  wzAddr[MAX_PEERNAME_LENGTH];

 

                  DWORD dwLen = (sizeof(wzAddr) / sizeof(wzAddr[0]));

 

                  // Convert a SocAddrIn6 address to a string. This method is defined in Ws2_32.lib

                  hr = WSAAddressToString(

                        (LPSOCKADDR) &pa.sin6,

                        sizeof(SOCKADDR_IN6), NULL, wzAddr, &dwLen);

 

                  // Marshal our native char array to a managed string and write it to the console.

                  System::String ^address = gcnew System::String(wzAddr);

                  Console::WriteLine(L"IPv6 Address:{0}", address);

            }                      

           

 

            // Put a space between entries.

            Console::WriteLine();        

           

      }    

 

      // Cleanup memory.

      PeerFreeData(items);   

      PeerEndEnumeration(hPeerEnum);

 

      hr = PeerCollabShutdown();

    return 0;

}

 

When I run the program I get this as my output:

Nick Name:Erik Porter

End Point Name:ERIKPOVISTA

IPv6 Address:[fe80::19f:84f3:bc7a:bb15%10]:52157

 

Nick Name:Karsten Januszewski

End Point Name:KARSTENJ-U5U7P

IPv6 Address:[fe80::d400:972a:1e29:a8a2%10]:51268

 

Press any key to continue . . .

 

When you run the program you might not get any output as you might not have anyone on your subnet who has a pnrp name register.  If there are other people on your subnet running Windows Vista then they can run “Windows Meeting Space” and tell it to start pnm when they start and you will get a listing for their machine since “Windows Meeting Space” uses PNM and publishes PNRP names. 

Now we will take a look in detail and the important parts of this application. We start by calling PeerCollabStartup(PEER_COLLAB_VERSION) which is responsible for starting up the p2p stack and must be called before any other p2p functions.

 Next we call PeerCollabEnumPeopleNearMe(&hPeerEnum). This function is the heart of our console application as it returns the enumeration of PNRP names on our subnet via the hPeerEnum handle.  At this point in time we just need to walk through the enumeration and print out the information we want.

The PeerGetItemCount(hPeerEnum, &Count) function takes the handle to the peer enumeration returns us the count of items in that enumeration via the Count variable.  To get the items out of the enumeration we call the PeerGetNextItem(hPeerEnum, &Count, &items) which takes the same handle and a number of items we want and returns an array of structures of whatever peer to peer type the enum contains.  This method is interesting as we can request more items then are in the enum and it will return us only the remaining amount.  If we had a large number of items in the enum and only wanted to process a few items at a time we could pass in the n number that we wanted and it would return us the next n number of items in the enumeration until it was empty.

The  PeerCollabEnumPeopleNearMe  function documentation states that  all the items in the enumeration it returns will be of type PEER_PEOPLE_NEAR_ME. Now that we have the pointer to our array of peer nodes we can loop through the items and cast each item to the PEER_PEOPLE_NEAR_ME structure. 

Now we start writing the desired information to the console with the help of some marshaling to System.String types.   The only other complex part of this application is getting the address information. First we check if address type is IPv6, this isn’t really necessary here, but will be when we look at other areas of p2p.

if(endpoint.address.sin6.sin6_family == AF_INET6)

{

WCHAR  wzAddr[MAX_PEERNAME_LENGTH];

 

      DWORD dwLen = (sizeof(wzAddr) / sizeof(wzAddr[0]));

 

      // Convert a SocAddrIn6 address to a string. This method is defined in Ws2_32.lib

      hr = WSAAddressToString(

                        (LPSOCKADDR) &pa.sin6,

                        sizeof(SOCKADDR_IN6), NULL, wzAddr, &dwLen);

 

                  // Marshal our native char array to a managed string and write it to the console.

      System::String ^address = gcnew System::String(wzAddr);

      Console::WriteLine(L"IPv6 Address:{0}", address);

}          

Then we declare a place to store the IPv6 address string and call the WSAAddressToString function which is a WinSock function that will convert our SocAddr6 address to a PWSTR for us.  Now we just have to marshal that and write it to the console.

Lastly we release the objects we have created as described by the p2p API documentation and call  PeerCollabShutdown() to shut down the p2p infrastructure for our application.  That is all there is to this application.  Next time we will look at PNRP and how we can register and resolve addresses over the internet and then use WCF to communicate between the nodes.