2025-11-28 00:35:46 +09:00

2006 lines
57 KiB
C

/********************************************************************++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) Microsoft Corporation. All Rights Reserved.
Module Name:
GraphChat.c
Abstract:
This C file includes sample code for a chat application built
with the Peer-to-Peer Graphing API.
--********************************************************************/
#pragma warning(disable:4201) // nameless struct/union
#pragma warning(disable:4306) // conversion to object of greater size
#include "GraphChat.h"
#include "pnrp.h"
// Global Variables:
//
HINSTANCE g_hInst = NULL; // The application instance
HWND g_hwndMain = NULL; // The main window
HWND g_hwndText = NULL; // The text to send for the user
HWND g_hwndMsg = NULL; // The message area
HWND g_hwndMembers = NULL; // The list view of active members
HWND g_hwndChatLabel = NULL; // The static text of message area
HWND g_hwndMemberLabel = NULL; // The static text of active members
HWND g_hwndSend = NULL; // The "Send" button
HWND g_hwndStatus = NULL; // The status bar window
BOOL g_fSynchronized = FALSE; // True if this graph is synchonized
HGRAPH g_hGraph = NULL; // The graph object handle
HPEEREVENT g_hPeerEvent = NULL; // The one PeerEvent handle
HANDLE g_hEvent = NULL; // Handle signaled by Graphing when we have an event
HANDLE g_hWait = NULL; // Handle from RegisterWaitForSingleObject
WCHAR g_wzGraphId[MAX_PEERNAME]; // Unique identifier for the graph
WCHAR g_wzPeerId[MAX_PEERNAME]; // Unique identifier for this peer
PWSTR g_pwzIdentity = NULL; // Identity string
BOOL g_fPnrpRegistered = FALSE; // True if we have registered in PNRP
ULONGLONG g_ullConnection = 0; // Stores DirectConnectionID
WCHAR g_wzWsprId[MAX_PEERNAME]; // Unique identifier for Whisper peer
PWSTR g_pwzWsprMsg = NULL; // Whisper Msg to send to the peer
BOOL g_fGlobalScope = TRUE; // True if the graph scope is global
// The unique identifier for chat messages
GUID CHAT_MESSAGE_RECORD_TYPE =
{0x4d5b2f11, 0x6522, 0x433b, {0x84, 0xef, 0xa2, 0x98, 0xe6, 0x7, 0x57, 0xb0}};
// The unique identifier for Whisper (Private chat) messages
GUID WHISPER_MESSAGE_TYPE =
{0x4d5b2f11, 0x6522, 0x433b, {0x84, 0xef, 0xa2, 0x98, 0xe6, 0x7, 0xbb, 0xbb}};
// Size of controls
const int c_dxBorder = 5;
const int c_dyBorder = 5;
const int c_dxMembers = 140;
const int c_dyStatic = 20;
const int c_dyText = 78;
const int c_dxButton = 70;
const int c_dyButton = 40;
const int c_dxWindow = 640;
const int c_dyWindow = 350;
// Local functions
HRESULT InitSystem(int nCmdShow);
BOOL ProcessSpecialKeys(MSG * pMsg);
void CmdDisconnect();
//-----------------------------------------------------------------------------
// Function: WinMain
//
// Purpose: This is the main entry point for the application.
//
// Returns: 0
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, __in LPSTR lpCmdLine, int nCmdShow)
{
BOOL bAcclKeySet = TRUE;
g_hInst = hInstance;
//Unreferenced parameter
hPrevInstance;
lpCmdLine;
SystemParametersInfo(SPI_SETKEYBOARDCUES, 0, &bAcclKeySet, 0);
if (SUCCEEDED(InitSystem(nCmdShow)))
{
MSG msg;
HACCEL hAccel = LoadAccelerators(g_hInst, (LPCTSTR)IDC_ACCEL);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
HWND hwndTranslate;
if ((msg.hwnd == g_hwndMain) || IsChild (g_hwndMain, msg.hwnd))
{
hwndTranslate = g_hwndMain;
}
else
{
hwndTranslate = msg.hwnd;
}
if (!TranslateAccelerator(hwndTranslate, hAccel, &msg))
{
if (!ProcessSpecialKeys(&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
else
{
MessageBox(NULL, L"One or more GraphChat Components could not be initialized, make sure Peer-To-Peer is installed and enabled", L"Graph Chat Error", MB_OK | MB_ICONWARNING);
}
// Cleanup
CleanupGraph();
DeleteIdentity();
WSACleanup();
PeerGraphShutdown();
return 0;
}
//-----------------------------------------------------------------------------
// Function: InitUI
//
// Purpose: Initialize the User Interface for the main window.
//
// Returns: HRESULT
//
HRESULT InitUI(void)
{
HRESULT hr = S_OK;
WNDCLASSEX wcex;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
InitCommonControls();
// The title bar text
LoadString(g_hInst, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
// the main window class name
LoadString(g_hInst, IDS_APP_CLASS, szWindowClass, MAX_LOADSTRING);
// Register the window class
ZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)MainProc;
wcex.hInstance = g_hInst;
wcex.hIcon = LoadIcon(g_hInst, (LPCTSTR)IDI_APP);
wcex.hIconSm = wcex.hIcon;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wcex.lpszMenuName = (LPCTSTR)IDC_MENU;
wcex.lpszClassName = szWindowClass;
RegisterClassEx(&wcex);
// Create the main window
CreateWindowEx(0,szWindowClass, szTitle,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, c_dxWindow, c_dyWindow,
NULL, NULL, g_hInst, NULL);
// g_hwndMain is set by WM_CREATE
if (g_hwndMain == NULL)
hr = E_FAIL;
return hr;
}
//-----------------------------------------------------------------------------
// Function: InitSystem
//
// Purpose: Initialize the main system (Peer-to-Peer, windows, controls, etc.)
//
// Returns: HRESULT
//
HRESULT InitSystem(int nCmdShow)
{
HRESULT hr = S_OK;
WORD wVersionRequested = MAKEWORD(2,2);
WSADATA wsaData;
// Initialize WinSock (required to use PNRP)
DWORD nRetVal = WSAStartup(wVersionRequested,&wsaData);
if (nRetVal != 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());;
}
// Initialize Peer-to-Peer
if (SUCCEEDED(hr))
{
PEER_VERSION_DATA peerVersion;
hr = PeerGraphStartup(PEER_GRAPH_VERSION, &peerVersion);
}
// Create an identity for PNRP to use
if (SUCCEEDED(hr))
{
hr = CreateIdentity();
}
if (SUCCEEDED(hr))
{
hr = InitUI();
}
if (SUCCEEDED(hr))
{
ShowWindow(g_hwndMain, nCmdShow);
UpdateWindow(g_hwndMain);
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// M A I N W I N D O W
//-----------------------------------------------------------------------------
// Function: ProcessSendButton
//
// Purpose: Get the text and send it as a chat message.
//
// Returns: nothing
//
void ProcessSendButton(void)
{
WCHAR wzMessage[MAX_CHAT_MESSAGE];
if (NULL == g_hGraph)
return;
if (0 == GetWindowText(g_hwndText, wzMessage, celems(wzMessage)))
return;
if (FAILED(AddChatRecord(wzMessage)))
return;
// Clear the text box and prepare for the next line
SetWindowText(g_hwndText, L"");
SetFocus(g_hwndText);
}
//-----------------------------------------------------------------------------
// Function: ProcessSpecialKeys
//
// Purpose: Conrol Tab key order and enter key.
//
// Returns: Returns TRUE if we don't want default msg processing to occur.
//
BOOL ProcessSpecialKeys(__in MSG * pMsg)
{
switch (pMsg->message)
{
case WM_CHAR:
switch (pMsg->wParam)
{
case VK_TAB:
{
BOOL fShift = 0 > GetKeyState(VK_SHIFT);
HWND hwnd = GetFocus();
if (hwnd == g_hwndText)
{
hwnd = fShift ? g_hwndMsg : g_hwndSend;
}
else if (hwnd == g_hwndSend)
{
hwnd = fShift ? g_hwndText : g_hwndMembers;
}
else if (hwnd == g_hwndMembers)
{
hwnd = fShift ? g_hwndSend : g_hwndMsg;
}
else // if (hwnd == g_hwndMsg)
{
hwnd = fShift ? g_hwndMembers : g_hwndText;
}
SetFocus(hwnd);
return TRUE;
}
case VK_RETURN:
ProcessSendButton();
return TRUE;
default:
// Any typing (except ctrl+C) moves the focus to the text window
if (((pMsg->hwnd == g_hwndMsg) && (pMsg->wParam != 0x03))
|| (pMsg->hwnd == g_hwndMain))
{
SetFocus(g_hwndText);
SendMessage(g_hwndText, pMsg->message, pMsg->wParam, pMsg->lParam);
return TRUE;
}
break;
}
break;
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: ProcessResizing
//
// Purpose: Ensures the dialog size stays within limits.
//
// Returns: Nothing
//
void ProcessResizing(__in LPRECT pRect)
{
const int dxMin = 300; // Minimum width of the main window
const int dyMin = 200; // Minimum height of the main window
if ((pRect->right - pRect->left) < dxMin)
{
pRect->right = pRect->left + dxMin;
}
if ((pRect->bottom - pRect->top) < dyMin)
{
pRect->bottom = pRect->top + dyMin;
}
}
//-----------------------------------------------------------------------------
// Function: ResizeMainWindow
//
// Purpose: Resizes the window to the specified width and height.
//
// Returns: Nothing
//
void ResizeMainWindow(int dxMain, int dyMain)
{
int dyStatus; // Height of the status bar
RECT rect; // temporary rectangle
HDWP hDWP = BeginDeferWindowPos(7); // 7 windows will be repositioned
GetWindowRect(g_hwndStatus, &rect);
dyStatus = rect.bottom - rect.top;
hDWP = DeferWindowPos(hDWP, g_hwndStatus, NULL,
0, dyMain - dyStatus, dxMain, dyStatus,
SWP_NOZORDER | SWP_NOOWNERZORDER);
dyMain -= (dyStatus + c_dyBorder);
hDWP = DeferWindowPos(hDWP, g_hwndChatLabel, NULL,
c_dxBorder, c_dyBorder,
dxMain - (c_dxMembers + 3*c_dxBorder), c_dyStatic,
SWP_NOZORDER | SWP_NOOWNERZORDER);
hDWP = DeferWindowPos(hDWP, g_hwndMsg, NULL,
c_dxBorder, c_dyBorder + c_dyStatic,
dxMain - (c_dxMembers + 3*c_dxBorder), dyMain - (c_dyText + 2*c_dyBorder),
SWP_NOZORDER | SWP_NOOWNERZORDER);
hDWP = DeferWindowPos(hDWP, g_hwndText, NULL,
c_dxBorder, dyMain - c_dyText + c_dyStatic,
dxMain - (c_dxMembers + 3*c_dxBorder), c_dyText - c_dyStatic,
SWP_NOZORDER | SWP_NOOWNERZORDER);
hDWP = DeferWindowPos(hDWP, g_hwndMemberLabel, NULL,
(dxMain - (c_dxMembers + 2*c_dxBorder)) + c_dxBorder, c_dyBorder,
c_dxMembers, c_dyStatic,
SWP_NOZORDER | SWP_NOOWNERZORDER);
hDWP = DeferWindowPos(hDWP, g_hwndMembers, NULL,
(dxMain - (c_dxMembers + 2*c_dxBorder)) + c_dxBorder, c_dyBorder + c_dyStatic,
c_dxMembers, dyMain - (c_dyText + 2*c_dyBorder),
SWP_NOZORDER | SWP_NOOWNERZORDER);
hDWP = DeferWindowPos(hDWP, g_hwndSend, NULL,
(dxMain - (c_dxMembers + c_dxBorder)) + (c_dxMembers - c_dxButton)/2,
(dyMain - c_dyText) + (c_dyText + c_dyStatic - c_dyButton)/2,
0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE);
EndDeferWindowPos(hDWP);
}
//-----------------------------------------------------------------------------
// Function: AddControls
//
// Purpose: Add the controls to the main window
//
// Returns: nothing
//
void AddControls(void)
{
HFONT hFont;
LOGFONT lf;
int rgStatusBarWidths[1+SB_PART_MESSAGE] = {
80, // SB_PART_LISTENING
180, // SB_PART_SYNCHRONIZED
260, // SB_PART_CONNECTED
510, // SB_PART_ADDRESS
-1}; // SB_PART_MESSAGE
// Create a font for the "Send" button
ZeroMemory(&lf, sizeof(lf));
hFont = CreateFontIndirect(&lf);
// Create the "Send" push button
g_hwndSend = CreateWindowEx(
0, WC_BUTTON, L"Send",
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP |
BS_PUSHBUTTON,
0, 0, c_dxButton, c_dyButton,
g_hwndMain, (HMENU) IDC_SEND, NULL, NULL);
SetWindowFont(g_hwndSend, hFont, FALSE);
// Create the edit control for the user to enter text
g_hwndText = CreateWindowEx(
0, WC_EDIT, L"",
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | WS_VSCROLL |
ES_MULTILINE | ES_AUTOVSCROLL,
c_dxBorder, 0, 0, c_dyText,
g_hwndMain, (HMENU) IDC_TEXTBOX, NULL, NULL);
SendMessage(g_hwndText, EM_SETLIMITTEXT, (MAX_CHAT_MESSAGE - 1), 0);
SetWindowFont(g_hwndText, hFont, FALSE);
// Create the static text "Chat Members"
g_hwndMemberLabel = CreateWindowEx(
0, WC_STATIC, L"Chat Members",
WS_CHILD | WS_VISIBLE | ES_READONLY | SS_CENTER,
0, c_dyBorder, c_dxMembers, c_dyStatic,
g_hwndMain, (HMENU) IDC_STATIC_MEMBERS, NULL, NULL);
SetWindowFont(g_hwndMemberLabel, hFont, FALSE);
// Create the listbox of active members
g_hwndMembers = CreateWindowEx(
0, WC_LISTBOX, L"",
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | WS_VSCROLL |
LBS_SORT | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT,
0, c_dyBorder, c_dxMembers, 0,
g_hwndMain, (HMENU) IDC_MEMBERS, NULL, NULL);
SetWindowFont(g_hwndMembers, (HFONT) GetStockObject(DEFAULT_GUI_FONT), FALSE);
// Create the static text for current Chat Status
g_hwndChatLabel = CreateWindowEx(
0, WC_STATIC, L"Offline",
WS_CHILD | WS_VISIBLE | ES_READONLY | SS_CENTER,
c_dxBorder, c_dyBorder, 0, c_dyStatic,
g_hwndMain, (HMENU) IDC_STATIC_MEMBERS, NULL, NULL);
SetWindowFont(g_hwndChatLabel, hFont, FALSE);
// Create the message area
g_hwndMsg = CreateWindowEx(
0, WC_EDIT, L"",
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | WS_VSCROLL |
ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY,
c_dxBorder, c_dyBorder, 0, 0,
g_hwndMain, (HMENU) IDC_MESSAGES, NULL, NULL);
SetWindowFont(g_hwndMsg, hFont, FALSE);
SendMessage(g_hwndMsg, EM_SETLIMITTEXT, 0, 0);
// Create the status bar
g_hwndStatus = CreateWindowEx(0,STATUSCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
g_hwndMain, (HMENU) IDC_STATUS, NULL, NULL);
SendMessage(
g_hwndStatus,
(UINT)SB_SETPARTS,
celems(rgStatusBarWidths),
(LPARAM) &rgStatusBarWidths[0]
);
}
//-----------------------------------------------------------------------------
// Function: MainProc
//
// Purpose: Processes messages for the main window.
//
// Returns: LRESULT (depends on message)
//
LRESULT CALLBACK MainProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
switch (message)
{
case WM_CREATE:
g_hwndMain = hWnd;
AddControls();
SetFocus(g_hwndText);
break;
case WM_SIZING:
ProcessResizing((LPRECT) lParam);
return TRUE; // the size may have been changed
case WM_SIZE:
ResizeMainWindow(LOWORD(lParam), HIWORD(lParam));
break;
case WM_CTLCOLORSTATIC:
// draw the read-only control with a normal window background
if (lParam == (LPARAM) g_hwndMsg)
{
return (LRESULT) GetSysColorBrush(COLOR_WINDOW);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_CLEARTEXT:
SetWindowText(g_hwndMsg, L"");
break;
case IDM_EXIT:
PostMessage(g_hwndMain, WM_CLOSE, 0, 0);
break;
case IDM_ABOUT:
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), g_hwndMain, AboutProc);
break;
case IDM_NEWGRAPH:
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_NEWGRAPH), g_hwndMain, NewGraphProc);
break;
case IDM_OPENGRAPH:
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_OPENGRAPH), g_hwndMain, OpenGraphProc);
break;
case IDC_MEMBERS:
if(HIWORD(wParam) == LBN_DBLCLK) //double click
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_WHISPER), g_hwndMain, WhisperProc);
break;
case IDM_DISCONNECT:
CmdDisconnect();
break;
case IDC_SEND:
ProcessSendButton();
break;
default:
break;
}
break;
default:
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
//-----------------------------------------------------------------------------
// Function: EnableDisconnectMenu
//
// Purpose: Enable (or disable) the "Disconnect" menu.
//
// Returns: nothing
//
void EnableDisconnectMenu()
{
BOOL fEnable = (g_hGraph != NULL);
EnableMenuItem(GetMenu(g_hwndMain), IDM_DISCONNECT,
fEnable ? MF_ENABLED : (MF_DISABLED | MF_GRAYED));
}
//-----------------------------------------------------------------------------
// Function: CmdDisconnect
//
// Purpose: Disconnect from the current graph.
//
// Returns: nothing
//
void CmdDisconnect()
{
CleanupGraph();
SetStatusPart(SB_PART_ADDRESS, L"");
ProcessStatusChangeEvent(0);
SetStatus(L"Disconnected");
}
//-----------------------------------------------------------------------------
// Function: AboutProc
//
// Purpose: DialogProc for the About box
//
// Returns: Returns TRUE if we don't want default msg processing to occur.
//
LRESULT CALLBACK AboutProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
//Unreferenced parameter
lParam;
switch (message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, 0);
}
break;
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: NewGraphProc
//
// Purpose: DialogProc to create a new graph
//
// Returns: Returns TRUE if we don't want default msg processing to occur.
//
LRESULT CALLBACK NewGraphProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT CreatorIdLen;
LRESULT GraphNameLen;
//Unreferenced parameter
lParam;
switch (message)
{
case WM_INITDIALOG:
// Use global cloud by default (check global scope radio button)
SendDlgItemMessage(hDlg, IDC_RADIO_GLOBAL_SCOPE, BM_SETCHECK, BST_CHECKED, 0);
EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
if (SUCCEEDED(HandleNewGraph(hDlg)))
{
SetStatus(L"graph created");
}
EndDialog(hDlg, IDOK);
break;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
case IDC_EDT_GRAPHNAME:
case IDC_EDT_CREATORID:
if (HIWORD(wParam) == EN_CHANGE)
{
GraphNameLen = SendDlgItemMessage(hDlg, IDC_EDT_GRAPHNAME, WM_GETTEXTLENGTH, 0, 0);
CreatorIdLen = SendDlgItemMessage(hDlg, IDC_EDT_CREATORID, WM_GETTEXTLENGTH, 0, 0);
EnableWindow(GetDlgItem(hDlg, IDOK), GraphNameLen > 0 && CreatorIdLen > 0);
}
break;
default:
break;
}
break; /* WM_COMMAND */
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: OpenGraphProc
//
// Purpose: DialogProc to open an existing graph
//
// Returns: Returns TRUE if we don't want default msg processing to occur.
//
LRESULT CALLBACK OpenGraphProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT GraphIdLen;
LRESULT PeerIdLen;
//Unreferenced parameter
lParam;
switch (message)
{
case WM_INITDIALOG:
// Use global scope by default
SendDlgItemMessage(hDlg, IDC_RADIO_GLOBAL_SCOPE, BM_SETCHECK, BST_CHECKED, 0);
EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
if (SUCCEEDED(HandleOpenGraph(hDlg)))
{
SetStatus(L"graph opened");
}
EndDialog(hDlg, IDOK);
break;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
case IDC_EDT_GRAPHID:
case IDC_EDT_PEERID:
if (HIWORD(wParam) == EN_CHANGE)
{
GraphIdLen = SendDlgItemMessage(hDlg, IDC_EDT_GRAPHID, WM_GETTEXTLENGTH, 0, 0);
PeerIdLen = SendDlgItemMessage(hDlg, IDC_EDT_PEERID, WM_GETTEXTLENGTH, 0, 0);
EnableWindow(GetDlgItem(hDlg, IDOK), GraphIdLen > 0 && PeerIdLen > 0);
}
break;
default:
break;
}
break; /* WM_COMMAND */
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: WhisperProc
//
// Purpose: DialogProc to do private messages in a graph
//
// Returns: Returns TRUE if we don't want default msg processing to occur.
//
LRESULT CALLBACK WhisperProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT WhisperLen;
HRESULT hr = S_OK;
//Unreferenced parameter
lParam;
switch (message)
{
case WM_INITDIALOG:
EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
SetFocus(GetDlgItem(hDlg, IDC_EDT_WHISPER));
SendMessage(GetDlgItem(hDlg, IDC_EDT_WHISPER), EM_SETLIMITTEXT, MAX_CHAT_MESSAGE, 0);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
hr = HandleDirectConnection(hDlg);
if (SUCCEEDED(hr))
{
SetStatus(L"Direct msg sent");
}
else if (PEER_E_CONNECT_SELF == hr)
{
DisplayHrError(L"Cannot Whisper to Self", hr);
}
else
{
DisplayHrError(L"Direct Communication Failed", hr);
}
EndDialog(hDlg, IDOK);
break;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
case IDC_EDT_WHISPER:
if (HIWORD(wParam) == EN_CHANGE)
{
WhisperLen = SendDlgItemMessage(hDlg, IDC_EDT_WHISPER, WM_GETTEXTLENGTH, 0, 0);
EnableWindow(GetDlgItem(hDlg, IDOK), WhisperLen > 0);
}
break;
default:
break;
}
break; /* WM_COMMAND */
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: HandleNewGraph
//
// Purpose: Extracts the information from the dialog and calls
// CreateGraph to do the actual work.
//
// Returns: HRESULT
//
HRESULT HandleNewGraph(__in HWND hDlg)
{
HRESULT hr = S_OK;
PEER_ADDRESS addr;
WCHAR wzCreatorId[MAX_PEERNAME];
WCHAR wzGraphName[MAX_PEERNAME];
g_fGlobalScope = (SendDlgItemMessage(hDlg, IDC_RADIO_GLOBAL_SCOPE, BM_GETCHECK, 0, 0) == BST_CHECKED);
SendDlgItemMessage(hDlg, IDC_EDT_GRAPHNAME, WM_GETTEXT, celems(wzGraphName), (LPARAM) wzGraphName);
// Check for preexisting graph with this name
hr = DiscoverAddress(wzGraphName, &addr);
if (hr == HRESULT_FROM_WIN32(WSA_E_NO_MORE))
{
// means no other graph with this name was found.
// Hence we can go ahead and register one.
SendDlgItemMessage(hDlg, IDC_EDT_CREATORID, WM_GETTEXT, celems(wzCreatorId), (LPARAM) wzCreatorId);
hr = CreateGraph(wzCreatorId, wzGraphName);
}
else if (SUCCEEDED(hr))
{
DisplayHrError(L"GraphName already in use, please choose another name", hr);
}
else
{
DisplayHrError(L"Unable to Create Graph", hr);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: HandleOpenGraph
//
// Purpose: Extracts the information from the dialog and calls
// OpenGraph to do the actual work.
//
// Returns: HRESULT
//
HRESULT HandleOpenGraph(__in HWND hDlg)
{
HRESULT hr = S_OK;
WCHAR wzGraphId[MAX_PEERNAME];
WCHAR wzPeerId[MAX_PEERNAME];
SendDlgItemMessage(hDlg, IDC_EDT_GRAPHID, WM_GETTEXT, celems(wzGraphId), (LPARAM) wzGraphId);
SendDlgItemMessage(hDlg, IDC_EDT_PEERID, WM_GETTEXT, celems(wzPeerId), (LPARAM) wzPeerId);
g_fGlobalScope = (SendDlgItemMessage(hDlg, IDC_RADIO_GLOBAL_SCOPE, BM_GETCHECK, 0, 0) == BST_CHECKED);
hr = OpenGraph(wzPeerId, wzGraphId);
if(FAILED(hr))
{
DisplayHrError(L"Unable to Open Graph", hr);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: HandleDirectConnection
//
// Purpose: Extracts the information from the dialog and calls
// OpenDirectConnection to do the actual work.
//
// Returns: HRESULT
//
HRESULT HandleDirectConnection(__in HWND hDlg)
{
LRESULT WhisperLen = 0;
int iItem = ListBox_GetCurSel(g_hwndMembers);
if (iItem < 0)
return E_FAIL;
ListBox_GetText(g_hwndMembers, iItem, g_wzWsprId);
WhisperLen = SendDlgItemMessage(hDlg, IDC_EDT_WHISPER, WM_GETTEXTLENGTH, 0, 0);
g_pwzWsprMsg = (WCHAR*)malloc( sizeof(WCHAR) * (WhisperLen+1) );
if (g_pwzWsprMsg == NULL)
{
return E_OUTOFMEMORY;
}
SendDlgItemMessage(hDlg, IDC_EDT_WHISPER, WM_GETTEXT, WhisperLen+1, (LPARAM) g_pwzWsprMsg);
return OpenDirectConnection();
}
//------------------------------------------------------------------
// Function: OpenDirectConnection
//
// Purpose: Opens Direct Connection to the peer
//
// Returns: HRESULT
//
HRESULT OpenDirectConnection()
{
HRESULT hr = S_OK;
HPEERENUM hPeerEnum;
PEER_NODE_INFO ** ppNodeInfo = NULL;
ULONG cItem = 0;
WCHAR wzMsg[MAX_PATH];
// Get a handle to an enumeration of peers corresponding to g_wzWsprID
hr = PeerGraphEnumNodes(g_hGraph, g_wzWsprId, &hPeerEnum);
if (SUCCEEDED(hr))
{
// Search for only one match
cItem = 1;
// Extract the first data item from the enumeration
hr = PeerGraphGetNextItem(hPeerEnum, &cItem, &ppNodeInfo);
PeerGraphEndEnumeration(hPeerEnum);
}
if (SUCCEEDED(hr))
{
if ( (cItem == 0) || (NULL == ppNodeInfo) )
{
hr = E_FAIL;
}
else if ((*ppNodeInfo)->cAddresses == 0)
{
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
// The direct connection uses the first address from the destination
// node's PEER_NODE_INFO struct (pAddresses[0])
hr = PeerGraphOpenDirectConnection(g_hGraph, g_wzWsprId,
&(*ppNodeInfo)->pAddresses[0], &g_ullConnection);
if (SUCCEEDED(hr))
{
StringCbPrintf(wzMsg, sizeof(wzMsg), L"ID: %08X", g_ullConnection);
SetStatusPart(SB_PART_MESSAGE, wzMsg);
}
}
if (NULL != ppNodeInfo)
{
PeerGraphFreeData(ppNodeInfo);
ppNodeInfo = NULL;
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: CreateIdentity
//
// Purpose: Create the identity that this node will use when interacting with
// PNRP
//
// Returns: HRESULT
//
HRESULT CreateIdentity()
{
return PeerIdentityCreate(
L"GraphChat",
L"GraphChatMember",
(HCRYPTPROV)NULL,
&g_pwzIdentity);
}
//-----------------------------------------------------------------------------
// Function: DeleteIdentity
//
// Purpose: Delete the identity created by CreateIdentity
//
// Returns: HRESULT
//
HRESULT DeleteIdentity()
{
HRESULT hr = S_OK;
if (g_pwzIdentity)
{
hr = PeerIdentityDelete(g_pwzIdentity);
g_pwzIdentity = NULL;
}
return hr;
}
//------------------------------------------------------------------
// Function: CreateGraph
//
// Parameters:
// ------------
// PCWSTR wzCreatorID - Name for graph creator
// PCWSTR wzGraphID - Name of graph to be created
//
// Purpose: Creates a new graph
//
// Returns: HRESULT
//
HRESULT CreateGraph(PCWSTR wzCreatorId, PCWSTR wzGraphId)
{
HRESULT hr = S_OK;
PEER_GRAPH_PROPERTIES props = {0};
WCHAR wzMessage[MAX_CHAT_MESSAGE] = {0};
DWORD dwLocalScopeID; // Identifies specific local connection (if app)
// Release any previous Graph resources
CleanupGraph();
// Delete old database if present.
PeerGraphDelete(wzGraphId, wzCreatorId, L"SampleChatGraph");
props.dwSize = sizeof(props);
props.pwzFriendlyName = L"Sample Graph";
props.pwzGraphId = (PWSTR) wzGraphId;
props.pwzCreatorId = (PWSTR) wzCreatorId;
props.cPresenceMax = (ULONG) -1; // publish presence for everyone.
// Set scope to match user input - global or local
if (g_fGlobalScope)
{
props.dwScope = (DWORD) PEER_GRAPH_SCOPE_GLOBAL;
}
else
{
props.dwScope = (DWORD) PEER_GRAPH_SCOPE_LINKLOCAL;
}
// save graphid for Registration, once we are synchronized and prepared to listen.
StringCbCopy(g_wzGraphId, sizeof(g_wzGraphId), wzGraphId);
// save peerid for use when registering w/ PNRP
StringCbCopy(g_wzPeerId, sizeof(g_wzPeerId), wzCreatorId);
hr = PeerGraphCreate(&props, L"SampleChatGraph", NULL, &g_hGraph);
if (SUCCEEDED(hr))
{
StringCchPrintf(wzMessage, celems(wzMessage), L"New graph created. GraphName = %s", wzGraphId);
DisplaySysMsg(wzMessage);
hr = PrepareToChat(wzGraphId, FALSE);
}
else
{
DisplayHrError(L"Failed to create a new Graph", hr);
}
// A brand new graph is always in sync, and ready to listen
if (SUCCEEDED(hr))
{
g_fSynchronized = TRUE;
if (g_fGlobalScope)
{
hr = PeerGraphListen(g_hGraph, PEER_GRAPH_SCOPE_GLOBAL, 0, GRAPHING_PORT);
}
else
{
// Just Retrieve the Scope ID, don't need the cloud name for now.
hr = GetLocalCloudInfo(0, NULL, &dwLocalScopeID);
if (SUCCEEDED(hr))
{
hr = PeerGraphListen(g_hGraph, PEER_GRAPH_SCOPE_LINKLOCAL, dwLocalScopeID, GRAPHING_PORT);
}
else
{
DisplayHrError(L"Error attempting to retrieve local scope ID", hr);
}
}
if (SUCCEEDED(hr))
{
UpdateParticipant(wzCreatorId, TRUE);
hr = RegisterAddress(g_wzGraphId);
if (FAILED(hr))
DisplayHrError(L"Error attempting to Register Graph ID.",hr);
}
else
{
DisplayHrError(L"Failed call to PeerGraphListen", hr);
}
}
return hr;
}
//------------------------------------------------------------------
// Function: OpenGraph
//
// Purpose: Opens an existing graph
//
// Returns: HRESULT
//
HRESULT OpenGraph(PCWSTR wzPeerId, PCWSTR wzGraphId)
{
HRESULT hr = S_OK;
HRESULT hrOpen = S_OK;
// Release any previous Graph resources
CleanupGraph();
// save graphid for Registration, once we are synchronized and prepared to listen.
StringCbCopy(g_wzGraphId, sizeof(g_wzGraphId), wzGraphId);
// save peerid for use when registering w/ PNRP
StringCbCopy(g_wzPeerId, sizeof(g_wzPeerId), wzPeerId);
hrOpen = PeerGraphOpen(wzGraphId, wzPeerId, L"SampleChatGraph", NULL, 0, NULL, &g_hGraph);
if (SUCCEEDED(hrOpen))
{
hr = PrepareToChat(wzGraphId, TRUE);
if (SUCCEEDED(hr))
{
UpdateParticipant(wzPeerId, TRUE);
}
if (hrOpen == PEER_S_GRAPH_DATA_CREATED)
{
if (hr == HRESULT_FROM_WIN32(WSA_E_NO_MORE))
{
MessageBox(GetWindow(g_hwndMain, GW_ENABLEDPOPUP), L"This is the first time the specified peer has tried to open the specified graph, on this machine.\r\nNobody could be found to connect to. Please try again later.", L"Graph Chat Info", MB_OK | MB_ICONWARNING);
}
else if(FAILED(hr))
{
DisplayHrError(L"Node could not connect to graph", hr);
}
}
}
else
{
hr = hrOpen;
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: PrepareToChat
//
// Purpose: Does the initial hookup of the graph that is required after a
// successful create or open
//
// Returns: HRESULT
//
HRESULT PrepareToChat(PCWSTR wzGraphId, __in BOOL fConnect)
{
PEER_ADDRESS addr;
HRESULT hr = RegisterForEvents();
if (SUCCEEDED(hr) && fConnect)
{
hr = DiscoverAddress(wzGraphId, &addr);
if (SUCCEEDED(hr))
{
hr = PeerGraphConnect(g_hGraph, NULL, &addr, NULL);
}
if (FAILED(hr))
{
DisplayHrError(L"Couldn't find/connect to Graph", hr);
}
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: RegisterForEvents
//
// Purpose: Registers the EventCallback function so it will be called for only
// those events that are specified.
//
// Returns: HRESULT
//
HRESULT RegisterForEvents()
{
HRESULT hr = S_OK;
PEER_GRAPH_EVENT_REGISTRATION regs[] = {
{ PEER_GRAPH_EVENT_RECORD_CHANGED, &CHAT_MESSAGE_RECORD_TYPE },
{ PEER_GRAPH_EVENT_NODE_CHANGED, 0 },
{ PEER_GRAPH_EVENT_STATUS_CHANGED, 0 },
{ PEER_GRAPH_EVENT_DIRECT_CONNECTION, 0 },
{ PEER_GRAPH_EVENT_INCOMING_DATA, &WHISPER_MESSAGE_TYPE },
{ PEER_GRAPH_EVENT_CONNECTION_REQUIRED, 0 },
};
g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (g_hEvent == NULL)
{
DisplayHrError(L"CreateEvent call failed", E_OUTOFMEMORY);
hr = HRESULT_FROM_WIN32(GetLastError());
}
else
{
hr = PeerGraphRegisterEvent(
g_hGraph,
g_hEvent,
celems(regs),
regs,
&g_hPeerEvent
);
if (FAILED(hr))
{
DisplayHrError(L"PeerGraphRegisterEvents failed", hr);
CloseHandle(g_hEvent);
g_hEvent = NULL;
}
}
if (SUCCEEDED(hr))
{
if (!RegisterWaitForSingleObject(&g_hWait, g_hEvent, EventCallback, NULL, INFINITE, WT_EXECUTEDEFAULT))
{
hr = HRESULT_FROM_WIN32(GetLastError());
DisplayHrError(L"Could not setup event callback", hr);
if (g_hPeerEvent != NULL)
{
PeerGraphUnregisterEvent(g_hPeerEvent);
g_hPeerEvent = NULL;
}
CloseHandle(g_hEvent);
g_hEvent = NULL;
}
}
return hr;
}
//-----------------------------------------------------------------------
// Function: EventCallback
//
// Purpose: Handle events raised by the graphing infrastructure
//
// Returns: nothing
//
VOID CALLBACK EventCallback(PVOID lpParam, BOOLEAN reason)
{
HRESULT hr= S_OK;
PEER_GRAPH_EVENT_DATA *pEventData = NULL;
//Unreferenced parameter
lpParam;
reason;
for (;;)
{
hr = PeerGraphGetEventData(g_hPeerEvent, &pEventData);
if (FAILED(hr) || (hr == PEER_S_NO_EVENT_DATA) || (NULL == pEventData) )
{
break;
}
switch (pEventData->eventType)
{
case PEER_GRAPH_EVENT_RECORD_CHANGED:
ProcessRecordChangeEvent(pEventData);
break;
case PEER_GRAPH_EVENT_STATUS_CHANGED:
ProcessStatusChangeEvent(pEventData->dwStatus);
break;
case PEER_GRAPH_EVENT_NODE_CHANGED:
ProcessNodeChangeEvent(pEventData);
break;
case PEER_GRAPH_EVENT_DIRECT_CONNECTION:
ProcessDirectConnectionEvent(pEventData);
break;
case PEER_GRAPH_EVENT_INCOMING_DATA:
ProcessIncomingDataEvent(pEventData);
break;
case PEER_GRAPH_EVENT_CONNECTION_REQUIRED:
ProcessConnectionRequiredEvent(pEventData);
break;
default:
break;
}
PeerGraphFreeData(pEventData);
}
}
//-------------------------------------------------------------------------
// Function: ProcessNodeChangeEvent
//
// Purpose: Processes the PEER_GRAPH_EVENT_NODE_CHANGED event.
//
// Returns: void
//
void ProcessNodeChangeEvent(__in PEER_GRAPH_EVENT_DATA *pEventData)
{
HRESULT hr = S_OK;
if (pEventData->nodeChangeData.changeType == PEER_NODE_CHANGE_CONNECTED)
{
UpdateParticipant(pEventData->nodeChangeData.pwzPeerId, TRUE);
}
else if (pEventData->nodeChangeData.changeType ==
PEER_NODE_CHANGE_DISCONNECTED
)
{
UpdateParticipant(pEventData->nodeChangeData.pwzPeerId, FALSE);
}
else if (
(pEventData->nodeChangeData.changeType == PEER_NODE_CHANGE_UPDATED) &&
(pEventData->nodeChangeData.ullNodeId == 0)
)
{
// PEER_GRAPH_EVENT_NODE_CHANGED event is generated when a
// node's attribute changes or when its IPv6 address changes.
// If we have received this event in the local node, we
// assume in this sample that it is because of an address
// change as we don't do anything to change the node attributes.
// When we get an address change re-register with PNRP.
SetStatus(L"Address Change Occured.");
hr = RegisterAddress(g_wzGraphId);
if (FAILED(hr))
{
DisplayHrError(L"Error attempting to Register Graph ID.",hr);
}
}
}
//-------------------------------------------------------------------------
// Function: ProcessStatusChangeEvent
//
// Purpose: Processes the PEER_GRAPH_EVENT_STATUS_CHANGED event.
//
// Returns: void
//
void ProcessStatusChangeEvent(__in DWORD dwStatus)
{
PWSTR pwzMsg = L"";
PEER_GRAPH_PROPERTIES * pGraphProperties = NULL;
WCHAR wzChatTitle[MAX_PEERNAME + 30] = L"Offline";
HRESULT hr = S_OK;
DWORD dwLocalScopeID;
hr = PeerGraphGetProperties(g_hGraph, &pGraphProperties);
pwzMsg = L"";
if (SUCCEEDED(hr))
{
if (pGraphProperties != NULL)
{
// Check if listening
if (dwStatus & PEER_GRAPH_STATUS_LISTENING)
{
pwzMsg = L"Listening";
StringCbPrintf(wzChatTitle, sizeof(wzChatTitle), L"Waiting to chat in %s",
pGraphProperties->pwzGraphId);
}
}
}
SetStatusPart(SB_PART_LISTENING, pwzMsg);
pwzMsg = L"";
if (SUCCEEDED(hr))
{
if (pGraphProperties != NULL)
{
// Check if has connections
if (dwStatus & PEER_GRAPH_STATUS_HAS_CONNECTIONS)
{
pwzMsg = L"Connected";
StringCbPrintf(wzChatTitle, sizeof(wzChatTitle), L"Chatting in %s",
pGraphProperties->pwzGraphId);
}
}
}
PeerGraphFreeData(pGraphProperties);
SetStatusPart(SB_PART_CONNECTED, pwzMsg);
// Check if database is synchronized
pwzMsg = L"";
if (dwStatus & PEER_GRAPH_STATUS_SYNCHRONIZED)
{
if (!g_fSynchronized)
{
g_fSynchronized = TRUE;
// If we aren't already listening, do so now.
if (!(dwStatus & PEER_GRAPH_STATUS_LISTENING))
{
if (g_fGlobalScope)
{
hr = PeerGraphListen(g_hGraph, PEER_GRAPH_SCOPE_GLOBAL, 0, GRAPHING_PORT);
}
else
{
hr = GetLocalCloudInfo(0, NULL, &dwLocalScopeID);
if (SUCCEEDED(hr))
{
hr = PeerGraphListen(g_hGraph, PEER_GRAPH_SCOPE_LINKLOCAL, dwLocalScopeID, GRAPHING_PORT);
}
else
{
DisplayHrError(L"Error retrieving local scope ID", hr);
}
}
if (SUCCEEDED(hr))
{
hr = RegisterAddress(g_wzGraphId);
if (FAILED(hr))
DisplayHrError(L"Error attempting to Register Graph ID.",hr);
}
else
{
DisplayHrError(L"Error attempting to listen", hr);
}
}
}
pwzMsg = L"Synchronized";
}
SetStatusPart(SB_PART_SYNCHRONIZED, pwzMsg);
SetWindowText(g_hwndChatLabel, wzChatTitle);
EnableDisconnectMenu();
}
//-------------------------------------------------------------------------
// Function: ProcessRecordChangeEvent
//
// Purpose: Processes the PEER_GRAPH_EVENT_RECORD_CHANGED event.
//
// Returns: nothing
//
void ProcessRecordChangeEvent(__in PEER_GRAPH_EVENT_DATA *pEventData)
{
if (pEventData->recordChangeData.changeType == PEER_RECORD_ADDED)
{
if (IsEqualGUID(&pEventData->recordChangeData.recordType, &CHAT_MESSAGE_RECORD_TYPE))
{
PEER_RECORD *pRecord = NULL;
HRESULT hr = PeerGraphGetRecord(g_hGraph, &pEventData->recordChangeData.recordId, &pRecord);
if (SUCCEEDED(hr))
{
DisplayMessage(pRecord->pwzCreatorId, (PCWSTR) pRecord->data.pbData);
PeerGraphFreeData(pRecord);
}
else
{
DisplayHrError(L"Error adding chat record", hr);
}
}
}
}
//-------------------------------------------------------------------------
// Function: ProcessDirectConnectionEvent
//
// Purpose: Processes the PEER_GRAPH_EVENT_DIRECT_CONNECTION event.
//
// Returns: void
//
void ProcessDirectConnectionEvent(__in PEER_GRAPH_EVENT_DATA *pEventData)
{
switch (pEventData->connectionChangeData.status)
{
case PEER_CONNECTED:
SetStatusPart(SB_PART_MESSAGE, L"DC Connected");
if (g_ullConnection == pEventData->connectionChangeData.ullConnectionId)
SendDirectData();
break;
case PEER_CONNECTION_FAILED:
g_ullConnection = 0;
SetStatusPart(SB_PART_MESSAGE, L"DC Failed");
break;
case PEER_DISCONNECTED:
g_ullConnection = 0;
SetStatusPart(SB_PART_MESSAGE, L"DC Disconnected");
break;
default:
break;
}
}
//-------------------------------------------------------------------------
// Function: ProcessIncomingDataEvent
//
// Purpose: Processes the PEER_GRAPH_EVENT_INCOMING_DATA event.
//
// Returns: void
//
void ProcessIncomingDataEvent(__in PEER_GRAPH_EVENT_DATA *pEventData)
{
WCHAR wzSentFrom[MAX_PEERNAME + 20] = {0};
HPEERENUM hPeerEnum = NULL;
HRESULT hr;
ULONG cItem;
g_ullConnection = (ULONGLONG)pEventData->incomingData.ullConnectionId;
hr = PeerGraphEnumConnections(g_hGraph, PEER_CONNECTION_DIRECT, &hPeerEnum);
if (SUCCEEDED(hr))
{
PEER_CONNECTION_INFO ** ppConnections = NULL;
hr = PeerGraphGetItemCount(hPeerEnum, &cItem);
if (SUCCEEDED(hr))
{
hr = PeerGraphGetNextItem(hPeerEnum, &cItem, (PVOID**) &ppConnections);
}
if (SUCCEEDED(hr))
{
ULONG i;
for (i=0; i < cItem; i++)
{
PEER_CONNECTION_INFO * pConnectionInfo = ppConnections[i];
if (g_ullConnection == pConnectionInfo->ullConnectionId)
{
StringCbPrintf(wzSentFrom, sizeof(wzSentFrom), L"Whisper from %s",
pConnectionInfo->pwzPeerId);
DisplayMessage(wzSentFrom, (PCWSTR) pEventData->incomingData.data.pbData);
break;
}
}
PeerGraphFreeData(ppConnections);
}
PeerGraphEndEnumeration(hPeerEnum);
}
PeerGraphCloseDirectConnection(g_hGraph, g_ullConnection);
g_ullConnection = 0;
}
//----------------------------------------------------------------------------
// Function: ProcessConnectionRequiredEvent
//
// Purpose : Process the PEER_GRAPH_EVENT_CONNECTION_REQUIRED event.
//
// Returns: void
//
void ProcessConnectionRequiredEvent(__in PEER_GRAPH_EVENT_DATA *pEventData)
{
HRESULT hr = S_OK;
PEER_ADDRESS addr;
//Unreferenced parameter
pEventData;
hr = DiscoverAddress(g_wzGraphId, &addr);
// We could find an address then connect to it.
if (SUCCEEDED(hr))
{
hr = PeerGraphConnect(g_hGraph, NULL, &addr, NULL);
}
}
//------------------------------------------------------------------
// Function: SendDirectData
//
// Purpose: Called by ProcessDirectConnectionEvent
// to send message to peer.
//
// Returns: nothing
//
void SendDirectData()
{
ULONG cb = 0;
HRESULT hr = S_OK;
WCHAR wzSentTo[MAX_PEERNAME + 20];
cb = (ULONG)(sizeof(WCHAR) * (wcslen(g_pwzWsprMsg)+1));
hr = PeerGraphSendData(g_hGraph, g_ullConnection, &WHISPER_MESSAGE_TYPE, cb, g_pwzWsprMsg);
if (SUCCEEDED(hr))
{
StringCbPrintf(wzSentTo, sizeof(wzSentTo), L"Whisper to %s", g_wzWsprId);
DisplayMessage(wzSentTo, g_pwzWsprMsg);
}
else
{
DisplayHrError(L"unable to send data",hr);
}
if (g_pwzWsprMsg)
{
free(g_pwzWsprMsg);
g_pwzWsprMsg = NULL;
}
}
//-------------------------------------------------------------------------
// Function: DisplayMessage
//
// Purpose: Utility function to display a message in the chat area
//
// Returns: nothing
//
void DisplayMessage(PCWSTR pwzName, PCWSTR pwzMsg)
{
WCHAR wzMessage[MAX_CHAT_MESSAGE + MAX_PEERNAME + 6] = {0};
StringCchPrintf(wzMessage, celems(wzMessage), L"[%s]: %s\r\n", pwzName, pwzMsg);
SendDlgItemMessage(g_hwndMain, IDC_MESSAGES, EM_SETSEL, 0x7fffffff, (LPARAM) -1);
SendDlgItemMessage(g_hwndMain, IDC_MESSAGES, EM_REPLACESEL, 0, (LPARAM) wzMessage);
SendDlgItemMessage(g_hwndMain, IDC_MESSAGES, EM_SCROLLCARET, 0, 0);
};
//-------------------------------------------------------------------------
// Function: ClearParticipantList
//
// Purpose: Small wrapper used to reset contents of participant list
//
// Returns: nothing
//
void ClearParticipantList(void)
{
ListBox_ResetContent(GetDlgItem(g_hwndMain, IDC_MEMBERS));
}
//-------------------------------------------------------------------------
// Function: UpdateParticipant
//
// Purpose: Adds or removes a person from the participant combo box
//
// Returns: nothing
//
void UpdateParticipant(PCWSTR wzPerson, __in BOOL fAdd)
{
LRESULT index;
WCHAR wzMsg[MAX_PEERNAME + 30] = {0};
if (fAdd)
{
SendDlgItemMessage(g_hwndMain, IDC_MEMBERS, LB_ADDSTRING, 0, (LPARAM) wzPerson);
StringCbPrintf(wzMsg, sizeof(wzMsg), L"<%s> has joined the Chat", wzPerson);
DisplaySysMsg(wzMsg);
}
else
{
index = SendDlgItemMessage(g_hwndMain, IDC_MEMBERS, LB_FINDSTRINGEXACT, (WPARAM) -1, (LPARAM) wzPerson);
if (index != LB_ERR)
{
SendDlgItemMessage(g_hwndMain, IDC_MEMBERS, LB_DELETESTRING, index, 0);
StringCbPrintf(wzMsg, sizeof(wzMsg), L"<%s> has left the Chat", wzPerson);
DisplaySysMsg(wzMsg);
}
}
};
//-----------------------------------------------------------------------------
// Function: AddChatRecord
//
// Purpose: This adds a new chat message record to the graph.
//
// Returns: HRESULT
//
HRESULT AddChatRecord(PCWSTR pwzMessage)
{
HRESULT hr = S_OK;
PEER_RECORD record = {0};
GUID idRecord;
FILETIME ftExpire;
// Calculate the expiration time
GetSystemTimeAsFileTime(&ftExpire);
hr = PeerGraphUniversalTimeToPeerTime(g_hGraph, &ftExpire, &ftExpire);
if (SUCCEEDED(hr))
{
*((ULONGLONG UNALIGNED*)&ftExpire) += 600000000; // Now + 60 seconds
// Set up the record
record.dwSize = sizeof(record);
record.data.cbData = ( ((ULONG) wcslen(pwzMessage)) +1) * sizeof(WCHAR);
record.data.pbData = (PBYTE) pwzMessage;
record.ftExpiration = ftExpire;
// Set the record type GUID
record.type = CHAT_MESSAGE_RECORD_TYPE;
// Add the record to the database
hr = PeerGraphAddRecord(g_hGraph, &record, &idRecord);
if (FAILED(hr))
{
DisplayHrError(L"Failed to add a chat record to the graph.", hr);
}
}
else
{
DisplayHrError(L"Failed to add a chat record to the graph.", hr);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: CleanupGraph
//
// Purpose: Cleans up all the global variables associated w/ this Graph.
//
// Returns: nothing
//
void CleanupGraph()
{
if (g_fPnrpRegistered)
{
UnregisterAddress(g_wzGraphId);
}
if (g_hPeerEvent != NULL)
{
PeerGraphUnregisterEvent(g_hPeerEvent);
g_hPeerEvent = NULL;
}
if (g_hWait != NULL)
{
UnregisterWaitEx(g_hWait, INVALID_HANDLE_VALUE);
g_hWait = NULL;
}
if (g_hEvent != NULL)
{
CloseHandle(g_hEvent);
g_hEvent = NULL;
}
if (g_hGraph != NULL)
{
PeerGraphClose(g_hGraph);
g_hGraph = NULL;
}
if (g_pwzWsprMsg)
{
free(g_pwzWsprMsg);
g_pwzWsprMsg = NULL;
}
ClearParticipantList();
g_fSynchronized = FALSE;
}
//-------------------------------------------------------------------------
// Function: FormatAddress
//
// Purpose: Simple wrapper function converting an IP address into a string
//
// Returns: nothing
//
void FormatAddress(__in SOCKADDR_IN6 * pSockAddr, __out PWSTR pwzBuff, int cchMax)
{
DWORD cb = sizeof(struct sockaddr_in6);
int err = WSAAddressToString((LPSOCKADDR)pSockAddr, cb, NULL, pwzBuff, (LPDWORD) &cchMax);
if (0 != err)
{
err = WSAGetLastError();
StringCchPrintf(pwzBuff, cchMax, L"err=%08X", err);
}
}
//-------------------------------------------------------------------------
// Function: DiscoverAddress
//
// Purpose: Given a graphID, uses PnrpResolve to find address(es)
//
// Returns: HRESULT
//
HRESULT DiscoverAddress(PCWSTR wzGraphId, __out PEER_ADDRESS *pPeerAddress)
{
HRESULT hr = S_OK;
HCURSOR hCursor;
PWSTR pwzUnsecuredName = NULL;
WCHAR wzCloudName[MAX_CLOUD_NAME];
DWORD dwLocalScopeID;
if(wzGraphId == NULL)
{
hr = E_INVALIDARG;
return hr;
}
// generate the unsecured PeerName [0.id]
hr = PeerCreatePeerName(NULL,wzGraphId,&pwzUnsecuredName);
if(FAILED(hr))
{
DisplayHrError(L"PeerName could not be created", hr);
return hr;
}
ZeroMemory(pPeerAddress, sizeof(PEER_ADDRESS));
pPeerAddress->dwSize = sizeof(PEER_ADDRESS);
hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
if (g_fGlobalScope)
{
hr = PnrpResolve(pwzUnsecuredName, NULL, &pPeerAddress->sin6);
}
else
{
hr = GetLocalCloudInfo(celems(wzCloudName), wzCloudName, &dwLocalScopeID);
if (SUCCEEDED(hr))
{
hr = PnrpResolve(pwzUnsecuredName, wzCloudName, &pPeerAddress->sin6);
}
else
{
DisplayHrError(L"Local Cloud Info could not be retrieved.", hr);
}
}
SetCursor(hCursor);
PeerFreeData(pwzUnsecuredName);
return hr;
}
//-------------------------------------------------------------------------
// Function: RegisterAddress
//
// Purpose: Registers the current node's address with PNRP
//
// Returns: HRESULT
//
HRESULT RegisterAddress(PCWSTR wzGraphId)
{
HRESULT hr = S_OK;
PEER_NODE_INFO *pNodeInfo = NULL;
PWSTR pwzUnsecuredName = NULL;
WCHAR wzCloudName[MAX_CLOUD_NAME];
DWORD dwLocalScopeID;
WCHAR wzTemp[512];
SOCKADDR_IN6 * pAddress;
// generate the unsecured PeerName [0.id]
if(wzGraphId != NULL)
{
hr = PeerCreatePeerName(NULL,wzGraphId,&pwzUnsecuredName);
}
if(FAILED(hr))
{
DisplayHrError(L"PeerName could not be created", hr);
return hr;
}
hr = PeerGraphGetNodeInfo(g_hGraph, 0, &pNodeInfo);
if (SUCCEEDED(hr) && pNodeInfo->cAddresses >= 1)
{
if (g_fGlobalScope)
{
hr = PnrpRegister(g_pwzIdentity, pwzUnsecuredName, NULL, pNodeInfo);
}
else
{
hr = GetLocalCloudInfo(celems(wzCloudName), wzCloudName, &dwLocalScopeID);
if (SUCCEEDED(hr))
{
hr = PnrpRegister(g_pwzIdentity, pwzUnsecuredName, wzCloudName, pNodeInfo);
}
}
if (SUCCEEDED(hr))
{
pAddress = &pNodeInfo->pAddresses->sin6;
FormatAddress(pAddress, wzTemp, celems(wzTemp));
SetStatusPart(SB_PART_ADDRESS, wzTemp);
g_fPnrpRegistered = TRUE;
}
else
{
DisplayHrError(L"Failed to register node in PNRP", hr);
}
PeerGraphFreeData(pNodeInfo);
}
else
{
DisplayHrError(L"Error getting local node info", hr);
}
PeerFreeData(pwzUnsecuredName);
return hr;
}
//-------------------------------------------------------------------------
// Function: UnregisterAddress
//
// Purpose: Unregisters the current node's address with PNRP
//
// Returns: HRESULT
//
HRESULT UnregisterAddress(PCWSTR wzGraphId)
{
HRESULT hr = S_OK;
PWSTR pwzUnsecuredName = NULL;
WCHAR wzCloudName[MAX_CLOUD_NAME];
DWORD dwLocalScopeID;
// generate the unsecured PeerName [0.id]
if(wzGraphId != NULL)
PeerCreatePeerName(NULL,wzGraphId,&pwzUnsecuredName);
if (g_fGlobalScope)
{
hr = PnrpUnregister(g_pwzIdentity, pwzUnsecuredName, NULL);
}
else
{
hr = GetLocalCloudInfo(celems(wzCloudName), wzCloudName, &dwLocalScopeID);
if (SUCCEEDED(hr))
{
hr = PnrpUnregister(g_pwzIdentity, pwzUnsecuredName, wzCloudName);
}
}
if (FAILED(hr))
{
DisplayHrError(L"Failed to unregister node in PNRP", hr);
}
else
g_fPnrpRegistered = FALSE;
PeerFreeData(pwzUnsecuredName);
return hr;
}
//-----------------------------------------------------------------------------
// Function: MsgErrHr
//
// Purpose: Display a message for a function with an error code.
//
// Returns: nothing
//
void MsgErrHr(PCWSTR pwzMsg, __in HRESULT hr, PCSTR pszFunction)
{
WCHAR wzError[512];
StringCchPrintf(wzError, celems(wzError), L"%s\r\n\r\nFunction: %S\r\nHRESULT=0x%08X",
pwzMsg, pszFunction, hr);
MessageBox(GetWindow(g_hwndMain, GW_ENABLEDPOPUP), wzError, L"Graph Chat Error", MB_OK | MB_ICONWARNING);
}
//-----------------------------------------------------------------------------
// Function: SetStatusPart
//
// Purpose: Set the text of the status bar.
//
// Returns: nothing
//
void SetStatusPart(int sbPart, PCWSTR pwzStatus)
{
SendMessage(g_hwndStatus, SB_SETTEXT, sbPart, (LPARAM) pwzStatus);
}