一个winhttp的服务程序

 

    公司的考勤系统有两套,一套是门卡打卡上班,另一套是计算机登陆考勤网站上班。像我这样成天乘公交上班的人,不知道什么时候一堵车就迟到了。因此无聊加无奈,才写了下面的程序。。。。
    首先考察一下门卡系统。因为要刷卡才能上班,刷卡数据保存在公司linux服务器上,没啥手脚可以动。于是转向网站考勤系统。这个网站是用jsp写得,于是打算用EffeTech HTTP Sniffer侦查一下。本来以为需要很费劲的,其实嗅探下来结果非常简单,登陆请求就是用URL rewrite技术包装的。把这个url请求截获之后,就可以开始模拟请求了。用啥语言呢,java中可以直接用socket发请求,但是win32的Platform Sdk中有更高层的HTTP协议包装——winhttp。于是就简单地设计了下面的请求发送类
 
//-----------------------------------------------------------------------
// Name: class CMyWinHttp
// Desc: Connect to the web server, process the interaction based on http
//-----------------------------------------------------------------------
class CMyWinHttp
{
       HINTERNET m_hSession;
       HINTERNET m_hConnect;
       HINTERNET m_hRequest;
 
public:
       CMyWinHttp();
 
       INT Init();
       VOID Close();
       BOOL ProcessOneRequest(LPCWSTR pwszObjectName);
 
};
 
    构造函数就不说了,反正是初始化成员变量为NULL。Init函数比较简单,其中HOST和PORT就是服务器IP和80端口。
///---------------------------------------------------------------
///     DESC: Init the connection
///     Unless the host/port/user-agent changes...
///---------------------------------------------------------------
INT CMyWinHttp::Init()
{
       // Use WinHttpOpen to obtain a session handle.
       m_hSession = WinHttpOpen( USER_AGENT, 
              WINHTTP_ACCESS_TYPE_NO_PROXY,
              WINHTTP_NO_PROXY_NAME,
              WINHTTP_NO_PROXY_BYPASS, 0 );
 
       // Specify an HTTP server.
       if( m_hSession )
              m_hConnect = WinHttpConnect(      m_hSession,
              HOST,
              PORT, 0 );
 
       if ( m_hConnect )
              return 0;
      
       return GetLastError();
}
 
    Close()函数也不说了,关闭一些句柄。对生成和发送Get请求的包装处理如下:
BOOL CMyWinHttp::ProcessOneRequest(LPCWSTR pwszObjectName)
{
       DWORD dwSize          = 0;
       DWORD dwDownloaded           = 0;
       LPSTR pszOutBuffer;
       BOOL bResults         = FALSE;
 
       // Create an HTTP request handle.
       if( m_hConnect )
              m_hRequest = WinHttpOpenRequest(      m_hConnect,
              L"GET",
              pwszObjectName,
              NULL,
              WINHTTP_NO_REFERER,
              WINHTTP_DEFAULT_ACCEPT_TYPES,
              0 );
 
       // Send a request.
       if( m_hRequest )
              bResults = WinHttpSendRequest( m_hRequest,
              ADDITIONAL, -1L,
              WINHTTP_NO_REQUEST_DATA, 0,
              0, 0 );
 
 
       // End the request.
       if( bResults )
              bResults = WinHttpReceiveResponse( m_hRequest, NULL );
 
       // Keep checking for data until there is nothing left.
       if( bResults )
       {
              do
              {
                     // Check for available data.
                     dwSize = 0;
                     if( !WinHttpQueryDataAvailable( m_hRequest, &dwSize ) )
                            printf( "Error %u in WinHttpQueryDataAvailable./n",
                            GetLastError( ) );
 
                     // Allocate space for the buffer.
                     pszOutBuffer = new char[dwSize+1];
                     if( !pszOutBuffer )
                     {
                            printf( "Out of memory/n" );
                            dwSize=0;
                     }
                     else
                     {
                            // Read the data.
                            ZeroMemory( pszOutBuffer, dwSize+1 );
 
                            if( !WinHttpReadData( m_hRequest, (LPVOID)pszOutBuffer,
                                   dwSize, &dwDownloaded ) )
                                   printf( "Error %u in WinHttpReadData./n",
                                   GetLastError( ) );
                            else
                                   printf( "%s", pszOutBuffer );
 
                            // Free the memory allocated to the buffer.
                            delete [] pszOutBuffer;
                     }
              } while( dwSize > 0 );
       }
 
 
       // Report any errors.
       if( !bResults )
              printf( "Error %d has occurred./n", GetLastError( ) );
 
       return bResults;
}
 
    主函数只要这么写就可以了,其中SUBMIT是解析出来的GET请求。
int _tmain(int argc, _TCHAR* argv[])
{
 
       CMyWinHttp* myWinHttp = new CMyWinHttp();
       if ( myWinHttp->Init() != 0 )
              return -1;
 
       if ( !myWinHttp->ProcessOneRequest(SUBMIT) )
              return -1;
 
       myWinHttp->Close();
       return 0;
}
 
    到此九阳神功略有小成,下面要关注的就是怎么让它每天都可以自己跑。可以选择放到计
划任务中,可是总有设置的麻烦。于是打算写个服务,只有在系统开机的时候运行一次,然后
再把机器BIOS设置为定时启动就可以了(俺rp不错,正好机器的BIOS中有这个功能,而且还能
选择autoboot only weekday,晕一个)。网上介绍编写系统服务的铺天盖地,我把那个
NTService修改了一下就可以用了。类设计如下:
//-----------------------------------------------------------------------------
// Name: class CService
// Desc: System service registration
//-----------------------------------------------------------------------------
class CService
{
       DWORD dwAttendantTime;
       TCHAR* lpServiceName;
public :
       CService();
       //~CService();
       static void WINAPI ServiceMain( DWORD argc, TCHAR*      argv[] );
       static void WINAPI ServiceControlHandler( DWORD controlCode );
       void InstallService();
       void UninstallService();
       void RunService();
 
       SERVICE_STATUS serviceStatus;
       SERVICE_STATUS_HANDLE serviceStatusHandle;
       static CService* m_This;
       HANDLE stopServiceEvent;
 
};
    其中有两个静态的成员函数,用来做win32api的回调函数。哎,没有c#的delegate爽。顺便提一下,网上那篇高性能c++委托的实现看起来不太实用,如果不强调高性能的话使用静态成员函数作Callback函数就可以了。注意有一个静态成员指针m_This指向类实例,在回调函数中可以使用它访问类的成员(这个事情在java中是完全不可思议的,因为Java中没有指针,所以Java只能使用内部类的技术来完成图形界面的事件相应)。跑题了,下面回过来看ServiceMain()函数
 
//-----------------------------------------------------------------------------
/// @brief    Static Function ServiceMain for CallBack
/// @note     Entry point of the service
//-----------------------------------------------------------------------------
void CService::ServiceMain( DWORD argc, TCHAR* argv[] )
{
       CService& ThisService = *m_This;
       struct tm* tm;
       time_t now;
       DWORD minnow;
       CMyWinHttp* myWinHttp;
       ThisService.dwAttendantTime = GenerateRandomTime();
       // initialise service status
       ThisService.serviceStatus.dwServiceType = SERVICE_WIN32;
       ThisService.serviceStatus.dwCurrentState = SERVICE_STOPPED;
       ThisService.serviceStatus.dwControlsAccepted = 0;
       ThisService.serviceStatus.dwWin32ExitCode = NO_ERROR;
       ThisService.serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
       ThisService.serviceStatus.dwCheckPoint = 0;
       ThisService.serviceStatus.dwWaitHint = 0;
 
       ThisService.serviceStatusHandle = RegisterServiceCtrlHandler(
              ThisService.lpServiceName,
              ServiceControlHandler );
 
       if ( ThisService.serviceStatusHandle )
       {
              // Service is starting
              ThisService.serviceStatus.dwCurrentState = SERVICE_START_PENDING;
              SetServiceStatus(    ThisService.serviceStatusHandle,
                     &(ThisService.serviceStatus) );
 
              // Do initialization
              ThisService.stopServiceEvent = CreateEvent( 0, FALSE, FALSE, 0);
 
              // Running
              ThisService.serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
              ThisService.serviceStatus.dwCurrentState = SERVICE_RUNNING;
              SetServiceStatus(    ThisService.serviceStatusHandle,
                     &(ThisService.serviceStatus) );
 
              do
              {
                     //Beep( 1000, 100 );
                     //Sleep(100);
                     time(&now);
                     tm = localtime(&now);
                     minnow = tm->tm_min;
                     // only exectue at 8:00~9:00 on weekday
                     if ( (tm->tm_wday == 0) || (tm->tm_wday == 6) || (tm->tm_hour != 8) )
                     {
                            Sleep(2000);
                            SetEvent( ThisService.stopServiceEvent );
                            break;
                     }
                     if (ThisService.dwAttendantTime+38<minnow)
                     {
                            // for example: now is 8:45, min is 26, then do following:
                            myWinHttp = new CMyWinHttp();
                            myWinHttp->Init();
                            myWinHttp->ProcessOneRequest(QIANZ_SUBMIT);
                            myWinHttp->Close();
                            SetEvent( ThisService.stopServiceEvent );
                     }
                     else
                     {
                            Sleep(1000);
                     }
              }
              while ( WaitForSingleObject( ThisService.stopServiceEvent, 1000 ) == WAIT_TIMEOUT );
 
              // Service was stopped
              ThisService.serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
              SetServiceStatus(    ThisService.serviceStatusHandle,
                     &(ThisService.serviceStatus) );
 
              // do cleanup here
              CloseHandle( ThisService.stopServiceEvent );
              ThisService.stopServiceEvent = 0;
 
              // service is now stopped
              ThisService.serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
              ThisService.serviceStatus.dwCurrentState = SERVICE_STOPPED;
              SetServiceStatus(    ThisService.serviceStatusHandle,
                     &(ThisService.serviceStatus) );
       }
}
    看明白了吗?使用引用CService& ThisService = *m_This来访问成员变量,并且只
有在早上八点到九点的时候向服务器发送考勤上班请求,然后自动关闭服务。这样以后就不会
迟到了。

你可能感兴趣的:(java,service,Class,callback,initialization,winapi)