USB手柄的控制

本文介绍了如何使用DirectX 8.1以上版本来控制USB手柄,通过下载Direct SDK,利用Dinput8.lib等库文件进行手柄识别与控制。首先实例化CDirectInput,然后初始化手柄,接着获取按键状态,最后解析DIJOYSTATE2结构体中的键值以实现控制。
AI助手已提取文章相关产品:

USB手柄的控制

                                                                                                                   Flyli

         USB手柄或游戏控制杆并不一定只在打游戏的时候才用得到,为了控制以PC机为核心的机器人能够在手动的控制下运动,略微研究了下USB手柄的控制方法。

         通过查阅相关资料,查到了2USB手柄的控制方法,一种是基于windows DDKUSB开发,由于没有搞到DDK所以暂且不谈这种方法

我们来说说第二种方法,就是以基于DirectX8.1以上的USB手柄控制方法,使用这种方法需要下载DirectSDK,运用到他的运行库。Dinput8.lib,DxErr.Lib,dxguid.lib另外还需要加载direct的一些头文件,至于头文件具体用到了那些我也没有测试,我把这些可能用到的程序都发到了我的资源中,地址是:http://download.youkuaiyun.com/source/402394

其中将所有的.h文件复制到vc的运行库下,把.lib文件加载到工程中就可以使用了。

其使用方法也是相当的简单:

1.       CDirectInput实例化

2.       调用InitDirectInput初始化USB手柄,通过其返回值判断手柄是否被识别

3.       声明一个DIJOYSTATE2 类型的值用于接收按键的状态

4.       GetState_Now()声明的DIJOYSTATE2的值进行赋值;

5.       DIJOYSTATE2 中部分键值的对应关系 lxly分别是对应上下左右的方向,rgbbutton[]的数组是分别用来对应其他的手柄按键。

遇到具体的问题可以再联系我哈

一下是我封装后的类

//********************************************************************

// DirectInput.h 文件

//********************************************************************************

#pragma once

#define DIRECTINPUT_VERSION 0x0800

#define _CRT_SECURE_NO_DEPRECATE

#ifndef _WIN32_DCOM

#define _WIN32_DCOM

#endif

 

#include <windows.h>

#include <commctrl.h>

#include <basetsd.h>

#include <dinput.h>

#include <dinputd.h>

#include <assert.h>

#include <oleauto.h>

#include <shellapi.h>

#include <wbemidl.h>

 

#pragma warning( disable : 4996 ) // disable deprecated warning

#include <strsafe.h>

#pragma warning( default : 4996 )

#include "resource.h"

//=============================================================

//宏定义

#define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }

#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }

struct XINPUT_DEVICE_NODE

{

    DWORD dwVidPid;

    XINPUT_DEVICE_NODE* pNext;

};

 

struct DI_ENUM_CONTEXT

{

    DIJOYCONFIG* pPreferredJoyCfg;

    bool bPreferredJoyCfgValid;

};

 

 

class CDirectInput

{

public:

     CDirectInput(void);

public:

     ~CDirectInput(void);

public:

 

     static LPDIRECTINPUT8       g_pDI;        

     static LPDIRECTINPUTDEVICE8 g_pJoystick ;

     static bool    g_bFilterOutXinputDevices;

     static XINPUT_DEVICE_NODE* g_pXInputDeviceList;

    static DIJOYSTATE2 HandState;

public:

     static BOOL CALLBACK    EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext );

     static BOOL CALLBACK    EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext );

     static HRESULT InitDirectInput(HWND hDlg);

     static VOID    FreeDirectInput();

     static DIJOYSTATE2 GetState_Now();

 

     // Stuff to filter out XInput devices

 

     static HRESULT SetupForIsXInputDevice();

     static bool    IsXInputDevice( const GUID* pGuidProductFromDirectInput );

     static void    CleanupForIsXInputDevice();

};

 

 

 

 

 

 

//********************************************************************

// DirectInput.h 文件

//********************************************************************************

 

 

#include "StdAfx.h"

#include "DirectInput.h"

 

 

LPDIRECTINPUT8       CDirectInput::g_pDI              = NULL;        

LPDIRECTINPUTDEVICE8 CDirectInput::g_pJoystick        = NULL;

bool    CDirectInput::g_bFilterOutXinputDevices = false;

XINPUT_DEVICE_NODE* CDirectInput::g_pXInputDeviceList = NULL;

DIJOYSTATE2 CDirectInput::HandState;

 

CDirectInput::CDirectInput(void)

{

}

 

CDirectInput::~CDirectInput(void)

{

}

//-----------------------------------------------------------------------------

// Name: InitDirectInput()

// Desc: Initialize the DirectInput variables.

//-----------------------------------------------------------------------------

HRESULT CDirectInput::InitDirectInput( HWND hDlg )

{

    HRESULT hr;

     //创建主com接口

    if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), /

         DIRECTINPUT_VERSION,IID_IDirectInput8,/

         (VOID**)&g_pDI, NULL ) ) )

     {   

         return hr;

     }

     //hr->

    if( g_bFilterOutXinputDevices )

        SetupForIsXInputDevice();

 

    DIJOYCONFIG PreferredJoyCfg = {0};

    DI_ENUM_CONTEXT enumContext;

    enumContext.pPreferredJoyCfg = &PreferredJoyCfg;

    enumContext.bPreferredJoyCfgValid = false;

 

    IDirectInputJoyConfig8* pJoyConfig = NULL; 

    if( FAILED( hr = g_pDI->QueryInterface( IID_IDirectInputJoyConfig8, (void **) &pJoyConfig ) ) )

        return hr;

 

    PreferredJoyCfg.dwSize = sizeof(PreferredJoyCfg);

    if( SUCCEEDED( pJoyConfig->GetConfig(0, &PreferredJoyCfg, DIJC_GUIDINSTANCE ) ) ) // This function is expected to fail if no Joystick is attached

        enumContext.bPreferredJoyCfgValid = true;

    SAFE_RELEASE( pJoyConfig );

    if( FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL,

                                         EnumJoysticksCallback,

                                         &enumContext, DIEDFL_ATTACHEDONLY ) ) )

        return hr;

 

    if( g_bFilterOutXinputDevices )

        CleanupForIsXInputDevice();

    if( NULL == g_pJoystick )

    {

         /*::MessageBox( NULL, TEXT("Joystick not found. The sample will now exit."), 

                    TEXT("DirectInput Sample"),

                    MB_ICONERROR | MB_OK );*/

       // EndDialog( hDlg, 0 );

        return 0-1;

    }

    if( FAILED( hr = g_pJoystick->SetDataFormat( &c_dfDIJoystick2 ) ) )

        return hr;

    if( FAILED( hr = g_pJoystick->SetCooperativeLevel( hDlg, DISCL_EXCLUSIVE |

                                                             DISCL_FOREGROUND ) ) )

        return hr;

    if( FAILED( hr = g_pJoystick->EnumObjects( EnumObjectsCallback,

                                                (VOID*)hDlg, DIDFT_ALL ) ) )

        return hr;

     CDirectInput::GetState_Now();

    return S_OK;

}

 

 

//-----------------------------------------------------------------------------

// Enum each PNP device using WMI and check each device ID to see if it contains

// "IG_" (ex. "VID_045E&PID_028E&IG_00").  If it does, then itan XInput device

// Unfortunately this information can not be found by just using DirectInput.

// Checking against a VID/PID of 0x028E/0x045E won't find 3rd party or future

// XInput devices.

//

// This function stores the list of xinput devices in a linked list

// at g_pXInputDeviceList, and IsXInputDevice() searchs that linked list

//-----------------------------------------------------------------------------

HRESULT CDirectInput::SetupForIsXInputDevice()

{

    IWbemServices*          pIWbemServices = NULL;

    IEnumWbemClassObject*   pEnumDevices   = NULL;

    IWbemLocator*           pIWbemLocator  = NULL;

    IWbemClassObject*       pDevices[20]   = {0};

    BSTR                    bstrDeviceID   = NULL;

    BSTR                    bstrClassName  = NULL;

    BSTR                    bstrNamespace  = NULL;

    DWORD                   uReturned      = 0;

    bool                    bCleanupCOM    = false;

    UINT                    iDevice        = 0;

    VARIANT                 var;

    HRESULT                 hr;

 

    // CoInit if needed

    hr = CoInitialize(NULL);

    bCleanupCOM = SUCCEEDED(hr);

 

    // Create WMI

    hr = CoCreateInstance( __uuidof(WbemLocator),

                           NULL,

                           CLSCTX_INPROC_SERVER,

                           __uuidof(IWbemLocator),

                           (LPVOID*) &pIWbemLocator);

    if( FAILED(hr) || pIWbemLocator == NULL )

        goto LCleanup;

 

    // Create BSTRs for WMI

    bstrNamespace = SysAllocString( L"////.//root//cimv2" ); if( bstrNamespace == NULL ) goto LCleanup;

    bstrDeviceID  = SysAllocString( L"DeviceID" );           if( bstrDeviceID == NULL )  goto LCleanup;       

    bstrClassName = SysAllocString( L"Win32_PNPEntity" );    if( bstrClassName == NULL ) goto LCleanup;       

   

    // Connect to WMI

    hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L,

                                       0L, NULL, NULL, &pIWbemServices );

    if( FAILED(hr) || pIWbemServices == NULL )

        goto LCleanup;

 

    // Switch security level to IMPERSONATE

    CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,

                       RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0 );                   

 

    // Get list of Win32_PNPEntity devices

    hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices );

    if( FAILED(hr) || pEnumDevices == NULL )

        goto LCleanup;

 

    // Loop over all devices

    for( ;; )

    {

        // Get 20 at a time

        hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned );

        if( FAILED(hr) )

            goto LCleanup;

        if( uReturned == 0 )

            break;

 

        for( iDevice=0; iDevice<uReturned; iDevice++ )

        {

            // For each device, get its device ID

            hr = pDevices[iDevice]->Get( bstrDeviceID, 0L, &var, NULL, NULL );

            if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL )

            {

                // Check if the device ID contains "IG_".  If it does, then itan XInput device

                // Unfortunately this information can not be found by just using DirectInput

                if( wcsstr( var.bstrVal, L"IG_" ) )

                {

                    // If it does, then get the VID/PID from var.bstrVal

                    DWORD dwPid = 0, dwVid = 0;

                    WCHAR* strVid = wcsstr( var.bstrVal, L"VID_" );

                    if( strVid && swscanf( strVid, L"VID_%4X", &dwVid ) != 1 )

                        dwVid = 0;

                    WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" );

                    if( strPid && swscanf( strPid, L"PID_%4X", &dwPid ) != 1 )

                        dwPid = 0;

 

                    DWORD dwVidPid = MAKELONG( dwVid, dwPid );

 

                    // Add the VID/PID to a linked list

                    XINPUT_DEVICE_NODE* pNewNode = new XINPUT_DEVICE_NODE;

                    if( pNewNode )

                    {

                        pNewNode->dwVidPid = dwVidPid;

                        pNewNode->pNext = g_pXInputDeviceList;

                        g_pXInputDeviceList = pNewNode;

                    }

                }

            }  

            SAFE_RELEASE( pDevices[iDevice] );

        }

    }

 

LCleanup:

    if(bstrNamespace)

        SysFreeString(bstrNamespace);

    if(bstrDeviceID)

        SysFreeString(bstrDeviceID);

    if(bstrClassName)

        SysFreeString(bstrClassName);

    for( iDevice=0; iDevice<20; iDevice++ )

        SAFE_RELEASE( pDevices[iDevice] );

    SAFE_RELEASE( pEnumDevices );

    SAFE_RELEASE( pIWbemLocator );

    SAFE_RELEASE( pIWbemServices );

 

    return hr;

}

 

 

//-----------------------------------------------------------------------------

// Returns true if the DirectInput device is also an XInput device.

// Call SetupForIsXInputDevice() before, and CleanupForIsXInputDevice() after

//-----------------------------------------------------------------------------

bool CDirectInput::IsXInputDevice( const GUID* pGuidProductFromDirectInput )

{

    // Check each xinput device to see if this device's vid/pid matches

    XINPUT_DEVICE_NODE* pNode = g_pXInputDeviceList;

    while( pNode )

    {

        if( pNode->dwVidPid == pGuidProductFromDirectInput->Data1 )

            return true;

        pNode = pNode->pNext;

    }

 

    return false;

}

//-----------------------------------------------------------------------------

// Cleanup needed for IsXInputDevice()

//-----------------------------------------------------------------------------

void CDirectInput::CleanupForIsXInputDevice()

{

    // Cleanup linked list

    XINPUT_DEVICE_NODE* pNode = g_pXInputDeviceList;

    while( pNode )

    {

        XINPUT_DEVICE_NODE* pDelete = pNode;

        pNode = pNode->pNext;

        SAFE_DELETE( pDelete );

    }

}

//-----------------------------------------------------------------------------

// Name: EnumJoysticksCallback()

// Desc: Called once for each enumerated Joystick. If we find one, create a

//       device interface on it so we can play with it.

//-----------------------------------------------------------------------------

BOOL CALLBACK CDirectInput::EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,

                                     VOID* pContext )

{

    DI_ENUM_CONTEXT* pEnumContext = (DI_ENUM_CONTEXT*) pContext;

    HRESULT hr;

 

    if( g_bFilterOutXinputDevices && IsXInputDevice( &pdidInstance->guidProduct ) )

        return DIENUM_CONTINUE;

 

    // Skip anything other than the perferred Joystick device as defined by the control panel. 

    // Instead you could store all the enumerated Joysticks and let the user pick.

    if( pEnumContext->bPreferredJoyCfgValid &&

        !IsEqualGUID( pdidInstance->guidInstance, pEnumContext->pPreferredJoyCfg->guidInstance ) )

        return DIENUM_CONTINUE;

 

    // Obtain an interface to the enumerated Joystick.

    hr = g_pDI->CreateDevice( pdidInstance->guidInstance, &g_pJoystick, NULL );

 

    // If it failed, then we can't use this Joystick. (Maybe the user unplugged

    // it while we were in the middle of enumerating it.)

    if( FAILED(hr) )

        return DIENUM_CONTINUE;

 

    // Stop enumeration. Note: we're just taking the first Joystick we get. You

    // could store all the enumerated Joysticks and let the user pick.

    return DIENUM_STOP;

}

//-----------------------------------------------------------------------------

// Name: EnumObjectsCallback()

// Desc: Callback function for enumerating objects (axes, buttons, POVs) on a

//       Joystick. This function enables user interface elements for objects

//       that are found to exist, and scales axes min/max values.

//-----------------------------------------------------------------------------

BOOL CALLBACK CDirectInput::EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,

                                   VOID* pContext )

{

    HWND hDlg = (HWND)pContext;

 

    static int nSliderCount = 0;  // Number of returned slider controls

    static int nPOVCount = 0;     // Number of returned POV controls

 

    // For axes that are returned, set the DIPROP_RANGE property for the

    // enumerated axis in order to scale min/max values.

    if( pdidoi->dwType & DIDFT_AXIS )

    {

        DIPROPRANGE diprg;

        diprg.diph.dwSize       = sizeof(DIPROPRANGE);

        diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);

        diprg.diph.dwHow        = DIPH_BYID;

        diprg.diph.dwObj        = pdidoi->dwType; // Specify the enumerated axis

        diprg.lMin              = -1000;

        diprg.lMax              = +1000;

   

        // Set the range for the axis

        if( FAILED( g_pJoystick->SetProperty( DIPROP_RANGE, &diprg.diph ) ) )

            return DIENUM_STOP;

        

    }

    return DIENUM_CONTINUE;

}

//-----------------------------------------------------------------------------

// Name: UpdateInputState()

// Desc: Get the input device's state and display it.

//-----------------------------------------------------------------------------

DIJOYSTATE2 CDirectInput::GetState_Now()

{

    HRESULT     hr;

    DIJOYSTATE2 js;           // DInput Joystick state

    hr = g_pJoystick->Poll();

    if( FAILED(hr) ) 

    {

        hr = g_pJoystick->Acquire();

        while( hr == DIERR_INPUTLOST )

            hr = g_pJoystick->Acquire();

        //return S_OK;

    }

    // Get the input's device state

    if( FAILED( hr = g_pJoystick->GetDeviceState( sizeof(DIJOYSTATE2), &js ) ) );

     // CString str;

     //str.Format("%d",js.lX);

     CDirectInput::HandState = js;

     return js;

     //AfxMessageBox(str);

 

}

//-----------------------------------------------------------------------------

// Name: FreeDirectInput()

// Desc: Initialize the DirectInput variables.

//-----------------------------------------------------------------------------

VOID CDirectInput::FreeDirectInput()

{

    // Unacquire the device one last time just in case

    // the app tried to exit while the device is still acquired.

    if( g_pJoystick )

        g_pJoystick->Unacquire();

   

    // Release any DirectInput objects.

    SAFE_RELEASE( g_pJoystick );

    SAFE_RELEASE( g_pDI );

}

 

您可能感兴趣的与本文相关内容

STM32F103C8 (Source Code) Serial(UART) to USB HID Keyboard Mouse Joystick 串口 转 USB键盘;鼠标;手柄 源码 (1) 使用Composite Device 组合(复合)设备 (1.1) 1个Device -> 1个 Configuation -> 3个Interfance (Keyboard & Mouse & Joystick) (1.2) Keyboard Interfance -> HID (boot mode) -> 2个Endpoint(IN_0x81 & OUT_0x01) -> KeyboardReportDescriptor(不使用Report ID) (1.3) Mouse Interfance -> HID (boot mode) -> 1个Endpoint(IN_0x82) -> MouseReportDescriptor(不使用Report ID) (1.4) Joystick Interfance -> HID -> 1个Endpoint(IN_0x83) -> JoyStickReportDescriptor (1.5) 使用HID boot模式, 不使用Report ID, 以便兼容在 计算器设定BIOS模式 中的操作 (1.6) 支持反馈Keyboard_LED灯号: All Off; Num Lock; Caps Lock; Scroll Lock; Compose; Kana (2) 串口接收 命令 (2.1) UART协议: 115200, n, 8, 1 (2.2) 1帧发送字符串格式, 以 '{'开始, '}'结束 ','分隔 共9个10进制数字 例如: {1,2,3,4,5,6,7,8,9} (2.3) 第1位 区分 Keyboard(128) 或是 Mouse(64) 或是 Joystick(32) 命令 例如: {32, 0,0,0,0,0,0,0,0} --- 发送Joystick命令 {64, 0,0,0,0,0,0,0,0} --- 发送Mouse命令 {128,0,0,0,0,0,0,0,0} --- 发送Keyboard命令 (3) 发送Keyboard键盘命令时 : 第2~9位 分别如下 (3.1) 第2位 : Modify_Key(修饰键) Key_Release = 0x00, Left_Control = 0x01, Left_Shift = 0x02, Left_Alt = 0x04, Left_GUI = 0x08, Right_Control = 0x10, Right_Shift = 0x20, Right_Alt = 0x40, Right_GUI = 0x80, 例如: {128, 8,0,0,0,0,0,0,0} --- 发送 Win_Key键 {128, 128,0,0,0,0,0,0,0} --- 发送 WinApp_Key键 {128, 32,0,0,0,0,0,0,0} --- 发送 右Shift键 (3.2) 第3位 : 保留,不使用,一律填0 (3.3) 第4~9位 : 可以同时发送6个Keyboard按键 例如: {128, 0,0,4,5,6,7,8,9} --- 发送 'abcdef'键 {128, 2,0,4,5,6,7,8,9} --- 按住 左Shift 发送 'abcdef'键 => 'ABCDEF' {128, 0,0,0,5,0,7,0,9} --- 发送 'bdf'键 (0表示 无按键) 按键码 可参阅: (HID Usage ID) http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf https://www.hiemalis.org/~keiji/PC/scancode-translate.pdf https://gist.github.com/MightyPork/6da26e382a7ad91b5496ee55fdc73db2 http://www.usb.org/developers/hidpage/Hut1_12v2.pdf (4) 发送Mouse鼠标命令时 : 第8~9位 分别如下 (4.1) 第2位 : 鼠标按钮(左,中,右)占3bits Button_Release = 0x00, Left_Button = 0x01, Right_Button = 0x02, Mid_Button = 0x04, 例如: {64, 1,0,0,0,0,0,0,0} --- 点击 左键 {64, 2,0,0,0,0,0,0,0} --- 点击 右键 {64, 4,0,0,0,0,0,0,0} --- 点击 中键 (4.2) 第3~5位 : 移动(X,Y), 滚轮(Wheel) X: -127~127:左右移动鼠标 Y: -127~127:上下移动鼠标 Wheel: -127~127:上下转动滚轮 例如: {64, 0,20,-10,0,0,0,0,0} --- 鼠标 右移20,上移10 {64, 0,0,0,-30,0,0,0,0} --- 滚轮-30 (4.3) 第6~9位 : 保留,不使用,一律填0 (5) 发送Joystick手柄命令时 : 第8~9位 分别如下 (5.1) 第2~4位 : 移动X,Y,Z X: -127~127:X轴左右移动手柄 Y: -127~127:Y轴上下移动手柄 Z: -127~127:Z轴转动手柄 例如: {32, -127,0,0,0,0,0,0,0} --- 移动手柄X轴到-127(最右边) {32, 0,127,0,0,0,0,0,0} --- 移动手柄Y轴到127(最下面) {32, -95,32,96,0,0,0,0,0} --- 移动手柄X,Y,Z轴到(-95,32,96) (5.2) 第5~7位 : 旋转X,Y,Z X: -127~127:X轴旋转 Y: -127~127:Y轴旋转 Z: -127~127:Z轴旋转 例如: {32, 0,0,0, 63,0,0,0,0} --- 旋转手柄X轴到63 {32, 0,0,0, 0,-32,0,0,0} --- 旋转手柄Y轴到-32 {32, 0,0,0, 0,0,127,0,0} --- 旋转手柄Z轴到127 (5.3) 第8位 : 2个 Hat_switch(方向帽) POV1, POV2 POV1_0 = 0x00, POV1_45 = 0x01, POV1_90 = 0x02, POV1_135 = 0x03, POV1_180 = 0x04, POV1_225 = 0x05, POV1_270 = 0x06, POV1_315 = 0x07, POV2_0 = 0x00, POV2_45 = 0x10, POV2_90 = 0x20, POV2_135 = 0x30, POV2_180 = 0x40, POV2_225 = 0x50, POV2_270 = 0x60, POV2_315 = 0x70, 例如: {32, 0,0,0, 0,0,0, 3,0} --- POV1到90度 {32, 0,0,0, 0,0,0, 103,0} --- POV1到135度(0x07);POV2到270度(0x60) 即(0x07+0x60=0x67=103) (5.4) 第9位 : 8个按钮(每个按钮占1bit) 例如: {32, 0,0,0, 0,0,0, 0,85} --- 按钮: (0x55=85) 1,3,5,7:ON; 2,4,6,8:OFF {32, 0,0,0, 0,0,0, 0,170} --- 按钮: (0xAA=170) 1,3,5,7:OFF; 2,4,6,8:ON
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值