Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3611 lines
84 KiB

// This is a part of the Active Template Library.
// Copyright (C) 1996-2001 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.
#ifndef __ATLUTIL_H__
#define __ATLUTIL_H__
#pragma once
#include <stdio.h>
#include <string.h>
#include <crtdbg.h>
#include <stdlib.h>
#include <mbstring.h>
#include <atldef.h>
#include <imagehlp.h>
#include <atlbase.h>
#include <atlstr.h>
#include <atlcoll.h>
#include <atlsiface.h>
#include <atlenc.h>
#include <atlcom.h>
#ifndef _ATL_NO_DEFAULT_LIBS
#pragma comment(lib, "imagehlp.lib")
#endif // !_ATL_NO_DEFAULT_LIBS
#pragma warning( push )
#pragma warning( disable: 4127 )
namespace ATL {
inline BOOL IsFullPath(LPCTSTR szPath)
{
size_t nLen = _tcslen(szPath);
if (nLen <= 1)
return FALSE;
if (*szPath == _T('"'))
{
szPath++;
}
if (szPath[1]==_T(':')) // drive: case
return TRUE;
if (nLen > 2 && szPath[0]==_T('\\') &&
szPath[1]==_T('\\')) // unc path name
return TRUE;
return FALSE;
}
inline BOOL IsFullPathA(LPCSTR szPath)
{
DWORD nLen = (DWORD) strlen(szPath);
if (nLen <= 1)
return FALSE;
if (*szPath == '"')
{
szPath++;
}
if (szPath[1]==':') // drive: case
return TRUE;
if (nLen > 2 && szPath[0]=='\\' &&
szPath[1]=='\\') // unc path name
return TRUE;
return FALSE;
}
#if(_WIN32_WINNT >= 0x0400)
// Helper class for reverting the thread impersonation token
// and then restoring it back to what it was
class CRevertThreadToken
{
public:
HANDLE m_hThreadToken;
CRevertThreadToken()
{
m_hThreadToken = INVALID_HANDLE_VALUE;
}
~CRevertThreadToken()
{
Restore();
}
// When called, this function
// makes a copy of the thread's impersonation token
// and then calls RevertToSelf() to revert the impersonation
// level to the process
// call Restore() to restore the impersonation
// token
// Restore is automatically called by the destructor
BOOL Initialize()
{
if (OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE, FALSE, &m_hThreadToken))
{
if (!RevertToSelf())
{
CloseHandle(m_hThreadToken);
m_hThreadToken = INVALID_HANDLE_VALUE;
return FALSE;
}
return TRUE;
}
return FALSE;
}
void Restore()
{
if (m_hThreadToken != INVALID_HANDLE_VALUE)
{
SetThreadToken(NULL, m_hThreadToken);
CloseHandle(m_hThreadToken);
m_hThreadToken = INVALID_HANDLE_VALUE;
}
}
};
#else
// Dummy version for downlevel support
class CRevertThreadToken
{
public:
BOOL Initialize()
{
return FALSE;
}
void Restore()
{
}
};
#endif // _WIN32_WINNT >= 0x0400)
#ifndef ATL_ISAPI_BUFFER_SIZE
#define ATL_ISAPI_BUFFER_SIZE 4096
#endif
//typedefs and defines for CUrl (essentially the same as the ones from wininet, but with an ATL_ prepended)
typedef WORD ATL_URL_PORT;
typedef enum {
ATL_URL_SCHEME_UNKNOWN = -1,
ATL_URL_SCHEME_FTP = 0,
ATL_URL_SCHEME_GOPHER = 1,
ATL_URL_SCHEME_HTTP = 2,
ATL_URL_SCHEME_HTTPS = 3,
ATL_URL_SCHEME_FILE = 4,
ATL_URL_SCHEME_NEWS = 5,
ATL_URL_SCHEME_MAILTO = 6,
ATL_URL_SCHEME_SOCKS = 7,
} ATL_URL_SCHEME;
#define ATL_URL_MAX_HOST_NAME_LENGTH 256
#define ATL_URL_MAX_USER_NAME_LENGTH 128
#define ATL_URL_MAX_PASSWORD_LENGTH 128
#define ATL_URL_MAX_PORT_NUMBER_LENGTH 5 // ATL_URL_PORT is unsigned short
#define ATL_URL_MAX_PORT_NUMBER_VALUE 65535 // maximum unsigned short value
#define ATL_URL_MAX_PATH_LENGTH 2048
#define ATL_URL_MAX_SCHEME_LENGTH 32 // longest protocol name length
#define ATL_URL_MAX_URL_LENGTH (ATL_URL_MAX_SCHEME_LENGTH \
+ sizeof("://") \
+ ATL_URL_MAX_PATH_LENGTH)
#define ATL_URL_INVALID_PORT_NUMBER 0 // use the protocol-specific default
#define ATL_URL_DEFAULT_FTP_PORT 21 // default for FTP servers
#define ATL_URL_DEFAULT_GOPHER_PORT 70 // " " gopher "
#define ATL_URL_DEFAULT_HTTP_PORT 80 // " " HTTP "
#define ATL_URL_DEFAULT_HTTPS_PORT 443 // " " HTTPS "
#define ATL_URL_DEFAULT_SOCKS_PORT 1080 // default for SOCKS firewall servers.
template <DWORD dwSizeT=ATL_ISAPI_BUFFER_SIZE>
class CAtlIsapiBuffer
{
protected:
char m_szBuffer[dwSizeT];
LPSTR m_pBuffer;
DWORD m_dwLen;
DWORD m_dwAlloc;
HANDLE m_hProcHeap;
public:
CAtlIsapiBuffer() throw()
{
if (dwSizeT > 0)
m_szBuffer[0] = 0;
m_pBuffer = m_szBuffer;
m_dwLen = 0;
m_dwAlloc = dwSizeT;
m_hProcHeap = GetProcessHeap();
}
CAtlIsapiBuffer(LPCSTR sz)
{
m_pBuffer = m_szBuffer;
m_dwLen = 0;
m_dwAlloc = dwSizeT;
m_hProcHeap = GetProcessHeap();
if (!Append(sz))
AtlThrow(E_OUTOFMEMORY);
}
~CAtlIsapiBuffer() throw()
{
Free();
}
BOOL Alloc(DWORD dwSize) throw()
{
if (m_dwAlloc >= dwSize)
{
return TRUE;
}
if (m_pBuffer != m_szBuffer)
{
HeapFree(m_hProcHeap, 0, m_pBuffer);
m_dwLen = 0;
m_dwAlloc = 0;
}
m_pBuffer = (LPSTR)HeapAlloc(m_hProcHeap, 0, dwSize);
if (m_pBuffer)
{
m_dwAlloc = dwSize;
return TRUE;
}
return FALSE;
}
BOOL ReAlloc(DWORD dwNewSize) throw()
{
if (dwNewSize <= m_dwAlloc)
return TRUE;
if (m_pBuffer == m_szBuffer)
{
BOOL bRet = Alloc(dwNewSize);
if (bRet)
memcpy(m_pBuffer, m_szBuffer, m_dwLen);
return bRet;
}
LPSTR pvNew = (LPSTR )HeapReAlloc(m_hProcHeap, 0, m_pBuffer, dwNewSize);
if (pvNew)
{
m_pBuffer = pvNew;
m_dwAlloc = dwNewSize;
return TRUE;
}
return FALSE;
}
void Free() throw()
{
if (m_pBuffer != m_szBuffer)
{
HeapFree(m_hProcHeap,0 , m_pBuffer);
m_dwAlloc = dwSizeT;
m_pBuffer = m_szBuffer;
}
Empty();
}
void Empty() throw()
{
if (m_pBuffer)
{
m_pBuffer[0]=0;
m_dwLen = 0;
}
}
DWORD GetLength() throw()
{
return m_dwLen;
}
BOOL Append(LPCSTR sz, int nLen = -1) throw()
{
if (nLen == -1)
nLen = (int) strlen(sz);
if (m_dwLen + nLen + 1 > m_dwAlloc)
{
if (!ReAlloc(m_dwAlloc + (nLen+1 > ATL_ISAPI_BUFFER_SIZE ? nLen+1 : ATL_ISAPI_BUFFER_SIZE)))
return FALSE;
}
memcpy(m_pBuffer + m_dwLen, sz, nLen);
m_dwLen += nLen;
m_pBuffer[m_dwLen]=0;
return TRUE;
}
operator LPCSTR() throw()
{
return m_pBuffer;
}
CAtlIsapiBuffer& operator+=(LPCSTR sz)
{
if (!Append(sz))
AtlThrow(E_OUTOFMEMORY);
return *this;
}
}; // class CAtlIsapiBuffer
__interface IStackDumpHandler
{
public:
void __stdcall OnBegin();
void __stdcall OnEntry(void *pvAddress, LPCSTR szModule, LPCSTR szSymbol);
void __stdcall OnError(LPCSTR szError);
void __stdcall OnEnd();
};
#define ATL_MODULE_NAME_LEN _MAX_PATH
#define ATL_SYMBOL_NAME_LEN 1024
// Helper class for generating a stack dump
// This is used internally by AtlDumpStack
class CStackDumper
{
public:
struct _ATL_SYMBOL_INFO
{
ULONG_PTR dwAddress;
ULONG_PTR dwOffset;
CHAR szModule[ATL_MODULE_NAME_LEN];
CHAR szSymbol[ATL_SYMBOL_NAME_LEN];
};
static LPVOID __stdcall FunctionTableAccess(HANDLE hProcess, ULONG_PTR dwPCAddress)
{
#ifdef _WIN64
return SymFunctionTableAccess(hProcess, dwPCAddress);
#else
return SymFunctionTableAccess(hProcess, (ULONG)dwPCAddress);
#endif
}
static ULONG_PTR __stdcall GetModuleBase(HANDLE hProcess, ULONG_PTR dwReturnAddress)
{
IMAGEHLP_MODULE moduleInfo;
#ifdef _WIN64
if (SymGetModuleInfo(hProcess, dwReturnAddress, &moduleInfo))
#else
if (SymGetModuleInfo(hProcess, (ULONG)dwReturnAddress, &moduleInfo))
#endif
return moduleInfo.BaseOfImage;
else
{
MEMORY_BASIC_INFORMATION memoryBasicInfo;
if (::VirtualQueryEx(hProcess, (LPVOID) dwReturnAddress,
&memoryBasicInfo, sizeof(memoryBasicInfo)))
{
DWORD cch = 0;
char szFile[MAX_PATH] = { 0 };
cch = GetModuleFileNameA((HINSTANCE)memoryBasicInfo.AllocationBase,
szFile, MAX_PATH);
// Ignore the return code since we can't do anything with it.
SymLoadModule(hProcess,
NULL, ((cch) ? szFile : NULL),
#ifdef _WIN64
NULL, (DWORD_PTR) memoryBasicInfo.AllocationBase, 0);
#else
NULL, (DWORD)(DWORD_PTR)memoryBasicInfo.AllocationBase, 0);
#endif
return (DWORD_PTR) memoryBasicInfo.AllocationBase;
}
}
return 0;
}
static BOOL ResolveSymbol(HANDLE hProcess, UINT_PTR dwAddress,
_ATL_SYMBOL_INFO &siSymbol)
{
BOOL fRetval = TRUE;
siSymbol.dwAddress = dwAddress;
CHAR szUndec[ATL_SYMBOL_NAME_LEN];
CHAR szWithOffset[ATL_SYMBOL_NAME_LEN];
LPSTR pszSymbol = NULL;
IMAGEHLP_MODULE mi;
memset(&siSymbol, 0, sizeof(_ATL_SYMBOL_INFO));
mi.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
#ifdef _WIN64
if (!SymGetModuleInfo(hProcess, dwAddress, &mi))
#else
if (!SymGetModuleInfo(hProcess, (UINT)dwAddress, &mi))
#endif
lstrcpyA(siSymbol.szModule, "<no module>");
else
{
LPSTR pszModule = strchr(mi.ImageName, '\\');
if (pszModule == NULL)
pszModule = mi.ImageName;
else
pszModule++;
lstrcpynA(siSymbol.szModule, pszModule, sizeof(siSymbol.szModule)/sizeof(siSymbol.szModule[0]));
}
__try
{
union
{
CHAR rgchSymbol[sizeof(IMAGEHLP_SYMBOL) + ATL_SYMBOL_NAME_LEN];
IMAGEHLP_SYMBOL sym;
} sym;
memset(&sym.sym, 0x00, sizeof(sym.sym));
sym.sym.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
#ifdef _WIN64
sym.sym.Address = dwAddress;
#else
sym.sym.Address = (DWORD)dwAddress;
#endif
sym.sym.MaxNameLength = ATL_SYMBOL_NAME_LEN;
#ifdef _WIN64
if (SymGetSymFromAddr(hProcess, dwAddress, &(siSymbol.dwOffset), &sym.sym))
#else
if (SymGetSymFromAddr(hProcess, (DWORD)dwAddress, &(siSymbol.dwOffset), &sym.sym))
#endif
{
pszSymbol = sym.sym.Name;
if (UnDecorateSymbolName(sym.sym.Name, szUndec, sizeof(szUndec)/sizeof(szUndec[0]),
UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS))
{
pszSymbol = szUndec;
}
else if (SymUnDName(&sym.sym, szUndec, sizeof(szUndec)/sizeof(szUndec[0])))
{
pszSymbol = szUndec;
}
if (siSymbol.dwOffset != 0)
{
wsprintfA(szWithOffset, "%s + %d bytes", pszSymbol, siSymbol.dwOffset);
pszSymbol = szWithOffset;
}
}
else
pszSymbol = "<no symbol>";
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
pszSymbol = "<EX: no symbol>";
siSymbol.dwOffset = dwAddress - mi.BaseOfImage;
}
lstrcpynA(siSymbol.szSymbol, pszSymbol, sizeof(siSymbol.szSymbol)/sizeof(siSymbol.szSymbol[0]));
return fRetval;
}
};
// Helper function to produce a stack dump
ATL_NOINLINE inline void AtlDumpStack(IStackDumpHandler *pHandler)
{
ATLASSERT(pHandler);
pHandler->OnBegin();
CAtlArray<void *> adwAddress;
HANDLE hProcess = ::GetCurrentProcess();
if (SymInitialize(hProcess, NULL, FALSE))
{
// force undecorated names to get params
DWORD dw = SymGetOptions();
dw &= ~SYMOPT_UNDNAME;
SymSetOptions(dw);
HANDLE hThread = ::GetCurrentThread();
CONTEXT threadContext;
threadContext.ContextFlags = CONTEXT_FULL;
if (::GetThreadContext(hThread, &threadContext))
{
//DumpContext(&threadContext);
STACKFRAME stackFrame;
memset(&stackFrame, 0, sizeof(stackFrame));
stackFrame.AddrPC.Mode = AddrModeFlat;
DWORD dwMachType;
#if defined(_M_IX86)
dwMachType = IMAGE_FILE_MACHINE_I386;
// program counter, stack pointer, and frame pointer
stackFrame.AddrPC.Offset = threadContext.Eip;
stackFrame.AddrStack.Offset = threadContext.Esp;
stackFrame.AddrStack.Mode = AddrModeFlat;
stackFrame.AddrFrame.Offset = threadContext.Ebp;
stackFrame.AddrFrame.Mode = AddrModeFlat;
#elif defined(_M_AMD64)
// only program counter
dwMachType = IMAGE_FILE_MACHINE_AMD64;
stackFrame.AddrPC.Offset = threadContext.Rip;
#elif defined(_M_IA64)
//IA64: What do we need to do here?
dwMachType = IMAGE_FILE_MACHINE_IA64;
#if 0
stackFrame.AddrPC.Offset = threadContext.StIIP;
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrStack.Offset = threadContext.IntSp;
stackFrame.AddrStack.Mode = AddrModeFlat;
stackFrame.AddrFrame.Offset = threadContext.IntSp;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrBStore.Offset = threadContext.RsBSP;
stackFrame.AddrBStore.Mode = AddrModeFlat;
stackFrame.AddrReturn.Offset = threadContext.BrRp;
stackFrame.AddrReturn.Mode = AddrModeFlat;
#endif
#else
#error("Unknown Target Machine");
#endif
adwAddress.SetCount(0, 16);
int nFrame;
for (nFrame = 0; nFrame < 5; nFrame++)
{
if (!StackWalk(dwMachType, hProcess, hProcess,
&stackFrame, &threadContext, NULL,
CStackDumper::FunctionTableAccess, CStackDumper::GetModuleBase, NULL))
{
break;
}
adwAddress.SetAtGrow(nFrame, (void*)(DWORD_PTR)stackFrame.AddrPC.Offset);
}
}
}
else
{
DWORD dw = GetLastError();
char sz[100];
wsprintfA(sz,
"AtlDumpStack Error: IMAGEHLP.DLL wasn't found. "
"GetLastError() returned 0x%8.8X\r\n", dw);
pHandler->OnError(sz);
}
// dump it out now
INT_PTR nAddress;
INT_PTR cAddresses = adwAddress.GetCount();
for (nAddress = 0; nAddress < cAddresses; nAddress++)
{
CStackDumper::_ATL_SYMBOL_INFO info;
UINT_PTR dwAddress = (UINT_PTR)adwAddress[nAddress];
LPCSTR szModule = NULL;
LPCSTR szSymbol = NULL;
if (CStackDumper::ResolveSymbol(hProcess, dwAddress, info))
{
szModule = info.szModule;
szSymbol = info.szSymbol;
}
pHandler->OnEntry((void *) dwAddress, szModule, szSymbol);
}
pHandler->OnEnd();
}
#define STACK_TRACE_PART_DELIMITER ';'
#define STACK_TRACE_LINE_DELIMITER '~'
// CReportHookDumpHandler is a stack dump handler
// that gathers the stack dump into the format
// used by CDebugReportHook
class CReportHookDumpHandler : public IStackDumpHandler
{
public:
CReportHookDumpHandler()
{
m_pstr = NULL;
}
void GetStackDump(CStringA *pstr)
{
ATLASSERT(pstr);
SetString(pstr);
AtlDumpStack(this);
SetString(NULL);
}
void SetString(CStringA *pstr)
{
m_pstr = pstr;
}
// implementation
// IStackDumpHandler methods
void __stdcall OnBegin()
{
}
void __stdcall OnEntry(void *pvAddress, LPCSTR szModule, LPCSTR szSymbol)
{
// make sure SetString was called before
// trying to get a stack dump
ATLASSERT(m_pstr);
if (!m_pstr)
return;
char szBuf[100];
sprintf(szBuf, "0x%p;", pvAddress);
*m_pstr += szBuf;
if (!szModule)
szModule = "Unknown";
if (!szSymbol)
szSymbol = "<No Info>";
*m_pstr += szModule;
*m_pstr += STACK_TRACE_PART_DELIMITER;
ATLASSERT(szSymbol);
*m_pstr += szSymbol;
*m_pstr += STACK_TRACE_PART_DELIMITER;
*m_pstr += STACK_TRACE_LINE_DELIMITER;
}
void __stdcall OnError(LPCSTR /*szError*/)
{
}
void __stdcall OnEnd()
{
}
protected:
CStringA *m_pstr;
};
#define PIPE_INPUT_BUFFER_SIZE 4096
#define PIPE_OUTPUT_BUFFER_SIZE 2048
enum { DEBUG_SERVER_MESSAGE_TRACE, DEBUG_SERVER_MESSAGE_ASSERT, DEBUG_SERVER_MESSAGE_QUIT };
struct DEBUG_SERVER_MESSAGE
{
DWORD dwType; // one of DEBUG_SERVER_MESSAGE_*
DWORD dwProcessId; // process id of client
DWORD dwClientNameLen; // length of client name
size_t dwTextLen; // length of text message including null terminator
BOOL bIsDebuggerAttached; // TRUE if the debugger is already attached
};
#ifdef _DEBUG
extern "C" WINBASEAPI
BOOL
WINAPI
IsDebuggerPresent(
VOID
);
class CDebugReportHook
{
protected:
_CRT_REPORT_HOOK m_pfnOldHook;
static char m_szPipeName[MAX_PATH+1];
static DWORD m_dwTimeout;
static DWORD m_dwClientNameLen;
static char m_szClientName[MAX_COMPUTERNAME_LENGTH+1];
public:
CDebugReportHook(LPCSTR szMachineName = ".", LPCSTR szPipeName = "AtlsDbgPipe", DWORD dwTimeout = 20000) throw()
{
if (SetPipeName(szMachineName, szPipeName))
{
SetTimeout(dwTimeout);
SetHook();
}
m_dwClientNameLen = sizeof(m_szClientName);
GetComputerNameA(m_szClientName, &m_dwClientNameLen);
}
~CDebugReportHook() throw()
{
RemoveHook();
}
BOOL SetPipeName(LPCSTR szMachineName = ".", LPCSTR szPipeName = "AtlsDbgPipe") throw()
{
size_t nLen1 = strlen(szMachineName);
size_t nLen2 = strlen(szPipeName);
if (nLen1 + nLen2 + 8 <= MAX_PATH)
{
_snprintf(m_szPipeName, MAX_PATH, "\\\\%s\\pipe\\%s", szMachineName, szPipeName);
return TRUE;
}
return FALSE;
}
void SetTimeout(DWORD dwTimeout)
{
m_dwTimeout = dwTimeout;
}
void SetHook() throw()
{
m_pfnOldHook = _CrtSetReportHook(CDebugReportHookProc);
}
void RemoveHook() throw()
{
_CrtSetReportHook(m_pfnOldHook);
}
static int __cdecl CDebugReportHookProc(int reportType, char *message, int *returnValue) throw()
{
DWORD dwWritten;
*returnValue = 0;
CRevertThreadToken revert;
revert.Initialize();
CHandle hdlPipe;
while (1)
{
HANDLE hPipe = CreateFileA(m_szPipeName, GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hPipe != INVALID_HANDLE_VALUE )
{
hdlPipe.Attach(hPipe);
break;
}
if (GetLastError() != ERROR_PIPE_BUSY)
{
if (reportType == _CRT_ASSERT)
return TRUE;
return FALSE;
}
//If the pipe is busy, we wait for up to m_dwTimeout
if (!WaitNamedPipeA(m_szPipeName, m_dwTimeout))
{
if (reportType == _CRT_ASSERT)
return TRUE;
return FALSE;
}
}
DEBUG_SERVER_MESSAGE Message;
Message.bIsDebuggerAttached = IsDebuggerPresent();
if (reportType == _CRT_ASSERT)
{
Message.dwType = DEBUG_SERVER_MESSAGE_ASSERT;
}
else
{
Message.dwType = DEBUG_SERVER_MESSAGE_TRACE;
}
Message.dwProcessId = GetCurrentProcessId();
Message.dwClientNameLen = m_dwClientNameLen+1; // add 1 for the null terminator
Message.dwTextLen = strlen(message)+1;
int nRet = 1;
WriteFile(hdlPipe, &Message, sizeof(DEBUG_SERVER_MESSAGE), &dwWritten, NULL);
WriteFile(hdlPipe, m_szClientName, Message.dwClientNameLen, &dwWritten, NULL);
WriteFile(hdlPipe, message, (DWORD)Message.dwTextLen, &dwWritten, NULL);
//Check to see whether or not to send stack trace
BOOL bRet = ReadFile(hdlPipe, &nRet, sizeof(nRet), &dwWritten, NULL);
//if nRet == 1, the user wants stack trace info
if (bRet && nRet)
{
_ATLTRY
{
CStringA str;
CReportHookDumpHandler stackDumper;
stackDumper.GetStackDump(&str);
if (!WriteFile(hdlPipe, (LPCSTR)str, str.GetLength(), &dwWritten, NULL))
return (reportType == _CRT_ASSERT ? TRUE : FALSE);
}
_ATLCATCHALL()
{
return (reportType == _CRT_ASSERT ? TRUE : FALSE);
}
}
if (bRet)
bRet = ReadFile(hdlPipe, &nRet, sizeof(nRet), &dwWritten, NULL);
if (!bRet)
nRet = 0;
revert.Restore();
// possible return values
// 0 -> Ignore or cancel
// 1 -> Retry
// 2 -> Abort
if (nRet == 0)
{
return (reportType == _CRT_ASSERT ? TRUE : FALSE);
}
if (nRet == 1)
{
if (IsDebuggerPresent())
{
DebugBreak();
}
}
if (nRet == 2)
abort();
return (reportType == _CRT_ASSERT ? TRUE : FALSE);
}
}; // class CDebugReportHook
__declspec(selectany) char CDebugReportHook::m_szPipeName[MAX_PATH+1];
__declspec(selectany) DWORD CDebugReportHook::m_dwTimeout;
__declspec(selectany) DWORD CDebugReportHook::m_dwClientNameLen;
__declspec(selectany) char CDebugReportHook::m_szClientName[MAX_COMPUTERNAME_LENGTH+1];
#endif
#ifndef ATL_POOL_NUM_THREADS
#define ATL_POOL_NUM_THREADS 0
#endif
#ifndef ATL_POOL_STACK_SIZE
#define ATL_POOL_STACK_SIZE 0
#endif
#ifndef ATLS_DEFAULT_THREADSPERPROC
#define ATLS_DEFAULT_THREADSPERPROC 2
#endif
#ifndef ATLS_DEFAULT_THREADPOOLSHUTDOWNTIMEOUT
#define ATLS_DEFAULT_THREADPOOLSHUTDOWNTIMEOUT 36000
#endif
//
// CThreadPool
// This class is a simple IO completion port based thread pool
// Worker:
// is a class that is responsible for handling requests
// queued on the thread pool.
// It must have a typedef for RequestType, where request type
// is the datatype to be queued on the pool
// RequestType must be castable to (DWORD)
// The value -1 is reserved for shutdown
// of the pool
// Worker must also have a void Execute(RequestType request, void *pvParam, OVERLAPPED *pOverlapped) function
// ThreadTraits:
// is a class that implements a static CreateThread function
// This allows for overriding how the threads are created
#define ATLS_POOL_SHUTDOWN ((OVERLAPPED*) ((__int64) -1))
template <class Worker, class ThreadTraits=DefaultThreadTraits>
class CThreadPool : public IThreadPoolConfig
{
protected:
CSimpleMap<DWORD, HANDLE> m_threadMap;
DWORD m_dwThreadEventId;
CComCriticalSection m_critSec;
DWORD m_dwStackSize;
DWORD m_dwMaxWait;
void *m_pvWorkerParam;
LONG m_bShutdown;
HANDLE m_hThreadEvent;
HANDLE m_hRequestQueue;
public:
CThreadPool() throw() :
m_hRequestQueue(NULL),
m_pvWorkerParam(NULL),
m_dwMaxWait(ATLS_DEFAULT_THREADPOOLSHUTDOWNTIMEOUT),
m_bShutdown(FALSE),
m_dwThreadEventId(0),
m_dwStackSize(0)
{
}
~CThreadPool() throw()
{
Shutdown();
}
// Initialize the thread pool
// if nNumThreads > 0, then it specifies the number of threads
// if nNumThreads < 0, then it specifies the number of threads per proc (-)
// if nNumThreads == 0, then it defaults to two threads per proc
// hCompletion is a handle of a file to associate with the completion port
// pvWorkerParam is a parameter that will be passed to Worker::Execute
// dwStackSize:
// The stack size to use when creating the threads
HRESULT Initialize(void *pvWorkerParam=NULL, int nNumThreads=0, DWORD dwStackSize=0, HANDLE hCompletion=INVALID_HANDLE_VALUE) throw()
{
ATLASSERT( m_hRequestQueue == NULL );
if (m_hRequestQueue) // Already initialized
return AtlHresultFromWin32(ERROR_ALREADY_INITIALIZED);
if (S_OK != m_critSec.Init())
return E_FAIL;
m_hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!m_hThreadEvent)
{
m_critSec.Term();
return AtlHresultFromLastError();
}
// Create IO completion port to queue the requests
m_hRequestQueue = CreateIoCompletionPort(hCompletion, NULL, 0, nNumThreads);
if (m_hRequestQueue == NULL)
{
// failed creating the Io completion port
m_critSec.Term();
CloseHandle(m_hThreadEvent);
return AtlHresultFromLastError();
}
m_pvWorkerParam = pvWorkerParam;
m_dwStackSize = dwStackSize;
HRESULT hr = SetSize(nNumThreads);
if (hr != S_OK)
{
// Close the request queue handle
CloseHandle(m_hRequestQueue);
// Clear the queue handle
m_hRequestQueue = NULL;
// Uninitialize the critical sections
m_critSec.Term();
CloseHandle(m_hThreadEvent);
return hr;
}
return S_OK;
}
// Shutdown the thread pool
// This function posts the shutdown request to all the threads in the pool
// It will wait for the threads to shutdown a maximum of dwMaxWait MS.
// If the timeout expires it just returns without terminating the threads.
void Shutdown(DWORD dwMaxWait=0) throw()
{
if (!m_hRequestQueue) // Not initialized
return;
CComCritSecLock<CComCriticalSection> lock(m_critSec, false);
if (FAILED(lock.Lock()))
{
// out of memory
ATLASSERT( FALSE );
return;
}
if (dwMaxWait == 0)
dwMaxWait = m_dwMaxWait;
HRESULT hr = InternalResizePool(0, dwMaxWait);
if (hr != S_OK)
ATLTRACE(atlTraceUtil, 0, _T("Thread pool not shutting down cleanly : %08x"), hr);
// If the threads have not returned, then something is wrong
for (int i = m_threadMap.GetSize() - 1; i >= 0; i--)
{
HANDLE hThread = m_threadMap.GetValueAt(i);
DWORD dwExitCode;
GetExitCodeThread(hThread, &dwExitCode);
if (dwExitCode == STILL_ACTIVE)
{
ATLTRACE(atlTraceUtil, 0, _T("Terminating thread"));
TerminateThread(hThread, 0);
}
CloseHandle(hThread);
}
// Close the request queue handle
CloseHandle(m_hRequestQueue);
// Clear the queue handle
m_hRequestQueue = NULL;
ATLASSERT(m_threadMap.GetSize() == 0);
// Uninitialize the critical sections
lock.Unlock();
m_critSec.Term();
CloseHandle(m_hThreadEvent);
}
// IThreadPoolConfig methods
HRESULT STDMETHODCALLTYPE SetSize(int nNumThreads) throw()
{
if (nNumThreads == 0)
nNumThreads = -ATLS_DEFAULT_THREADSPERPROC;
if (nNumThreads < 0)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
nNumThreads = (int) (-nNumThreads) * si.dwNumberOfProcessors;
}
return InternalResizePool(nNumThreads, m_dwMaxWait);
}
HRESULT STDMETHODCALLTYPE GetSize(int *pnNumThreads) throw()
{
if (!pnNumThreads)
return E_POINTER;
*pnNumThreads = GetNumThreads();
return S_OK;
}
HRESULT STDMETHODCALLTYPE SetTimeout(DWORD dwMaxWait) throw()
{
m_dwMaxWait = dwMaxWait;
return S_OK;
}
HRESULT STDMETHODCALLTYPE GetTimeout(DWORD *pdwMaxWait) throw()
{
if (!pdwMaxWait)
return E_POINTER;
*pdwMaxWait = m_dwMaxWait;
return S_OK;
}
// IUnknown methods
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) throw()
{
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
InlineIsEqualGUID(riid, __uuidof(IThreadPoolConfig)))
{
*ppv = static_cast<IThreadPoolConfig*>(this);
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef() throw()
{
return 1;
}
ULONG STDMETHODCALLTYPE Release() throw()
{
return 1;
}
HANDLE GetQueueHandle() throw()
{
return m_hRequestQueue;
}
int GetNumThreads() throw()
{
return m_threadMap.GetSize();
}
// QueueRequest adds a request to the thread pool
// it will be picked up by one of the threads and dispatched to the worker
// in WorkerThreadProc
BOOL QueueRequest(Worker::RequestType request) throw()
{
if (!PostQueuedCompletionStatus(m_hRequestQueue, 0, (ULONG_PTR) request, NULL))
return FALSE;
return TRUE;
}
protected:
DWORD ThreadProc() throw()
{
DWORD dwBytesTransfered;
ULONG_PTR dwCompletionKey;
OVERLAPPED* pOverlapped;
// We instantiate an instance of the worker class on the stack
// for the life time of the thread.
Worker theWorker;
if (theWorker.Initialize(m_pvWorkerParam) == FALSE)
{
return 1;
}
SetEvent(m_hThreadEvent);
// Get the request from the IO completion port
while (GetQueuedCompletionStatus(m_hRequestQueue, &dwBytesTransfered, &dwCompletionKey, &pOverlapped, INFINITE))
{
if (pOverlapped == ATLS_POOL_SHUTDOWN) // Shut down
{
m_dwThreadEventId = GetCurrentThreadId();
LONG bResult = InterlockedExchange(&m_bShutdown, FALSE);
if (bResult) // Shutdown has not been cancelled
break;
m_dwThreadEventId = 0;
// else, shutdown has been cancelled -- continue as before
}
else // Do work
{
Worker::RequestType request = (Worker::RequestType) dwCompletionKey;
// Process the request. Notice the following:
// (1) It is the worker's responsibility to free any memory associated
// with the request if the request is complete
// (2) If the request still requires some more processing
// the worker should queue the request again for dispatching
theWorker.Execute(request, m_pvWorkerParam, pOverlapped);
}
}
SetEvent(m_hThreadEvent);
theWorker.Terminate(m_pvWorkerParam);
return 0;
}
static DWORD WINAPI WorkerThreadProc(LPVOID pv) throw()
{
CThreadPool* pThis =
reinterpret_cast< CThreadPool* >(pv);
return pThis->ThreadProc();
}
HRESULT InternalResizePool(int nNumThreads, int dwMaxWait) throw()
{
if (!m_hRequestQueue) // Not initialized
return E_FAIL;
CComCritSecLock<CComCriticalSection> lock(m_critSec, false);
if (FAILED(lock.Lock()))
{
// out of memory
ATLASSERT( FALSE );
return E_FAIL;
}
int nCurThreads = m_threadMap.GetSize();
if (nNumThreads == nCurThreads)
{
return S_OK;
}
else if (nNumThreads < nCurThreads)
{
int nNumShutdownThreads = nCurThreads - nNumThreads;
for (int nThreadIndex = 0; nThreadIndex < nNumShutdownThreads; nThreadIndex++)
{
ResetEvent(m_hThreadEvent);
m_bShutdown = TRUE;
PostQueuedCompletionStatus(m_hRequestQueue, 0, 0, ATLS_POOL_SHUTDOWN);
DWORD dwRet = WaitForSingleObject(m_hThreadEvent, dwMaxWait);
if (dwRet == WAIT_TIMEOUT)
{
LONG bResult = InterlockedExchange(&m_bShutdown, FALSE);
if (bResult) // Nobody picked up the shutdown message
{
// m_critSec.Unlock();
return HRESULT_FROM_WIN32(WAIT_TIMEOUT);
}
}
else if (dwRet != WAIT_OBJECT_0)
{
// m_critSec.Unlock();
return AtlHresultFromLastError();
}
int nIndex = m_threadMap.FindKey(m_dwThreadEventId);
if (nIndex != -1)
{
HANDLE hThread = m_threadMap.GetValueAt(nIndex);
CloseHandle(hThread);
m_threadMap.RemoveAt(nIndex);
}
}
}
else
{
int nNumNewThreads = nNumThreads - nCurThreads;
// Create and initialize worker threads
for (int nThreadIndex = 0; nThreadIndex < nNumNewThreads; nThreadIndex++)
{
DWORD dwThreadID;
ResetEvent(m_hThreadEvent);
// HANDLE hThread = ThreadTraits::CreateThread(NULL, m_dwStackSize, WorkerThreadProc, (LPVOID)this, 0, &dwThreadID);
CHandle hdlThread( ThreadTraits::CreateThread(NULL, m_dwStackSize, WorkerThreadProc, (LPVOID)this, 0, &dwThreadID) );
if (!hdlThread)
{
HRESULT hr = AtlHresultFromLastError();
ATLASSERT(hr != S_OK);
// m_critSec.Unlock();
return hr;
}
DWORD dwRet = WaitForSingleObject(m_hThreadEvent, dwMaxWait);
if (dwRet != WAIT_OBJECT_0)
{
if (dwRet == WAIT_TIMEOUT)
{
// m_critSec.Unlock();
return HRESULT_FROM_WIN32(WAIT_TIMEOUT);
}
else
{
// m_critSec.Unlock();
return AtlHresultFromLastError();
}
}
if (m_threadMap.Add(dwThreadID, hdlThread) != FALSE)
{
hdlThread.Detach();
}
}
}
// m_critSec.Unlock();
return S_OK;
}
}; // class CThreadPool
//
// CNonStatelessWorker
// This class is a simple wrapper for use with CThreadPool.
// It instantiates one instance of Worker per request
// this allows Worker to hold state for each request
// and depend on the destructor being called
// Worker:
// is a class that is responsible for handling requests
// queued on the thread pool (See CThreadPool)
template <class Worker>
class CNonStatelessWorker
{
public:
typedef Worker::RequestType RequestType;
BOOL Initialize(void * /*pvParam*/) throw()
{
return TRUE;
}
void Execute(Worker::RequestType request, void *pvWorkerParam, OVERLAPPED *pOverlapped)
{
Worker worker;
worker.Execute(request, pvWorkerParam, pOverlapped);
}
void Terminate(void* /*pvParam*/) throw()
{
}
}; // class CNonStatelessWorker
//Flags
#define ATL_URL_ESCAPE 1 // (un)escape URL characters
#define ATL_URL_NO_ENCODE 2 // Don't convert unsafe characters to escape sequence
#define ATL_URL_DECODE 4 // Convert %XX escape sequences to characters
#define ATL_URL_NO_META 8 // Don't convert .. etc. meta path sequences
#define ATL_URL_ENCODE_SPACES_ONLY 16 // Encode spaces only
#define ATL_URL_BROWSER_MODE 32 // Special encode/decode rules for browser
#define ATL_URL_ENCODE_PERCENT 64 // Encode percent (by default, not encoded)
#define ATL_URL_CANONICALIZE 128 // Internal: used by Canonicalize for AtlEscapeUrl: Cannot be set via SetFlags
#define ATL_URL_COMBINE 256 // Internal: Cannot be set via SetFlags
//Get the decimal value of a hexadecimal character
inline short AtlHexValue(char chIn)
{
unsigned char ch = (unsigned char)chIn;
if (ch >= '0' && ch <= '9')
return (short)(ch - '0');
if (ch >= 'A' && ch <= 'F')
return (short)(ch - 'A' + 10);
if (ch >= 'a' && ch <= 'f')
return (short)(ch - 'a' + 10);
return -1;
}
//Determine if the character is unsafe under the URI RFC document
inline BOOL AtlIsUnsafeUrlChar(char chIn) throw()
{
unsigned char ch = (unsigned char)chIn;
switch(ch)
{
case ';': case '\\': case '?': case '@': case '&':
case '=': case '+': case '$': case ',': case ' ':
case '<': case '>': case '#': case '%': case '\"':
case '{': case '}': case '|':
case '^': case '[': case ']': case '`':
return TRUE;
default:
{
if (ch < 32 || ch > 126)
return TRUE;
return FALSE;
}
}
}
//Get the default internet port for a particular scheme
inline ATL_URL_PORT AtlGetDefaultUrlPort(ATL_URL_SCHEME m_nScheme) throw()
{
switch (m_nScheme)
{
case ATL_URL_SCHEME_FTP:
return ATL_URL_DEFAULT_FTP_PORT;
case ATL_URL_SCHEME_GOPHER:
return ATL_URL_DEFAULT_GOPHER_PORT;
case ATL_URL_SCHEME_HTTP:
return ATL_URL_DEFAULT_HTTP_PORT;
case ATL_URL_SCHEME_HTTPS:
return ATL_URL_DEFAULT_HTTPS_PORT;
case ATL_URL_SCHEME_SOCKS:
return ATL_URL_DEFAULT_SOCKS_PORT;
default:
return ATL_URL_INVALID_PORT_NUMBER;
}
}
//Escape a meta sequence with lpszOutUrl as the base url and lpszInUrl as the relative url
//i.e. lpszInUrl = ./* or ../*
ATL_NOINLINE inline BOOL AtlEscapeUrlMetaHelper(
LPSTR* ppszOutUrl,
DWORD dwOutLen,
LPSTR* ppszInUrl,
DWORD* pdwLen,
DWORD dwFlags = 0,
DWORD dwColonPos = ATL_URL_MAX_URL_LENGTH) throw()
{
ATLASSERT( ppszOutUrl != NULL );
ATLASSERT( ppszInUrl != NULL );
ATLASSERT( pdwLen != NULL);
LPSTR szOut = *ppszOutUrl;
LPSTR szIn = *ppszInUrl;
DWORD dwUrlLen = dwOutLen;
char chPrev = *(szOut-1);
BOOL bRet = FALSE;
//if the previous character is a directory delimiter
if (chPrev == '/' || chPrev == '\\')
{
char chNext = *szIn;
//if the next character is a directory delimiter
if (chNext == '/' || chNext == '\\')
{
//the meta sequence is of the form /./*
szIn++;
bRet = TRUE;
}
else if (chNext == '.' && ((chNext = *(szIn+1)) == '/' ||
chNext == '\\' || chNext == '\0'))
{
//otherwise if the meta sequence is of the form "/../"
//skip the preceding "/"
szOut--;
//skip the ".." of the meta sequence
szIn+= 2;
DWORD dwOutPos = dwUrlLen-1;
LPSTR szTmp = szOut;
//while we are not at the beginning of the base url
while (dwOutPos)
{
szTmp--;
dwOutPos--;
//if it is a directory delimiter
if (*szTmp == '/' || *szTmp == '\\')
{
//if we are canonicalizing the url and NOT combining it
//and if we have encountered the ':' or we are at a position before the ':'
if ((dwFlags & ATL_URL_CANONICALIZE) && ((dwFlags & ATL_URL_COMBINE) == 0) &&
(dwColonPos && (dwOutPos <= dwColonPos+1)))
{
//NOTE: this is to match the way that InternetCanonicalizeUrl and
// InternetCombineUrl handle this case
break;
}
//otherwise, set the current output string position to right after the '/'
szOut = szTmp+1;
//update the length to match
dwUrlLen = dwOutPos+1;
bRet = TRUE;
break;
}
}
//if we could not properly escape the meta sequence
if (dwUrlLen != dwOutPos+1)
{
//restore everything to its original value
szIn-= 2;
szOut++;
}
else
{
bRet = TRUE;
}
}
}
//update the strings
*ppszOutUrl = szOut;
*ppszInUrl = szIn;
*pdwLen = dwUrlLen;
return bRet;
}
//Convert all unsafe characters in szStringIn to escape sequences
//lpszStringIn and lpszStringOut should be different strings
inline BOOL AtlEscapeUrlA(
LPCSTR szStringIn,
LPSTR szStringOut,
DWORD* pdwStrLen,
DWORD dwMaxLength,
DWORD dwFlags = 0) throw()
{
ATLASSERT( szStringIn != NULL );
ATLASSERT( szStringOut != NULL );
ATLASSERT( szStringIn != szStringOut );
char ch;
DWORD dwLen = 0;
BOOL bRet = TRUE;
BOOL bSchemeFile = FALSE;
DWORD dwColonPos = 0;
DWORD dwFlagsInternal = dwFlags;
while((ch = *szStringIn++) != '\0')
{
//if we are at the maximum length, set bRet to FALSE
//this ensures no more data is written to szStringOut, but
//the length of the string is still updated, so the user
//knows how much space to allocate
if (dwLen == dwMaxLength)
{
bRet = FALSE;
}
//Keep track of the first ':' position to match the weird way
//InternetCanonicalizeUrl handles it
if (ch == ':' && (dwFlagsInternal & ATL_URL_CANONICALIZE) && !dwColonPos)
{
if (bRet)
{
*szStringOut = '\0';
_strlwr(szStringOut-dwLen);
if (dwLen == 4 && !strncmp("file", (szStringOut-4), 4))
{
bSchemeFile = TRUE;
}
}
dwColonPos = dwLen+1;
}
else if (ch == '%' && (dwFlagsInternal & ATL_URL_DECODE))
{
//decode the escaped sequence
ch = (char)(16*AtlHexValue(*szStringIn++));
ch = (char)(ch+AtlHexValue(*szStringIn++));
}
else if ((ch == '?' || ch == '#') && (dwFlagsInternal & ATL_URL_BROWSER_MODE))
{
//ATL_URL_BROWSER mode does not encode after a '?' or a '#'
dwFlagsInternal |= ATL_URL_NO_ENCODE;
}
if ((dwFlagsInternal & ATL_URL_CANONICALIZE) && (dwFlagsInternal & ATL_URL_NO_ENCODE)==0)
{
//canonicalize the '\' to '/'
if (ch == '\\' && (dwColonPos || (dwFlagsInternal & ATL_URL_COMBINE)) && bRet)
{
//if the scheme is not file or it is file and the '\' is in "file:\\"
//NOTE: This is to match the way InternetCanonicalizeUrl handles this case
if (!bSchemeFile || (dwLen < 7))
{
ch = '/';
}
}
else if (ch == '.' && dwLen > 0 && (dwFlagsInternal & ATL_URL_NO_META)==0)
{
//if we are escaping meta sequences, attempt to do so
if (AtlEscapeUrlMetaHelper(&szStringOut, dwLen, (char**)(&szStringIn), &dwLen, dwFlagsInternal, dwColonPos))
continue;
}
}
//if we are encoding and it is an unsafe character
if (AtlIsUnsafeUrlChar(ch) && (dwFlagsInternal & ATL_URL_NO_ENCODE)==0)
{
//if we are only encoding spaces, and ch is not a space or
//if we are not encoding meta sequences and it is a dot or
//if we not encoding percents and it is a percent
if (((dwFlagsInternal & ATL_URL_ENCODE_SPACES_ONLY) && ch != ' ') ||
((dwFlagsInternal & ATL_URL_NO_META) && ch == '.') ||
(((dwFlagsInternal & ATL_URL_ENCODE_PERCENT) == 0) && ch == '%'))
{
//just output it without encoding
if (bRet)
*szStringOut++ = ch;
}
else
{
//if there is not enough space for the escape sequence
if (dwLen >= (dwMaxLength-3))
{
bRet = FALSE;
}
if (bRet)
{
//output the percent, followed by the hex value of the character
*szStringOut++ = '%';
// sprintf(szStringOut, "%.2X", (unsigned char)(ch));
_itoa((int)ch, szStringOut, 16);
szStringOut+= 2;
}
dwLen += 2;
}
}
else //safe character
{
if (bRet)
*szStringOut++ = ch;
}
dwLen++;
}
if ((dwFlags & ATL_URL_BROWSER_MODE)==0)
{
//trim trailing whitespace
szStringOut--;
while (1)
{
if (*szStringOut == ' ')
{
--szStringOut;
continue;
}
if (!strncmp(szStringOut-2, "%20", 3))
{
szStringOut -= 3;
continue;
}
break;
}
szStringOut++;
}
if (bRet)
*szStringOut = '\0';
if (pdwStrLen)
*pdwStrLen = dwLen;
return bRet;
}
inline BOOL AtlEscapeUrlW(
LPCWSTR szStringIn,
LPWSTR szStringOut,
DWORD* pdwStrLen,
DWORD dwMaxLength,
DWORD dwFlags = 0) throw()
{
// convert to UTF8
BOOL bRet = FALSE;
int nSrcLen = (int) wcslen(szStringIn);
int nCnt = AtlUnicodeToUTF8(szStringIn, nSrcLen, NULL, 0);
if (nCnt != 0)
{
nCnt++;
CHeapPtr<char> szIn;
char szInBuf[ATL_URL_MAX_URL_LENGTH];
char *pszIn = szInBuf;
// try to avoid allocation
if (nCnt > ATL_URL_MAX_URL_LENGTH)
{
if (!szIn.AllocateBytes(nCnt))
{
// out of memory
return FALSE;
}
pszIn = szIn;
}
nCnt = AtlUnicodeToUTF8(szStringIn, nSrcLen, pszIn, nCnt);
ATLASSERT( nCnt != 0 );
pszIn[nCnt] = '\0';
char szOutBuf[ATL_URL_MAX_URL_LENGTH];
char *pszOut = szOutBuf;
CHeapPtr<char> szTmp;
// try to avoid allocation
if (dwMaxLength > ATL_URL_MAX_URL_LENGTH)
{
if (!szTmp.AllocateBytes(dwMaxLength))
{
// out of memory
return FALSE;
}
pszOut = szTmp;
}
DWORD dwStrLen = 0;
bRet = AtlEscapeUrlA(pszIn, pszOut, &dwStrLen, dwMaxLength, dwFlags);
if (bRet != FALSE)
{
// it is now safe to convert using any codepage, since there
// are no non-ASCII characters
pszOut[dwStrLen] = '\0';
_ATLTRY
{
memcpy(szStringOut, CA2W( pszOut ), dwStrLen*sizeof(wchar_t));
szStringOut[dwStrLen] = '\0';
}
_ATLCATCHALL()
{
bRet = FALSE;
}
}
if (pdwStrLen)
{
*pdwStrLen = dwStrLen;
}
}
return bRet;
}
//Convert all escaped characters in szString to their real values
//lpszStringIn and lpszStringOut can be the same string
inline BOOL AtlUnescapeUrlA(
LPCSTR szStringIn,
LPSTR szStringOut,
LPDWORD pdwStrLen,
DWORD dwMaxLength) throw()
{
ATLASSERT(szStringIn != NULL);
ATLASSERT(szStringOut != NULL);
int nValue = 0;
char ch;
DWORD dwLen = 0;
BOOL bRet = TRUE;
while ((ch = *szStringIn) != 0)
{
if (dwLen == dwMaxLength)
bRet = FALSE;
if (bRet)
{
if (ch == '%')
{
if ((*(szStringIn+1) == '\0') || (*(szStringIn+2) == '\0'))
{
bRet = FALSE;
break;
}
ch = *(++szStringIn);
//currently assuming 2 hex values after '%'
//as per the RFC 2396 document
nValue = 16*AtlHexValue(ch);
nValue+= AtlHexValue(*(++szStringIn));
*szStringOut++ = (char) nValue;
}
else //non-escape character
{
if (bRet)
*szStringOut++ = ch;
}
}
dwLen++;
szStringIn++;
}
if (bRet)
*szStringOut = '\0';
if (pdwStrLen)
*pdwStrLen = dwLen;
return TRUE;
}
inline BOOL AtlUnescapeUrlW(
LPCWSTR szStringIn,
LPWSTR szStringOut,
LPDWORD pdwStrLen,
DWORD dwMaxLength) throw()
{
/// convert to UTF8
BOOL bRet = FALSE;
int nSrcLen = (int) wcslen(szStringIn);
int nCnt = AtlUnicodeToUTF8(szStringIn, nSrcLen, NULL, 0);
if (nCnt != 0)
{
nCnt++;
CHeapPtr<char> szIn;
char szInBuf[ATL_URL_MAX_URL_LENGTH];
char *pszIn = szInBuf;
// try to avoid allocation
if (nCnt > ATL_URL_MAX_URL_LENGTH)
{
if (!szIn.AllocateBytes(nCnt))
{
// out of memory
return FALSE;
}
pszIn = szIn;
}
nCnt = AtlUnicodeToUTF8(szStringIn, nSrcLen, pszIn, nCnt);
ATLASSERT( nCnt != 0 );
pszIn[nCnt] = '\0';
char szOutBuf[ATL_URL_MAX_URL_LENGTH];
char *pszOut = szOutBuf;
CHeapPtr<char> szTmp;
// try to avoid allocation
if (dwMaxLength > ATL_URL_MAX_URL_LENGTH)
{
if (!szTmp.AllocateBytes(dwMaxLength))
{
// out of memory
return FALSE;
}
pszOut = szTmp;
}
DWORD dwStrLen = 0;
bRet = AtlUnescapeUrlA(pszIn, pszOut, &dwStrLen, dwMaxLength);
if (bRet != FALSE)
{
// it is now safe to convert using any codepage, since there
// are no non-ASCII characters
pszOut[dwStrLen] = '\0';
_ATLTRY
{
memcpy(szStringOut, CA2W( pszOut ), dwStrLen*sizeof(wchar_t));
szStringOut[dwStrLen] = '\0';
}
_ATLCATCHALL()
{
bRet = FALSE;
}
}
if (pdwStrLen)
{
*pdwStrLen = dwStrLen;
}
}
return bRet;
}
#ifdef UNICODE
#define AtlEscapeUrl AtlEscapeUrlW
#define AtlUnescapeUrl AtlUnescapeUrlW
#else
#define AtlEscapeUrl AtlEscapeUrlA
#define AtlUnescapeUrl AtlUnescapeUrlA
#endif
//Canonicalize a URL (same as InternetCanonicalizeUrl)
inline BOOL AtlCanonicalizeUrl(
LPCTSTR szUrl,
LPTSTR szCanonicalized,
DWORD* pdwMaxLength,
DWORD dwFlags = 0) throw()
{
ATLASSERT( szUrl != NULL );
ATLASSERT( szCanonicalized != NULL );
ATLASSERT( pdwMaxLength != NULL);
return AtlEscapeUrl(szUrl, szCanonicalized, pdwMaxLength, *pdwMaxLength, dwFlags | ATL_URL_CANONICALIZE);
}
//Combine a base and relative URL (same as InternetCombineUrl)
inline BOOL AtlCombineUrl(
LPCTSTR szBaseUrl,
LPCTSTR szRelativeUrl,
LPTSTR szBuffer,
DWORD* pdwMaxLength,
DWORD dwFlags = 0) throw()
{
ATLASSERT(szBaseUrl != NULL);
ATLASSERT(szRelativeUrl != NULL);
ATLASSERT(szBuffer != NULL);
ATLASSERT(pdwMaxLength != NULL);
size_t nLen1 = _tcslen(szBaseUrl);
TCHAR szCombined[2*ATL_URL_MAX_URL_LENGTH];
if (nLen1 >= 2*ATL_URL_MAX_URL_LENGTH)
{
return FALSE;
}
_tcscpy(szCombined, szBaseUrl);
// if last char of szBaseUrl is not a slash, add it.
if (nLen1 > 0 && szCombined[nLen1-1] != _T('/'))
{
szCombined[nLen1] = _T('/');
nLen1++;
szCombined[nLen1] = _T('\0');
}
size_t nLen2 = _tcslen(szRelativeUrl);
if (nLen2+nLen1+1 >= 2*ATL_URL_MAX_URL_LENGTH)
{
return FALSE;
}
_tcsncpy(szCombined+nLen1, szRelativeUrl, nLen2+1);
DWORD dwLen = (DWORD) (nLen1+nLen2);
if (dwLen >= *pdwMaxLength)
{
*pdwMaxLength = dwLen;
return FALSE;
}
return AtlEscapeUrl(szCombined, szBuffer, pdwMaxLength, *pdwMaxLength, dwFlags | ATL_URL_COMBINE | ATL_URL_CANONICALIZE);
}
class CUrl
{
private:
//scheme names cannot contain escape/unsafe characters
TCHAR m_szScheme[ATL_URL_MAX_SCHEME_LENGTH+1];
//host names cannot contain escape/unsafe characters
TCHAR m_szHostName[ATL_URL_MAX_HOST_NAME_LENGTH+1];
TCHAR m_szUserName[ATL_URL_MAX_USER_NAME_LENGTH+1];
TCHAR m_szPassword[ATL_URL_MAX_PASSWORD_LENGTH+1];
TCHAR m_szUrlPath[ATL_URL_MAX_PATH_LENGTH+1];
TCHAR m_szExtraInfo[ATL_URL_MAX_PATH_LENGTH+1];
ATL_URL_PORT m_nPortNumber;
ATL_URL_SCHEME m_nScheme;
DWORD m_dwSchemeNameLength;
DWORD m_dwHostNameLength;
DWORD m_dwUserNameLength;
DWORD m_dwPasswordLength;
DWORD m_dwUrlPathLength;
DWORD m_dwExtraInfoLength;
public:
//Empty constructor
CUrl() throw()
{
InitFields();
SetScheme(ATL_URL_SCHEME_HTTP);
}
//Copy constructor--maybe make private
CUrl(const CUrl& urlThat) throw()
{
CopyFields(urlThat);
}
//Destructor (empty)
~CUrl() throw()
{
}
CUrl& operator=(const CUrl& urlThat) throw()
{
CopyFields(urlThat);
return (*this);
}
//Set the url
BOOL CrackUrl(LPCTSTR lpszUrl, DWORD dwFlags = 0) throw()
{
ATLASSERT(lpszUrl != NULL);
InitFields();
BOOL bRet = FALSE;
if (dwFlags & ATL_URL_DECODE)
{
//decode the url before parsing it
TCHAR szDecodedUrl[ATL_URL_MAX_URL_LENGTH];
DWORD dwLen;
if (!AtlUnescapeUrl(lpszUrl, szDecodedUrl, &dwLen, ATL_URL_MAX_URL_LENGTH))
return FALSE;
bRet = Parse(szDecodedUrl);
}
else
{
bRet = Parse(lpszUrl);
}
if (bRet && (dwFlags & ATL_URL_ESCAPE))
{
bRet = AtlUnescapeUrl(m_szUserName, m_szUserName,
&m_dwUserNameLength, ATL_URL_MAX_USER_NAME_LENGTH);
if (bRet)
{
bRet = AtlUnescapeUrl(m_szPassword, m_szPassword,
&m_dwPasswordLength, ATL_URL_MAX_PASSWORD_LENGTH);
if (bRet)
{
bRet = AtlUnescapeUrl(m_szUrlPath, m_szUrlPath,
&m_dwUrlPathLength, ATL_URL_MAX_PATH_LENGTH);
if (bRet)
{
bRet = AtlUnescapeUrl(m_szExtraInfo, m_szExtraInfo,
&m_dwExtraInfoLength, ATL_URL_MAX_PATH_LENGTH);
}
}
}
}
return bRet;
}
inline BOOL CreateUrl(LPTSTR lpszUrl, DWORD* pdwMaxLength, DWORD dwFlags = 0) const throw()
{
ATLASSERT(lpszUrl != NULL);
ATLASSERT(pdwMaxLength != NULL);
//build URL: <scheme>://<user>:<pass>@<domain>:<port><path><extra>
TCHAR szPortNumber[ATL_URL_MAX_PORT_NUMBER_LENGTH+2];
DWORD dwLength = *pdwMaxLength;
*pdwMaxLength = GetUrlLength()+1;
if (*pdwMaxLength > dwLength)
return FALSE;
_stprintf(szPortNumber, _T(":%d"), m_nPortNumber);
LPTSTR lpszOutUrl = lpszUrl;
*lpszUrl = '\0';
if (*m_szScheme)
{
_tcsncpy(lpszUrl, m_szScheme, m_dwSchemeNameLength);
lpszUrl += m_dwSchemeNameLength;
*lpszUrl++ = ':';
if (m_nScheme != ATL_URL_SCHEME_MAILTO)
{
*lpszUrl++ = '/';
*lpszUrl++ = '/';
}
}
if (*m_szUserName)
{
_tcsncpy(lpszUrl, m_szUserName, m_dwUserNameLength);
lpszUrl += m_dwUserNameLength;
if (*m_szPassword)
{
*lpszUrl++ = ':';
_tcsncpy(lpszUrl, m_szPassword, m_dwPasswordLength);
lpszUrl += m_dwPasswordLength;
}
*lpszUrl++ = '@';
}
if (*m_szHostName)
{
_tcsncpy(lpszUrl, m_szHostName, m_dwHostNameLength);
lpszUrl += m_dwHostNameLength;
if (m_nPortNumber != AtlGetDefaultUrlPort(m_nScheme))
{
DWORD dwPortLen = (DWORD) _tcslen(szPortNumber);
_tcsncpy(lpszUrl, szPortNumber, dwPortLen);
lpszUrl += dwPortLen;
}
if (*m_szUrlPath && *m_szUrlPath != '/' && *m_szUrlPath != '\\')
*lpszUrl++ = '/';
}
if (*m_szUrlPath)
{
_tcsncpy(lpszUrl, m_szUrlPath, m_dwUrlPathLength);
lpszUrl+= m_dwUrlPathLength;
}
if (*m_szExtraInfo)
{
_tcsncpy(lpszUrl, m_szExtraInfo, m_dwExtraInfoLength);
lpszUrl += m_dwExtraInfoLength;
}
*lpszUrl = '\0';
(*pdwMaxLength)--;
if (dwFlags & ATL_URL_ESCAPE)
{
TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
_tcsncpy(szUrl, lpszOutUrl, *pdwMaxLength+1);
return AtlUnescapeUrl(szUrl, lpszOutUrl, pdwMaxLength, dwLength);
}
return TRUE;
}
inline void Clear() throw()
{
InitFields();
}
inline DWORD GetUrlLength() const throw()
{
//The conditionals in this method are related to the conditionals in the CreateUrl method
//scheme + ':'
DWORD dwUrlLength = m_dwSchemeNameLength+1;
//i.e. "//"
if (m_nScheme != ATL_URL_SCHEME_MAILTO)
dwUrlLength += 2;
dwUrlLength += m_dwUserNameLength;
//i.e. "username@"
if (m_dwUserNameLength > 0)
dwUrlLength += m_dwUserNameLength+1;
//i.e. ":password"
if (m_dwPasswordLength > 0)
dwUrlLength += m_dwPasswordLength+1;
dwUrlLength += m_dwHostNameLength;
// will need to add an extra '/' in this case
if (m_dwHostNameLength && m_dwUrlPathLength && *m_szUrlPath != '/' && *m_szUrlPath != '\\')
dwUrlLength++;
//i.e. ":xx" where "xx" is the port number
if (m_nPortNumber != AtlGetDefaultUrlPort(m_nScheme))
{
TCHAR szPortTmp[6];
dwUrlLength += _stprintf(szPortTmp, _T(":%d"), m_nPortNumber);
}
dwUrlLength += m_dwUrlPathLength + m_dwExtraInfoLength;
return dwUrlLength;
}
//Get the Scheme Name (i.e. http, ftp, etc.)
inline LPCTSTR GetSchemeName() const throw()
{
return m_szScheme;
}
//Get the Scheme Name length
inline DWORD GetSchemeNameLength() const throw()
{
return m_dwSchemeNameLength;
}
//This method will incur the cost of
//validating the scheme and updating the scheme name
inline BOOL SetSchemeName(LPCTSTR lpszSchm) throw()
{
ATLASSERT(lpszSchm != NULL);
const _schemeinfo *pSchemes = GetSchemes();
ATLASSERT( pSchemes != NULL );
int nScheme = -1;
for (int i=0; i<s_nSchemes; i++)
{
if (_tcsicmp(lpszSchm, pSchemes[i].szSchemeName) == 0)
{
nScheme = i;
break;
}
}
if (nScheme != -1)
{
m_nScheme = (ATL_URL_SCHEME) nScheme;
m_dwSchemeNameLength = pSchemes[nScheme].dwSchemeLength;
m_nPortNumber = (ATL_URL_PORT) pSchemes[nScheme].nUrlPort;
}
else
{
// unknown scheme
m_nScheme = ATL_URL_SCHEME_UNKNOWN;
m_dwSchemeNameLength = (DWORD) _tcslen(lpszSchm);
m_nPortNumber = ATL_URL_INVALID_PORT_NUMBER;
}
_tcsncpy(m_szScheme, lpszSchm, m_dwSchemeNameLength);
m_szScheme[m_dwSchemeNameLength] = '\0';
return TRUE;
}
inline BOOL SetScheme(ATL_URL_SCHEME nScheme) throw()
{
if ((nScheme < 0) || (nScheme >= s_nSchemes))
{
// invalid scheme
return FALSE;
}
const _schemeinfo *pSchemes = GetSchemes();
ATLASSERT( pSchemes != NULL );
m_nScheme = (ATL_URL_SCHEME) nScheme;
m_dwSchemeNameLength = pSchemes[nScheme].dwSchemeLength;
m_nPortNumber = (ATL_URL_PORT) pSchemes[nScheme].nUrlPort;
_tcsncpy(m_szScheme, pSchemes[nScheme].szSchemeName, m_dwSchemeNameLength);
return TRUE;
}
inline ATL_URL_SCHEME GetScheme() const throw()
{
return m_nScheme;
}
//Get the host name
inline LPCTSTR GetHostName() const throw()
{
return m_szHostName;
}
//Get the host name's length
inline DWORD GetHostNameLength() const throw()
{
return m_dwHostNameLength;
}
//Set the Host name
inline BOOL SetHostName(LPCTSTR lpszHost) throw()
{
ATLASSERT(lpszHost != NULL);
DWORD dwLen = (DWORD) _tcslen(lpszHost);
if (dwLen > ATL_URL_MAX_HOST_NAME_LENGTH)
return FALSE;
_tcsncpy(m_szHostName, lpszHost, dwLen+1);
m_dwHostNameLength = dwLen;
return TRUE;
}
//Get the port number in terms of ATL_URL_PORT
inline ATL_URL_PORT GetPortNumber() const throw()
{
return m_nPortNumber;
}
//Set the port number in terms of ATL_URL_PORT
inline BOOL SetPortNumber(ATL_URL_PORT nPrt) throw()
{
m_nPortNumber = nPrt;
return TRUE;
}
//Get the user name
inline LPCTSTR GetUserName() const throw()
{
return m_szUserName;
}
//Get the user name's length
inline DWORD GetUserNameLength() const throw()
{
return m_dwUserNameLength;
}
//Set the user name
inline BOOL SetUserName(LPCTSTR lpszUser) throw()
{
ATLASSERT(lpszUser != NULL);
DWORD dwLen = (DWORD) _tcslen(lpszUser);
if (dwLen > ATL_URL_MAX_USER_NAME_LENGTH)
return FALSE;
_tcsncpy(m_szUserName, lpszUser, dwLen+1);
m_dwUserNameLength = dwLen;
return TRUE;
}
//Get the password
inline LPCTSTR GetPassword() const throw()
{
return m_szPassword;
}
//Get the password's length
inline DWORD GetPasswordLength() const throw()
{
return m_dwPasswordLength;
}
//Set the password
inline BOOL SetPassword(LPCTSTR lpszPass) throw()
{
ATLASSERT(lpszPass != NULL);
if (*lpszPass && !*m_szUserName)
return FALSE;
DWORD dwLen = (DWORD) _tcslen(lpszPass);
if (dwLen > ATL_URL_MAX_PASSWORD_LENGTH)
return FALSE;
_tcsncpy(m_szPassword, lpszPass, dwLen+1);
m_dwPasswordLength = dwLen;
return TRUE;
}
//Get the url path (everything after scheme and
//before extra info)
inline LPCTSTR GetUrlPath() const throw()
{
return m_szUrlPath;
}
//Get the url path's length
inline DWORD GetUrlPathLength() const throw()
{
return m_dwUrlPathLength;
}
//Set the url path
inline BOOL SetUrlPath(LPCTSTR lpszPath) throw()
{
ATLASSERT(lpszPath != NULL);
DWORD dwLen = (DWORD) _tcslen(lpszPath);
if (dwLen > ATL_URL_MAX_PATH_LENGTH)
return FALSE;
_tcsncpy(m_szUrlPath, lpszPath, dwLen+1);
m_dwUrlPathLength = dwLen;
return TRUE;
}
//Get extra info (i.e. ?something or #something)
inline LPCTSTR GetExtraInfo() const throw()
{
return m_szExtraInfo;
}
//Get extra info's length
inline DWORD GetExtraInfoLength() const throw()
{
return m_dwExtraInfoLength;
}
//Set extra info
inline BOOL SetExtraInfo(LPCTSTR lpszInfo) throw()
{
ATLASSERT(lpszInfo != NULL);
DWORD dwLen = (DWORD) _tcslen(lpszInfo);
if (dwLen > ATL_URL_MAX_PATH_LENGTH)
return FALSE;
_tcsncpy(m_szExtraInfo, lpszInfo, dwLen+1);
m_dwExtraInfoLength = dwLen;
return TRUE;
}
//Insert Escape characters into URL
inline BOOL Canonicalize(DWORD dwFlags = 0) throw()
{
_tcslwr(m_szScheme);
TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
_tcscpy(szTmp, m_szUserName);
BOOL bRet = AtlEscapeUrl(szTmp, m_szUserName, &m_dwUserNameLength, ATL_URL_MAX_USER_NAME_LENGTH, dwFlags);
if (bRet)
{
_tcscpy(szTmp, m_szPassword);
bRet = AtlEscapeUrl(szTmp, m_szPassword, &m_dwPasswordLength, ATL_URL_MAX_PASSWORD_LENGTH, dwFlags);
}
if (bRet)
{
_tcscpy(szTmp, m_szHostName);
bRet = AtlEscapeUrl(szTmp, m_szHostName, &m_dwHostNameLength, ATL_URL_MAX_HOST_NAME_LENGTH, dwFlags);
}
if (bRet)
{
_tcscpy(szTmp, m_szUrlPath);
bRet = AtlEscapeUrl(szTmp, m_szUrlPath, &m_dwUrlPathLength, ATL_URL_MAX_PATH_LENGTH, dwFlags);
}
//in ATL_URL_BROWSER mode, the portion of the URL following the '?' or '#' is not encoded
if (bRet && (dwFlags & ATL_URL_BROWSER_MODE) == 0)
{
_tcscpy(szTmp, m_szExtraInfo);
bRet = AtlEscapeUrl(szTmp+1, m_szExtraInfo+1, &m_dwExtraInfoLength, ATL_URL_MAX_PATH_LENGTH-1, dwFlags);
if (bRet)
m_dwExtraInfoLength++;
}
return bRet;
}
private:
const static DWORD s_nSchemes = 8;
struct _schemeinfo
{
LPCTSTR szSchemeName;
DWORD dwSchemeLength;
ATL_URL_PORT nUrlPort;
};
const _schemeinfo * GetSchemes() throw()
{
const static _schemeinfo s_schemes[] =
{
{ _T("ftp"), sizeof("ftp")-1, ATL_URL_DEFAULT_FTP_PORT },
{ _T("gopher"), sizeof("gopher")-1, ATL_URL_DEFAULT_GOPHER_PORT },
{ _T("http"), sizeof("http")-1, ATL_URL_DEFAULT_HTTP_PORT },
{ _T("https"), sizeof("https")-1, ATL_URL_DEFAULT_HTTPS_PORT },
{ _T("file"), sizeof("file")-1, ATL_URL_INVALID_PORT_NUMBER },
{ _T("news"), sizeof("news")-1, ATL_URL_INVALID_PORT_NUMBER },
{ _T("mailto"), sizeof("mailto")-1, ATL_URL_INVALID_PORT_NUMBER },
{ _T("socks"), sizeof("socks")-1, ATL_URL_DEFAULT_SOCKS_PORT }
};
return s_schemes;
}
inline BOOL Parse(LPCTSTR lpszUrl) throw()
{
ATLASSERT(lpszUrl != NULL);
TCHAR ch;
BOOL bGotScheme = FALSE;
BOOL bGotUserName = FALSE;
BOOL bGotHostName = FALSE;
BOOL bGotPortNumber = FALSE;
TCHAR szCurrentUrl[ATL_URL_MAX_URL_LENGTH+6];
TCHAR* pszCurrentUrl = szCurrentUrl;
//parse lpszUrl using szCurrentUrl to store temporary data
//this loop will get the following if it exists:
//<protocol>://user:pass@server:port
while ((ch = *lpszUrl) != '\0')
{
if (ch == ':')
{
//3 cases:
//(1) Just encountered a scheme
//(2) Port number follows
//(3) Form of username:password@
// Check to see if we've just encountered a scheme
*pszCurrentUrl = '\0';
if (!bGotScheme)
{
if (!SetSchemeName(szCurrentUrl))
goto error;
//Set a flag to avoid checking for
//schemes everytime we encounter a :
bGotScheme = TRUE;
if (*(lpszUrl+1) == '/')
{
if (*(lpszUrl+2) == '/')
{
//the mailto scheme cannot have a '/' following the "mailto:" portion
if (bGotScheme && m_nScheme == ATL_URL_SCHEME_MAILTO)
goto error;
//Skip these characters and continue
lpszUrl+= 2;
}
else
{
//it is an absolute path
//no domain name, port, username, or password is allowed in this case
//break to loop that gets path
lpszUrl++;
pszCurrentUrl = szCurrentUrl;
break;
}
}
//reset pszCurrentUrl
pszCurrentUrl = szCurrentUrl;
lpszUrl++;
//if the scheme is file, skip to getting the path information
if (m_nScheme == ATL_URL_SCHEME_FILE)
break;
continue;
}
else if (!bGotUserName || !bGotPortNumber)
{
//It must be a username:password or a port number
*pszCurrentUrl = '\0';
pszCurrentUrl = szCurrentUrl;
TCHAR tmpBuf[ATL_URL_MAX_PASSWORD_LENGTH];
TCHAR* pTmpBuf = tmpBuf;
int nCnt = 0;
//get the user or portnumber (break on either '/', '@', or '\0'
while (((ch = *(++lpszUrl)) != '/') && (ch != '@') && (ch != '\0'))
{
if (nCnt >= ATL_URL_MAX_PASSWORD_LENGTH)
goto error;
*pTmpBuf++ = ch;
nCnt++;
}
*pTmpBuf = '\0';
//if we broke on a '/' or a '\0', it must be a port number
if (!bGotPortNumber && (ch == '/' || ch == '\0'))
{
//the host name must immediately preced the port number
if (!SetHostName(szCurrentUrl))
goto error;
//get the port number
m_nPortNumber = (ATL_URL_PORT) _ttoi(tmpBuf);
if (m_nPortNumber < 0)
goto error;
bGotPortNumber = bGotHostName = TRUE;
}
else if (!bGotUserName && ch=='@')
{
//otherwise it must be a username:password
if (!SetUserName(szCurrentUrl) || !SetPassword(tmpBuf))
goto error;
bGotUserName = TRUE;
lpszUrl++;
}
else
{
goto error;
}
}
}
else if (ch == '@')
{
if (bGotUserName)
goto error;
//type is userinfo@
*pszCurrentUrl = '\0';
if (!SetUserName(szCurrentUrl))
goto error;
bGotUserName = TRUE;
lpszUrl++;
pszCurrentUrl = szCurrentUrl;
}
else if (ch == '/' || ch == '?' || (!*(lpszUrl+1)))
{
//we're at the end of this loop
//set the domain name and break
if (!*(lpszUrl+1) && ch != '/' && ch != '?')
{
*pszCurrentUrl++ = ch;
lpszUrl++;
}
*pszCurrentUrl = '\0';
if (!bGotHostName)
{
if (!SetHostName(szCurrentUrl))
goto error;
}
pszCurrentUrl = szCurrentUrl;
break;
}
else
{
*pszCurrentUrl++ = ch;
lpszUrl++;
}
}
if (!bGotScheme)
goto error;
//Now build the path
while ((ch = *lpszUrl) != '\0')
{
//break on a '#' or a '?', which delimit "extra information"
if (m_nScheme != ATL_URL_SCHEME_FILE && (ch == '#' || ch == '?'))
{
break;
}
*pszCurrentUrl++ = ch;
lpszUrl++;
}
*pszCurrentUrl = '\0';
if (*szCurrentUrl != '\0' && !SetUrlPath(szCurrentUrl))
goto error;
pszCurrentUrl = szCurrentUrl;
while ((ch = *lpszUrl++) != '\0')
{
*pszCurrentUrl++ = ch;
}
*pszCurrentUrl = '\0';
if (*szCurrentUrl != '\0' && !SetExtraInfo(szCurrentUrl))
goto error;
switch(m_nScheme)
{
case ATL_URL_SCHEME_FILE:
m_nPortNumber = ATL_URL_INVALID_PORT_NUMBER;
break;
case ATL_URL_SCHEME_NEWS:
m_nPortNumber = ATL_URL_INVALID_PORT_NUMBER;
break;
case ATL_URL_SCHEME_MAILTO:
m_nPortNumber = ATL_URL_INVALID_PORT_NUMBER;
break;
default:
if (!bGotPortNumber)
m_nPortNumber = (unsigned short)AtlGetDefaultUrlPort(m_nScheme);
}
return TRUE;
error:
InitFields();
return FALSE;
}
ATL_NOINLINE void InitFields() throw()
{
m_nPortNumber = ATL_URL_INVALID_PORT_NUMBER;
m_nScheme = ATL_URL_SCHEME_UNKNOWN;
m_dwSchemeNameLength = 0;
m_dwHostNameLength = 0;
m_dwUserNameLength = 0;
m_dwUrlPathLength = 0;
m_dwPasswordLength = 0;
m_dwExtraInfoLength = 0;
m_szScheme[0] = '\0';
m_szHostName[0] = '\0';
m_szUserName[0] = '\0';
m_szPassword[0] = '\0';
m_szUrlPath[0] = '\0';
m_szExtraInfo[0] = '\0';
}
//copy all fields from urlThat
inline void CopyFields(const CUrl& urlThat) throw()
{
_tcsncpy(m_szScheme, urlThat.m_szScheme, urlThat.m_dwSchemeNameLength+1);
_tcsncpy(m_szHostName, urlThat.m_szHostName, urlThat.m_dwHostNameLength+1);
_tcsncpy(m_szUserName, urlThat.m_szUserName, urlThat.m_dwUserNameLength+1);
_tcsncpy(m_szPassword, urlThat.m_szPassword, urlThat.m_dwPasswordLength+1);
_tcsncpy(m_szUrlPath, urlThat.m_szUrlPath, urlThat.m_dwUrlPathLength+1);
_tcsncpy(m_szExtraInfo, urlThat.m_szExtraInfo, urlThat.m_dwExtraInfoLength+1);
m_nPortNumber = urlThat.m_nPortNumber;
m_nScheme = urlThat.m_nScheme;
m_dwSchemeNameLength = urlThat.m_dwSchemeNameLength;
m_dwHostNameLength = urlThat.m_dwHostNameLength;
m_dwUserNameLength = urlThat.m_dwUserNameLength;
m_dwPasswordLength = urlThat.m_dwPasswordLength;
m_dwUrlPathLength = urlThat.m_dwUrlPathLength;
m_dwExtraInfoLength = urlThat.m_dwExtraInfoLength;
}
}; // class CUrl
typedef CUrl* LPURL;
typedef const CUrl * LPCURL;
#ifndef ATL_WORKER_THREAD_WAIT
#define ATL_WORKER_THREAD_WAIT 10000 // time to wait when shutting down
#endif
//
// CWorkerThread
// This class creates a worker thread that waits on kernel
// object handles and executes a specified client
// function when the handle is signaled
// To use it, construct an instance, call Initialize
// then call add AddHandle with the handle of a kernel
// object and pass a pointer to your implementation
// of IWorkerThreadClient. Execute on your IWorkerThreadClient
// implementation will be called when the handle is signaled
// You can also use AddTimer() to add a waitable timer
// to the worker thread.
// If the thread is still active when your object is destroyed
// you must call RemoveHandle() on each handle that your object
// owns.
// To terminate the thread, call Shutdown
//
template <class ThreadTraits=DefaultThreadTraits>
class CWorkerThread
{
protected:
HANDLE m_hThread;
DWORD m_dwThreadId;
CWorkerThread<ThreadTraits> *m_pThread;
struct WorkerClientEntry
{
IWorkerThreadClient *pClient;
DWORD_PTR dwParam;
};
CSimpleArray<HANDLE> m_hWaitHandles;
CSimpleArray<WorkerClientEntry, CSimpleArrayEqualHelperFalse<WorkerClientEntry> > m_ClientEntries;
CComCriticalSection m_critSec;
HANDLE m_hRefreshComplete;
void Refresh() throw()
{
ATLASSERT(m_hRefreshComplete);
BOOL bRet = SetEvent(m_hWaitHandles[1]);
ATLASSERT(bRet);
bRet; // unused
WaitForSingleObject(m_hRefreshComplete, INFINITE);
}
public:
CWorkerThread() throw() :
m_hThread(NULL),
m_dwThreadId(0),
m_hRefreshComplete(NULL),
m_pThread(NULL)
{
}
~CWorkerThread() throw()
{
Shutdown();
}
DWORD GetThreadId() throw()
{
if (m_pThread)
return m_pThread->GetThreadId();
return m_dwThreadId;
}
HANDLE GetThreadHandle() throw()
{
if (m_pThread)
return m_pThread->GetThreadHandle();
return m_hThread;
}
HRESULT Initialize() throw()
{
if (m_pThread)
return E_UNEXPECTED; // already initialized!
// the object should be initialized first
ATLASSERT(m_hWaitHandles.GetSize() == 0);
m_critSec.Init();
// create the refresh complete event
m_hRefreshComplete = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!m_hRefreshComplete)
{
m_critSec.Term();
return AtlHresultFromLastError();
}
// add the shutdown event
HRESULT hr;
HANDLE hEventShutdown = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!hEventShutdown)
{
hr = AtlHresultFromLastError();
Shutdown();
return hr;
}
hr = AddHandle(hEventShutdown, NULL, 0);
if (FAILED(hr))
{
CloseHandle(hEventShutdown);
Shutdown();
return hr;
}
// create the refresh event
HANDLE hEventRefresh = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!hEventRefresh)
{
hr = AtlHresultFromLastError();
Shutdown();
return hr;
}
hr = AddHandle(hEventRefresh, NULL, 0);
if (FAILED(hr))
{
CloseHandle(hEventRefresh);
Shutdown();
return hr;
}
m_hThread = ThreadTraits::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) _WorkerThreadProc,
this, 0, &m_dwThreadId);
if (!m_hThread)
{
hr = AtlHresultFromLastError();
Shutdown();
return hr;
}
WaitForSingleObject(m_hRefreshComplete, INFINITE);
return S_OK;
}
HRESULT Initialize(CWorkerThread<ThreadTraits> *pThread)
{
if (!pThread)
return E_INVALIDARG;
if (m_hThread)
return E_UNEXPECTED; // already initialized
if (!m_pThread)
{
m_pThread = pThread;
}
return S_OK;
}
HRESULT AddHandle(HANDLE hObject, IWorkerThreadClient *pClient, DWORD_PTR dwParam) throw()
{
if (m_pThread)
return m_pThread->AddHandle(hObject, pClient, dwParam);
// Make sure the object has been initialized
ATLASSERT(m_hRefreshComplete != NULL);
CComCritSecLock<CComCriticalSection> lock(m_critSec, false);
HRESULT hr = lock.Lock();
if (FAILED(hr))
return hr;
if (m_hWaitHandles.GetSize() == MAXIMUM_WAIT_OBJECTS)
{
return AtlHresultFromWin32(ERROR_INVALID_PARAMETER);
}
BOOL bRet = m_hWaitHandles.Add(hObject);
if (!bRet)
{
return E_OUTOFMEMORY;
}
WorkerClientEntry entry;
entry.pClient = pClient;
entry.dwParam = dwParam;
bRet = m_ClientEntries.Add(entry);
if (!bRet)
{
m_hWaitHandles.RemoveAt(m_hWaitHandles.GetSize()-1);
return E_OUTOFMEMORY;
}
if (m_hWaitHandles.GetSize() > 2)
{
// tell the worker thread to refresh
Refresh();
}
return S_OK;
}
#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
HRESULT AddTimer(DWORD dwInterval, IWorkerThreadClient *pClient, DWORD_PTR dwParam, HANDLE *phTimer) throw()
{
if (m_pThread)
return m_pThread->AddTimer(dwInterval, pClient, dwParam, phTimer);
// Make sure the object has been initialized
ATLASSERT(m_hRefreshComplete != NULL);
ATLASSERT(phTimer);
*phTimer = NULL;
HANDLE hTimer = CreateWaitableTimer(NULL, FALSE, NULL);
if (!hTimer)
{
return AtlHresultFromLastError();
}
HRESULT hr;
LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -10000 * (__int64) dwInterval;
BOOL bRet = SetWaitableTimer(hTimer, &liDueTime, dwInterval, NULL, NULL, FALSE);
if (!bRet)
{
hr = AtlHresultFromLastError();
CloseHandle(hTimer);
return hr;
}
hr = AddHandle(hTimer, pClient, dwParam);
if (FAILED(hr))
{
CloseHandle(hTimer);
return hr;
}
if (phTimer)
*phTimer = hTimer;
return S_OK;
}
#endif
HRESULT RemoveHandle(HANDLE hObject) throw()
{
if (m_pThread)
return m_pThread->RemoveHandle(hObject);
// Make sure the object has been initialized
ATLASSERT(m_hRefreshComplete != NULL);
CComCritSecLock<CComCriticalSection> lock(m_critSec, false);
HRESULT hr = lock.Lock();
if (FAILED(hr))
return hr;
int nIndex = m_hWaitHandles.Find(hObject);
if (nIndex >= 0)
{
ATLASSERT(nIndex < m_ClientEntries.GetSize());
IWorkerThreadClient *pClient = m_ClientEntries[nIndex].pClient;
m_hWaitHandles.RemoveAt(nIndex);
m_ClientEntries.RemoveAt(nIndex);
Refresh();
// now it is safe to close the handle
if (!pClient || FAILED(pClient->CloseHandle(hObject)))
CloseHandle(hObject);
}
return S_OK;
}
HRESULT Shutdown(DWORD dwWait=ATL_WORKER_THREAD_WAIT) throw()
{
if (m_pThread)
return S_OK;
if (!m_hThread)
{
RemoveAllClients();
m_critSec.Term();
if (m_hRefreshComplete)
{
CloseHandle(m_hRefreshComplete);
m_hRefreshComplete = NULL;
}
return S_OK;
}
ATLASSERT(m_hWaitHandles.GetSize() > 0);
SetEvent(m_hWaitHandles[0]);
DWORD dwRet = WaitForSingleObject(m_hThread, dwWait);
RemoveAllClients();
CloseHandle(m_hThread);
m_hThread = NULL;
if (m_hRefreshComplete)
{
CloseHandle(m_hRefreshComplete);
m_hRefreshComplete = NULL;
}
m_critSec.Term();
return (dwRet == WAIT_OBJECT_0) ? S_OK : AtlHresultFromWin32(dwRet);
}
protected:
void RemoveAllClients() throw()
{
ATLASSERT(m_hWaitHandles.GetSize() == m_ClientEntries.GetSize());
int nLen = m_hWaitHandles.GetSize();
for (int i = 0; i < nLen; i++)
{
WorkerClientEntry& entry = m_ClientEntries[i];
if (!entry.pClient || FAILED(entry.pClient->CloseHandle(m_hWaitHandles[i])))
CloseHandle(m_hWaitHandles[i]);
}
m_hWaitHandles.RemoveAll();
m_ClientEntries.RemoveAll();
}
DWORD WorkerThreadProc() throw()
{
// Make sure the object has been initialized
ATLASSERT(m_hRefreshComplete != NULL);
CSimpleArray<HANDLE> handles(m_hWaitHandles);
CSimpleArray<WorkerClientEntry, CSimpleArrayEqualHelperFalse<WorkerClientEntry> > clientEntries(m_ClientEntries);
// tell the main thread we're done copying
SetEvent(m_hRefreshComplete);
while (TRUE)
{
DWORD dwRet = WaitForMultipleObjects(handles.GetSize(), handles.GetData(),
FALSE, INFINITE);
// check for shutdown
if (dwRet == WAIT_OBJECT_0)
return 0;
else if (dwRet == WAIT_OBJECT_0+1) // check for refresh
{
handles = m_hWaitHandles;
clientEntries = m_ClientEntries;
// tell the main thread we're done copying
SetEvent(m_hRefreshComplete);
continue;
}
else if (dwRet > WAIT_OBJECT_0 && dwRet < WAIT_OBJECT_0 + handles.GetSize())
{
// execute the approriate client
WorkerClientEntry& entry = clientEntries[dwRet - WAIT_OBJECT_0];
// We ignore the error code because nothing useful can be done with it in this
// implementation
entry.pClient->Execute(entry.dwParam, handles[dwRet - WAIT_OBJECT_0]);
}
else
{
// this probably means an invalid handle was added
ATLASSERT(FALSE);
return 1;
}
}
return 0;
}
static DWORD WINAPI _WorkerThreadProc(CWorkerThread *pThis) throw()
{
return pThis->WorkerThreadProc();
}
}; // class CWorkerThread
// Use CNoWorkerThread as a template argument for classes
// that need a worker thread type as a template argument but
// don't require the services of a worker thread. An example
// would be CDllCache (atlutil.h) when you want to create a
// CDllCache with no sweeper thread.
class CNoWorkerThread
{
public:
DWORD GetThreadId() throw()
{
return 0;
}
HANDLE GetThreadHandle() throw()
{
return NULL;
}
HRESULT Initialize() throw()
{
return S_OK;
}
HRESULT AddHandle(HANDLE /*hObject*/, IWorkerThreadClient * /*pClient*/, DWORD_PTR /*dwParam*/) throw()
{
return S_OK;
}
HRESULT AddTimer(DWORD /*dwInterval*/, IWorkerThreadClient * /*pClient*/, DWORD_PTR /*dwParam*/, HANDLE * /*phTimer*/) throw()
{
return S_OK;
}
HRESULT RemoveHandle(HANDLE /*hObject*/) throw()
{
return S_OK;
}
HRESULT Shutdown(DWORD dwWait=ATL_WORKER_THREAD_WAIT) throw()
{
dwWait;
return S_OK;
}
}; // CNoWorkerThread
class CBrowserCaps : public IBrowserCaps, public CComObjectRootEx<CComSingleThreadModel>
{
public:
BEGIN_COM_MAP(CBrowserCaps)
COM_INTERFACE_ENTRY(IBrowserCaps)
END_COM_MAP()
CBrowserCaps()
{
}
void FinalRelease()
{
if (m_pParent)
m_pParent->Release();
}
HRESULT Initialize(IXMLDOMNode * pNode, IBrowserCapsSvc * pSvc)
{
if (!pNode)
return E_POINTER;
HRESULT hr = pNode->QueryInterface(__uuidof(IXMLDOMElement), (void **)&m_spElement);
if (FAILED(hr))
return hr;
CComPtr<IXMLDOMNamedNodeMap> spList;
hr = pNode->get_attributes(&spList);
if (FAILED(hr))
return hr;
CComPtr<IXMLDOMNode> spItem;
hr = spList->getNamedItem((BSTR)L"parent", &spItem);
if (FAILED(hr))
return hr;
if (hr == S_FALSE)
m_pParent = NULL;
else
{
if (!spItem)
return E_FAIL;
CComVariant varVal;
hr = spItem->get_nodeValue(&varVal);
if (FAILED(hr))
return hr;
varVal.ChangeType(VT_BSTR);
hr = pSvc->GetCapsUserAgent(varVal.bstrVal, (IBrowserCaps **)&m_pParent);
if (FAILED(hr))
return hr;
}
return S_OK;
}
HRESULT GetPropertyString(BSTR bstrProperty, BSTR * pbstrOut)
{
ATLASSERT(m_spElement);
if (!m_spElement)
return E_FAIL;
if (!pbstrOut)
return E_POINTER;
*pbstrOut = NULL;
CComPtr<IXMLDOMNodeList> spList;
HRESULT hr = m_spElement->getElementsByTagName(bstrProperty, &spList);
if (FAILED(hr))
return hr;
long nLength;
hr = spList->get_length(&nLength);
if (FAILED(hr))
return hr;
if (nLength == 0)
{
if (m_pParent)
return m_pParent->GetPropertyString(bstrProperty, pbstrOut);
else
return E_FAIL;
}
// Assume the first one is the correct node
CComPtr<IXMLDOMNode> spNode;
hr = spList->get_item(0, &spNode);
if (FAILED(hr))
return hr;
CComBSTR bstrValue;
hr = spNode->get_text(&bstrValue);
if (FAILED(hr))
return hr;
*pbstrOut = bstrValue.Detach();
return hr;
}
HRESULT GetBooleanPropertyValue(BSTR bstrProperty, BOOL* pbOut)
{
if (!pbOut)
return E_POINTER;
CComBSTR bstrOut;
HRESULT hr = GetPropertyString(bstrProperty, &bstrOut);
if (FAILED(hr))
return hr;
if (bstrOut[0] == L'1' && bstrOut.Length() == 1)
*pbOut = TRUE;
else
*pbOut = FALSE;
return S_OK;
}
HRESULT GetBrowserName(BSTR * pbstrName)
{
return GetPropertyString(L"name", pbstrName);
}
HRESULT GetPlatform(BSTR * pbstrPlatform)
{
return GetPropertyString(L"platform", pbstrPlatform);
}
HRESULT GetVersion(BSTR * pbstrVersion)
{
return GetPropertyString(L"version", pbstrVersion);
}
HRESULT GetMajorVer(BSTR * pbstrMajorVer)
{
return GetPropertyString(L"majorver", pbstrMajorVer);
}
HRESULT GetMinorVer(BSTR * pbstrMinorVer)
{
return GetPropertyString(L"minorver", pbstrMinorVer);
}
HRESULT SupportsFrames(BOOL* pbFrames)
{
return GetBooleanPropertyValue(L"frames", pbFrames);
}
HRESULT SupportsTables(BOOL* pbTables)
{
return GetBooleanPropertyValue(L"tables", pbTables);
}
HRESULT SupportsCookies(BOOL* pbCookies)
{
return GetBooleanPropertyValue(L"cookies", pbCookies);
}
HRESULT SupportsBackgroundSounds(BOOL* pbBackgroundSounds)
{
return GetBooleanPropertyValue(L"backgroundsounds", pbBackgroundSounds);
}
HRESULT SupportsVBScript(BOOL* pbVBScript)
{
return GetBooleanPropertyValue(L"vbscript", pbVBScript);
}
HRESULT SupportsJavaScript(BOOL* pbJavaScript)
{
return GetBooleanPropertyValue(L"javascript", pbJavaScript);
}
HRESULT SupportsJavaApplets(BOOL* pbJavaApplets)
{
return GetBooleanPropertyValue(L"javaapplets", pbJavaApplets);
}
HRESULT SupportsActiveXControls(BOOL* pbActiveXControls)
{
return GetBooleanPropertyValue(L"ActiveXControls", pbActiveXControls);
}
HRESULT SupportsCDF(BOOL* pbCDF)
{
return GetBooleanPropertyValue(L"CDF", pbCDF);
}
HRESULT SupportsAuthenticodeUpdate(BOOL* pbAuthenticodeUpdate)
{
return GetBooleanPropertyValue(L"AuthenticodeUpdate", pbAuthenticodeUpdate);
}
HRESULT IsBeta(BOOL* pbIsBeta)
{
return GetBooleanPropertyValue(L"beta", pbIsBeta);
}
HRESULT IsCrawler(BOOL* pbIsCrawler)
{
return GetBooleanPropertyValue(L"Crawler", pbIsCrawler);
}
HRESULT IsAOL(BOOL* pbIsAOL)
{
return GetBooleanPropertyValue(L"AOL", pbIsAOL);
}
HRESULT IsWin16(BOOL* pbIsWin16)
{
return GetBooleanPropertyValue(L"Win16", pbIsWin16);
}
HRESULT IsAK(BOOL* pbIsAK)
{
return GetBooleanPropertyValue(L"AK", pbIsAK);
}
HRESULT IsSK(BOOL* pbIsSK)
{
return GetBooleanPropertyValue(L"SK", pbIsSK);
}
HRESULT IsUpdate(BOOL* pbIsUpdate)
{
return GetBooleanPropertyValue(L"Update", pbIsUpdate);
}
private:
CComPtr<IXMLDOMElement> m_spElement;
CComObjectNoLock<CBrowserCaps> * m_pParent;
};
template <class TVal>
class CWildCardEqualHelper
{
public:
static bool IsEqualKey(LPCWSTR szPattern, LPCWSTR szInput)
{
while (*szPattern && *szInput && *szPattern == *szInput)
{
szPattern++;
szInput++;
}
if (*szPattern == *szInput)
return TRUE;
if (*szPattern == '*')
{
szPattern++;
if (!*szPattern)
return true;
while(*szInput)
{
if (IsEqualKey(szPattern, szInput))
return TRUE;
szInput++;
}
}
return FALSE;
}
static bool IsEqualValue(TVal& v1, const TVal& v2)
{
return (v1 == v2);
}
};
class CBrowserCapsSvc : public IBrowserCapsSvc,
public CComObjectRootEx<CComSingleThreadModel>
{
public:
BEGIN_COM_MAP(CBrowserCapsSvc)
COM_INTERFACE_ENTRY(IBrowserCapsSvc)
END_COM_MAP()
HRESULT GetCaps(IHttpServerContext * pContext, IBrowserCaps ** ppOut)
{
if (!pContext)
return E_POINTER;
if (!ppOut)
return E_POINTER;
*ppOut = NULL;
char szUserAgent[256];
DWORD dwSize = sizeof(szUserAgent);
if (!pContext->GetServerVariable("HTTP_USER_AGENT", szUserAgent, &dwSize))
return E_FAIL;
CComBSTR bstrAgent = szUserAgent;
return GetCapsUserAgent(bstrAgent, ppOut);
}
HRESULT GetCapsUserAgent(BSTR bstrAgent, IBrowserCaps ** ppOut)
{
if (!bstrAgent)
return E_POINTER;
if (!ppOut)
return E_POINTER;
*ppOut = NULL;
CComPtr<IXMLDOMNode> spNode;
spNode = m_Map.Lookup((LPCWSTR)bstrAgent);
if (spNode != NULL)
{
CComObjectNoLock<CBrowserCaps> *pRet = NULL;
ATLTRY(pRet = new CComObjectNoLock<CBrowserCaps>);
if (!pRet)
return E_OUTOFMEMORY;
HRESULT hr = pRet->Initialize(spNode, this);
if (FAILED(hr))
{
delete pRet;
return hr;
}
pRet->AddRef();
*ppOut = pRet;
return S_OK;
}
return E_FAIL;
}
HRESULT Initialize(HINSTANCE hInstance)
{
HRESULT hr = _Initialize(hInstance);
if (FAILED(hr))
Clear();
return hr;
}
HRESULT Uninitialize()
{
Clear();
return S_OK;
}
private:
HRESULT _Initialize(HINSTANCE hInstance)
{
if (m_spDoc) // Already initialized
return S_OK;
HRESULT hr;
hr = m_spDoc.CoCreateInstance(__uuidof(DOMDocument), NULL, CLSCTX_INPROC);
if (FAILED(hr))
return hr;
if (!m_spDoc)
return E_FAIL;
hr = m_spDoc->put_async(VARIANT_FALSE);
if (FAILED(hr))
return hr;
char szPath[MAX_PATH];
int nRet = GetModuleFileNameA(hInstance, szPath, MAX_PATH);
if (!nRet)
return AtlHresultFromLastError();
LPSTR szMark = strrchr(szPath, '\\');
ATLASSERT(szMark);
if (szMark)
*szMark = '\0';
CComBSTR bstrFile;
bstrFile += "file://";
bstrFile += szPath ;
bstrFile += "\\browscap.xml";
CComVariant varFile(bstrFile);
VARIANT_BOOL varBool;
hr = m_spDoc->load(varFile, &varBool);
if (FAILED(hr))
return hr;
if (!varBool)
return E_FAIL;
hr = m_spDoc->get_documentElement(&m_spRoot);
if (FAILED(hr))
return hr;
if (!m_spRoot)
return E_FAIL;
CComPtr<IXMLDOMElement> spElement;
hr = m_spRoot->QueryInterface(&spElement);
if (FAILED(hr))
return hr;
if (!spElement)
return E_FAIL;
CComPtr<IXMLDOMNodeList> spList;
hr = spElement->getElementsByTagName(L"browser", &spList);
if (FAILED(hr))
return hr;
if (!spList)
return E_FAIL;
CComPtr<IXMLDOMNode> spCurrent;
hr = spList->nextNode(&spCurrent);
if (FAILED(hr))
return hr;
while (spCurrent)
{
CComPtr<IXMLDOMNamedNodeMap> spAttrs;
CComPtr<IXMLDOMNode> spItem;
DOMNodeType nodeType;
hr = spCurrent->get_nodeType(&nodeType);
if (FAILED(hr))
return hr;
if (nodeType == NODE_ELEMENT)
{
hr = spCurrent->get_attributes(&spAttrs);
if (FAILED(hr))
return hr;
hr = spAttrs->getNamedItem((BSTR)L"user-agent", &spItem);
if (FAILED(hr))
return hr;
CComVariant varVal;
hr = spItem->get_nodeValue(&varVal);
if (FAILED(hr))
return hr;
hr = varVal.ChangeType(VT_BSTR);
if (FAILED(hr))
return hr;
CComBSTR bstrValue = varVal.bstrVal;
m_Map.Add((LPCWSTR)bstrValue, spCurrent);
bstrValue.Detach();
}
CComPtr<IXMLDOMNode> spNext;
spList->nextNode(&spNext);
spCurrent = spNext;
}
return S_OK;
}
void Clear()
{
if (!m_spDoc)
return;
m_Map.RemoveAll();
m_spRoot.Release();
m_spDoc.Release();
}
CSimpleMap<LPCWSTR, CComPtr<IXMLDOMNode>, CWildCardEqualHelper< CComPtr<IXMLDOMNode> > > m_Map;
CComCriticalSection m_critSec;
CComPtr<IXMLDOMDocument> m_spDoc;
CComPtr<IXMLDOMElement> m_spRoot;
};
// Copies a CString into a null-terminated string.
// pdwDestLen on input is the size of the buffer in characters (including the null)
// On success, pdwDestLen contains the length of the string in characters (not including the null)
// On failure, pdwDestLen contains the length of the string including the null.
template <class StringType>
inline BOOL CopyCString(const StringType& str, StringType::PXSTR szDest, DWORD *pdwDestLen) throw()
{
if (!pdwDestLen)
return FALSE;
DWORD dwLen = str.GetLength();
if (!szDest || *pdwDestLen < (dwLen + 1))
{
*pdwDestLen = dwLen + 1;
return FALSE;
}
StringType::PCXSTR szBuffer = str;
if (szBuffer)
{
memcpy(szDest, szBuffer, (dwLen+1) * sizeof(StringType::XCHAR));
*pdwDestLen = dwLen;
return TRUE;
}
return FALSE;
}
// Call this function to convert from a SYSTEMTIME
// structure to an Http-date as defined in rfc2616
inline void SystemTimeToHttpDate(const SYSTEMTIME& st, CStringA &strTime)
{
static LPCSTR szDays[] = { "Sun", "Mon", "Tue",
"Wed", "Thu", "Fri", "Sat" };
static LPCSTR szMonth[] = { "Jan", "Feb", "Mar", "Apr",
"May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Nov", "Dec" };
strTime.Format("%s, %02d %s %d %02d:%02d:%02d GMT",
szDays[st.wDayOfWeek], st.wDay, szMonth[st.wMonth-1], st.wYear,
st.wHour, st.wMinute, st.wSecond);
}
// RGBToHtml - Converts a COLORREF to a color that can be used in HTML.
// Eg. RGB(11,22,33) would be converted to #112233
// color: The color to convert.
// pbOut: The output buffer that will hold the the resulting color.
// nBuffer: Specifies the number of bytes in pbOut.
bool inline RGBToHtml(COLORREF color, LPTSTR pbOut, long nBuffer)
{
// make sure the buffer is big enough
if (nBuffer < (7 * sizeof(TCHAR)))
return false;
wsprintf(pbOut, _T("#%0.2x%0.2x%0.2x"),
GetRValue(color), GetGValue(color), GetBValue(color));
return true;
}
} // namespace ATL
#pragma warning( pop )
#endif // __ATLUTIL_H__