// prints the event to the console
class EventSink : public IChatSessionEvents
{
public:
STDMETHODIMP QueryInterface(REFIID riid, void**ppv)
{
if (riid == IID_IUnknown)
*ppv = static_cast
else if (riid == IID_IChatSessionEvents)
*ppv = static_cast
else
return (*ppv = 0), E_NOINTERFACE;
reinterpret_cast
return S_OK;
}
STDMETHODIMP_(ULONG) AddRef(void)
{
return 2;
}
STDMETHODIMP_(ULONG) Release(void)
{
return 1;
}
STDMETHODIMP OnNewStatement(const OLECHAR *pwszUser,
const OLECHAR *pwszStmt)
{
wprintf(L"%-14s: %s\n", pwszUser, pwszStmt);
return S_OK;
}
STDMETHODIMP OnNewUser(const OLECHAR *pwszUser)
{
wprintf(L"\n\n>>> Say Hello to %s\n\n", pwszUser);
return S_OK;
}
STDMETHODIMP OnUserLeft(const OLECHAR *pwszUser)
{
wprintf(L"\n\n>>> Say Bye to %s\n\n", pwszUser);
return S_OK;
}
};
// type of operations this client can perform
enum ACTION
{
ACTION_NONE,
ACTION_CHAT,
ACTION_DELETE_SESSION,
ACTION_LIST_SESSION_NAMES,
};
// run chat command
void Chat(const OLECHAR *pwszSession,
IChatSessionManager *pcsm, // manager
COAUTHIDENTITY *pcai, // user
bool bAnonymous) // anonymous
{
// create or get the named session
IChatSession *pcs = 0;
HRESULT hr = pcsm->FindSession(pwszSession, FALSE,
TRUE, &pcs);
if (SUCCEEDED(hr))
{
// adjust security blanket for session interface
if (!bAnonymous)
hr = CoSetProxyBlanket(pcs, RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE, 0,
RPC_C_AUTHN_LEVEL_PKT,
RPC_C_IMP_LEVEL_IDENTIFY,
pcai, EOAC_NONE);
// catch up on past messages
PrintToDate(pcs);
// hook up event sink to receive new messages
EventSink es;
DWORD dwReg;
hr = pcs->Advise(&es, &dwReg);
if (SUCCEEDED(hr))
{
// run UI loop to get statements from console and send them
OLECHAR wszStmt[4096];
while (_getws(wszStmt))
{
hr = pcs->Say(wszStmt);
if (FAILED(hr))
Error(hr, «Say»);
}
// tear down connection for event sink
pcs->Unadvise(dwReg);
}
else
Error(hr, «Advise»);
// release chat session
pcs->Release();
}
else
Error(hr, «FindSession»);
}
// run delete command
void Delete(const OLECHAR *pwszSession,
IChatSessionManager *pcsm)
{
HRESULT hr = pcsm->DeleteSession(pwszSession);
if (FAILED(hr))
Error(hr, «DeleteSession»);
}
// run list command
void List(IChatSessionManager *pcsm)
{
IEnumString *pes = 0;
HRESULT hr = pcsm->GetSessionNames(&pes);
if (SUCCEEDED(hr))
{
printf(«Active Sessions:\n»);
PrintAllStrings(pes);
pes->Release();
}
}
int main(int argc, char **argv)
{
// declare client control state
bool bAnonymous = false;
static OLECHAR wszSessionName[1024];
static OLECHAR wszDomainName[1024];
static OLECHAR wszUserName[1024];
static OLECHAR wszPassword[1024];
static OLECHAR wszHostName[1024];
COSERVERINFO csi = { 0, wszHostName, 0, 0 };
COSERVERINFO *pcsi = 0;
COAUTHIDENTITY cai = {
wszUserName,
0,
wszDomainName,
0,
wszPassword,
0,
SEC_WINNT_AUTH_IDENTITY_UNICODE
};
static COAUTHIDENTITY *pcai = 0;
static ACTION action = ACTION_NONE;
// parse command line
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], «/anonymous») == 0)
bAnonymous = true;
else if (strstr(argv[i], «/delete:») == argv[i])
{
if (action != ACTION_NONE)
return Usage();
action = ACTION_DELETE_SESSION;
mbstowcs(wszSessionName, argv[i] + 8, 1024);
}
else if (strstr(argv[i], «/chat:») == argv[i])
{
if (action != ACTION_NONE)
return Usage();
action = ACTION_CHAT;
mbstowcs(wszSessionName, argv[i] + 6, 1024);
}
else if (strcmp(argv[i], «/sessions») == 0)
{
if (action != ACTION_NONE)
return Usage();
action = ACTION_LIST_SESSION_NAMES;
}
else if (strstr(argv[i], «/host:») == argv[i])
{
if (pcsi != 0)
return Usage();
mbstowcs(wszHostName, argv[i] + 6, 1024);
pcsi = &csi;
}
else if (strstr(argv[i], «/password:») == argv[i])
{
mbstowcs(wszPassword, argv[i] + 10, 1024);
cai.PasswordLength = wcslen(wszPassword);
}
else if (strstr(argv[i], «/user:») == argv[i])
{
if (pcai != 0 || bAnonymous)
return Usage();
char *pszDelim = strchr(argv[i] + 7, '\\');
if (pszDelim == 0)
return Usage();
*pszDelim = 0;
pszDelim++;
mbstowcs(wszDomainName, argv[i] + 6, 1024);
cai.DomainLength = wcslen(wszDomainName);
mbstowcs(wszUserName, pszDelim, 1024);
cai.UserLength = wcslen(wszUserName);
pcai = &cai;
}
}
if (action == ACTION_NONE)
return Usage();
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hr))
return hr;
// allow anonymous callbacks from chat server
hr = CoInitializeSecurity(0, -1, 0, 0,
RPC_C_AUTHN_LEVEL_NONE,
RPC_C_IMP_LEVEL_ANONYMOUS,
0, EOAC_NONE, 0);
if (SUCCEEDED(hr))
{
// grab the requested session manager
IChatSessionManager *pcsm = 0;
hr = CoGetClassObject(CLSID_ChatSession, CLSCTX_ALL,
pcsi, IID_IChatSessionManager,
(void**)&pcsm);
if (SUCCEEDED(hr))
{
// apply security blanket if desired
if (!bAnonymous)
hr = CoSetProxyBlanket(pcsm, RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE, 0,
RPC_C_AUTHN_LEVEL_PKT,
RPC_C_IMP_LEVEL_IDENTIFY,
pcai, EOAC_NONE);
// dispatch request
switch (action)
{
case ACTION_CHAT:
Chat(wszSessionName, pcsm, pcai, bAnonymous);
break;
case ACTION_DELETE_SESSION:
Delete(wszSessionName, pcsm);
break;
case ACTION_LIST_SESSION_NAMES:
List(pcsm);
break;
default:
Usage();
}
// release session manager
pcsm->Release();
}
}
CoUninitialize();
return hr;
}
/////////////////////////////////////////////////////
//
// ChatSession.h
//
// Copyright 1997, Don Box/Addison Wesley
//
// This code accompanies the book "The Component
// Object Model" from Addison Wesley. Blah blah blah
//
//
#ifndef _CHATSESSION_H
#define _CHATSESSION_H
// this pragma shuts up the compiler warnings due to
// the pre MSC11SP1 debugger choking on long template names.
#pragma warning(disable:4786)
#define _WIN32_WINNT 0x403
#include
#include
#include
#include
using namespace std;
// bring in IDL-generated interface definitions
#include «..\include\COMChat.h»
// this class models a particular chat session
class ChatSession : public IChatSession
{
friend class StatementEnumerator;
LONG m_cRef;
CRITICAL_SECTION m_csStatementLock;
CRITICAL_SECTION m_csAdviseLock;
OLECHAR m_wszSessionName[1024];
bool m_bIsDeleted;
bool m_bAllowAnonymousAccess;
vector
struct LISTENER
{
LISTENER *pPrev;
LISTENER *pNext;
OLECHAR *pwszUser;