基于visual c++之windows核心编程代码分析(9)实现Windows服务并安装,控制


//我们进行Windows编程的时候,经常涉及到Windows服务编程,针对Windows服务我们该怎么编程呢,
//我们先来实现一个Windows服务并实现之,请见注释代码分析。

/* 头文件 */
#include <windows.h>    
/* 全局变量 */
SERVICE_STATUS          SplSrvServiceStatus;     
SERVICE_STATUS_HANDLE   SplSrvServiceStatusHandle;     
/* 函数声明 */
VOID SvcDebugOut(LPSTR String, DWORD Status);    
VOID WINAPI SplSrvServiceCtrlHandler (DWORD opcode);     
VOID WINAPI SplSrvServiceStart (DWORD argc, LPTSTR *argv);     
DWORD SplSrvServiceInitialization (DWORD argc, LPTSTR *argv,     
                                   DWORD *specificError);     
          
/*************************************   
* VOID WINAPI SplSrvServiceStart (DWORD argc, LPTSTR *argv)   
* 功能    服务启动函数   
*   
* 参数    未使用   
**************************************/
VOID WINAPI SplSrvServiceStart (DWORD argc, LPTSTR *argv)     
{     
    DWORD status;     
    DWORD specificError;     
    // 填充SERVICE_STATUS 结构    
    SplSrvServiceStatus.dwServiceType        = SERVICE_WIN32;     
    SplSrvServiceStatus.dwCurrentState           
        = SERVICE_START_PENDING;        // 服务在运行    
    SplSrvServiceStatus.dwControlsAccepted       
        = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;     
    SplSrvServiceStatus.dwWin32ExitCode      = 0;     
    SplSrvServiceStatus.dwServiceSpecificExitCode = 0;     
    SplSrvServiceStatus.dwCheckPoint         = 0;     
    SplSrvServiceStatus.dwWaitHint           = 0;     
    // 注册服务控制请求处理例程    
    SplSrvServiceStatusHandle = RegisterServiceCtrlHandler(     
        "Sample_Srv",   // 服务名,在创建服务时使用了    
        // SERVICE_WIN32_OWN_PROCESS,因此本参数被忽略。    
        SplSrvServiceCtrlHandler);  // 控制请求处理例程,函数名    
          
    if (SplSrvServiceStatusHandle == (SERVICE_STATUS_HANDLE)0)     
    {     
        SvcDebugOut(" [SPLSRV_SERVICE] RegisterServiceCtrlHandler "
            "failed %dn", GetLastError());     
        return;     
    }      
    // 初始化工作,本示例未使用,函数为空    
    status = SplSrvServiceInitialization(argc,argv, &specificError);      
    // 初始化出错,用户自行修改    
    if (status != NO_ERROR)     
    {     
        SplSrvServiceStatus.dwCurrentState       = SERVICE_STOPPED;     
        SplSrvServiceStatus.dwCheckPoint         = 0;     
        SplSrvServiceStatus.dwWaitHint           = 0;     
        SplSrvServiceStatus.dwWin32ExitCode      = status;     
        SplSrvServiceStatus.dwServiceSpecificExitCode = specificError;     
          
        SetServiceStatus (SplSrvServiceStatusHandle, &SplSrvServiceStatus);     
        return;     
    }     
    // 初始化完成,设置运行状态    
    SplSrvServiceStatus.dwCurrentState       = SERVICE_RUNNING;     
    SplSrvServiceStatus.dwCheckPoint         = 0;     
    SplSrvServiceStatus.dwWaitHint           = 0;     
          
    if (!SetServiceStatus (SplSrvServiceStatusHandle, &SplSrvServiceStatus))     
    {     
        status = GetLastError();     
        SvcDebugOut(" [SPLSRV_SERVICE] SetServiceStatus error %ldn",status);     
    }     
    // 用户自行修改,用于完成服务的工作    
    SvcDebugOut(" [SPLSRV_SERVICE] Returning the Main Thread n",0);     
          
    return;     
}     
          
          
/*************************************   
* DWORD SplSrvServiceInitialization(DWORD   argc,    
*   LPTSTR  *argv,    
*   DWORD *specificError)   
* 功能    初始化,这里未进行任何工作,留待读者修改   
*   
* 参数       
**************************************/
DWORD SplSrvServiceInitialization(DWORD   argc,     
                                  LPTSTR  *argv,     
                                  DWORD *specificError)     
{     
    return(0);     
}    
          
/*************************************   
* VOID WINAPI SplSrvServiceCtrlHandler (DWORD Opcode)   
* 功能    服务控制请求的处理函数,与ControlService函数配合。   
*   
* 参数    服务控制码   
**************************************/
VOID WINAPI SplSrvServiceCtrlHandler (DWORD Opcode)     
{     
    DWORD status;     
    switch(Opcode)     
    {     
    case SERVICE_CONTROL_PAUSE:     
        // 完成相关功能    
        SplSrvServiceStatus.dwCurrentState = SERVICE_PAUSED;     
        break;     
    case SERVICE_CONTROL_CONTINUE:     
        // 完成相关功能    
        SplSrvServiceStatus.dwCurrentState = SERVICE_RUNNING;     
        break;     
    case SERVICE_CONTROL_STOP:     
        // 完成相关功能    
        SplSrvServiceStatus.dwWin32ExitCode = 0;     
        SplSrvServiceStatus.dwCurrentState  = SERVICE_STOPPED;     
        SplSrvServiceStatus.dwCheckPoint    = 0;     
        SplSrvServiceStatus.dwWaitHint      = 0;     
          
        if (!SetServiceStatus (SplSrvServiceStatusHandle,     
            &SplSrvServiceStatus))    
        {     
            status = GetLastError();     
            SvcDebugOut(" [SPLSRV_SERVICE] SetServiceStatus error %ldn",     
                status);     
        }      
        SvcDebugOut(" [SPLSRV_SERVICE] Leaving SplSrvService n",0);     
        return;     
    case SERVICE_CONTROL_INTERROGATE:     
        // 收到此请求后发出声响,演示服务控制请求的处理过程,读者可自行修改    
        MessageBeep(MB_OK);    
        break;     
    default:     
        SvcDebugOut(" [SPLSRV_SERVICE] Unrecognized opcode %ldn",     
            Opcode);     
    }     
    // 当前状态    
    if (!SetServiceStatus (SplSrvServiceStatusHandle,  &SplSrvServiceStatus))     
    {     
        status = GetLastError();     
        SvcDebugOut(" [SPLSRV_SERVICE] SetServiceStatus error %ldn",     
            status);     
    }     
    return;     
}    
/*************************************   
* void main( )    
* 功能    程序入口函数,注册服务启动函数等。   
*   
* 参数    服务控制码   
**************************************/
void main( )     
{     
    // 设置SERVICE_TABLE_ENTRY 数据结构,以NULL 结构结束,    
    // 作为StartServiceCtrlDispatcher 函数的参数。    
    SERVICE_TABLE_ENTRY   DispatchTable[] =     
    {     
        { "Sample_Srv", (LPSERVICE_MAIN_FUNCTION) SplSrvServiceStart },     
        { NULL, NULL }     
    };     
    if (!StartServiceCtrlDispatcher( DispatchTable))     
    {     
        SvcDebugOut(" [SPLSRV_SERVICE] StartServiceCtrlDispatcher (%d)n",     
            GetLastError());     
    }     
}     
/*************************************   
* VOID SvcDebugOut(LPSTR String, DWORD Status)    
* 功能    显示信息给调试器。   
*   
* 参数    LPSTR String    消息字符串   
*       DWORD Status    状态   
**************************************/
VOID SvcDebugOut(LPSTR String, DWORD Status)     
{     
    CHAR  Buffer[1024];     
    if (strlen(String) < 1000)     
    {     
        wsprintf(Buffer, String, Status);     
        OutputDebugString(Buffer);     
    }     
}


//具体实现代码如下,请见代码分析
**************************************/    
/* 头文件 */
#include <windows.h>    
#include <tchar.h>    
#include <stdio.h>    
/* 全局变量 */
SC_HANDLE schService;    
SC_HANDLE schSCManager;    
LPTSTR szServiceName = TEXT("Sample_Srv");    
          
/*************************************   
* BOOL CreateSampleService(LPTSTR szPath, LPSTR szServiceName)    
* 功能    创建服务   
*   
* 参数   
*       SC_HANDLE schSCManager,SCM句柄   
*       LPTSTR szPath, 服务程序的路径   
*       LPSTR szServiceName, 服务名   
**************************************/
BOOL CreateSampleService(     
                         SC_HANDLE schSCManager,    
                         LPTSTR szPath,    
                         LPSTR szServiceName)     
{     
    // 创建服务    
    schService = CreateService(     
        schSCManager,               // SCM 句柄    
        szServiceName,              // 服务名    
        "Service sample",           // 显示的服务名    
        SERVICE_ALL_ACCESS,         // 存取权限    
        SERVICE_WIN32_OWN_PROCESS,  // 服务类别    
        SERVICE_DEMAND_START,       // 启动类别    
        SERVICE_ERROR_NORMAL,       // 错误控制类别    
        szPath,                     // 服务的可执行文件路径    
        NULL,                       // no load ordering group     
        NULL,                       // no tag identifier     
        NULL,                       // no dependencies     
        NULL,                       // LocalSystem account     
        NULL);                      // no password     
          
    if (schService == NULL)     
    {    
        printf("CreateService failed (%d)n", GetLastError());     
        return FALSE;    
    }    
    else
    {    
        printf("CreateService succeededn");     
        CloseServiceHandle(schService);     
        return TRUE;    
    }    
}    
          
/*************************************   
* BOOL DeleteSampleService(LPTSTR szNameOfService)    
* 功能    删除服务   
*   
* 参数    LPTSTR szNameOfService  服务的名字   
**************************************/
BOOL DeleteSampleService(LPTSTR szNameOfService)     
{     
    schService = OpenService(     
        schSCManager,       // SCM 句柄     
        szNameOfService,    // 服务名    
        DELETE);            // 可删除    
          
    if (schService == NULL)    
    {     
        printf("OpenService failed (%d)n", GetLastError());     
        return FALSE;    
    }    
    // 删除服务    
    if (! DeleteService(schService) )     
    {    
        printf("DeleteService failed (%d)n", GetLastError());     
        return FALSE;    
    }    
    else 
        printf("DeleteService succeededn");     
    // 关闭句柄    
    CloseServiceHandle(schService);     
    return TRUE;    
}    
/*************************************   
* void main( int argc, TCHAR *argv[] )   
* 功能    演示   
*   
* 参数    未使用   
**************************************/
void main( int argc, TCHAR *argv[] )     
{    
    TCHAR szBinFilePath[MAX_PATH];    
    PTCHAR pTemp;    
    DWORD dwStopError;    
    // 构造服务可执行程序的路径    
    GetModuleFileName(NULL,szBinFilePath,MAX_PATH);    
    pTemp = szBinFilePath+lstrlen(szBinFilePath);    
    while(*--pTemp!='');    
    lstrcpy(pTemp,TEXT("SplSrv.exe"));    
          
    // 打开 SCM    
    schSCManager = OpenSCManager(     
        NULL,                    // local machine     
        NULL,                    // ServicesActive database     
        SC_MANAGER_ALL_ACCESS);  // full access rights     
          
    if (NULL == schSCManager)     
        printf("OpenSCManager failed (%d)n", GetLastError());    
          
    // 创建服务    
    CreateSampleService(schSCManager, szBinFilePath, szServiceName);    
    // 启动服务    
    StartSampleService(schSCManager,szServiceName);    
    // 发送请求控制    
    ControlSampleService(SERVICE_CONTROL_INTERROGATE);    
    ControlSampleService(SERVICE_CONTROL_CONTINUE);    
    // 停止服务    
    dwStopError = StopService( schSCManager, szServiceName,     
        TRUE, 1000);    
          
    if(ERROR_SUCCESS == dwStopError)    
    {    
        printf("Service Stopedn");     
    }     
    else
    {    
        printf("Service stoped error (%u)n",dwStopError);     
    }    
    // 删除服务    
    DeleteSampleService(szServiceName);    
    CloseServiceHandle(schSCManager);     
}


//然后我们如何控制服务呢,我们来实现启动、停止服务,向服务发送请求。
/* 头文件 */
#include <windows.h>    
#include <tchar.h>    
#include <stdio.h>    
/* 全局变量 */
extern SC_HANDLE schService;    // 在init.c中定义,下同    
extern SC_HANDLE schSCManager;    
extern LPTSTR szServiceName;    
          
/*************************************   
* BOOL StartSampleService(SC_HANDLE schSCManager,LPTSTR szServiceName)    
* 功能    启动服务   
*   
* 参数    SC_HANDLE schSCManager  SCM 句柄   
*       LPTSTR szServiceName    服务名   
**************************************/
BOOL StartSampleService(SC_HANDLE schSCManager,LPTSTR szServiceName)     
{     
    SC_HANDLE schService;    
    SERVICE_STATUS_PROCESS ssStatus;     
    DWORD dwOldCheckPoint;     
    DWORD dwStartTickCount;    
    DWORD dwWaitTime;    
    DWORD dwBytesNeeded;    
    // 打开服务    
    schService = OpenService(     
        schSCManager,           // SCM database     
        szServiceName,          // service name    
        SERVICE_ALL_ACCESS);     
    if (schService == NULL)     
    {     
        return 0;     
    }    
    // 启动服务    
    if (!StartService(    
        schService,  // handle to service     
        0,           // number of arguments     
        NULL) )      // no arguments     
    {    
        printf("Service start error (%u).n",GetLastError());     
        return 0;     
    }    
    else 
    {    
        printf("Service start pending.n");     
    }    
          
    // 验证状态    
    if (!QueryServiceStatusEx(     
        schService,             // handle to service     
        SC_STATUS_PROCESS_INFO, // info level    
        (LPBYTE)&ssStatus,              // address of structure    
        sizeof(SERVICE_STATUS_PROCESS), // size of structure    
        &dwBytesNeeded ) )              // if buffer too small    
    {    
        return 0;     
    }    
          
    // tick count & checkpoint.    
    dwStartTickCount = GetTickCount();    
    dwOldCheckPoint = ssStatus.dwCheckPoint;    
    // 查询状态,确定 PENDING 状态结束    
    while (ssStatus.dwCurrentState == SERVICE_START_PENDING)     
    {     
        // 等待一段时间    
        dwWaitTime = ssStatus.dwWaitHint / 10;    
        if( dwWaitTime < 1000 )    
            dwWaitTime = 1000;    
        else if ( dwWaitTime > 10000 )    
            dwWaitTime = 10000;    
        Sleep( dwWaitTime );    
        // 再次查询    
        if (!QueryServiceStatusEx(     
            schService,             // handle to service     
            SC_STATUS_PROCESS_INFO, // info level    
            (LPBYTE)&ssStatus,              // address of structure    
            sizeof(SERVICE_STATUS_PROCESS), // size of structure    
            &dwBytesNeeded ) )              // if buffer too small    
            break;     
        if ( ssStatus.dwCheckPoint > dwOldCheckPoint )    
        {    
            // 进程创建中    
            dwStartTickCount = GetTickCount();    
            dwOldCheckPoint = ssStatus.dwCheckPoint;    
        }    
        else
        {    
            if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)    
            {    
                // WaitHint 时间到    
                break;    
            }    
        }    
    }     
    // 关闭句柄    
    CloseServiceHandle(schService);     
    // 判断是否创建成功(状态由PENDING变为RUNNING)    
    if (ssStatus.dwCurrentState == SERVICE_RUNNING)     
    {    
        printf("StartService SUCCESS.n");     
        return 1;    
    }    
    else 
    {     
        printf("nService not started. n");    
        printf("  Current State: %dn", ssStatus.dwCurrentState);     
        printf("  Exit Code: %dn", ssStatus.dwWin32ExitCode);     
        printf("  Service Specific Exit Code: %dn",     
            ssStatus.dwServiceSpecificExitCode);     
        printf("  Check Point: %dn", ssStatus.dwCheckPoint);     
        printf("  Wait Hint: %dn", ssStatus.dwWaitHint);     
        return 0;    
    }     
}    
          
/*************************************   
* DWORD StopService( SC_HANDLE hSCM,    
LPTSTR szServiceName,    
BOOL fStopDependencies,    
DWORD dwTimeout )     
* 功能    停止服务   
*   
* 参数    SC_HANDLE hSCM          SCM 句柄   
*       LPTSTR szServiceName    服务名   
*       BOOL fStopDependencies  是否结束依赖的服务   
*       DWORD dwTimeout         超时   
**************************************/
DWORD StopService(SC_HANDLE hSCM,     
                  LPTSTR szServiceName,     
                  BOOL fStopDependencies,     
                  DWORD dwTimeout )     
{    
    SERVICE_STATUS_PROCESS ssp;    
    SERVICE_STATUS ss;    
    DWORD dwStartTime = GetTickCount();    
    DWORD dwBytesNeeded;    
    // 打开服务    
    SC_HANDLE hService = OpenService(     
        hSCM,          // SCM 句柄     
        szServiceName,          // 服务名    
        SERVICE_ALL_ACCESS);     
          
    // 查询状态,确定是否已经停止    
    if ( !QueryServiceStatusEx(     
        hService,     
        SC_STATUS_PROCESS_INFO,    
        (LPBYTE)&ssp,     
        sizeof(SERVICE_STATUS_PROCESS),    
        &dwBytesNeeded ) )    
    {    
        return GetLastError();    
    }    
    if ( ssp.dwCurrentState == SERVICE_STOPPED )     
    {    
        return ERROR_SUCCESS;    
    }    
    // 如果是 STOP_PENDING 状态,则只需等待    
    while ( ssp.dwCurrentState == SERVICE_STOP_PENDING )     
    {    
        Sleep( ssp.dwWaitHint );    
        // 循环查询,直到状态改变    
        if ( !QueryServiceStatusEx(     
            hService,     
            SC_STATUS_PROCESS_INFO,    
            (LPBYTE)&ssp,     
            sizeof(SERVICE_STATUS_PROCESS),    
            &dwBytesNeeded ) )    
        {    
            return GetLastError();    
        }    
        if ( ssp.dwCurrentState == SERVICE_STOPPED )    
        {    
            return ERROR_SUCCESS;    
        }    
        if ( GetTickCount() - dwStartTime > dwTimeout )    
        {    
            return ERROR_TIMEOUT;    
        }    
    }    
          
    // 先结束依赖服务    
    if ( fStopDependencies )     
    {    
        DWORD i;    
        DWORD dwBytesNeeded;    
        DWORD dwCount;    
          
        LPENUM_SERVICE_STATUS   lpDependencies = NULL;    
        ENUM_SERVICE_STATUS     ess;    
        SC_HANDLE               hDepService;    
          
        // 使用 0 大小的 buf,获取buf的大小    
        // 如果 EnumDependentServices 直接返回成功,说明没有依赖服务    
        if ( !EnumDependentServices( hService, SERVICE_ACTIVE,     
            lpDependencies, 0, &dwBytesNeeded, &dwCount ) )     
        {    
            if ( GetLastError() != ERROR_MORE_DATA )    
                return GetLastError(); // Unexpected error    
          
            // 分配缓冲区存储依赖服务的数据    
            lpDependencies = (LPENUM_SERVICE_STATUS) HeapAlloc(     
                GetProcessHeap(), HEAP_ZERO_MEMORY, dwBytesNeeded );    
          
            if ( !lpDependencies )    
                return GetLastError();    
          
            __try {    
                // 获得依赖服务    
                if ( !EnumDependentServices( hService, SERVICE_ACTIVE,     
                    lpDependencies, dwBytesNeeded, &dwBytesNeeded,    
                    &dwCount ) )    
                    return GetLastError();    
          
                for ( i = 0; i < dwCount; i++ )     
                {    
                    ess = *(lpDependencies + i);    
          
                    // 打开服务    
                    hDepService = OpenService( hSCM, ess.lpServiceName,     
                        SERVICE_STOP | SERVICE_QUERY_STATUS );    
                    if ( !hDepService )    
                        return GetLastError();    
          
                    __try {    
                        // 结束服务    
                        if ( !ControlService( hDepService,     
                            SERVICE_CONTROL_STOP,    
                            &ss ) )    
                            return GetLastError();    
          
                        // 等待服务结束    
                        while ( ss.dwCurrentState != SERVICE_STOPPED )     
                        {    
                            Sleep( ss.dwWaitHint );    
                            if ( !QueryServiceStatusEx(     
                                hDepService,     
                                SC_STATUS_PROCESS_INFO,    
                                (LPBYTE)&ssp,     
                                sizeof(SERVICE_STATUS_PROCESS),    
                                &dwBytesNeeded ) )    
                                return GetLastError();    
          
                            if ( ss.dwCurrentState == SERVICE_STOPPED )    
                                break;    
          
                            if ( GetTickCount() - dwStartTime > dwTimeout )    
                                return ERROR_TIMEOUT;    
                        }    
          
                    }     
                    __finally     
                    {    
                        // 关闭服务    
                        CloseServiceHandle( hDepService );    
          
                    }    
                }    
            }     
            __finally     
            {    
                // 释放内存    
                HeapFree( GetProcessHeap(), 0, lpDependencies );    
            }    
        }     
    }    
          
    // 所有的依赖服务已经结束,结束指定服务    
    if ( !ControlService( hService, SERVICE_CONTROL_STOP, &ss ) )    
        return GetLastError();    
    while ( ss.dwCurrentState != SERVICE_STOPPED )     
    {    
        Sleep( ss.dwWaitHint );    
        if ( !QueryServiceStatusEx(     
            hService,     
            SC_STATUS_PROCESS_INFO,    
            (LPBYTE)&ssp,     
            sizeof(SERVICE_STATUS_PROCESS),    
            &dwBytesNeeded ) )    
            return GetLastError();    
          
        if ( ss.dwCurrentState == SERVICE_STOPPED )    
            break;    
          
        if ( GetTickCount() - dwStartTime > dwTimeout )    
            return ERROR_TIMEOUT;    
    }    
    return ERROR_SUCCESS;    
}    
          
/*************************************   
* BOOL ControlSampleService(DWORD fdwControl)    
* 功能    向服务发送控制码   
*   
* 参数    DWORD fdwControl        控制码值   
*       SCM 句柄,服务名直接使用全局变量   
**************************************/
BOOL ControlSampleService(DWORD fdwControl)     
{     
    SERVICE_STATUS ssStatus;     
    DWORD fdwAccess;     
    DWORD dwStartTickCount, dwWaitTime;    
          
    // Access    
    switch (fdwControl)     
    {     
    case SERVICE_CONTROL_STOP:     
        fdwAccess = SERVICE_STOP;     
        break;     
    case SERVICE_CONTROL_PAUSE:     
    case SERVICE_CONTROL_CONTINUE:     
        fdwAccess = SERVICE_PAUSE_CONTINUE;     
        break;     
    case SERVICE_CONTROL_INTERROGATE:     
        fdwAccess = SERVICE_INTERROGATE;     
        break;     
    default:     
        fdwAccess = SERVICE_INTERROGATE;     
    }     
          
    // 打开服务    
    schService = OpenService(     
        schSCManager,        // SCManager 句柄     
        szServiceName,       // 服务名    
        fdwAccess);          // 存取权限    
    if (schService == NULL)     
    {    
        printf("OpenService failed (%d)n", GetLastError());     
        return FALSE;    
    }    
          
    // 发送控制码    
    if (! ControlService(     
        schService,   // 服务的句柄    
        fdwControl,   // 控制码    
        &ssStatus) )  // 状态    
    {    
        printf("ControlService failed (%d)n", GetLastError());     
        return FALSE;    
    }    
          
    // 显示状态    
    printf("nStatus of Sample_Srv: n");    
    printf("  Service Type: 0x%xn", ssStatus.dwServiceType);     
    printf("  Current State: 0x%xn", ssStatus.dwCurrentState);     
    printf("  Controls Accepted: 0x%xn",     
        ssStatus.dwControlsAccepted);     
    printf("  Exit Code: %dn", ssStatus.dwWin32ExitCode);     
    printf("  Service Specific Exit Code: %dn",     
        ssStatus.dwServiceSpecificExitCode);     
    printf("  Check Point: %dn", ssStatus.dwCheckPoint);     
    printf("  Wait Hint: %dn", ssStatus.dwWaitHint);     
          
    return TRUE;     
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值