Дональд Бокс - Сущность технологии СОМ. Библиотека программиста
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/////////////////////////////////////////////////////
//
// 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 <windows.h>
#include <map>
#include <vector>
#include <string>
using namespace std;
// bring in IDL-generated interface definitions
#include «..includeCOMChat.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<wstring> m_statements;
struct LISTENER
{
LISTENER *pPrev;
LISTENER *pNext;
OLECHAR *pwszUser;
IChatSessionEvents *pItf;
};
LISTENER *m_pHeadListeners;
void SLock(void);
void SUnlock(void);
void ALock(void);
void AUnlock(void);
bool CheckAccess(const OLECHAR *pwszUser);
protected:
virtual ~ChatSession(void);
void Fire_OnNewStatement(const OLECHAR *pwszUser,
const OLECHAR *pwszStatement);
void Fire_OnNewUser(const OLECHAR *pwszUser);
void Fire_OnUserLeft(const OLECHAR *pwszUser);
public:
ChatSession(const OLECHAR *pwszSessionName,
bool bAllowAnonymousAccess);
void Disconnect(void);
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IChatSession methods
STDMETHODIMP get_SessionName(OLECHAR **ppwsz);
STDMETHODIMP Say(const OLECHAR *pwszStatement);
STDMETHODIMP GetStatements(IEnumString **ppes);
STDMETHODIMP Advise(IChatSessionEvents *pEventSink,
DWORD *pdwReg);
STDMETHODIMP Unadvise(DWORD dwReg);
};
// this class enumerates the statements of a session
class StatementEnumerator : public IEnumString
{
LONG m_cRef;
ChatSession *m_pThis;
vector<wstring>::iterator m_cursor;
CRITICAL_SECTION m_csLock;
protected:
void Lock(void);
void Unlock(void);
virtual ~StatementEnumerator(void);
public:
StatementEnumerator(ChatSession *pThis);
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IEnumString methods
STDMETHODIMP Next(ULONG cElems, OLECHAR **rgElems,
ULONG *pcFetched);
STDMETHODIMP Skip(ULONG cElems);
STDMETHODIMP Reset(void);
STDMETHODIMP Clone(IEnumString **ppes);
};
// this class models the management of chat sessions
// and acts as the class object for CLSID_ChatSession
class ChatSessionClass : public IChatSessionManager,
public IExternalConnection
{
friend class SessionNamesEnumerator;
typedef map<wstring, ChatSession *> SESSIONMAP;
LONG m_cStrongLocks;
SESSIONMAP m_sessions;
CRITICAL_SECTION m_csSessionLock;
void Lock(void);
void Unlock(void);
bool CheckAccess(const OLECHAR *pwszUser);
public:
virtual ~ChatSessionClass(void);
ChatSessionClass(void);
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IExternalConnection methods
STDMETHODIMP_(DWORD) AddConnection(DWORD extconn, DWORD);
STDMETHODIMP_(DWORD) ReleaseConnection(DWORD extconn, DWORD,
BOOL bLastReleaseKillsStub);
// IChatSessionManager methods
STDMETHODIMP GetSessionNames(IEnumString **ppes);
STDMETHODIMP FindSession(const OLECHAR *pwszSessionName,
BOOL bDontCreate,
BOOL bAllowAnonymousAccess,
IChatSession **ppcs);
STDMETHODIMP DeleteSession(const OLECHAR *pwszSessionName);
};
// this class enumerates the session names of a server
class SessionNamesEnumerator : public IEnumString
{
LONG m_cRef;
vector<wstring> *m_pStrings;
SessionNamesEnumerator *m_pCloneSource;
vector<wstring>::iterator m_cursor;
CRITICAL_SECTION m_csLock;
protected:
vector<wstring>& Strings(void);
void Lock(void);
void Unlock(void);
virtual ~SessionNamesEnumerator(void);
public:
SessionNamesEnumerator(ChatSessionClass *pSessionClass);
SessionNamesEnumerator(SessionNamesEnumerator *pCloneSource);
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IEnumString methods
STDMETHODIMP Next(ULONG cElems, OLECHAR **rgElems,
ULONG *pcFetched);
STDMETHODIMP Skip(ULONG cElems);
STDMETHODIMP Reset(void);
STDMETHODIMP Clone(IEnumString **ppes);
};
#endif
ChatSession.cpp/////////////////////////////////////////////////////
//
// ChatSession.cpp
//
// Copyright 1997, Don Box/Addison Wesley
//
// This code accompanies the book "The Component
// Object Model" from Addison Wesley. Blah blah blah
//
//
#include «ChatSession.h»
#include <iaccess.h>
// these routines are defined in svc.cpp to
// control server lifetime
extern void ModuleLock(void);
extern void ModuleUnlock(void);
// these access control objects are created
// in svc.cpp to control various privileged
// operations. Most operations in this class
// are non-privileged, so anyone can get in.
extern IAccessControl *g_pacUsers;
extern IAccessControl *g_pacAdmins;
// utility functions /////////////////////////
// duplicate an OLECHAR * using CoTaskMemAlloc
OLECHAR *OLESTRDUP(const OLECHAR *pwsz)
{
DWORD cb = sizeof(OLECHAR)*(wcslen(pwsz) + 1);
OLECHAR *pwszResult = (OLECHAR*)CoTaskMemAlloc(cb);
if (pwszResult)
wcscpy(pwszResult, pwsz);
return pwszResult;
}
// get the caller's username (or «anonymous» if
// no authentication was specified by the caller).
OLECHAR *GetCaller(void)
{
OLECHAR *pwsz = 0;
HRESULT hr = CoQueryClientBlanket(0,0,0,0,0,(void**)&pwsz,0);
if (SUCCEEDED(hr))
return OLESTRDUP(pwsz);
else
return OLESTRDUP(OLESTR(«anonymous»));
}
// class ChatSession ///////////////////////////////
ChatSession::ChatSession(const OLECHAR *pwszSessionName,
bool bAllowAnonymousAccess)
: m_cRef(0),
m_bAllowAnonymousAccess(bAllowAnonymousAccess),
m_pHeadListeners(0)
{
wcscpy(m_wszSessionName, pwszSessionName);
InitializeCriticalSection(&m_csStatementLock);
InitializeCriticalSection(&m_csAdviseLock);
}
ChatSession::~ChatSession(void)
{
DeleteCriticalSection(&m_csStatementLock);
DeleteCriticalSection(&m_csAdviseLock);
// tear down connected listeners
while (m_pHeadListeners)
{
LISTENER *pThisNode = m_pHeadListeners;
if (pThisNode->pItf)
pThisNode->pItf->Release();
if (pThisNode->pwszUser)
CoTaskMemFree(pThisNode->pwszUser);
m_pHeadListeners = pThisNode->pNext;
delete pThisNode;
}
}
// helper methods ///////////
void ChatSession::Disconnect(void)
{
CoDisconnectObject(this, 0);
// tear down connected listeners
ALock();
while (m_pHeadListeners)
{
LISTENER *pThisNode = m_pHeadListeners;
if (pThisNode->pItf)
pThisNode->pItf->Release();
if (pThisNode->pwszUser)
CoTaskMemFree(pThisNode->pwszUser);
m_pHeadListeners = pThisNode->pNext;
delete pThisNode;
}
AUnlock();
}
// send the OnNewStatement event to all listeners
void
ChatSession::Fire_OnNewStatement(const OLECHAR *pwszUser,
const OLECHAR *pwszStatement)
{
ALock();
for (LISTENER *pNode = m_pHeadListeners;
pNode != 0; pNode = pNode->pNext)
{
if (pNode->pItf)
pNode->pItf->OnNewStatement(pwszUser, pwszStatement);
}
AUnlock();
}
// send the OnNewUser event to all listeners
void
ChatSession::Fire_OnNewUser(const OLECHAR *pwszUser)
{
ALock();
for (LISTENER *pNode = m_pHeadListeners;
pNode != 0; pNode = pNode->pNext)
{
if (pNode->pItf)
pNode->pItf->OnNewUser(pwszUser);
}
AUnlock();
}
// send the OnUserLeft event to all listeners
void
ChatSession::Fire_OnUserLeft(const OLECHAR *pwszUser)
{
ALock();
for (LISTENER *pNode = m_pHeadListeners;
pNode != 0; pNode = pNode->pNext)
{
if (pNode->pItf)
pNode->pItf->OnUserLeft(pwszUser);
}
AUnlock();
}
// lock wrappers
void ChatSession::SLock(void)
{
EnterCriticalSection(&m_csStatementLock);
}
void ChatSession::SUnlock(void)
{
LeaveCriticalSection(&m_csStatementLock);
}
void ChatSession::ALock(void)
{
EnterCriticalSection(&m_csAdviseLock);
}
void ChatSession::AUnlock(void)
{
LeaveCriticalSection(&m_csAdviseLock);
}
// helper method to check access to Say method
bool
ChatSession::CheckAccess(const OLECHAR *pwszUser)
{
if (wcscmp(pwszUser, L"anonymous") == 0)
return m_bAllowAnonymousAccess;
// form trustee from caller and use Access Control
// object hardwired to COMChat Users group
TRUSTEEW trustee = {
0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME,
TRUSTEE_IS_USER,
const_cast<OLECHAR*>(pwszUser)
};
BOOL bIsAllowed;
HRESULT hr = g_pacUsers->IsAccessAllowed(&trustee,0,
COM_RIGHTS_EXECUTE,
&bIsAllowed);
return SUCCEEDED(hr) && bIsAllowed != FALSE;
}
// IUnknown methods
STDMETHODIMP
ChatSession::QueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IUnknown)
*ppv = static_cast<IChatSession*>(this);
else if (riid == IID_IChatSession)
*ppv = static_cast<IChatSession*>(this);
else
return (*ppv = 0), E_NOINTERFACE;
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG)
ChatSession::AddRef(void)
{
ModuleLock();
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG)
ChatSession::Release(void)
{
LONG res = InterlockedDecrement(&m_cRef);
if (res == 0)
delete this;
ModuleUnlock();
return res;
}
// IChatSession methods
STDMETHODIMP
ChatSession::get_SessionName(OLECHAR **ppwsz)