3082 lines
86 KiB
C
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);
|
|
}
|