589 lines
15 KiB
C++
589 lines
15 KiB
C++
//---------------------------------------------------------------------------
|
|
#include <vcl.h>
|
|
#include <Wininet.h>
|
|
#include <tchar.h>
|
|
#include <stdio.h>
|
|
#include <vector>
|
|
//#include <System.DateUtils.hpp>
|
|
#pragma hdrstop
|
|
|
|
#include "HttpThread.h"
|
|
#include "GlobalUnit.h"
|
|
//---------------------------------------------------------------------------
|
|
#pragma package(smart_init)
|
|
#pragma comment(lib, "wininet")
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
DWORD WINAPI HttpProc(LPVOID args)
|
|
{
|
|
THttpParam* param = (THttpParam*)args;
|
|
|
|
TCHAR hdrsStream[] = _T("Content-Type: application/octet-stream\r\n"),
|
|
hdrsXForm[] = _T("Content-Type: application/x-www-form-urlencoded\r\n"),
|
|
*hdrs = param->iMode==0 ? hdrsStream : hdrsXForm;
|
|
LPCTSTR lpszAccept[] = { _T("text/html"), NULL };//{ _T("application/json"), NULL };//
|
|
char szBuffer[1024];
|
|
DWORD dwFlags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE,
|
|
dwByteRead = 0,
|
|
dwAllBytes = 0,
|
|
dwSize = 4;
|
|
int pos = 0;
|
|
bool bSent = false;
|
|
// char srvAddr[256];
|
|
|
|
//Open internet connection
|
|
param->hSession = InternetOpen( L"HttpPost/1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
|
|
if( !param->hSession ){
|
|
param->errCode = ERROR_INTERNET_OPEN;
|
|
return 0;
|
|
}
|
|
|
|
char addr[80];
|
|
int port = 80;
|
|
|
|
char* p = strstr( param->server, ":" );
|
|
if( p ){
|
|
memcpy( addr, param->server, p - param->server );
|
|
addr[p - param->server] = 0;
|
|
|
|
sscanf( p+1, "%d", &port );
|
|
}
|
|
else
|
|
strcpy( addr, param->server );
|
|
|
|
param->hConnect = InternetConnect(param->hSession,
|
|
(String(addr)).w_str(),
|
|
port,//INTERNET_DEFAULT_HTTP_PORT,
|
|
NULL,
|
|
NULL,
|
|
INTERNET_SERVICE_HTTP,
|
|
0,
|
|
1);
|
|
if( param->hConnect ){
|
|
if( port==INTERNET_DEFAULT_HTTPS_PORT )
|
|
dwFlags |= INTERNET_FLAG_SECURE;
|
|
|
|
param->hRequest = HttpOpenRequest(param->hConnect,
|
|
_T("POST"),
|
|
((String)param->api).c_str(),
|
|
NULL,
|
|
NULL,
|
|
NULL,//lpszAccept,//
|
|
dwFlags,
|
|
1);
|
|
|
|
if( param->hRequest ){
|
|
if( port==INTERNET_DEFAULT_HTTPS_PORT ){
|
|
DWORD flags = 0;
|
|
DWORD bufflen = sizeof(flags);
|
|
|
|
InternetQueryOption(param->hRequest, INTERNET_OPTION_SECURITY_FLAGS,
|
|
(LPVOID)&flags, &bufflen);
|
|
flags |= SECURITY_FLAG_IGNORE_REVOCATION;
|
|
InternetSetOption(param->hRequest, INTERNET_OPTION_SECURITY_FLAGS,
|
|
(LPVOID)&flags, sizeof(flags));
|
|
}
|
|
|
|
if( HttpSendRequest( param->hRequest, hdrs, wcslen(hdrs),
|
|
param->dataIn, param->sizeIn) ){
|
|
bSent = true;
|
|
}
|
|
else{
|
|
param->errCode = GetLastError();
|
|
}
|
|
}
|
|
else{
|
|
param->errCode = GetLastError();
|
|
}
|
|
}
|
|
else{
|
|
param->errCode = GetLastError();
|
|
}
|
|
|
|
if( !param->hConnect ){
|
|
param->errCode = ERROR_INTERNET_CONN;
|
|
return 0;
|
|
}
|
|
|
|
if( !param->hRequest ){
|
|
param->errCode = ERROR_INTERNET_REQ;
|
|
return 0;
|
|
}
|
|
|
|
if( !bSent ){
|
|
param->errCode = ERROR_INTERNET_SEND;
|
|
return 0;
|
|
}
|
|
|
|
// if( param->bGetDT ){
|
|
// SYSTEMTIME sysTime;
|
|
// SecureZeroMemory(&sysTime, sizeof(SYSTEMTIME));
|
|
// DWORD dwSize = sizeof(SYSTEMTIME);
|
|
// if( HttpQueryInfo( param->hRequest, HTTP_QUERY_DATE|HTTP_QUERY_FLAG_SYSTEMTIME,
|
|
// &sysTime, &dwSize, NULL ) ){
|
|
// TDateTime dt = SystemTimeToDateTime(sysTime);
|
|
// param->dwDateTime = DateTimeToUnix(dt);
|
|
// }
|
|
// else
|
|
// param->dwDateTime = 0;
|
|
// }
|
|
|
|
if( !HttpQueryInfo( param->hRequest, HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER,
|
|
&dwAllBytes, &dwSize, NULL ) ){
|
|
dwAllBytes = 0;
|
|
}
|
|
|
|
// 防止乱码的方法就是建立完变量立即清空
|
|
ZeroMemory(szBuffer, sizeof(szBuffer));
|
|
ZeroMemory(param->dataOut, param->sizeOut);
|
|
|
|
// 循环读取缓冲区内容直到结束
|
|
while( param->hRequest && InternetReadFile(param->hRequest, szBuffer, sizeof(szBuffer),
|
|
&dwByteRead) && dwByteRead > 0 ){
|
|
if( pos + dwByteRead > param->sizeOut ){
|
|
param->errCode = ERROR_SIZE;
|
|
return 0;
|
|
}
|
|
|
|
// 复制输出数据
|
|
memcpy( param->dataOut+pos, szBuffer, dwByteRead );
|
|
pos += dwByteRead;
|
|
|
|
// 清空缓冲区以备下一次读取
|
|
ZeroMemory(szBuffer, sizeof(szBuffer));
|
|
}
|
|
|
|
if( !param->hRequest ){
|
|
param->errCode = ERROR_INTERNET_REQ;
|
|
}
|
|
else if( dwAllBytes>0 && pos!=dwAllBytes ){
|
|
param->errCode = ERROR_SIZE;
|
|
}
|
|
else{
|
|
param->dataOut[pos] = 0;
|
|
param->sizeOut = pos;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
DWORD WINAPI VeloProc(LPVOID args)
|
|
{
|
|
TVeloParam* param = (TVeloParam*)args;
|
|
|
|
TCHAR hdrs[] = _T("Content-Type: application/x-www-form-urlencoded\r\n");
|
|
// TCHAR hdrs[] = _T("Content-Type: text/html\r\n");
|
|
LPCTSTR lpszAccept[] = { _T("*/*"), NULL };
|
|
// char srvAddr[256];
|
|
|
|
//Open internet connection
|
|
param->hSession = InternetOpen(L"Velocity",INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
|
|
if( !param->hSession ){
|
|
param->errCode = ERROR_INTERNET_OPEN;
|
|
return 0;
|
|
}
|
|
|
|
param->hConnect = InternetConnect(param->hSession,
|
|
(String(param->addr)).w_str(),
|
|
param->port,
|
|
NULL,
|
|
NULL,
|
|
INTERNET_SERVICE_HTTP,
|
|
0,
|
|
0);
|
|
if( !param->hConnect ){
|
|
param->errCode = ERROR_INTERNET_CONN;
|
|
return 0;
|
|
}
|
|
|
|
param->hRequest = HttpOpenRequest(param->hConnect,
|
|
_T("GET"),
|
|
((String)param->api).c_str(),
|
|
_T("HTTP/1.1"),
|
|
NULL,
|
|
NULL,//lpszAccept,
|
|
param->flag,
|
|
0);
|
|
if( !param->hRequest ){
|
|
//param->errCode = ERROR_INTERNET_REQ;
|
|
return 0;
|
|
}
|
|
|
|
if( param->port==INTERNET_DEFAULT_HTTPS_PORT ){
|
|
DWORD flags = 0;
|
|
DWORD bufflen = sizeof(flags);
|
|
|
|
InternetQueryOption(param->hRequest, INTERNET_OPTION_SECURITY_FLAGS,
|
|
(LPVOID)&flags, &bufflen);
|
|
flags |= SECURITY_FLAG_IGNORE_REVOCATION | SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
|
|
InternetSetOption(param->hRequest, INTERNET_OPTION_SECURITY_FLAGS,
|
|
(LPVOID)&flags, sizeof(flags));
|
|
}
|
|
|
|
if( !HttpSendRequest( param->hRequest, hdrs, wcslen(hdrs), NULL, 0) ){
|
|
//param->errCode = ERROR_INTERNET_SEND;
|
|
int err = GetLastError();
|
|
return 0;
|
|
}
|
|
|
|
DWORD dwCode = 0,
|
|
dwSize = sizeof(DWORD);
|
|
if( HttpQueryInfo( param->hRequest, HTTP_QUERY_STATUS_CODE |HTTP_QUERY_FLAG_NUMBER,
|
|
&dwCode, &dwSize, NULL ) ){
|
|
if( dwCode<HTTP_STATUS_SERVER_ERROR ){
|
|
param->errCode = 1;
|
|
ReleaseSemaphore( param->hSema, 1, NULL );
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
DWORD WINAPI ProbProc(LPVOID args)
|
|
{
|
|
TProbParam* param = (TProbParam*)args;
|
|
|
|
param->hSession = InternetOpen( L"RookIE/1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
|
|
if( param->hSession ){
|
|
String api = L"http://" + (String)param->server + L"/Api/chkServer";
|
|
|
|
param->hRequest = InternetOpenUrl( param->hSession, api.c_str(), NULL, 0,
|
|
INTERNET_FLAG_DONT_CACHE|INTERNET_FLAG_RELOAD|INTERNET_FLAG_PRAGMA_NOCACHE, 0 );
|
|
if( param->hRequest ){
|
|
DWORD dwByteRead = 0;
|
|
char szBuffer[1024];
|
|
|
|
ZeroMemory(szBuffer, sizeof(szBuffer));
|
|
|
|
InternetReadFile(param->hRequest, szBuffer, sizeof(szBuffer), &dwByteRead);
|
|
|
|
if( dwByteRead==2 && szBuffer[0]=='o' && szBuffer[1]=='k' ){
|
|
param->errCode = 1;
|
|
ReleaseSemaphore( param->hSema, 1, NULL );
|
|
}
|
|
}
|
|
else
|
|
param->errCode = ERROR_INTERNET_REQ;
|
|
}
|
|
else
|
|
param->errCode = ERROR_INTERNET_OPEN;
|
|
|
|
return 0;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
DWORD WINAPI UrlProc(LPVOID args)
|
|
{
|
|
TUrlParam* param = (TUrlParam*)args;
|
|
|
|
param->hSession = InternetOpen( L"RookIE/1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
|
|
if( param->hSession ){
|
|
param->hRequest = InternetOpenUrl( param->hSession, param->url.c_str(), param->header.c_str(), 0,
|
|
INTERNET_FLAG_DONT_CACHE|INTERNET_FLAG_RELOAD|INTERNET_FLAG_PRAGMA_NOCACHE, 0 );
|
|
if( param->hRequest ){
|
|
int pos = 0;
|
|
DWORD dwByteRead = 0;
|
|
char szBuffer[10240];
|
|
|
|
ZeroMemory(szBuffer, sizeof(szBuffer));
|
|
ZeroMemory(param->dataOut, param->sizeOut);
|
|
|
|
// 循环读取缓冲区内容直到结束
|
|
while( param->hRequest && InternetReadFile(param->hRequest, szBuffer, 10240, &dwByteRead)
|
|
&& dwByteRead > 0){
|
|
if( pos + dwByteRead > param->sizeOut ){
|
|
param->errCode = ERROR_SIZE;
|
|
break;
|
|
}
|
|
|
|
// 复制输出数据
|
|
memcpy( param->dataOut+pos, szBuffer, dwByteRead );
|
|
pos += dwByteRead;
|
|
|
|
// 清空缓冲区以备下一次读取
|
|
ZeroMemory(szBuffer, 10240);
|
|
}
|
|
|
|
param->dataOut[pos] = 0;
|
|
param->sizeOut = pos;
|
|
|
|
if( param->hRequest )
|
|
return 0;
|
|
}
|
|
|
|
param->errCode = ERROR_INTERNET_REQ;
|
|
}
|
|
else
|
|
param->errCode = ERROR_INTERNET_OPEN;
|
|
|
|
return 0;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool IsPureUrl(String url)
|
|
{
|
|
bool bPure = false;
|
|
int num = 0;
|
|
int len = url.Length();
|
|
|
|
for( int i=1; i<=len && num<5; i++ ){
|
|
if( url[i]=='/' ){
|
|
num++;
|
|
}
|
|
}
|
|
|
|
if( num<3 )
|
|
bPure = true;
|
|
else if( num==3 && url[len]=='/' )
|
|
bPure = true;
|
|
|
|
return bPure;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// TBaseWebThread
|
|
__fastcall TBaseWebThread::TBaseWebThread( HWND handle )
|
|
: TThread(true)
|
|
{
|
|
m_hWnd = handle;
|
|
m_hSema = CreateSemaphore( NULL, 0, 1, NULL );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
__fastcall TBaseWebThread::~TBaseWebThread()
|
|
{
|
|
if( m_hSema )
|
|
CloseHandle( m_hSema );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void __fastcall TBaseWebThread::Cancel()
|
|
{
|
|
Terminate();
|
|
ReleaseSemaphore( m_hSema, 1, NULL );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// TAsynHttpThread
|
|
__fastcall TAsynHttpThread::TAsynHttpThread( HWND handle, char* server, String api,
|
|
char* dataIn, int sizeIn, int op, int mode )
|
|
: TBaseWebThread( handle )
|
|
{
|
|
memcpy( m_dataIn, dataIn, sizeIn );
|
|
m_dataIn[sizeIn] = 0;
|
|
m_sizeIn = sizeIn;
|
|
|
|
memset( m_dataOut, 0, sizeof(m_dataOut) );
|
|
m_sizeOut = 0;
|
|
|
|
strcpy( m_server, server );
|
|
m_api = api;
|
|
m_iOp = op;
|
|
m_iMode = mode;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void __fastcall TAsynHttpThread::Execute()
|
|
{
|
|
char szReturn[10240];
|
|
int errCode = 0;
|
|
|
|
THttpParam param;
|
|
param.iMode = m_iMode;
|
|
param.sizeIn = m_sizeIn;
|
|
param.dataIn = m_dataIn;
|
|
param.sizeOut = 10240;
|
|
param.dataOut = szReturn;
|
|
strcpy( param.server, m_server );
|
|
strcpy( param.api, ((AnsiString)m_api).c_str() );
|
|
|
|
DWORD id;
|
|
HANDLE hThread = CreateThread( NULL, 0, HttpProc, ¶m, 0, &id );
|
|
if( hThread ){
|
|
HANDLE aHandle[2] = { m_hSema, hThread };
|
|
|
|
DWORD dwRet = WaitForMultipleObjects( 2, aHandle, FALSE, 15000 );
|
|
if( param.hRequest )
|
|
InternetCloseHandle( param.hRequest );
|
|
if( param.hConnect )
|
|
InternetCloseHandle( param.hConnect );
|
|
if( param.hSession )
|
|
InternetCloseHandle( param.hSession );
|
|
|
|
param.hSession = param.hConnect = param.hRequest = NULL;
|
|
|
|
WaitForSingleObject( hThread, 200 );
|
|
CloseHandle( hThread );
|
|
|
|
if( dwRet==WAIT_OBJECT_0 ){ //用户退出
|
|
return;//errCode = ERROR_INTERNET_CANCEL;
|
|
}
|
|
else if( dwRet==WAIT_OBJECT_0+1 && param.errCode==0 ){ //线程正常退出
|
|
m_sizeOut = param.sizeOut;
|
|
memcpy( m_dataOut, szReturn, m_sizeOut );
|
|
m_dataOut[m_sizeOut] = 0;
|
|
}
|
|
else if( dwRet==WAIT_TIMEOUT ) //超时
|
|
errCode = ERROR_INTERNET_TMOUT;
|
|
else if( param.errCode==0 )//出错了
|
|
errCode = ERROR_INTERNET_UNKNOWN;
|
|
}
|
|
else
|
|
errCode = ERROR_INTERNET_UNKNOWN;
|
|
|
|
if( m_hWnd )
|
|
PostMessage( m_hWnd, UM_HTTPRETURN, errCode, m_iOp );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// TAsynUrlThread
|
|
__fastcall TAsynUrlThread::TAsynUrlThread( HWND handle, String api, char* pzOut, int& szOut, int op, String header )
|
|
: TBaseWebThread( handle ), m_szOut(szOut)
|
|
{
|
|
m_url = api;
|
|
m_pzOut = pzOut;
|
|
m_iOp = op;
|
|
m_header = header;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void __fastcall TAsynUrlThread::Execute()
|
|
{
|
|
int errCode = 0;
|
|
|
|
TUrlParam param;
|
|
param.url = m_url;
|
|
param.header = m_header;
|
|
param.dataOut = m_pzOut;
|
|
param.sizeOut = m_szOut;
|
|
|
|
DWORD id;
|
|
HANDLE hThread = CreateThread( NULL, 0, UrlProc, ¶m, 0, &id );
|
|
if( hThread ){
|
|
HANDLE aHandle[2] = { m_hSema, hThread };
|
|
|
|
DWORD dwRet = WaitForMultipleObjects( 2, aHandle, FALSE, 15000 );
|
|
if( param.hRequest )
|
|
InternetCloseHandle( param.hRequest );
|
|
if( param.hSession )
|
|
InternetCloseHandle( param.hSession );
|
|
|
|
param.hSession = param.hRequest = NULL;
|
|
|
|
WaitForSingleObject( hThread, 200 );
|
|
CloseHandle( hThread );
|
|
|
|
if( dwRet==WAIT_OBJECT_0 ){ //用户退出
|
|
return;
|
|
}
|
|
else if( dwRet==WAIT_OBJECT_0+1 ){ //线程正常退出
|
|
m_szOut = param.sizeOut;
|
|
errCode = param.errCode;
|
|
}
|
|
else if( dwRet==WAIT_TIMEOUT ) //超时
|
|
errCode = ERROR_INTERNET_TMOUT;
|
|
else if( param.errCode==0 )//出错了
|
|
errCode = ERROR_INTERNET_UNKNOWN;
|
|
}
|
|
else
|
|
errCode = ERROR_INTERNET_UNKNOWN;
|
|
|
|
if( m_hWnd )
|
|
PostMessage( m_hWnd, UM_URLRETURN, errCode, m_iOp );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// TVeloThread
|
|
__fastcall TVeloThread::TVeloThread( HWND handle, int nWS, char* websites[],
|
|
int ports[], char* api, int msWait )
|
|
: TBaseWebThread( handle )
|
|
{
|
|
m_nWS = nWS;
|
|
m_websites = websites;
|
|
m_ports = ports;
|
|
m_msWait = msWait;
|
|
strcpy(m_sApi, api);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
void __fastcall TVeloThread::Execute()
|
|
{
|
|
HANDLE *aHandle = new HANDLE[m_nWS];
|
|
TVeloParam* aParam = new TVeloParam[m_nWS];
|
|
HANDLE hWaitSema = CreateSemaphore( NULL, 0, m_nWS>1 ? 2 : 1, NULL );
|
|
DWORD id;
|
|
|
|
for( int i=0; i<m_nWS; i++ ){
|
|
if(m_sWebsite.Length()>0 && m_sWebsite.Pos(m_websites[i])){
|
|
aHandle[i] = NULL;
|
|
}
|
|
else{
|
|
aParam[i].hSema = hWaitSema;
|
|
aParam[i].port = m_ports ? m_ports[i] : INTERNET_DEFAULT_HTTPS_PORT;
|
|
aParam[i].flag = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE;
|
|
if( aParam[i].port==INTERNET_DEFAULT_HTTPS_PORT )
|
|
aParam[i].flag |= INTERNET_FLAG_SECURE;
|
|
|
|
strcpy( aParam[i].addr, m_websites[i] );
|
|
strcpy( aParam[i].api, m_sApi );
|
|
//aParam[i].api[0] = 0;
|
|
|
|
aHandle[i] = CreateThread( NULL, 0, VeloProc, &aParam[i], CREATE_SUSPENDED, &id );
|
|
ResumeThread(aHandle[i]);
|
|
}
|
|
}
|
|
|
|
DWORD dwRet = WaitForSingleObject( hWaitSema, m_msWait );
|
|
|
|
int sel = -1;
|
|
for( int i=0; i<m_nWS; i++ ){
|
|
if( aHandle[i] ){
|
|
if( aParam[i].hRequest )
|
|
InternetCloseHandle( aParam[i].hRequest );
|
|
if( aParam[i].hConnect )
|
|
InternetCloseHandle( aParam[i].hConnect );
|
|
if( aParam[i].hSession )
|
|
InternetCloseHandle( aParam[i].hSession );
|
|
|
|
aParam[i].hSession = aParam[i].hConnect = aParam[i].hRequest = NULL;
|
|
|
|
WaitForSingleObject( aHandle[i], 800 );
|
|
|
|
CloseHandle( aHandle[i] );
|
|
|
|
if( aParam[i].errCode==1 && sel<0 )
|
|
sel = i;
|
|
}
|
|
}
|
|
|
|
if( sel<0 ){ //线程非正常退出
|
|
sel = GetTickCount() % m_nWS;
|
|
if( m_sWebsite.Pos( m_websites[sel] ) )
|
|
sel = (sel+1) % m_nWS;
|
|
}
|
|
|
|
if( !m_ports || m_ports[sel]==INTERNET_DEFAULT_HTTPS_PORT )
|
|
m_sWebsite = Format( "https://%s", ARRAYOFCONST((m_websites[sel])) );
|
|
else if( m_ports[sel]==INTERNET_DEFAULT_HTTP_PORT )
|
|
m_sWebsite = Format( "http://%s", ARRAYOFCONST((m_websites[sel])) );
|
|
else
|
|
m_sWebsite = Format( "http://%s:%d", ARRAYOFCONST((m_websites[sel], m_ports[sel])) );
|
|
|
|
CloseHandle( hWaitSema );
|
|
delete[] aHandle;
|
|
delete[] aParam;
|
|
|
|
if( !Terminated )
|
|
PostMessage( m_hWnd, UM_VELOCITY, sel, (LPARAM)this );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|