打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
利用C++编写Windows服务程序的一般框架
下面是我写的一个Service程序框架,实现了如下功能: 1。Service监视窗口的创建。 2。Service中消息处理的实现 3。C++ 类的使用。 4。工作线程函数的使用。 ****如果需要完成特定的工作,只需将线程函数具体化和实例化***。 (程序的注释是用日文写的,因我机器的OS是日文的WIN 2000,VC也是日文的VC6.0不能支持中文,抱歉)
/*
日付           バージョン   名前     内容        20040706       1.00     SecBug      新規作成
*/ class MyServiceModel
{
public:
MyServiceModel();
virtual ~MyServiceModel(); private:      //変数
// サービス名
LPCTSTR serviceName;
//サービスの状態を保持する
SERVICE_STATUS serviceStatus;
// private サービスのハンドル
SERVICE_STATUS_HANDLE serviceStatusHandle;  HWND hCMain_seivice_data;  //---------------私-----------
// Event used to hold ServiceMain from completing
HANDLE terminateEvent;
// Thread for the actual work
HANDLE threadHandle;
// Flags holding current state of service
BOOL pauseService ;
BOOL runningService ; //メソッド public:
//メインメソッド:
int  main(int argc, TCHAR* argv[]);
//サービスインストール:
void InstallService();
// サービスアンインストール:
void UninstallService();
//サービスメイン:
void WINAPI ServiceMain(DWORD argc, TCHAR* argv[]);
//サービスコントロールハンドラー:
void WINAPI ServiceControlHandler(DWORD contorlCode );
//ウインドウプロシージャ:
LRESULT CALLBACK WndProc(HWND hDlg, UINT Msg, WPARAM wParam ,LPARAM lParam);
//---------------私-----------  //ウインドウプロシージャ
static LRESULT CALLBACK _WndProc(
HWND hDlg,
UINT Msg,
WPARAM wParam ,
LPARAM lParam
);
//サービスコントロールハンドラー
static void WINAPI _ServiceControlHandler(DWORD contorlCode );
//サービスメイン
static void WINAPI _ServiceMain( DWORD argc, TCHAR * argv[] );
BOOL CreateSvcWindow();
//エラー 処理
void ErrorHandler(char *s, DWORD err);
//サービス終了する
VOID terminate(DWORD error);
//ステータス報告する
BOOL SendStatusToSCM (
DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode,
DWORD dwCheckPoint,
DWORD dwWaitHint
);
//サービス終了
VOID StopService();
//初期化サービス
BOOL InitService();
//レジメサービス
VOID ResumeService();
//ポーズサービス
VOID PauseService();
//
BOOL RunService(char *sSvcName);
};
//スレッド関数
DWORD WINAPI ServiceThread(LPDWORD param);     /*
日付           バージョン   名前     内容     20040706       1.00     SecBug      新規作成
*/   //変数宣言
extern MyServiceModel objApp; //////////////////////////////////////////////////////////////////////
// 構築/消滅
////////////////////////////////////////////////////////////////////// /*
機 能 構築
static
可視性 public
メソッド名 MyServiceModel
引 数
なし
戻り値
なし
*/
MyServiceModel::MyServiceModel()
{
// サービス名
serviceName = "MyServiceName";
//サービスの状態を保持する
serviceStatusHandle = NULL;
}
/*
機 能 消滅
static
可視性 public
メソッド名 ~MyServiceModel
引 数
なし
戻り値
なし
*/
MyServiceModel::~MyServiceModel()
{
} /*   機 能メインメソッド
static
可視性 public
メソッド名 main
引 数
引数の数  [IN] argc int 初期値なし
各引数   [IN] argv  TCHAR *[] 初期値なし
戻り値
int 常に0
備 考
なし
*/
int  MyServiceModel::main(int argc, TCHAR * argv[])
{
int iRet = 0;
TCHAR TServceName[256];
strcpy(TServceName,SVCSTARTNAME);
SERVICE_TABLE_ENTRY  dispatchTable[]=
{
{TEXT(TServceName),(LPSERVICE_MAIN_FUNCTION)_ServiceMain},
{ NULL,NULL}
};  //引数のチェック:
if(argc<=1)
{
//ブランクの場合は、usage(使用方法)表示
cout<<"usage :"<<ENDL;
//cout<<"zxz\"zxz"<<ENDL;
//"install"
cout<<"\"install\"";
//"or"
cout<<"or";
//"uninstall"
cout<<"\"uninstall\"";
//"exec"
cout<<"\"exec \"";
cout<<" parameter required in the command line"<<ENDL;??
}
//installの場合、サービスの登録を行う(InstallService呼出)
if(argc>1)
{
if(_stricmp("install",argv[1])==0)
{
InstallService();
}
//uninstallの場合、サービスの削除を行う(UninstallService呼出)
else if(_stricmp("uninstall",argv[1])==0)
{
UninstallService();
}
//execの場合、サービスの起動を行う(ServiceMain呼出)
else if(_stricmp("exec",argv[1])==0)
{
RunService(SVCSTARTNAME);
}
//ブランクの場合は、usage(使用方法)表示
else
{
cout<<"usage :"<<ENDL;
//"install"
cout<<"\"install\"";
//"or"
cout<<"or";
//"uninstall"
cout<<"\"uninstall\"";
//"exec"
cout<<"\"exec \"";
//"or"
cout<<"or";
cout<<" parameter required in the command line"<<ENDL;
}
}
//ブランクの場合、サービスの起動を行う(RunService呼出)
else
{
//RunService呼出
BOOL success;
success = StartServiceCtrlDispatcher(dispatchTable);
if(success)
{
ErrorHandler("In StartServiceCtrlDispatcher",GetLastError());
}
}
//戻り値 常に0
iRet = 0;
return iRet;
}
/*
機 能 サービスインストール
static
可視性 public
メソッド名 InstallService
引 数
なし
戻り値
なし
備 考
サービスのインストールを行う
*/
void MyServiceModel::InstallService()
{
SC_HANDLE schService;
SC_HANDLE schSCManager;
TCHAR szPath[512];
cout<<"Install Starting..."<<ENDL;
//ファイルパースお獲得する
if(!GetModuleFileName(NULL,szPath,512))
{
ErrorHandler("In GetModuleFileName",GetLastError());
return;
}
// open コネクション to SCM
schSCManager = OpenSCManager(
0,  // pointer to machine name string
0,  // pointer to database name string
SC_MANAGER_CREATE_SERVICE // type of access
);
if (!schSCManager)
{
ErrorHandler("In OpenScManager",GetLastError());
return;
}
// Install 新サービス
schService = CreateService(
schSCManager, // handle to service control manager database
SVCSTARTNAME, // pointer to name of service to start
SVCDISPLNAME, // pointer to display name
SERVICE_ALL_ACCESS,// type of access to service
SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS ,// type of service
SERVICE_DEMAND_START,// when to start service
SERVICE_ERROR_NORMAL,// severity if service fails to start
szPath,       // pointer to name of binary file
NULL,         // pointer to name of load ordering group
NULL,         // pointer to variable to get tag identifier
NULL,         // pointer to array of dependency names
NULL,         // pointer to account name of service
NULL          // pointer to password for service account
);
if (!schService)
{
ErrorHandler("In CreateService",GetLastError());
return;
}
else
{
cout << "Service installed\n";
}
// clean up
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
cout << "Install Ending...\n";
}
/*
機 能 サービスのアンインストール
static
可視性 public
メソッド名 UninstallService
引 数
なし
戻り値
なし
備 考
サービスのアンインストールを行う
*/
void MyServiceModel::UninstallService()
{
SC_HANDLE schService;
SC_HANDLE schSCManager;
BOOL success;
SERVICE_STATUS svcStatus;
cout << "Uninstall Starting..."<<ENDL;?
// open コネクション to SCM
schSCManager = OpenSCManager(
0,// pointer to machine name string
0,// pointer to database name string
SC_MANAGER_CREATE_SERVICE // type of access
);
if (!schSCManager)
{
ErrorHandler("In OpenScManager",GetLastError());
return;
}
// サービス ハンドル 獲得する
schService = OpenService(
schSCManager,              // handle to service control manager database
SVCSTARTNAME,              // pointer to name of service to start
SERVICE_ALL_ACCESS | DELETE// type of access to service
);
if (!schService)
{
ErrorHandler("In OpenService",GetLastError());
return;
}
// サービス終了 (if necessary)
success = QueryServiceStatus(schService, &svcStatus);
if (!success)
{
ErrorHandler("In QueryServiceStatus",GetLastError());
return;
}
if (svcStatus.dwCurrentState != SERVICE_STOPPED)
{
cout << "Stopping service..."<<ENDL;
success = ControlService(
schService,           // handle to service
SERVICE_CONTROL_STOP, // control code
&svcStatus            // pointer to service status structure
);
if (!success)
{
ErrorHandler("In ControlService",GetLastError());
return;
}
}
//サービス終了待つこと
do
{
QueryServiceStatus(schService,&svcStatus);
Sleep(500);
}while(SERVICE_STOP_PENDING==svcStatus.dwCurrentState);
if(SERVICE_STOPPED!=svcStatus.dwCurrentState)
{
ErrorHandler("Failed to Stop Service",GetLastError());
return;
}
// サービス削除する
success = DeleteService(schService);
if (success)
{
cout << "Service removed\n";
}
else
{
ErrorHandler("In DeleteService",GetLastError());
return;
}
// サービスClean up
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
cout << "Uninstal Ending..."<<ENDL;?
} /*
機 能 サービスメイン
static
可視性 public
メソッド名 ServiceMain
引 数
引数の個数     [IN] argc DWORD なし
各引数      [IN] argv TCHAR *[] なし        戻り値
なし
備 考
コントロール パネルのサービス アプリケーションを開いて、サービスを選択して [開始] をクリックした際に、SCMから呼び出される。
サービスのメイン処理を行う:
*/
void  WINAPI MyServiceModel::ServiceMain(DWORD argc,TCHAR *argv[])
{
BOOL success;    //  SCM にハンドラ関数を渡す
serviceStatusHandle = RegisterServiceCtrlHandler(
SVCSTARTNAME,
(LPHANDLER_FUNCTION)_ServiceControlHandler
);
if (! serviceStatusHandle)
{
terminate(GetLastError());
return;
}
// サービスを開始状態にする
success =  SendStatusToSCM(
SERVICE_START_PENDING,
NO_ERROR,
0,
1,
5000
);
if (!success)
{
terminate(GetLastError());
return;
}
// 終了イベント創造する
terminateEvent = CreateEvent (
0,
TRUE,
FALSE,
0
);
if (! terminateEvent)
{
terminate(GetLastError());
return;
}
// SCM 通報する
success =  SendStatusToSCM(
SERVICE_START_PENDING,
NO_ERROR,
0,
2,
1000
);
if (!success)
{
terminate(GetLastError());
return;
}
// 初期化サービス,
//メッセージIDを取得して、変数お代入
//ウインドウの作成
success =  InitService();
if (!success)
{
terminate(GetLastError());
return;
}
// サービス 実行.
//SCM 通報する
success =  SendStatusToSCM(
SERVICE_RUNNING,
NO_ERROR,
0,
0,
0
);
if (!success)
{
terminate(GetLastError());
return;
}
// サービスの停止待ち
WaitForSingleObject ( terminateEvent, INFINITE);
//サービスを停止状態にする。
terminate(0);
} /*
機 能 サービスコントロールハンドラー
static
可視性 public
メソッド名 ServiceControlHandler
引 数
状態コード   [IN] contorlCode DWORD なし
戻り値
なし
備 考
"SCM がサービスのステータスを得たり、停止や一時停止などの各種の命令をサービスに送るためにSCMから呼び出される。
SCM は Handler に、実行すべき処理の種類を表す処理コードを渡します。
1. サービス停止通知が来た場合、サービス停止中に変更
2. 停止処理を行う(ServiceMainに停止を通知)
3. その他の場合は何もしない
*/
void WINAPI MyServiceModel::ServiceControlHandler(DWORD contorlCode )
{
DWORD  currentState = 0;
BOOL success;
switch(contorlCode)
{
// There is no START option because
// ServiceMain gets called on a start
// サービス終了する
case SERVICE_CONTROL_STOP:
currentState = SERVICE_STOP_PENDING;
// Tell the SCM what's happening
success =  SendStatusToSCM(
SERVICE_STOP_PENDING,
NO_ERROR,
0,
1,
5000
);
// Not much to do if not successful
// Stop the service
StopService();
return;
// サービスポーズ
case SERVICE_CONTROL_PAUSE:
if (  runningService && !  pauseService)
{
// Tell the SCM what's happening
success =   SendStatusToSCM(
SERVICE_PAUSE_PENDING,
NO_ERROR,
0,
1,
1000);
PauseService();
currentState = SERVICE_PAUSED;
}
break;
//サービス再び始める
case SERVICE_CONTROL_CONTINUE:
if (  runningService &&   pauseService)
{
// Tell the SCM what's happening
success =   SendStatusToSCM(
SERVICE_CONTINUE_PENDING,
NO_ERROR,
0,
1,
1000
);
ResumeService();
currentState = SERVICE_RUNNING;
}
break;
// サービスの現在のステータス更新
case SERVICE_CONTROL_INTERROGATE:
// it will fall to bottom and send status
break;
// Do nothing in a shutdown. Could do cleanup
// here but it must be very quick.
case SERVICE_CONTROL_SHUTDOWN:
// Do nothing on shutdown
return;
default:
break;
}
//サービスの現在のステータス更新
SendStatusToSCM(
currentState,
NO_ERROR,
0,
0,
0
);
}
/*
機 能 ウインドウプロシージャ
static
可視性 public
メソッド名 WndProc
引 数
ウインドウハンドル [IN] hDlg      HWND なし
メッセージ   [IN] Msg  UINT なし
パラメタ1   [IN] wParam      WPARAM     なし
パラメタ2   [IN] lParam       LPARAM       なし
戻り値
LRESULT
備 考
"ウインドウメッセージの解析処理を行う
*/
LRESULT CALLBACK MyServiceModel::WndProc(HWND hDlg, UINT Msg, WPARAM wParam ,LPARAM lParam)
{LRESULT lrRet;    /*       //test begin
HDC hdc;
PAINTSTRUCT ps;
int x,y;
x = 60;
y = 20;       test end
*/
switch(Msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
};
//その他のメッセージの場合は、DefWindowProcを呼出"
lrRet = DefWindowProc(hDlg,Msg,wParam,lParam);
return lrRet;
}
/*
機 能 エラー 処理
static
可視性 private
メソッド名 ErrorHandler
引 数
パラメタ1   [IN] char *s     なし
パラメタ2   [IN]  DWORD err       なし
戻り値
なし
備 考
なし
*/
void MyServiceModel::ErrorHandler(char *s, DWORD err)
{
cout <<S<<ENDL;
cout << "Error number: " << err << endl;
char str1[50];
char str2[20];
memset(str1,0,sizeof(str1));
memset(str2,0,sizeof(str2));
strcpy(str1,s);
strcat(str1,", Error Code: ");
itoa(err,str2,10);
strcat(str1,str2);
ExitProcess(err);
}
/*
機 能 サービス終了する
static
可視性     private
メソッド名 terminate
引 数
パラメタ   DWORD error     なし
戻り値
なし
備 考
サービス終了する
*/
VOID MyServiceModel::terminate(DWORD error)
{
// if terminateEvent has been created, close it.
if (terminateEvent)
CloseHandle(terminateEvent);
// Send a message to the scm to tell about
// stopage
if (serviceStatusHandle)
SendStatusToSCM(SERVICE_STOPPED, error,
0, 0, 0);
// If the thread has started kill it off
if (threadHandle)
CloseHandle(threadHandle);
// Do not need to close serviceStatusHandle
}
/*
機 能 ステータス報告する
static
可視性     private
メソッド名
引 数
パラメタ1 DWORD dwCurrentState,
パラメタ2 DWORD dwWin32ExitCode,
パラメタ3 DWORD dwServiceSpecificExitCode,
パラメタ4 DWORD dwCheckPoint,
パラメタ5 DWORD dwWaitHint
戻り値
なし
備 考
consolidates the activities of updating the service status with SetServiceStatus
*/
BOOL MyServiceModel::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;
// Pass the status record to the SCM
success = SetServiceStatus (serviceStatusHandle,
&serviceStatus);
if (!success)
StopService();
return success;
}
/*
機 能 初期化サービス
static
可視性     private
メソッド名 InitService
引 数
なし
戻り値
なし
備 考
Initializes the service by starting its thread
*/
BOOL MyServiceModel::InitService()
{
DWORD id;    // Start the service's thread
threadHandle = CreateThread(0, 0,
(LPTHREAD_START_ROUTINE) ServiceThread,
0, 0, &id);
if (threadHandle==0)
return FALSE;
else
{
runningService = TRUE;
return TRUE;
}
}   /*
機 能 Startサービス
static
可視性     private
メソッド名 StartService
引 数
sSvcName :サービス名
戻り値
なし
備 考
Resumes a paused service
*/   BOOL MyServiceModel::RunService(char *sSvcName)
{
SC_HANDLE schSCManager;
SC_HANDLE scHandle;
BOOL boolRet;    // open コネクション to SCM
schSCManager = OpenSCManager(
0,
0,
SC_MANAGER_ALL_ACCESS
);    //Open サービス
scHandle = OpenService(
schSCManager,
sSvcName,
SERVICE_ALL_ACCESS
);
//start サービス
boolRet = StartService(
scHandle,
0,
NULL);
return boolRet;
}
/*
機 能 レジメサービス
static
可視性     private
メソッド名 ResumeService
引 数
なし
戻り値
なし
備 考
Resumes a paused service
*/
VOID MyServiceModel::ResumeService()
{
//pauseService=FALSE;
//ResumeThread(threadHandle);
return;
}
/*
機 能 ポーズサービス
static
可視性     private
メソッド名 PauseService
引 数
なし
戻り値
なし
備 考
Pauses the service
*/
VOID MyServiceModel::PauseService()
{
//pauseService = TRUE;
//SuspendThread(threadHandle);
return;
}
/*
機 能 サービス終了
static
可視性     private
メソッド名
引 数
パラメタ なし
戻り値
なし
備 考
Stops the service by allowing ServiceMain to complete
*/
VOID MyServiceModel::StopService()
{
runningService=FALSE;
// Set the event that is holding ServiceMain
// so that ServiceMain can return
SetEvent(terminateEvent);
}
/*
機 能  スレッド関数
static
可視性     private
メソッド名 ServiceThread
引 数
パラメタ LPDWORD param
戻り値
0
備 考
なし
*/
DWORD WINAPI ServiceThread(LPDWORD param)
{
//ウインドウの作成
objApp.CreateSvcWindow();
return 0;
}
/*
機 能 ウインドウの作成
static
可視性     private
メソッド名 CreateSvcWindow
引 数
パラメタ なし
戻り値
なし
備 考
なし
*/
BOOL MyServiceModel:: CreateSvcWindow()
{
//ウインドウの作成
//the handle of the windows
HWND hwnd;
//the struct of the WNDCALSS
WNDCLASS wc;
MSG msg;
HINSTANCE hInstance=(HINSTANCE)::GetModuleHandle(NULL);
wc.style = CS_VREDRAW|CS_HREDRAW;
wc.lpfnWndProc = (WNDPROC)(_WndProc);
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance,IDI_APPLICATION);
wc.hCursor =LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "PDFMainSvcWindow";
//register the windows class
ATOM  aaa = RegisterClass(&wc);
if(aaa == 0)
{
cout<<" RegisterClass Error:"<<GETLASTERROR()<<ENDL;
}
//ウインドウの作成
hwnd = CreateWindow(
"PDFMainSvcWindow",
"メインサービスウインドウ",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT ,
CW_USEDEFAULT ,
CW_USEDEFAULT ,
CW_USEDEFAULT ,
NULL,
NULL,
hInstance,
NULL
);
if(!hwnd)
return FALSE;
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);    hCMain_seivice_data = hwnd;
//最初回のタイマーを設定する。
SetTimer(hwnd,TIMER_SYS_ID,TIMER_ELAPSE_VALUE,NULL);
//メッセージループの開始
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//while(1){};
return FALSE;
}
/*
機 能 サービスメイン
static
可視性 public
メソッド名 ServiceMain
引 数
引数の個数   [IN] argc DWORD なし
各引数   [IN] argv TCHAR *[] なし
戻り値
なし
備 考
コントロール パネルのサービス アプリケーションを開いて、サービスを選択して [開始] をクリックした際に、SCMから呼び出される。
サービスのメイン処理を行う:
*/
void WINAPI MyServiceModel::_ServiceMain(DWORD argc, TCHAR *argv[])
{
objApp.ServiceMain(argc,argv);
}
/*
機 能 サービスコントロールハンドラー
static
可視性 public
メソッド名 ServiceControlHandler
引 数
状態コード   [IN] contorlCode DWORD なし
戻り値
なし
備 考
"SCM がサービスのステータスを得たり、停止や一時停止などの各種の命令をサービスに送るためにSCMから呼び出される。
SCM は Handler に、実行すべき処理の種類を表す処理コードを渡します。
1. サービス停止通知が来た場合、サービス停止中に変更
2. 停止処理を行う(ServiceMainに停止を通知)
3. その他の場合は何もしない"
*/
void WINAPI MyServiceModel::_ServiceControlHandler(DWORD contorlCode)
{
objApp.ServiceControlHandler(contorlCode);
}
/*
機 能 ウインドウプロシージャ
static
可視性 public
メソッド名 WndProc
引 数
ウインドウハンドル   [IN] hDlg      HWND なし
メッセージ   [IN] Msg  UINT なし
パラメタ1   [IN] wParam      WPARAM     なし
パラメタ2   [IN] lParam       LPARAM       なし
戻り値
LRESULT
備 考
"ウインドウメッセージの解析処理を行う
*/
LRESULT CALLBACK MyServiceModel::_WndProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
return objApp.WndProc(hDlg,Msg,wParam,lParam);
}
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Win7下dll远程注入
C++使用内存映射文件入门
vc 堆管理函数
Windows CE文件操作
windows安全初探之命名管道
WaitForMultipleObjects,CreateEvent()函数在线程通信中的...
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服