wy

posts - 3, comments - 0, trackbacks - 0, articles - 7
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

Windows服务程序模板

Posted on 2008-04-30 17:25 鱼儿 阅读(856) 评论(0)  编辑 收藏 引用 所属分类: SDK

msdn偶得,感觉是那么的干净漂亮,好东西大家分享
 
 
 
/***************************************************************\
*    TEMPLATE CPP FOR SERVICE PROGRAM 01/08/2003 *
*      BY JONATHAN NG       *
*                *
*                *
* Usage:              *
* 1) Add service.cpp to project workspace     *
* 2) Change the ServiceName under the global variable  *
*  to desire name           *
* 3) Add your function into ServiceThread()     *
* 4) DONE!             *
*                *
* Install:             *
* 1) Use command prompt.          *
* 2) Locate the exe.           *
* 3) Type <programname> -i  Install      *
*  Type <programname> -u  Uninstall     *
*  Type <programname> HELP  For more     *
*                *
\***************************************************************/
#include <windows.h>
#include <iostream.h>
#include <stdio.h>
static struct ErrEntry {
 int code;
 const char* msg;
} ErrList[] = {
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/system_error_codes.asp
 { 0, "No error" },
 { 1055, "The service database is locked." },
 { 1056, "An instance of the service is already running." },
 { 1060, "The service does not exist as an installed service." },
 { 1061, "The service cannot accept control messages at this time." },
 { 1062, "The service has not been started." },
 { 1063, "The service process could not connect to the service controller." },
 { 1064, "An exception occurred in the service when handling the control request." },
 { 1065, "The database specified does not exist." },
 { 1066, "The service has returned a service-specific error code." },
 { 1067, "The process terminated unexpectedly." },
 { 1068, "The dependency service or group failed to start." },
 { 1069, "The service did not start due to a logon failure." },
 { 1070, "After starting, the service hung in a start-pending state." },
 { 1071, "The specified service database lock is invalid." },
 { 1072, "The service marked for deletion." },
 { 1073, "The service already exists." },
 { 1078, "The name is already in use as either a service name or a service display name." },
};
const int nErrList = sizeof(ErrList) / sizeof(ErrEntry);

//// Global /////////////////////////////////////////////////////////
FILE*  pLog;      
char*  ServiceName = "ServiceTest";  // Name of the service
HANDLE  terminateEvent = NULL;   // Event used to hold ServerMain from completing
           // Handle used to communicate status info with
           // the SCM. Created by RegisterServiceCtrlHandler
HANDLE  threadHandle = 0;    // Thread for the actual work
BOOL  pauseService = FALSE;   // Flags holding current state of service
BOOL  runningService = FALSE;   //
SERVICE_STATUS_HANDLE serviceStatusHandle; //
DWORD WINAPI ServiceThread( LPDWORD lParam);
BOOL InitService();
BOOL SendStatusToSCM(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint);
void ResumeService();
void PauseService();
void StopService();
void terminate(DWORD error);
void ServiceCtrlHandler(DWORD controlCode);//服务操作
void ServiceMain(DWORD argc, LPTSTR *argv);//注册服务
void ErrorHandler(char *s, int err);
void GetStatus(SC_HANDLE service);
void ShowUsage();
// service config program tasks
bool InstallService();
bool UninstallService();
bool GetConfiguration();
bool ChangeConfig();
// service control program tasks
bool ServiceRun();
bool ServiceControl(char* CONTROL);
void main(int argc, char *argv[])
{
 SERVICE_TABLE_ENTRY serviceTable[] =
 {
  { ServiceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
  { NULL, NULL}
 };
 BOOL success;
 
 if(argc == 2)
 {
  if (stricmp("-i", argv[1]) == 0)
   InstallService();
  else if (stricmp("-u", argv[1]) == 0)  
   UninstallService();
  else if (stricmp("-r", argv[1]) == 0)
   ServiceRun();
  else if (stricmp("-s", argv[1]) == 0)
   ServiceControl("STOP");
  else if (stricmp("-p", argv[1]) == 0)
   ServiceControl("PAUSE");
  else if (stricmp("-c", argv[1]) == 0)
   ServiceControl("RESUME");
  else if (stricmp("status", argv[1]) == 0)
  {
   SC_HANDLE scm, service;
   //Open connection to SCM
   scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
   if (!scm)
    ErrorHandler("OpenSCManager", GetLastError());
   //Get service's handle
   service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS);
   if (!service)
    ErrorHandler("OpenService", GetLastError());
   cout << "STATUS: ";
   GetStatus(service);
  }
  else if (stricmp("config", argv[1]) == 0)
   GetConfiguration();
  else if (stricmp("help", argv[1]) == 0)
   ShowUsage();
  //add other custom commands here and
  //update ShowUsage function
  else
   ShowUsage();
 }
 
 else
 {
  //register with SCM
  success = StartServiceCtrlDispatcher(serviceTable);
  if (!success)
   ErrorHandler("StartServiceCtrlDispatcher",GetLastError());
 }
}

void ServiceMain(DWORD argc, LPTSTR *argv)
{
 BOOL success;
 //immediately call registration function
 serviceStatusHandle = RegisterServiceCtrlHandler(ServiceName, (LPHANDLER_FUNCTION)ServiceCtrlHandler);
 if (!serviceStatusHandle)
 {
  terminate(GetLastError());
  return;
 }
 //notify SCM
 success = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 1, 5000);
 if (!success)
 {
  terminate(GetLastError());
  return;
 }
 //create termination event
 terminateEvent = CreateEvent (0, TRUE, FALSE, 0);
 if (!terminateEvent)
 {
  terminate(GetLastError());
  return;
 }
 //notify SCM
 success = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 2, 1000);
 if (!success)
 {
  terminate(GetLastError());
  return;
 }
 /*
 //check for startup parameter
 if (argc == 2)
 
 else
 
 */
 //notify SCM
 success = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 3, 5000);
 if (!success)
 {
  terminate(GetLastError());
  return;
 }
 //start service
 success = InitService();
 if (!success)
 {
  terminate(GetLastError());
  return;
 }
 //notify SCM service is runnning
 success = SendStatusToSCM(SERVICE_RUNNING, NO_ERROR, 0 , 0, 0);
 if (!success)
 {
  terminate(GetLastError());
  return;
 }
 //wait for stop signal and then terminate
 WaitForSingleObject(terminateEvent, INFINITE);
 terminate(0);
}

DWORD WINAPI ServiceThread(LPDWORD lParam)
{
 
 //DO YOUR THINGS HERE
 return 0;
}
//initialises the service by starting its thread
BOOL InitService()
{
 DWORD id;
 // Start the service's thread
 threadHandle = CreateThread(
 NULL,
 0,
 (LPTHREAD_START_ROUTINE) ServiceThread,
 NULL,
 0,
 &id);
 
 if (threadHandle == 0)
  return FALSE;
 else
 {
  runningService = TRUE;
  return TRUE;
 }
}
//resumes paused service
void ResumeService()
{
 pauseService = FALSE;
 ResumeThread(threadHandle);
}
//pauses service
void PauseService()
{
 pauseService = TRUE;
 SuspendThread(threadHandle);
}
//stops service by allowing ServiceMain to complete
void StopService()
{
 runningService = FALSE;
 //set the event that is holding ServiceMain
 SetEvent(terminateEvent);
}
//this function consolidates the activities of updating
//the service status with SetServiceStatus
BOOL SendStatusToSCM(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)
{
 BOOL success;
 SERVICE_STATUS serviceStatus;
 //fill in all of the SERVICE_STATUS fields
 serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
 serviceStatus.dwCurrentState = dwCurrentState;
 //if in the process of something, then accept
 //no control events, else accept anything
 if (dwCurrentState == SERVICE_START_PENDING)
  serviceStatus.dwControlsAccepted = 0;
 else
  serviceStatus.dwControlsAccepted =
   SERVICE_ACCEPT_STOP |
   SERVICE_ACCEPT_PAUSE_CONTINUE |
   SERVICE_ACCEPT_SHUTDOWN;
 //if a specific exit code is defines, set up the win32 exit code properly
 if (dwServiceSpecificExitCode == 0)
  serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
 else
  serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
 
 serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
 serviceStatus.dwCheckPoint = dwCheckPoint;
 serviceStatus.dwWaitHint = dwWaitHint;
 
 success = SetServiceStatus (serviceStatusHandle, &serviceStatus);
 if (!success)
  StopService();
 return success;
}
void ServiceCtrlHandler(DWORD controlCode)
{
 DWORD currentState = 0;
 BOOL success;
 switch(controlCode)
 {
  // START = ServiceMain()
  // STOP
  case SERVICE_CONTROL_STOP:
   currentState = SERVICE_STOP_PENDING;
   //notify SCM
   success = SendStatusToSCM(
    SERVICE_STOP_PENDING,
    NO_ERROR,
    0,
    1,
    5000);
   //stop service
   StopService();
   return;
  // PAUSE
  case SERVICE_CONTROL_PAUSE:
   if (runningService && !pauseService)
   {
    //notify SCM
    success = SendStatusToSCM(
     SERVICE_PAUSE_PENDING,
     NO_ERROR,
     0,
     1,
     1000);
   
    PauseService();
    currentState = SERVICE_PAUSED;
   }
   break;
  
  // RESUME
  case SERVICE_CONTROL_CONTINUE:
   if (runningService && pauseService)
   {
    //notify SCM
    success = SendStatusToSCM(
     SERVICE_CONTINUE_PENDING,
     NO_ERROR,
     0,
     1,
     1000);
   
    ResumeService();
    currentState = SERVICE_RUNNING;
   }
   break;
  // UPDATE
  case SERVICE_CONTROL_INTERROGATE:
   //update status out of switch()
   break;
  
  case SERVICE_CONTROL_SHUTDOWN:
   //do nothing
   return;
  default:
   break;
 }
 //notify SCM current state
 SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);
}
 
//handle an error from ServiceMain by cleaning up and tell SCM service didn't start.
void terminate(DWORD error)
{
 //close event handle
 if (terminateEvent)
  CloseHandle(terminateEvent);
 //notify SCM service stopped
 if (serviceStatusHandle)
  SendStatusToSCM(SERVICE_STOPPED, error, 0, 0, 0);
 //close thread handle
 if (threadHandle)
  CloseHandle(threadHandle);
}
 
void ErrorHandler(char *s, int err)
{
 cout << s << " failed" << endl;
 cout << "Error (" << err << "): ";
 int i;
 for (i = 0; i < nErrList; ++i) {
  if (ErrList[i].code == err) {
   cout << ErrList[i].msg;
   break;
  }
 }
 if (i == nErrList) {
  cout << "unknown error";
 }
 cout << endl;
 pLog = fopen("server.log","a");
 fprintf(pLog, "%s failed, error code = %d\n",s , err);
 fclose(pLog);
 ExitProcess(err);
}
void ShowUsage()
{
  cout << endl;
  cout << "USAGE:" << endl;
  cout << "server -i\tInstall service" << endl;
  cout << "server -u\tUninstall service" << endl;
  cout << "server -r\tRun service" << endl;
  cout << "server -s\tStop service" << endl;
  cout << "server -p\tPause service" << endl;
  cout << "server -c\tResume service" << endl;
  cout << "server status\tCurrent status" << endl;
  cout << endl;
}

////////////////////////////////////////////////////////////////////////////////
// Purpose :Install service into SCM.
// Parameter:N/A
// Returns :N/A
////////////////////////////////////////////////////////////////////////////////
bool InstallService()
{
 SC_HANDLE newService;
 SC_HANDLE scm;
    char szBuffer[255];
    char szPath[MAX_PATH];
 //get file path
 GetModuleFileName( GetModuleHandle(NULL), szPath, MAX_PATH );
    strcpy( szBuffer, "\"" );
    strcat( szBuffer, szPath );
    strcat( szBuffer, "\"" );
 //open connection to SCM
 scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
 if (!scm)
  ErrorHandler("OpenSCManager", GetLastError());
 //install service
 newService = CreateService(
  scm,      //scm database
  ServiceName,    //service name
  ServiceName,    //display name
  SERVICE_ALL_ACCESS,   //access rights to the service
  SERVICE_WIN32_OWN_PROCESS, //service type
  SERVICE_AUTO_START,   //service start type
  SERVICE_ERROR_NORMAL,  //error control type
  szBuffer,     //service path
  NULL,      //no load ordering group
  NULL,      //no tag identifier
  NULL,      //no dependencies
  NULL,      //LocalSystem account
  NULL);      //no password
 if(!newService)
 {
  ErrorHandler("CreateService", GetLastError());
  return false;
 }
 else
 {
  cout << "Service Installed" << endl;
  ServiceRun();
 }
 //clean up
 CloseServiceHandle(newService);
 CloseServiceHandle(scm);
 
 return true;
}
////////////////////////////////////////////////////////////////////////////////
// Purpose :Uninstall service from SCM.
// Parameter:N/A
// Returns :N/A
////////////////////////////////////////////////////////////////////////////////
bool UninstallService()
{
 SC_HANDLE service;
 SC_HANDLE scm;
 BOOL SUCCESS;
 SERVICE_STATUS status;
 //Open connection to SCM
 scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
 if (!scm)
  ErrorHandler("OpenSCManager", GetLastError());
 //Get service's handle
 service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS | DELETE);
 if (!service)
  ErrorHandler("OpenService", GetLastError());
 //Get service status
 SUCCESS = QueryServiceStatus(service, &status);
 if (!SUCCESS)
  ErrorHandler("QueryServiceStatus", GetLastError());
 
 //Stop service if necessary 
 if (status.dwCurrentState != SERVICE_STOPPED)
 {
  cout << "Stopping service..." << endl;
  SUCCESS = ControlService(service, SERVICE_CONTROL_STOP, &status);
  if (!SUCCESS)
   ErrorHandler("ControlService", GetLastError());
  Sleep(500);
 }
 //Delete service
 SUCCESS = DeleteService(service);
 if (SUCCESS)
  cout << "Service Uninstalled" << endl;
 else
  ErrorHandler("DeleteService", GetLastError());
 //Clean up
 CloseServiceHandle(service);
 CloseServiceHandle(scm);
 return true;
}

////////////////////////////////////////////////////////////////////////////////
// Purpose :Run service
// Parameter:N/A
// Returns :N/A
////////////////////////////////////////////////////////////////////////////////
bool ServiceRun()
{
    SC_HANDLE scm, Service;
 SERVICE_STATUS ssStatus;
    DWORD dwOldCheckPoint;
    DWORD dwStartTickCount;
    DWORD dwWaitTime;
    DWORD dwStatus;
 
 //open connection to SCM
 scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 if (!scm)
  ErrorHandler("OpenSCManager", GetLastError());
 //open service
 Service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS);
 if(!Service)
 {
  ErrorHandler("OpenService", GetLastError());
  return false;
 }
 else
 {
  //start service
  StartService(Service, 0, NULL);
  GetStatus(Service);
  // Check the status until the service is no longer start pending.
  if (!QueryServiceStatus( Service, &ssStatus) )
   ErrorHandler("QueryServiceStatus", GetLastError());
  // Save the tick count and initial checkpoint.
  dwStartTickCount = GetTickCount();
  dwOldCheckPoint = ssStatus.dwCheckPoint;
  while (ssStatus.dwCurrentState == SERVICE_START_PENDING)
  {
   // Do not wait longer than the wait hint. A good interval is
   // one tenth the wait hint, but no less than 1 second and no
   // more than 10 seconds.
   dwWaitTime = ssStatus.dwWaitHint / 10;
   if( dwWaitTime < 1000 )
    dwWaitTime = 1000;
   else if ( dwWaitTime > 10000 )
    dwWaitTime = 10000;
   Sleep( dwWaitTime );
   // Check the status again.
   if (!QueryServiceStatus(Service, &ssStatus) )
    break;
   if ( ssStatus.dwCheckPoint > dwOldCheckPoint )
   {
    // The service is making progress.
    dwStartTickCount = GetTickCount();
    dwOldCheckPoint = ssStatus.dwCheckPoint;
   }
   else
   {
    if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
    {
     // No progress made within the wait hint
     break;
    }
   }
  }
 
  if (ssStatus.dwCurrentState == SERVICE_RUNNING)
  {
   GetStatus(Service);
   dwStatus = NO_ERROR;
  }
  else
  {
   cout << "\nService not started." << endl;
   cout << "  Current State: " << ssStatus.dwCurrentState << endl;
   cout << "  Exit Code: " << ssStatus.dwWin32ExitCode << endl;
   cout << "  Service Specific Exit Code: " << ssStatus.dwServiceSpecificExitCode << endl;
   cout << "  Check Point: " << ssStatus.dwCheckPoint << endl;
   cout << "  Wait Hint: " << ssStatus.dwWaitHint << endl;
   dwStatus = GetLastError();
  } 
 }
 CloseServiceHandle(scm);
    CloseServiceHandle(Service);
    return true;
}

////////////////////////////////////////////////////////////////////////////////
// Purpose :Control service (STOP, PAUSE, CONTINUE).
// Parameter:N/A
// Returns :N/A
////////////////////////////////////////////////////////////////////////////////
bool ServiceControl(char* CONTROL)
{
 SC_HANDLE service;
 SC_HANDLE scm;
 BOOL SUCCESS;
 SERVICE_STATUS status;
 //Open connection to SCM
 scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 if (!scm)
  ErrorHandler("OpenSCManager", GetLastError());
 //Get service's handle
 service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS);
 if (!service)
  ErrorHandler("OpenService", GetLastError());
 
 //stop the service
 if (stricmp(CONTROL, "STOP") == 0)
 {
  cout << "Service is stopping..." << endl;
  SUCCESS = ControlService(service, SERVICE_CONTROL_STOP, &status);
 }
 //pause the service
 else if (stricmp(CONTROL, "PAUSE") == 0)
 {
  cout << "Service is pausing..." << endl;
  SUCCESS = ControlService(service, SERVICE_CONTROL_PAUSE, &status);
 }
 //continue the service
 else if (stricmp(CONTROL, "RESUME") == 0)
 {
  cout << "Service is resuming..." << endl;
  SUCCESS = ControlService(service, SERVICE_CONTROL_CONTINUE, &status);
 }
 if (!SUCCESS)
  ErrorHandler("ControlService", GetLastError());
 else
  GetStatus(service);
 //Clean up
 CloseServiceHandle(service);
 CloseServiceHandle(scm);
 return true;
}

////////////////////////////////////////////////////////////////////////////////
// Purpose :Get the current status of the service
// Parameter:service handle.
// Returns :N/A
////////////////////////////////////////////////////////////////////////////////
void GetStatus(SC_HANDLE service)
{
 BOOL SUCCESS;
 SERVICE_STATUS status;
 DWORD CurrentState;
 SUCCESS = QueryServiceStatus(service, &status);
 
 switch(status.dwCurrentState)
 {
  case SERVICE_RUNNING:
   CurrentState = SERVICE_RUNNING;
   cout << "Service RUNNING." << endl;
   break;
  case SERVICE_STOPPED:
   CurrentState = SERVICE_STOPPED;
   cout << "Service STOPPED." << endl;
   break;
  case SERVICE_PAUSED:
   CurrentState = SERVICE_PAUSED;
   cout << "Service PAUSED." << endl;
   break;
  case SERVICE_CONTINUE_PENDING:
   CurrentState = SERVICE_CONTINUE_PENDING;
   cout << "Service is resuming..." << endl;
   break;
  case SERVICE_PAUSE_PENDING:
   CurrentState = SERVICE_PAUSE_PENDING;
   cout << "Service is pausing..." << endl;
   break;
  case SERVICE_START_PENDING:
   CurrentState = SERVICE_START_PENDING;
   cout << "Service is starting..." << endl;
   break;
  case SERVICE_STOP_PENDING:
   CurrentState = SERVICE_STOP_PENDING;
   cout << "Service is stopping..." << endl;
   break;
  default:
   break;
 }
 SendStatusToSCM(CurrentState, NO_ERROR, 0, 0, 0);
}
////////////////////////////////////////////////////////////////////////////////
// Purpose :Get configuration of service
// Parameter:N/A
// Returns :N/A
////////////////////////////////////////////////////////////////////////////////
bool GetConfiguration()
{
 SC_HANDLE service;
 SC_HANDLE scm;
 BOOL success;
 LPQUERY_SERVICE_CONFIG buffer;
 DWORD sizeNeeded;
 //open connection to SCM
 scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 if (!scm)
  ErrorHandler("OpenSCManager", GetLastError());
 //get service's handle
 service = OpenService(scm, ServiceName, SERVICE_QUERY_CONFIG);
 if (!service)
  ErrorHandler("OpenService", GetLastError());
  //allocate space for buffer
 buffer = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, 4096);
    // Get the configuration information.
 success = QueryServiceConfig(service, buffer, 4096, &sizeNeeded);
 if (!success)
  ErrorHandler("QueryServiceConfig", GetLastError());
 //display the info
 cout << "Service name\t: " << buffer->lpDisplayName << endl;
 cout << "Service type\t: " << buffer->dwServiceType << endl;
 cout << "Start type\t: " << buffer->dwStartType << endl;
 cout << "Start name\t: " << buffer->lpServiceStartName << endl;
 cout << "Path\t\t: " << buffer->lpBinaryPathName << endl;
 LocalFree(buffer);
 CloseServiceHandle(service);
 CloseServiceHandle(scm);
 return TRUE;
}

bool ChangeConfig()
{
 SC_HANDLE service;
 SC_HANDLE scm;
 BOOL success;
 SC_LOCK lock;
 //open connection to SCM
 scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS | GENERIC_WRITE);
 if (!scm)
  ErrorHandler("OpenSCManager", GetLastError());
 //lock the database to guarantee exclusive access
 lock = LockServiceDatabase(scm);
 if (lock == 0)
  ErrorHandler("LockServiceDatabase", GetLastError());
 
 //get service's handle
 service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS);
 if (!service)
  ErrorHandler("OpenService", GetLastError());
 
// serviceType = SERVICE_NO_CHANGE;
// serviceStart = SERVICE_NO_CHANGE;
// serviceError = SERVICE_NO_CHANGE;
// path = 0;
 //change service config
 success = ChangeServiceConfig(
  service,
  SERVICE_NO_CHANGE,
  SERVICE_NO_CHANGE,
  SERVICE_NO_CHANGE,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL);
 if (!success)
 {
  UnlockServiceDatabase(lock);
  ErrorHandler("ChangeServiceConfig", GetLastError());
 }
 //unlock database
 success = UnlockServiceDatabase(lock);
 if (!success)
  ErrorHandler("UnlockServiceDatabase", GetLastError());
 
 //clean up
 CloseServiceHandle(service);
 CloseServiceHandle(scm);
 return TRUE;
}


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理