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

3082 lines
86 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:
GroupChat.c
Abstract:
This C file includes sample code for a chat application built
with the Peer-to-Peer Grouping API.
Note:
This peer to peer application requires global IPv6 connectivity.
--********************************************************************/
#pragma warning(disable:4201) // nameless struct/union
#pragma warning(disable:4306) // conversion to object of greater size
#include "GroupChat.h"
// Global Variables:
//
HINSTANCE g_hInst = NULL; // application instance
HWND g_hwndMain = NULL; // main window
HWND g_hwndText = NULL; // text to send for the user
HWND g_hwndChatLabel = NULL; // static text of message area
HWND g_hwndMsg = NULL; // message area
HWND g_hwndMembers = NULL; // list view of active members
HWND g_hwndMembersLabel = NULL; // static text of active members
HWND g_hwndSend = NULL; // "Send" button
HWND g_hwndStatus = NULL; // status bar window
HGROUP g_hGroup = NULL; // group object
HPEEREVENT g_hPeerEvent = NULL; // PeerEvent handle
HANDLE g_hEvent = NULL; // event handle
HANDLE g_hWait = NULL; // wait handle
ULONGLONG g_ullConnectionId = 0; // 64-bit integer that identifies the direct connection
DWORD g_dwAuth = PEER_GROUP_GMC_AUTHENTICATION; // Authentication scheme (password or GMC)
BOOL g_fGlobalScope = TRUE; // global (TRUE) or local (FALSE) group scope
WCHAR g_wzDCName[MAX_USERNAME];
WCHAR g_wzName[MAX_USERNAME];
// The unique identifier for chat messages
GUID RECORD_TYPE_CHAT_MESSAGE =
{0x4d5b2f11, 0x6522, 0x433b, {0x84, 0xef, 0xa2, 0x98, 0xe6, 0x7, 0x57, 0xb0}};
// The unique identifier for Whisper (Private chat) messages
GUID DATA_TYPE_WHISPER_MESSAGE =
{ 0x4d5b2f11, 0x6522, 0x433b, { 0x84, 0xef, 0xa2, 0x98, 0xe6, 0x7, 0xbb, 0xbb } };
// File Extensions
const PCWSTR c_wzFileExtInv = L"inv"; // Group Invitation
const PCWSTR c_wzFileExtIdt = L"idt"; // Peer Identity
// 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;
// Window sizing
const int c_dxMin = 300; // Minimum width of the main window
const int c_dyMin = 200; // Minimum height of the main window
// Local functions
HRESULT InitSystem(int nCmdShow);
BOOL ProcessSpecialKeys(MSG * pMsg);
//-----------------------------------------------------------------------------
// 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)
{
//Unreferenced parameter
lpCmdLine;
hPrevInstance;
g_hInst = hInstance;
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 Group Chat components could not be initialized. Make sure Peer-to-Peer is installed and enabled.", L"Group Chat Error", MB_OK | MB_ICONWARNING);
}
// Cleanup
CleanupGroup( );
PeerGroupShutdown( );
return 0;
}
//-----------------------------------------------------------------------------
// Function: InitUI
//
// Purpose: Initialize the User Interface for the main window.
//
// Returns: S_OK if the main window was created successfully
//
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
CreateWindow(szWindowClass, szTitle,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 300,
NULL, NULL, g_hInst, NULL);
if (g_hwndMain == NULL) // g_hwndMain is set by WM_CREATE
hr = E_FAIL;
return hr;
}
//-----------------------------------------------------------------------------
// Function: InitSystem
//
// Purpose: Initialize the main system (Peer-to-Peer, windows, controls, etc.)
//
// Returns: S_OK if the system was successfully initialized
//
HRESULT InitSystem(int nCmdShow)
{
HRESULT hr = S_OK;
PEER_VERSION_DATA peerVersion;
WSADATA wsaData = {0};
// Setup Winsock
hr = WSAStartup(MAKEWORD(2,2), &wsaData);
if (FAILED(hr))
{
DisplayHrError(L"Unable to Intialize WSA.", hr);
return E_UNEXPECTED;
}
hr = PeerGroupStartup(PEER_GROUP_VERSION, &peerVersion);
if (FAILED(hr))
{
return hr;
}
hr = (InitUI( ));
if (FAILED(hr))
{
return 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_hGroup)
{
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: Handle the TABs for the window.
//
// Returns: BOOL
//
BOOL ProcessSpecialKeys(MSG * pMsg)
{
BOOL fShift;
HWND hwnd;
switch (pMsg->message)
{
case WM_CHAR:
switch (pMsg->wParam)
{
case VK_TAB:
fShift = 0 > GetKeyState(VK_SHIFT);
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; /* WM_CHAR */
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: ProcessResizing
//
// Purpose: Ensures the dialog size stays within limits.
//
// Returns: Nothing
//
void ProcessResizing(LPRECT pRect)
{
if ((pRect->right - pRect->left) < c_dxMin)
{
pRect->right = pRect->left + c_dxMin;
}
if ((pRect->bottom - pRect->top) < c_dyMin)
{
pRect->bottom = pRect->top + c_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_hwndMembersLabel, 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[2] = {
100, // SB_PART_STATUS - "Listening"/"Connected"
-1}; // SB_PART_MESSAGE - generic messages
// 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_hwndMembersLabel = 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_hwndMembersLabel, 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_DISABLENOSCROLL | LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_NOTIFY,
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 "Conversation"
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_CREATEGROUP:
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_CREATEGROUP), g_hwndMain, NewGroupProc);
break;
case IDM_OPENGROUP:
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_OPENGROUP), g_hwndMain, OpenGroupProc);
break;
case IDM_JOINGROUP:
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_JOINGROUP), g_hwndMain, JoinGroupProc);
break;
case IDM_DELETEGROUP:
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_DELETEGROUP), g_hwndMain, DeleteGroupProc);
break;
case IDM_CLOSEGROUP:
CmdCloseGroup( );
break;
case IDM_SAVEIDENTITYINFO:
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_SAVEIDENTITYINFO), g_hwndMain, SaveIdentityInfoProc);
break;
case IDM_CREATEIDENTITY:
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_NEWIDENTITY), g_hwndMain, NewIdentityProc);
break;
case IDM_DELETEIDENTITY:
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_DELETEIDENTITY), g_hwndMain, DeleteIdentityProc);
break;
case IDM_CREATEINVITATION:
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_CREATEINVITATION), g_hwndMain, CreateInvitationProc);
break;
case IDC_SEND:
ProcessSendButton( );
break;
case IDC_MEMBERS:
if (HIWORD(wParam) == LBN_DBLCLK)
{
PCWSTR pwzIdentity = GetSelectedChatMember();
if (SUCCEEDED(SetupDirectConnection(pwzIdentity)))
{
DialogBox(g_hInst, MAKEINTRESOURCE(IDD_WHISPERMESSAGE), g_hwndMain, WhisperMessageProc);
}
}
break;
default:
break;
}
break; /* WM_COMMAND */
default:
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
///////////////////////////////////////////////////////////////////////////////
// A B O U T B O X
//-----------------------------------------------------------------------------
// Function: AboutProc
//
// Purpose: DialogProc for the About box
//
// Returns: Returns TRUE if it processed the message, and FALSE if it did not.
//
LRESULT CALLBACK AboutProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
//Unreferenced parameter
lParam;
switch (message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCANCEL:
case IDOK:
EndDialog(hDlg, LOWORD(wParam));
break;
default:
break;
}
break; /* WM_COMMAND */
default:
break;
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
// N E W G R O U P
//-----------------------------------------------------------------------------
// Function: NewGroupProc
//
// Purpose: DialogProc to create a new group
//
// Returns: Returns TRUE if it processed the message, and FALSE if it did not.
//
LRESULT CALLBACK NewGroupProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static PEER_NAME_PAIR **ppNamePairs = NULL;
BOOL fPass = FALSE;
//Unreferenced parameter
lParam;
switch (message)
{
case WM_INITDIALOG:
// Use Non-password based invitations by default
SendDlgItemMessage(hDlg, IDC_RADIO_AUTH_INVITE, BM_SETCHECK, BST_CHECKED, 0);
SendDlgItemMessage(hDlg, IDC_EDT_GROUPNAME, EM_SETLIMITTEXT, MAX_GROUPNAME - 1, 0);
RefreshIdentityCombo(GetDlgItem(hDlg, IDC_CB_IDENTITY), TRUE, &ppNamePairs);
// Use global scope by default
SendDlgItemMessage(hDlg, IDC_RADIO_GLOBAL_SCOPE, BM_SETCHECK, BST_CHECKED, 0);
return TRUE;
case WM_DESTROY:
if (ppNamePairs != NULL)
{
PeerFreeData(ppNamePairs);
ppNamePairs = NULL;
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
if (SUCCEEDED(HandleNewGroup(hDlg)))
{
SetStatus(L"Group created");
EndDialog(hDlg, IDOK);
}
break;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
case IDC_BTN_NEW_IDENTITY:
if (IDOK == DialogBox(g_hInst, MAKEINTRESOURCE(IDD_NEWIDENTITY), hDlg, NewIdentityProc))
{
if (ppNamePairs != NULL)
{
PeerFreeData(ppNamePairs);
ppNamePairs = NULL;
}
RefreshIdentityCombo(GetDlgItem(hDlg, IDC_CB_IDENTITY), TRUE, &ppNamePairs);
}
break;
case IDC_RADIO_AUTH_PASSW:
case IDC_RADIO_AUTH_INVITE:
fPass = (SendDlgItemMessage(hDlg, IDC_RADIO_AUTH_PASSW, BM_GETCHECK, BST_CHECKED, 0) == BST_CHECKED);
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_PASSWORD), fPass);
EnableWindow(GetDlgItem(hDlg, IDC_EDIT_PASSWORD), fPass);
break;
default:
break;
}
break; /* WM_COMMAND */
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: HandleNewGroup
//
// Purpose: Extracts the information from the dialog and calls
// CreateGroup to do the actual work.
//
// Returns: HRESULT
//
HRESULT HandleNewGroup(HWND hDlg)
{
HRESULT hr = S_OK;
WCHAR wzFriendlyName[MAX_GROUPNAME];
WCHAR wzPassword[MAX_PASSWORD] = {0};
LRESULT iCurSel = SendDlgItemMessage(hDlg, IDC_CB_IDENTITY, CB_GETCURSEL, 0, 0);
PWSTR pwzIdentity = (PWSTR) SendDlgItemMessage(hDlg, IDC_CB_IDENTITY, CB_GETITEMDATA, (WPARAM) iCurSel, 0);
// Retrieve user's selection on authentication method
if (SendDlgItemMessage(hDlg, IDC_RADIO_AUTH_PASSW, BM_GETCHECK, 0, 0) == BST_CHECKED)
{
SendDlgItemMessage(hDlg, IDC_EDIT_PASSWORD, WM_GETTEXT, celems(wzPassword), (LPARAM) wzPassword);
g_dwAuth = PEER_GROUP_PASSWORD_AUTHENTICATION;
}
else
{
g_dwAuth = PEER_GROUP_GMC_AUTHENTICATION;
}
g_fGlobalScope = (SendDlgItemMessage(hDlg, IDC_RADIO_GLOBAL_SCOPE, BM_GETCHECK, 0, 0) == BST_CHECKED);
SendDlgItemMessage(hDlg, IDC_EDT_GROUPNAME, WM_GETTEXT, celems(wzFriendlyName), (LPARAM) wzFriendlyName);
hr = CreateGroup(wzFriendlyName, pwzIdentity, wzPassword);
return hr;
}
//-----------------------------------------------------------------------------
// Function: CreateGroup
//
// Purpose: Creates a new group with the friendly name.
//
// Parameters:
// pwzName [in] : Friendly name of group
// pwzIdentity [in] : Path to identity file of group creator
// pwzPassword [in] : Password of group (if applicable)
//
// Returns: HRESULT
//
HRESULT CreateGroup(PCWSTR pwzName, PCWSTR pwzIdentity, PWSTR pwzPassword)
{
HRESULT hr = S_OK;
PEER_GROUP_PROPERTIES props = {0};
if (SUCCEEDED(hr))
{
if ((NULL == pwzName) || (0 == *pwzName))
{
hr = E_INVALIDARG;
DisplayHrError(L"Please enter a group name.", hr);
}
}
if (SUCCEEDED(hr))
{
CleanupGroup( );
props.dwSize = sizeof(props);
props.pwzClassifier = L"SampleChatGroup";
props.pwzFriendlyName = (PWSTR) pwzName;
props.pwzCreatorPeerName = (PWSTR) pwzIdentity;
props.dwAuthenticationSchemes = g_dwAuth;
if (g_dwAuth == PEER_GROUP_PASSWORD_AUTHENTICATION)
{
props.groupPasswordRole = PEER_GROUP_ROLE_ADMIN;
props.pwzGroupPassword = (PWSTR) pwzPassword;
}
if (g_fGlobalScope)
{
props.pwzCloud = NULL;
hr = PeerGroupCreate(&props, &g_hGroup);
}
else
{
WCHAR pwzCloudName[MAX_CLOUD_NAME] = {0};
hr = GetLocalCloudName(celems(pwzCloudName), pwzCloudName);
if (SUCCEEDED(hr))
{
props.pwzCloud = pwzCloudName;
hr = PeerGroupCreate(&props, &g_hGroup);
}
}
if (FAILED(hr))
{
if (hr == PEER_E_PASSWORD_DOES_NOT_MEET_POLICY)
{
DisplayHrError(L"Password does not meet local policy.", hr);
}
else
{
DisplayHrError(L"Failed to create a new group.", hr);
}
}
}
if (SUCCEEDED(hr))
{
hr = PrepareToChat( );
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// O P E N G R O U P
//-----------------------------------------------------------------------------
// Function: OpenGroupProc
//
// Purpose: DialogProc to open an existing group
//
// Returns: Returns TRUE if it processed the message, and FALSE if it did not.
//
LRESULT CALLBACK OpenGroupProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static PEER_NAME_PAIR **ppIdentities = NULL;
static PEER_NAME_PAIR **ppGroups = NULL;
//Unreferenced parameter
lParam;
switch (message)
{
case WM_INITDIALOG:
if (SUCCEEDED(RefreshIdentityCombo(GetDlgItem(hDlg, IDC_CB_IDENTITY), TRUE, &ppIdentities)))
{
PCWSTR pwzIdentity = GetSelectedIdentity(hDlg);
RefreshGroupCombo(GetDlgItem(hDlg, IDC_CB_GROUP), pwzIdentity, &ppGroups);
}
return TRUE;
case WM_DESTROY:
if (ppGroups != NULL)
{
PeerFreeData(ppGroups);
ppGroups = NULL;
}
if (ppIdentities != NULL)
{
PeerFreeData(ppIdentities);
ppIdentities = NULL;
}
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
if (SUCCEEDED(HandleOpenGroup(hDlg)))
{
SetStatus(L"Group opened");
EndDialog(hDlg, IDOK);
}
break;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
case IDC_CB_IDENTITY:
// When identities change, refresh the group list.
if (HIWORD(wParam) == CBN_SELCHANGE)
{
PCWSTR pwzIdentity = GetSelectedIdentity(hDlg);
if (ppGroups != NULL)
{
PeerFreeData(ppGroups);
ppGroups = NULL;
}
RefreshGroupCombo(GetDlgItem(hDlg, IDC_CB_GROUP), pwzIdentity, &ppGroups);
}
break;
default:
break;
}
break; /* WM_COMMAND */
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: HandleOpenGroup
//
// Purpose: Extracts the information from the dialog and calls
// OpenGroup to do the actual work.
//
// Returns: HRESULT
//
HRESULT HandleOpenGroup(HWND hDlg)
{
PCWSTR pwzGroup = GetSelectedGroup(hDlg);
PCWSTR pwzIdentity = GetSelectedIdentity(hDlg);
HRESULT hr = OpenGroup(pwzIdentity, pwzGroup);
if (SUCCEEDED(hr))
{
GetFriendlyNameForIdentity(pwzIdentity, g_wzName, celems(g_wzName));
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: OpenGroup
//
// Purpose: Open an existing group for a particular identity.
// Displays a message if there was an error.
//
// Returns: HRESULT
//
HRESULT OpenGroup(PCWSTR pwzIdentity, PCWSTR pwzName)
{
HRESULT hr = S_OK;
if (SUCCEEDED(hr))
{
if ((NULL == pwzName) || (0 == *pwzName))
{
hr = E_INVALIDARG;
DisplayHrError(L"Please select a group.", hr);
}
}
if (SUCCEEDED(hr))
{
// Release any previous group resources
CleanupGroup( );
// The NULL parameter indicates that we'll let grouping try to
// automatically determine which cloud to use
hr = PeerGroupOpen(pwzIdentity, pwzName, NULL, &g_hGroup);
// If NULL does not work, we try the first link local cloud
if (FAILED(hr))
{
WCHAR wzCloudName[MAX_CLOUD_NAME] = {0};
hr = GetLocalCloudName(celems(wzCloudName), wzCloudName);
if (SUCCEEDED(hr))
{
hr = PeerGroupOpen(pwzIdentity, pwzName, wzCloudName, &g_hGroup);
}
}
// Otherwise, return failure
if (FAILED(hr))
{
DisplayHrError(L"Failed to open group.", hr);
}
}
if (SUCCEEDED(hr))
{
hr = PrepareToChat( );
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// N E W I D E N T I T Y
//-----------------------------------------------------------------------------
// Function: NewIdentityProc
//
// Purpose: DialogProc to create a new identity
//
// Returns: Returns TRUE if it processed the message, and FALSE if it did not.
//
LRESULT CALLBACK NewIdentityProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
//Unreferenced parameter
lParam;
switch (message)
{
case WM_INITDIALOG:
SendDlgItemMessage(hDlg, IDC_EDT_FRIENDLYNAME, EM_SETLIMITTEXT, MAX_IDENTITY - 1, 0);
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
if (SUCCEEDED(HandleCreateIdentity(hDlg)))
EndDialog(hDlg, IDOK);
break;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
}
break; /* WM_COMMAND */
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: HandleCreateIdentity
//
// Purpose: Extracts the friendly name from the dialog and
// creates a new identity.
//
// Returns: HRESULT
//
HRESULT HandleCreateIdentity(HWND hDlg)
{
HRESULT hr = S_OK;
WCHAR wzName[MAX_USERNAME];
UINT cch = GetDlgItemText(hDlg, IDC_EDT_FRIENDLYNAME, wzName, celems(wzName));
if (0 == cch)
{
hr = E_FAIL;
DisplayHrError(L"Please type a name for the identity.", hr);
}
if (SUCCEEDED(hr))
{
PCWSTR pwzClassifier = L"GroupChatMember";
PWSTR pwzIdentity = NULL;
hr = PeerIdentityCreate(pwzClassifier, wzName, 0, &pwzIdentity);
if (FAILED(hr))
{
DisplayHrError(L"Failed to create identity", hr);
}
PeerFreeData(pwzIdentity);
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// D E L E T E I D E N T I T Y
//-----------------------------------------------------------------------------
// Function: DeleteIdentityProc
//
// Purpose: DialogProc to delete an identity
//
// Returns: Returns TRUE if it processed the message, and FALSE if it did not.
//
LRESULT CALLBACK DeleteIdentityProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static PEER_NAME_PAIR **ppIdentities = NULL;
//Unreferenced parameter
lParam;
switch (message)
{
case WM_INITDIALOG:
(void)RefreshIdentityCombo(GetDlgItem(hDlg, IDC_CB_IDENTITY), FALSE, &ppIdentities);
return TRUE;
case WM_DESTROY:
if (ppIdentities != NULL)
{
PeerFreeData(ppIdentities);
ppIdentities = NULL;
}
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
if (SUCCEEDED(HandleDeleteIdentity(hDlg)))
EndDialog(hDlg, IDOK);
break;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
}
break; /* WM_COMMAND */
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: HandleDeleteIdentity
//
// Purpose: Extracts the selected identity from the dialog
// and deletes it.
//
// Returns: HRESULT
//
HRESULT HandleDeleteIdentity(HWND hDlg)
{
HRESULT hr = S_OK;
PCWSTR pwzIdentity = GetSelectedIdentity(hDlg);
hr = DeleteIdentity(pwzIdentity);
return hr;
}
//-----------------------------------------------------------------------------
// Function: DeleteIdentity
//
// Purpose: Deletes an identity
//
// Parameters:
// pwzIdentity [in] : The peer identity to delete
//
// Returns: HRESULT
//
HRESULT DeleteIdentity(PCWSTR pwzIdentity)
{
HRESULT hr = S_OK;
if ((NULL == pwzIdentity) || (0 == *pwzIdentity))
{
hr = E_INVALIDARG;
DisplayHrError(L"Please select an identity.", hr);
}
if (SUCCEEDED(hr))
{
CleanupGroup( );
hr = PeerIdentityDelete(pwzIdentity);
if (FAILED(hr))
{
DisplayHrError(L"Failed to delete identity.", hr);
}
}
if (SUCCEEDED(hr))
{
SetStatus(L"Deleted identity");
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// J O I N G R O U P
//-----------------------------------------------------------------------------
// Function: JoinGroupProc
//
// Purpose: DialogProc to join some existing group with an invitation
//
// Returns: Returns TRUE if it processed the message, and FALSE if it did not.
//
LRESULT CALLBACK JoinGroupProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static PEER_NAME_PAIR **ppIdentities = NULL;
BOOL fPass = FALSE;
//Unreferenced parameters
lParam;
switch (message)
{
case WM_INITDIALOG:
EnableWindow(GetDlgItem(hDlg, IDC_EDT_PASSWORD), FALSE);
RefreshIdentityCombo(GetDlgItem(hDlg, IDC_CB_IDENTITY), TRUE, &ppIdentities);
return TRUE;
case WM_DESTROY:
if (ppIdentities != NULL)
{
PeerFreeData(ppIdentities);
ppIdentities = NULL;
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
case IDOK:
if (SUCCEEDED(HandleJoinGroup(hDlg)))
{
SetStatus(L"Joined group");
EndDialog(hDlg, IDOK);
}
break;
case IDC_BTN_BROWSE:
BrowseHelper(hDlg, IDC_EDT_LOCATION, L"Group Invitation", c_wzFileExtInv, TRUE);
break;
case IDC_CHECK_PASSWORD:
fPass = (SendDlgItemMessage(hDlg, IDC_CHECK_PASSWORD, BM_GETCHECK, BST_CHECKED, 0) == BST_CHECKED);
EnableWindow(GetDlgItem(hDlg, IDC_EDT_PASSWORD), fPass);
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_PASSWORD), fPass);
break;
}
break;
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: HandleJoinGroup
//
// Purpose: Extracts the information from the dialog and calls
// JoinGroup to do the actual work.
//
// Returns: HRESULT
//
HRESULT HandleJoinGroup(HWND hDlg)
{
HRESULT hr = S_OK;
WCHAR wzInvitation[MAX_PATH];
WCHAR wzPassword[MAX_PASSWORD] = {0};
PCWSTR pwzIdentity = GetSelectedIdentity(hDlg);
if (SendDlgItemMessage(hDlg, IDC_CHECK_PASSWORD, BM_GETCHECK, 0, 0) == BST_CHECKED)
{
GetDlgItemText(hDlg, IDC_EDT_PASSWORD, wzPassword, celems(wzPassword));
g_dwAuth = PEER_GROUP_PASSWORD_AUTHENTICATION;
}
else
{
g_dwAuth = PEER_GROUP_GMC_AUTHENTICATION;
}
GetDlgItemText(hDlg, IDC_EDT_LOCATION, wzInvitation, celems(wzInvitation));
hr = JoinGroup(pwzIdentity, wzInvitation, wzPassword);
return hr;
}
//-----------------------------------------------------------------------------
// Function: JoinGroup
//
// Purpose: Uses the invitation to join a group with a specific identity.
// Displays a message if there was an error.
//
// Parameters:
// pwzIdentity [in] : Path to identity file (not used w/password based groups)
// pwzFileName [in] : Path to the group invitation
// pwzPassword [in] : Password of group (if applicable)
//
// Returns: HRESULT
//
HRESULT JoinGroup(PCWSTR pwzIdentity, PCWSTR pwzFileName, PWSTR pwzPassword)
{
HRESULT hr = S_OK;
WCHAR wzInvitation[MAX_INVITATION] = {0};
FILE *file = NULL;
errno_t err;
err = _wfopen_s(&file, pwzFileName, L"rb");
if (err != 0)
{
hr = E_FAIL;
DisplayHrError(L"Error opening group invitation file", hr);
return hr;
}
else
{
fread(wzInvitation, sizeof(WCHAR), MAX_INVITATION, file);
if (ferror(file))
{
hr = E_FAIL;
DisplayHrError(L"File read error occurred.", hr);
}
fclose(file);
if (g_dwAuth == PEER_GROUP_GMC_AUTHENTICATION)
{
// NULL parameter indicates that the cloud name will be selected automatically
hr = PeerGroupJoin(pwzIdentity, wzInvitation, NULL, &g_hGroup);
// In case of failure, try using a local cloud name
if (FAILED(hr))
{
WCHAR wzCloudName[MAX_CLOUD_NAME] = {0};
hr = GetLocalCloudName(celems(wzCloudName), wzCloudName);
if (FAILED(hr))
{
DisplayHrError(L"Could not find local cloud name.", hr);
}
else
{
hr = PeerGroupJoin(pwzIdentity, wzInvitation, wzCloudName, &g_hGroup);
}
}
}
else
{
WCHAR wzCloudName[MAX_CLOUD_NAME] = {0};
// NULL parameter indicates that the cloud name will be selected automatically
hr = PeerGroupPasswordJoin(pwzIdentity, wzInvitation, pwzPassword,
NULL, &g_hGroup);
// In case of failure, try using a local cloud name
if (FAILED(hr))
{
hr = GetLocalCloudName(celems(wzCloudName), wzCloudName);
if (FAILED(hr))
{
DisplayHrError(L"Could not find local cloud name.", hr);
}
else
{
hr = PeerGroupPasswordJoin(pwzIdentity, wzInvitation, pwzPassword,
wzCloudName, &g_hGroup);
}
}
}
}
if (SUCCEEDED(hr))
{
hr = PrepareToChat( );
}
else
{
DisplayHrError(L"Failed to join group.", hr);
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// D E L E T E G R O U P
//-----------------------------------------------------------------------------
// Function: DeleteGroupProc
//
// Purpose: DialogProc to delete a group
//
// Returns: Returns TRUE if it processed the message, and FALSE if it did not.
//
LRESULT CALLBACK DeleteGroupProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static PEER_NAME_PAIR **ppIdentities = NULL;
static PEER_NAME_PAIR **ppGroups = NULL;
//Unreferenced parameters
lParam;
switch (message)
{
case WM_INITDIALOG:
if (SUCCEEDED(RefreshIdentityCombo(GetDlgItem(hDlg, IDC_CB_IDENTITY), TRUE, &ppIdentities)))
{
PCWSTR pwzIdentity = GetSelectedIdentity(hDlg);
RefreshGroupCombo(GetDlgItem(hDlg, IDC_CB_GROUP), pwzIdentity, &ppGroups);
}
return TRUE;
case WM_DESTROY:
if (ppGroups != NULL)
{
PeerFreeData(ppGroups);
ppGroups = NULL;
}
if (ppIdentities != NULL)
{
PeerFreeData(ppIdentities);
ppIdentities = NULL;
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
if (SUCCEEDED(HandleDeleteGroup(hDlg)))
{
SetStatus(L"Group Deleted");
EndDialog(hDlg, IDOK);
}
break;
case IDC_CB_IDENTITY:
// When identities change, refresh the group list.
if (HIWORD(wParam) == CBN_SELCHANGE)
{
PCWSTR pwzIdentity = GetSelectedIdentity(hDlg);
if (ppGroups != NULL)
{
PeerFreeData(ppGroups);
ppGroups = NULL;
}
RefreshGroupCombo(GetDlgItem(hDlg, IDC_CB_GROUP), pwzIdentity, &ppGroups);
}
break;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
default:
break;
}
break; /* WM_COMMAND */
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: HandleDeleteGroup
//
// Purpose: Extracts the information from the dialog and calls
// DeleteGroup to do the actual work.
//
// Returns: HRESULT
//
HRESULT HandleDeleteGroup(HWND hDlg)
{
HRESULT hr = S_OK;
PCWSTR pwzGroup = GetSelectedGroup(hDlg);
PCWSTR pwzIdentity = GetSelectedIdentity(hDlg);
hr = DeleteGroup(pwzGroup, pwzIdentity);
return hr;
}
//-----------------------------------------------------------------------------
// Function: DeleteGroup
//
// Purpose: Deletes a group
//
// Parameters:
// pwzName [in] : Friendly name of group
// pwzIdentity [in] : The peer identity of the group creator
//
// Returns: HRESULT
//
HRESULT DeleteGroup(PCWSTR pwzName, PCWSTR pwzIdentity)
{
HRESULT hr = S_OK;
if (SUCCEEDED(hr))
{
CleanupGroup( );
hr = PeerGroupDelete(pwzIdentity, pwzName);
if (FAILED(hr))
{
DisplayHrError(L"Failed to delete group.", hr);
}
}
if (SUCCEEDED(hr))
{
ProcessStatusChanged(0);
SetStatus(L"Deleted group");
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// S A V E I D E N T I T Y I N F O R M A T I O N
//-----------------------------------------------------------------------------
// Function: SaveIdentityInfoProc
//
// Purpose: DialogProc to save the information for a local identity to a file.
//
// Returns: Returns TRUE if it processed the message, and FALSE if it did not.
//
LRESULT CALLBACK SaveIdentityInfoProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static PEER_NAME_PAIR **ppIdentities = NULL;
//Unreferenced parameter
lParam;
switch (message)
{
case WM_INITDIALOG:
RefreshIdentityCombo(GetDlgItem(hDlg, IDC_CB_IDENTITY), FALSE, &ppIdentities);
return TRUE;
case WM_DESTROY:
if (ppIdentities != NULL)
{
PeerFreeData(ppIdentities);
ppIdentities = NULL;
}
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
case IDOK:
if (SUCCEEDED(HandleSaveIdentityInfo(hDlg)))
{
EndDialog(hDlg, IDOK);
}
break;
case IDC_BTN_NEW_IDENTITY:
if (IDOK == DialogBox(g_hInst, MAKEINTRESOURCE(IDD_NEWIDENTITY), hDlg, NewIdentityProc))
{
if (ppIdentities != NULL)
{
PeerFreeData(ppIdentities);
ppIdentities = NULL;
}
RefreshIdentityCombo(GetDlgItem(hDlg, IDC_CB_IDENTITY), FALSE, &ppIdentities);
}
break;
case IDC_BTN_BROWSE:
BrowseHelper(hDlg, IDC_EDT_LOCATION, L"Identity Information", c_wzFileExtIdt, FALSE);
break;
}
break; /* WM_COMMAND */
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: HandleSaveIdentityInfo
//
// Purpose: Extracts the information from the dialog and calls
// SaveIdentityInfo to do the actual work.
//
// Returns: HRESULT
//
HRESULT HandleSaveIdentityInfo(HWND hDlg)
{
HRESULT hr = S_OK;
WCHAR wzFile[MAX_PATH];
PCWSTR pwzIdentity = GetSelectedIdentity(hDlg);
if ((NULL == pwzIdentity) || (0 == *pwzIdentity))
{
hr = E_FAIL;
DisplayHrError(L"Please select an identity.", hr);
}
if (SUCCEEDED(hr))
{
LRESULT cch = SendDlgItemMessage(hDlg, IDC_EDT_LOCATION, WM_GETTEXT, celems(wzFile), (LPARAM) wzFile);
if (0 == cch)
{
hr = E_FAIL;
DisplayHrError(L"Please enter a filename where the identity will be saved.", hr);
}
}
if (SUCCEEDED(hr))
{
hr = SaveIdentityInfo(pwzIdentity, wzFile);
if (FAILED(hr))
{
DisplayHrError(L"Failed to save identity information.", hr);
}
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: SaveIdentityInfo
//
// Purpose: Saves the the information for an identity to a file.
// Displays a message if there was an error.
//
// Returns: HRESULT
//
HRESULT SaveIdentityInfo(PCWSTR pwzIdentity, PCWSTR pwzFile)
{
PWSTR pwzXML = NULL;
HRESULT hr = PeerIdentityGetXML(pwzIdentity, &pwzXML);
if (FAILED(hr))
{
DisplayHrError(L"Unable to retreive the XML data for the identity.", hr);
}
else
{
FILE *pFile = NULL;
errno_t err = 0;
err = _wfopen_s(&pFile, pwzFile, L"wb");
if (err != 0)
{
hr = E_FAIL;
DisplayHrError(L"Please choose a valid path", hr);
}
else
{
if (fputws(pwzXML, pFile) == WEOF)
{
hr = E_FAIL;
DisplayHrError(L"End of file error.", hr);
}
fclose(pFile);
}
PeerFreeData(pwzXML);
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// C R E A T E I N V I T A T I O N
//-----------------------------------------------------------------------------
// Function: CreateInvitationProc
//
// Purpose: DialogProc to create and save an invitation to a file.
//
// Returns: Returns TRUE if it processed the message, and FALSE if it did not.
//
LRESULT CALLBACK CreateInvitationProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
//Unreferenced parameter
lParam;
switch (message)
{
case WM_INITDIALOG:
if (g_dwAuth == PEER_GROUP_PASSWORD_AUTHENTICATION)
{
SendDlgItemMessage(hDlg, IDC_EDT_IDENT_LOCATION, WM_SETTEXT, 0, (LPARAM) L"Password-based group");
EnableWindow(GetDlgItem(hDlg, IDC_EDT_IDENT_LOCATION), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_STATIC_IDENT), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_BTN_IDENT_BROWSE), FALSE);
}
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_BTN_IDENT_BROWSE:
BrowseHelper(hDlg, IDC_EDT_IDENT_LOCATION, L"Identity Information", c_wzFileExtIdt, TRUE);
break;
case IDC_BTN_INV_BROWSE:
BrowseHelper(hDlg, IDC_EDT_INV_LOCATION, L"Group Invitation", c_wzFileExtInv, FALSE);
break;
case IDOK:
if (SUCCEEDED(HandleCreateInvitation(hDlg)))
{
EndDialog(hDlg, IDOK);
}
break;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
break;
default:
break;
}
break; /* WM_COMMAND */
default:
break;
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: HandleCreateInvitation
//
// Purpose: Extracts the information from the dialog and calls CreateInvitation
// to do the actual work.
//
// Returns: HRESULT
//
HRESULT HandleCreateInvitation(HWND hDlg)
{
HRESULT hr = S_OK;
WCHAR wzIdentityInfo[MAX_PATH] = {0};
WCHAR wzInvitation[MAX_PATH] = {0};
SendDlgItemMessage(hDlg, IDC_EDT_INV_LOCATION, WM_GETTEXT,
celems(wzInvitation), (LPARAM) wzInvitation);
if (g_dwAuth == PEER_GROUP_GMC_AUTHENTICATION)
{
SendDlgItemMessage(hDlg, IDC_EDT_IDENT_LOCATION, WM_GETTEXT,
celems(wzIdentityInfo), (LPARAM) wzIdentityInfo);
}
hr = CreateInvitation(wzIdentityInfo, wzInvitation);
return hr;
}
//-----------------------------------------------------------------------------
// Function: CreateInvitation
//
// Purpose: Creates an invitation file for an identity.
// Displays a message if there was an error.
//
// Parameters:
// wzIdentityInfoPath [in] : Path to identity information file
// (not used in password based invitations)
// wzInvitationPath [in] : Path to invitation file
//
// Returns: HRESULT
//
HRESULT CreateInvitation(PCWSTR wzIdentityInfoPath, PCWSTR wzInvitationPath)
{
HRESULT hr = S_OK;
WCHAR wzIdentityInfo[MAX_INVITATION] = {0};
PWSTR pwzInvitation = NULL;
// Do not try to open an identity file if we are creating an invitation
// for a password based group
if (g_dwAuth == PEER_GROUP_GMC_AUTHENTICATION)
{
FILE* pFile = NULL;
errno_t err = 0;
err = _wfopen_s(&pFile, wzIdentityInfoPath, L"rb");
if (err != 0)
{
hr = E_FAIL;
DisplayHrError(L"Please choose a valid path to the identity information file.", hr);
}
else
{
fread(wzIdentityInfo, sizeof(WCHAR), MAX_INVITATION, pFile);
if (ferror(pFile))
{
hr = E_FAIL;
DisplayHrError(L"File read error occurred.", hr);
}
fclose(pFile);
}
}
if (SUCCEEDED(hr))
{
ULONGLONG ulExpire; // adjust time using this structure
GetSystemTimeAsFileTime((FILETIME *)&ulExpire);
// 15 days in 100 nanoseconds resolution
ulExpire += ((ULONGLONG) (60 * 60 * 24 * 15)) * ((ULONGLONG)1000*1000*10);
if (g_dwAuth == PEER_GROUP_GMC_AUTHENTICATION)
{
hr = PeerGroupCreateInvitation(g_hGroup, wzIdentityInfo, (FILETIME*)&ulExpire, 1,
(PEER_ROLE_ID*) &PEER_GROUP_ROLE_MEMBER, &pwzInvitation);
}
else
{
hr = PeerGroupCreatePasswordInvitation(g_hGroup, &pwzInvitation);
}
if (FAILED(hr))
{
DisplayHrError(L"Failed to create the invitation.", hr);
}
}
// Save invitation to file
if (SUCCEEDED(hr))
{
FILE* pFile = NULL;
errno_t err = 0;
err = _wfopen_s(&pFile, wzInvitationPath, L"wb");
if (err != 0)
{
hr = E_FAIL;
DisplayHrError(L"Please choose a valid path to the invitation file.", hr);
}
else
{
if (fputws(pwzInvitation, pFile) == WEOF)
{
hr = E_FAIL;
DisplayHrError(L"End of file error.", hr);
}
fclose(pFile);
}
}
PeerFreeData(pwzInvitation);
return hr;
}
//-----------------------------------------------------------------------------
// Function: BrowseHelper
//
// Purpose: Use the common dialog to get/set a path.
//
// Returns: nothing
//
void BrowseHelper(HWND hDlg, int idEditbox, PCWSTR pwzFileType, PCWSTR pwzFileExtension, BOOL fOpen)
{
OPENFILENAME ofn = {0};
WCHAR wzPath[MAX_PATH] = {0};
WCHAR wzFilter[512] = {0};
BOOL fSuccess = FALSE;
StringCbPrintf(wzFilter, sizeof(wzFilter), L"%s (*.%s)%c*.%s", pwzFileType, pwzFileExtension, L'\0', pwzFileExtension);
ofn.lStructSize = sizeof (OPENFILENAME);
ofn.hwndOwner = hDlg;
ofn.lpstrFilter = wzFilter;
ofn.lpstrFile = wzPath;
ofn.lpstrDefExt = pwzFileExtension;
ofn.nMaxFile = celems(wzPath);
if (fOpen)
{
fSuccess = GetOpenFileName(&ofn);
}
else
{
fSuccess = GetSaveFileName(&ofn);
}
if (fSuccess)
{
SetDlgItemText(hDlg, idEditbox, wzPath);
}
}
//-----------------------------------------------------------------------------
// Function: RegisterForEvents
//
// Purpose: Registers the EventCallback function so it will be called for only
// those events that are specified.
//
// Returns: HRESULT
//
HRESULT RegisterForEvents(void)
{
HRESULT hr = S_OK;
PEER_GROUP_EVENT_REGISTRATION regs[] = {
{ PEER_GROUP_EVENT_RECORD_CHANGED, &RECORD_TYPE_CHAT_MESSAGE },
{ PEER_GROUP_EVENT_MEMBER_CHANGED, 0 },
{ PEER_GROUP_EVENT_STATUS_CHANGED, 0 },
{ PEER_GROUP_EVENT_DIRECT_CONNECTION, &DATA_TYPE_WHISPER_MESSAGE },
{ PEER_GROUP_EVENT_INCOMING_DATA, 0 },
};
g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (g_hEvent == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
else
{
hr = PeerGroupRegisterEvent(g_hGroup, g_hEvent, celems(regs), regs, &g_hPeerEvent);
}
if (SUCCEEDED(hr))
{
if (!RegisterWaitForSingleObject(&g_hWait, g_hEvent, EventCallback, NULL, INFINITE, WT_EXECUTEDEFAULT))
{
hr = E_UNEXPECTED;
}
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: ProcessRecordChanged
//
// Purpose: Processes the PEER_GROUP_EVENT_RECORD_CHANGED event.
//
// Returns: nothing
//
void ProcessRecordChanged(PEER_EVENT_RECORD_CHANGE_DATA * pData)
{
switch (pData->changeType)
{
case PEER_RECORD_ADDED:
if (IsEqualGUID(&pData->recordType, &RECORD_TYPE_CHAT_MESSAGE))
{
PEER_RECORD * pRecord = {0};
HRESULT hr = PeerGroupGetRecord(g_hGroup, &pData->recordId, &pRecord);
if (SUCCEEDED(hr))
{
DisplayChatMessage(pRecord->pwzCreatorId, (PCWSTR) pRecord->data.pbData);
PeerFreeData(pRecord);
}
}
break;
case PEER_RECORD_UPDATED:
case PEER_RECORD_DELETED:
case PEER_RECORD_EXPIRED:
break;
default:
break;
}
}
//-----------------------------------------------------------------------------
// Function: ProcessStatusChanged
//
// Purpose: Processes the PEER_GROUP_EVENT_STATUS_CHANGED event.
//
// Returns: nothing
//
void ProcessStatusChanged(DWORD dwStatus)
{
HRESULT hr = S_OK;
PWSTR pwzStatus = L"";
PEER_GROUP_PROPERTIES * pProperties = NULL;
WCHAR wzChatTitle[MAX_GROUPNAME + 20] = {0};
StringCbPrintf(wzChatTitle, sizeof(wzChatTitle), L"Offline");
if (dwStatus & PEER_GROUP_STATUS_HAS_CONNECTIONS)
{
pwzStatus = L"connected";
hr = PeerGroupGetProperties(g_hGroup, &pProperties);
if (SUCCEEDED(hr))
StringCbPrintf(wzChatTitle, sizeof(wzChatTitle), L"Chatting in %s ", pProperties->pwzFriendlyName);
}
else if (dwStatus & PEER_GROUP_STATUS_LISTENING)
{
pwzStatus = L"listening";
hr = PeerGroupGetProperties(g_hGroup, &pProperties);
if (SUCCEEDED(hr))
StringCbPrintf(wzChatTitle, sizeof(wzChatTitle), L"Waiting to chat in %s ", pProperties->pwzFriendlyName);
}
SetWindowText(g_hwndChatLabel, wzChatTitle);
PeerFreeData(pProperties);
SendMessage(g_hwndStatus, SB_SETTEXT, SB_PART_STATUS, (LPARAM) pwzStatus);
UpdateParticipantList( );
}
//-----------------------------------------------------------------------------
// Function: PrepareToChat
//
// Purpose: Does the initial hookup of the group required after a successful
// create, open or join.
//
// Returns: HRESULT
//
HRESULT PrepareToChat(void)
{
HRESULT hr = RegisterForEvents( );
DWORD dwStatus;
if (FAILED(hr))
{
DisplayHrError(L"Unable to register for events.", hr);
}
if (SUCCEEDED(hr))
{
hr = PeerGroupConnect(g_hGroup);
if (FAILED(hr))
{
DisplayHrError(L"Unable to connect to the group.", hr);
}
}
if (SUCCEEDED(hr))
{
EnableMenuItem(GetMenu(g_hwndMain), IDM_CREATEINVITATION, MF_ENABLED);
hr = PeerGroupGetStatus(g_hGroup, &dwStatus);
if (SUCCEEDED(hr))
{
ProcessStatusChanged(dwStatus);
}
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: FindParticipant
//
// Purpose: Find a participant of the group chat in the list based on their identity.
//
// Returns: the index of position in the list or -1 if not found.
//
int FindParticipant(PCWSTR pwzIdentity)
{
int iItem = ListBox_GetCount(g_hwndMembers);
while ((--iItem) >= 0)
{
PCWSTR pwzData = (PCWSTR) ListBox_GetItemData(g_hwndMembers, iItem);
if (pwzData != NULL)
{
if (0 == wcscmp(pwzData, pwzIdentity))
{
return iItem;
}
}
}
return -1;
}
//-----------------------------------------------------------------------------
// Function: AddParticipantName
//
// Purpose: Adds a participant name to the list.
// Allocates a copy of the identity string to store in the data area
// of the list item.
//
// Returns: nothing
//
void AddParticipantName(PCWSTR pwzIdentity, PCWSTR pwzUserName)
{
int iItem;
int cch;
PWSTR pwzData = NULL;
iItem = ListBox_AddString(g_hwndMembers, pwzUserName);
if (iItem < 0)
{
DisplayError(L"Unable to add participant name");
}
else
{
cch = (int) wcslen(pwzIdentity);
pwzData = malloc((cch+1) * sizeof(WCHAR));
if (NULL != pwzData)
{
StringCchCopy(pwzData, cch + 1, pwzIdentity);
}
ListBox_SetItemData(g_hwndMembers, iItem, pwzData);
}
}
//-----------------------------------------------------------------------------
// Function: AddParticipant
//
// Purpose: Adds a participant name to the member list.
// Allocates a copy of the identity string to store in the data area
// of the list item.
//
// Returns: nothing
//
void AddParticipant(PCWSTR pwzIdentity)
{
WCHAR wzUserName[MAX_USERNAME];
GetFriendlyNameForIdentity(pwzIdentity, wzUserName, celems(wzUserName));
AddParticipantName(pwzIdentity, wzUserName);
DisplayChatMessage(pwzIdentity, L"has joined the group");
}
//-----------------------------------------------------------------------------
// Function: DeleteParticipant
//
// Purpose: Deletes the participant and associated data from the member list.
//
// Returns: nothing
//
void DeleteParticipant(int iItem)
{
PWSTR pwzData = (PWSTR) ListBox_GetItemData(g_hwndMembers, iItem);
free(pwzData);
ListBox_DeleteString(g_hwndMembers, iItem);
}
//-----------------------------------------------------------------------------
// Function: RemoveParticipant
//
// Purpose: Removes a participant from the member list based on their identity.
//
// Returns: nothing
//
void RemoveParticipant(PCWSTR pwzIdentity)
{
int iItem = FindParticipant(pwzIdentity);
if (iItem < 0)
{
DisplayError(L"Unable to find participant");
}
else
{
DeleteParticipant(iItem);
DisplayChatMessage(pwzIdentity, L"has left the group");
}
}
//-----------------------------------------------------------------------------
// Function: ClearParticipantList
//
// Purpose: Clear the list of partipants.
//
// Returns: nothing
//
void ClearParticipantList(void)
{
while (0 != ListBox_GetCount(g_hwndMembers))
{
DeleteParticipant(0);
}
}
//-----------------------------------------------------------------------------
// Function: UpdateParticipantList
//
// Purpose: Update the list of partipants.
//
// Returns: nothing
//
void UpdateParticipantList(void)
{
HRESULT hr = S_OK;
HPEERENUM hPeerEnum = NULL;
PEER_MEMBER ** ppMember = NULL;
ClearParticipantList( );
if (NULL == g_hGroup)
{
return;
}
// Retreive only the members currently present in the group.
hr = PeerGroupEnumMembers(g_hGroup, PEER_MEMBER_PRESENT, NULL, &hPeerEnum);
if (SUCCEEDED(hr))
{
while (SUCCEEDED(hr))
{
ULONG cItem = 1;
hr = PeerGetNextItem(hPeerEnum, &cItem, (PVOID **) &ppMember);
if (SUCCEEDED(hr))
{
if (0 == cItem)
{
PeerFreeData(ppMember);
break;
}
}
if (SUCCEEDED(hr))
{
if (0 != ((*ppMember)->dwFlags & PEER_MEMBER_PRESENT))
{
AddParticipantName((*ppMember)->pwzIdentity, (*ppMember)->pCredentialInfo->pwzFriendlyName);
}
PeerFreeData(ppMember);
}
}
PeerEndEnumeration(hPeerEnum);
}
}
//-----------------------------------------------------------------------------
// Function: ProcessMemberChanged
//
// Purpose: Processes the PEER_GROUP_EVENT_MEMBER_CHANGED event.
//
// Returns: nothing
//
void ProcessMemberChanged(PEER_EVENT_MEMBER_CHANGE_DATA * pData)
{
switch (pData->changeType)
{
case PEER_MEMBER_JOINED:
break;
case PEER_MEMBER_LEFT:
break;
case PEER_MEMBER_CONNECTED:
// This check must be made in case PEER_MEMBER_UPDATED is fired first
if (FindParticipant(pData->pwzIdentity) == -1)
{
AddParticipant(pData->pwzIdentity);
}
break;
case PEER_MEMBER_DISCONNECTED:
RemoveParticipant(pData->pwzIdentity);
break;
case PEER_MEMBER_UPDATED:
if (FindParticipant(pData->pwzIdentity) != -1)
{
RemoveParticipant(pData->pwzIdentity);
}
AddParticipant(pData->pwzIdentity);
break;
default:
break;
}
}
//-----------------------------------------------------------------------------
// Function: EventCallback
//
// Purpose: Handle events raised by the grouping infrastructure.
//
// Returns: nothing
//
VOID CALLBACK EventCallback(PVOID lpParam, BOOLEAN reason)
{
//Unreferenced parameters
lpParam;
reason;
for (;;)
{
PEER_GROUP_EVENT_DATA *pEventData = NULL;
HRESULT hr = PeerGroupGetEventData(g_hPeerEvent, &pEventData);
if (FAILED(hr) || (PEER_S_NO_EVENT_DATA == hr) || (NULL == pEventData))
{
break;
}
switch (pEventData->eventType)
{
case PEER_GROUP_EVENT_RECORD_CHANGED:
ProcessRecordChanged(&pEventData->recordChangeData);
break;
case PEER_GROUP_EVENT_STATUS_CHANGED:
ProcessStatusChanged(pEventData->dwStatus);
break;
case PEER_GROUP_EVENT_MEMBER_CHANGED:
ProcessMemberChanged(&pEventData->memberChangeData);
break;
case PEER_GROUP_EVENT_DIRECT_CONNECTION:
break;
case PEER_GROUP_EVENT_INCOMING_DATA:
ProcessIncomingData(pEventData);
break;
default:
break;
}
PeerFreeData(pEventData);
}
}
//-----------------------------------------------------------------------------
// Function: RefreshGroupCombo
//
// Purpose: Given an identity name (in pwzIdentity), fills the combo box (specified by hwnd) with
// all the groups accessible by the specified identity. The ItemData of each entry
// in the combobox points to the groupid. For these pointers to remain valid after
// the call, the PEER_NAME_PAIR array is returned, and must be freed via PeerFreeData( )
// by the calling function OpenGroupProc( ).
//
// Returns: HRESULT
//
HRESULT RefreshGroupCombo(HWND hwndCtrl, PCWSTR pwzIdentity, PEER_NAME_PAIR ***pppNamePairs)
{
ULONG cGroups = 0;
HPEERENUM hPeerEnum = NULL;
HRESULT hr = S_OK;
*pppNamePairs = NULL;
SendMessage(hwndCtrl, CB_RESETCONTENT, 0, 0);
hr = PeerEnumGroups(pwzIdentity, &hPeerEnum);
if (SUCCEEDED(hr))
{
hr = PeerGetItemCount(hPeerEnum, &cGroups);
if (SUCCEEDED(hr))
{
hr = PeerGetNextItem(hPeerEnum, &cGroups, (VOID***) pppNamePairs);
}
if (SUCCEEDED(hr))
{
ULONG i;
for (i = 0; i < cGroups; i++)
{
int iItem = ComboBox_AddString(hwndCtrl, (*pppNamePairs)[i]->pwzFriendlyName);
ComboBox_SetItemData(hwndCtrl, iItem, (*pppNamePairs)[i]->pwzPeerName);
}
if (cGroups > 0)
{
ComboBox_SetCurSel(hwndCtrl, 0);
}
}
PeerEndEnumeration(hPeerEnum);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: RefreshIdentityCombo
//
// Purpose: Fills the specified combo box w/ all the available identities. The combo box will show
// the friendly names for the identities, and the ItemData will point to the PeerNames. For
// these pointers to remain valid the PEER_NAME_PAIR array is returned w/ the function and
// must be freed via PeerFreeData( ) by the calling function.
//
// Returns: HRESULT
//
HRESULT RefreshIdentityCombo(HWND hwndCtrl, BOOL bAddNullIdentity, PEER_NAME_PAIR ***pppNamePairs)
{
ULONG cIdentities = 0;
HPEERENUM hPeerEnum = NULL;
HRESULT hr = S_OK;
*pppNamePairs = NULL;
ComboBox_ResetContent(hwndCtrl);
hr = PeerEnumIdentities(&hPeerEnum);
if (SUCCEEDED(hr))
{
hr = PeerGetItemCount(hPeerEnum, &cIdentities);
if (SUCCEEDED(hr))
{
hr = PeerGetNextItem(hPeerEnum, &cIdentities, (VOID***) pppNamePairs);
}
if (SUCCEEDED(hr))
{
ULONG i;
for (i = 0; i < cIdentities; i++)
{
int iIdentity = ComboBox_AddString(hwndCtrl, (*pppNamePairs)[i]->pwzFriendlyName);
ComboBox_SetItemData(hwndCtrl, iIdentity, (*pppNamePairs)[i]->pwzPeerName);
}
if (cIdentities > 0)
{
ComboBox_SetCurSel(hwndCtrl, 0);
}
if (bAddNullIdentity)
{
ComboBox_AddString(hwndCtrl, L"NULL Identity");
if (cIdentities == 0)
{
ComboBox_SetCurSel(hwndCtrl, 0);
}
}
}
PeerEndEnumeration(hPeerEnum);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: DisplayChatMessage
//
// Purpose: Display a chat message for an identity.
//
// Returns: nothing
//
void DisplayChatMessage(PCWSTR pwzIdentity, PCWSTR pwzMsg)
{
WCHAR wzName[MAX_USERNAME];
WCHAR wzMessage[MAX_CHAT_MESSAGE + MAX_USERNAME + 10];
// Retrieve the friendly name for the identity
GetFriendlyNameForIdentity(pwzIdentity, wzName, celems(wzName));
// Format the message
StringCbPrintf(wzMessage, sizeof(wzMessage), L"[%s]: %s\r\n", wzName, pwzMsg);
DisplayMsg(wzMessage);
}
//-----------------------------------------------------------------------------
// Function: DisplayReceivedWhisper
//
// Purpose: Display a whispered message for an identity.
//
// Returns: nothing
//
void DisplayReceivedWhisper(PCWSTR pwzIdentity, PCWSTR pwzMsg)
{
WCHAR wzName[MAX_USERNAME];
WCHAR wzMsg[MAX_CHAT_MESSAGE + MAX_USERNAME + 20];
// Format the incoming message with the originator's name
GetFriendlyNameForIdentity(pwzIdentity, wzName, celems(wzName));
// Format the message
StringCbPrintf(wzMsg, sizeof(wzMsg), L"<Whisper from %s>: %s\r\n", wzName, pwzMsg);
DisplayMsg(wzMsg);
}
//-----------------------------------------------------------------------------
// Function: DisplaySentWhisper
//
// Purpose: Display a whispered message that was sent.
//
// Returns: nothing
//
void DisplaySentWhisper(PCWSTR pwzMsg)
{
WCHAR wzMsg[MAX_CHAT_MESSAGE + MAX_USERNAME + 20];
// Format the originator's message
StringCbPrintf(wzMsg, sizeof(wzMsg), L"<Whisper to %s>: %s\r\n", g_wzDCName, pwzMsg);
DisplayMsg(wzMsg);
}
//-----------------------------------------------------------------------------
// Function: DisplayMsg
//
// Purpose: Display a message in the window.
//
// Returns: nothing
//
void DisplayMsg(PCWSTR pwzMsg)
{
SendMessage(g_hwndMsg, EM_SETSEL, 0x7fffffff, (LPARAM) -1);
SendMessage(g_hwndMsg, EM_REPLACESEL, 0, (LPARAM) pwzMsg);
SendMessage(g_hwndMsg, EM_SCROLLCARET, 0, 0);
}
//-----------------------------------------------------------------------------
// Function: AddChatRecord
//
// Purpose: This adds a new chat message record to the group.
//
// Returns: HRESULT
//
HRESULT AddChatRecord(PCWSTR pwzMessage)
{
HRESULT hr = S_OK;
PEER_RECORD record = {0};
GUID idRecord;
ULONGLONG ulExpire;
ULONG cch = (ULONG) wcslen(pwzMessage);
GetSystemTimeAsFileTime((FILETIME *) &ulExpire);
// calculate 2 minute expiration time in 100 nanosecond resolution
ulExpire += ((ULONGLONG) 60 * 2) * ((ULONGLONG)1000*1000*10);
// Set up the record
record.dwSize = sizeof(record);
record.data.cbData = (cch+1) * sizeof(WCHAR);
record.data.pbData = (PBYTE) pwzMessage;
memcpy(&record.ftExpiration, &ulExpire, sizeof(ulExpire));
PeerGroupUniversalTimeToPeerTime(g_hGroup, &record.ftExpiration, &record.ftExpiration);
// Set the record type GUID
record.type = RECORD_TYPE_CHAT_MESSAGE;
// Add the record to the database
hr = PeerGroupAddRecord(g_hGroup, &record, &idRecord);
if (FAILED(hr))
{
DisplayHrError(L"Failed to add a chat record to the group.", hr);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: CleanupGroup
//
// Purpose: Clean up all the global variables associated with this group.
//
// Returns: nothing
//
void CleanupGroup(void)
{
if (g_hPeerEvent != NULL)
{
PeerGroupUnregisterEvent(g_hPeerEvent);
g_hPeerEvent = NULL;
}
if (g_hEvent != NULL)
{
CloseHandle(g_hEvent);
g_hEvent = NULL;
}
if (g_hWait != NULL)
{
UnregisterWaitEx(g_hWait, INVALID_HANDLE_VALUE);
g_hWait = NULL;
}
if (g_hGroup != NULL)
{
PeerGroupClose(g_hGroup);
g_hGroup = NULL;
}
}
void CmdCloseGroup(void)
{
CleanupGroup( );
ProcessStatusChanged(0);
SetStatus(L"Closed group");
}
///////////////////////////////////////////////////////////////////////////////
// D I R E C T C O N N E C T I O N R O U T I N E S
//-----------------------------------------------------------------------------
// Function: ProcessIncomingData
//
// Purpose: Processes the PEER_GROUP_EVENT_INCOMING_DATA event.
//
// Returns: nothing
//
void ProcessIncomingData(PEER_GROUP_EVENT_DATA * pEventData)
{
HRESULT hr = S_OK;
HPEERENUM hPeerEnum;
PEER_CONNECTION_INFO ** ppConnectionInfo = NULL;
ULONG ulCount = 0;
// Get a list of all the active direct connections
hr = PeerGroupEnumConnections(g_hGroup, PEER_CONNECTION_DIRECT, &hPeerEnum);
if (SUCCEEDED(hr))
{
hr = PeerGetItemCount(hPeerEnum, &ulCount);
if (SUCCEEDED(hr))
{
hr = PeerGetNextItem(hPeerEnum, &ulCount, (PVOID**) &ppConnectionInfo);
}
if (SUCCEEDED(hr))
{
ULONG i;
for (i=0; i < ulCount; i++)
{
PEER_CONNECTION_INFO * pConnectionInfo = ppConnectionInfo[i];
if (pEventData->incomingData.ullConnectionId == pConnectionInfo->ullConnectionId)
{
// assume pbData is a null terminated string
DisplayReceivedWhisper(pConnectionInfo->pwzPeerId, (PCWSTR) pEventData->incomingData.data.pbData);
break;
}
}
PeerFreeData(ppConnectionInfo);
}
hr = PeerEndEnumeration(hPeerEnum);
}
hr = PeerGroupCloseDirectConnection(g_hGroup, pEventData->incomingData.ullConnectionId);
}
//-----------------------------------------------------------------------------
// Function: SetupDirectConnection
//
// Purpose: Setup the DirectConnection for the Whisper.
//
// Returns: HRESULT
//
HRESULT SetupDirectConnection(PCWSTR pwzIdentity)
{
HRESULT hr = S_OK;
HPEERENUM hPeerEnum;
ULONG cItem = 1;
PEER_MEMBER ** ppMember = NULL;
GetFriendlyNameForIdentity(pwzIdentity, g_wzDCName, celems(g_wzDCName));
// Determine the identity of the peer
hr = PeerGroupEnumMembers(g_hGroup, PEER_MEMBER_PRESENT, pwzIdentity, &hPeerEnum);
if (SUCCEEDED(hr))
{
// Get the PEER_MEMBER info for this peer to later obtain its pAddresses
hr = PeerGetNextItem(hPeerEnum, &cItem, (PVOID**) &ppMember);
PeerEndEnumeration(hPeerEnum);
}
if (SUCCEEDED(hr))
{
hr = PeerGroupOpenDirectConnection(g_hGroup, pwzIdentity, (*ppMember)->pAddresses, &g_ullConnectionId);
if (FAILED(hr))
{
if (PEER_E_CONNECT_SELF == hr)
{
DisplayHrError(L"\nCan't whisper to yourself.\n\nPEER_E_CONNECT_SELF", hr);
}
else if (E_INVALIDARG == hr)
{
DisplayHrError(L"Can t open direct connection.", hr);
}
}
PeerFreeData(ppMember);
}
return hr;
}
//-----------------------------------------------------------------------------
// Function: WhisperMessageProc
//
// Purpose: DialogProc to send a Whisper
//
// Returns: Returns TRUE if it processed the message, and FALSE if it did not.
//
LRESULT CALLBACK WhisperMessageProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
//Unreferenced parameters
lParam;
switch (message)
{
case WM_INITDIALOG:
SendDlgItemMessage(hDlg, IDC_EDT_MESSAGE, EM_SETLIMITTEXT, MAX_CHAT_MESSAGE - 1, 0);
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDSEND:
if (SUCCEEDED(HandleWhisperMessage(hDlg)))
{
EndDialog(hDlg, IDOK);
}
break;
case IDCANCEL:
PeerGroupCloseDirectConnection(g_hGroup, g_ullConnectionId);
EndDialog(hDlg, IDCANCEL);
break;
}
break;
default:
break; /* WM_COMMAND */
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Function: HandleWhisperMessage
//
// Purpose: Extracts the message from the dialog and
// readies a whisper to be sent via PeerGroupOpenDirectConnection.
//
// Returns: HRESULT
//
HRESULT HandleWhisperMessage(HWND hDlg)
{
HRESULT hr = S_OK;
WCHAR wzMessage[MAX_CHAT_MESSAGE];
UINT cch = GetDlgItemText(hDlg, IDC_EDT_MESSAGE, wzMessage, celems(wzMessage));
if (0 == cch)
{
DisplayHrError(L"Please type a msg for the Whisper.", hr);
return E_FAIL;
}
// Send the message using the current direct connection
hr = PeerGroupSendData(g_hGroup, g_ullConnectionId, &DATA_TYPE_WHISPER_MESSAGE,
sizeof(WCHAR) * (cch+1), wzMessage);
if (FAILED(hr))
{
DisplayHrError(L"Failed to send data across direct connection.", hr);
}
else
{
DisplaySentWhisper(wzMessage);
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// U T I L I T Y R O U T I N E S
//-----------------------------------------------------------------------------
// Function: GetSelectedIdentity
//
// Purpose: Get the currently selected identity.
//
// Returns: A pointer to the identity string.
//
PCWSTR GetSelectedIdentity(HWND hDlg)
{
PCWSTR pwzIdentity = NULL;
HWND hwndCtrl = GetDlgItem(hDlg, IDC_CB_IDENTITY);
int iItem = ComboBox_GetCurSel(hwndCtrl);
if (iItem >= 0)
{
pwzIdentity = (PCWSTR) ComboBox_GetItemData(hwndCtrl, iItem);
}
return pwzIdentity;
}
//-------------------------------------------------------------------------
// Function: GetLocalCloudName
//
// Purpose: Retrieve first available local cloud name
//
// Arguments:
// cchCloudNameSize[in]: number of characters in pwzCloudName
// (usually MAX_CLOUD_NAME)
// pwzCloudName [out] : location to which cloud name will be copied
//
// Returns: HRESULT
//
HRESULT GetLocalCloudName(ULONG cchCloudName, __out_ecount(cchCloudName) PWSTR pwzCloudName)
{
HRESULT hr = S_OK;
PNRPCLOUDINFO CloudInfo = {0};
BLOB blPnrpData = {0};
WSAQUERYSET querySet = {0};
WSAQUERYSET* pResults = NULL;
HANDLE hLookup = NULL;
INT iErr = 0;
DWORD dwErr = 0;
DWORD dwResultSize = 0;
// Fill out information for WSA query
CloudInfo.dwSize = sizeof(CloudInfo);
CloudInfo.Cloud.Scope = PNRP_LINK_LOCAL_SCOPE;
blPnrpData.cbSize = sizeof(CloudInfo);
blPnrpData.pBlobData = (LPBYTE)&CloudInfo;
querySet.dwSize = sizeof(querySet);
querySet.dwNameSpace = NS_PNRPCLOUD;
querySet.lpServiceClassId = (LPGUID)&SVCID_PNRPCLOUD;
querySet.lpBlob = &blPnrpData;
iErr = WSALookupServiceBegin(&querySet, LUP_RETURN_NAME,
&hLookup);
if (iErr != 0)
{
return iErr;
}
else
{
WSAQUERYSET tempResultSet = {0};
dwResultSize = sizeof(tempResultSet);
// Get size of results
iErr = WSALookupServiceNext(hLookup, 0, &dwResultSize, &tempResultSet);
if (iErr != 0)
{
dwErr = WSAGetLastError();
if (dwErr == WSAEFAULT)
{
// allocate space for results
pResults = (WSAQUERYSET*) malloc(dwResultSize);
if (pResults == NULL)
{
hr = HRESULT_FROM_WIN32(WSAGetLastError());
}
}
else
{
hr = HRESULT_FROM_WIN32(dwErr);
}
}
}
if (SUCCEEDED(hr))
{
// retrieve the local cloud information
iErr = WSALookupServiceNext(hLookup, 0, &dwResultSize, pResults);
if (iErr != 0)
{
hr = HRESULT_FROM_WIN32(WSAGetLastError());
}
}
// Copy the cloud name (if applicable) and scope ID
if (SUCCEEDED(hr))
{
hr = StringCchCopy(pwzCloudName, cchCloudName, pResults->lpszServiceInstanceName);
if (FAILED(hr))
{
DisplayHrError(L"Failed to copy cloud name", hr);
}
}
if (hLookup != NULL)
{
(void) WSALookupServiceEnd(hLookup);
}
free(pResults);
return hr;
}
//-----------------------------------------------------------------------------
// Function: GetSelectedGroup
//
// Purpose: Get the currently selected group.
//
// Returns: A pointer to the group PeerName.
//
PCWSTR GetSelectedGroup(HWND hDlg)
{
PCWSTR pwzGroup = NULL;
HWND hwndCtrl = GetDlgItem(hDlg, IDC_CB_GROUP);
int iItem = ComboBox_GetCurSel(hwndCtrl);
if (iItem >= 0)
{
pwzGroup = (PCWSTR) ComboBox_GetItemData(hwndCtrl, iItem);
}
return pwzGroup;
}
//-----------------------------------------------------------------------------
// Function: GetSelectedChatMember
//
// Purpose: Get the currently selected member from the members list.
//
// Returns: A pointer to the identity string.
//
PCWSTR GetSelectedChatMember( )
{
PCWSTR pwzIdentity = NULL;
int iItem = ListBox_GetCurSel(g_hwndMembers);
if (iItem >= 0)
{
pwzIdentity = (PWSTR) ListBox_GetItemData(g_hwndMembers, iItem);
}
return pwzIdentity;
}
//-----------------------------------------------------------------------------
// Function: SetStatus
//
// Purpose: Set the text of the status bar.
//
// Returns: nothing
//
void SetStatus(PCWSTR pwzStatus)
{
SendMessage(g_hwndStatus, SB_SETTEXT, SB_PART_MESSAGE, (LPARAM) pwzStatus);
}
//-----------------------------------------------------------------------------
// Function: GetFriendlyNameForIdentity
//
// Purpose: Retrieve the friendly name for an identity
//
// Returns: The number of characters copied to the buffer
// (not including the terminating null.)
//
int GetFriendlyNameForIdentity(
PCWSTR pwzIdentity, // The identity to find
__out PWSTR pwzName, // The buffer for the friendly name
int cchMax) // The size of the buffer (WCHARs)
{
HRESULT hr = S_OK;
HPEERENUM hPeerEnum = NULL;
PEER_MEMBER ** ppMember = NULL;
// Always provide a default friendly name
StringCchCopy(pwzName, cchMax, L"?");
hr = PeerGroupEnumMembers(g_hGroup, 0, pwzIdentity, &hPeerEnum);
if (SUCCEEDED(hr))
{
ULONG cItem = 1;
hr = PeerGetNextItem(hPeerEnum, &cItem, (PVOID **) &ppMember);
if (SUCCEEDED(hr) && (cItem > 0) && (NULL != ppMember))
{
StringCchCopy(pwzName, cchMax, (*ppMember)->pCredentialInfo->pwzFriendlyName);
}
PeerFreeData(ppMember);
PeerEndEnumeration(hPeerEnum);
}
return (int) wcslen(pwzName);
}
//-----------------------------------------------------------------------------
// Function: DisplayError
//
// Purpose: Display an error message.
//
// Returns: nothing
//
void DisplayError(PCWSTR pwzMsg)
{
WCHAR wzTitle[MAX_LOADSTRING];
LoadString(g_hInst, IDS_APP_ERROR, wzTitle, MAX_LOADSTRING);
MessageBox(GetWindow(g_hwndMain, GW_ENABLEDPOPUP), pwzMsg, wzTitle, MB_OK | MB_ICONWARNING);
}
//-----------------------------------------------------------------------------
// Function: GetErrorMsg
//
// Purpose: Generate an error string from a HRESULT error code.
//
// Returns: HRESULT
//
HRESULT GetErrorMsg(HRESULT hrError, ULONG cchMsg, __out_ecount(cchMsg) PWSTR pwzMsg)
{
DWORD cch = 0;
if (cchMsg == 0)
{
return E_FAIL;
}
if (HRESULT_FACILITY(hrError) == FACILITY_P2P)
{
HMODULE hResDll = GetModuleHandle(L"p2p.dll");
if (NULL != hResDll)
{
cch = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
hResDll,
HRESULTTOWIN32(hrError),
0,
pwzMsg,
cchMsg,
NULL);
FreeLibrary(hResDll);
}
}
else
{
cch = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
HRESULTTOWIN32(hrError),
0,
pwzMsg,
cchMsg,
NULL);
}
if (cch == 0)
return E_FAIL;
else
return S_OK;
}
//-----------------------------------------------------------------------------
// Function: MsgErrHr
//
// Purpose: Display a message for a function with an error code.
//
// Returns: nothing
//
void MsgErrHr(PCWSTR pwzText, HRESULT hrErr, PCSTR pszFunction)
{
WCHAR wzError[512];
WCHAR wzMsg[512];
const WCHAR *pwzMsg;
HRESULT hr = GetErrorMsg(hrErr, celems(wzMsg), wzMsg);
if (FAILED(hr))
pwzMsg = L"Unknown";
else
pwzMsg = wzMsg;
StringCchPrintf(wzError, celems(wzError), L"%s\r\n\r\nFunction: %S\r\nHRESULT=0x%08X (%s)",
pwzText, pszFunction, hrErr, pwzMsg);
MessageBox(GetWindow(g_hwndMain, GW_ENABLEDPOPUP), wzError, L"Graph Chat Error", MB_OK | MB_ICONWARNING);
}