3612 lines
84 KiB
C++
3612 lines
84 KiB
C++
// 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__
|