Win32 API 三两事

学习下一些底层的API调用,所有内容来自网络


1、APIENTRY,WINAPI,CALLBACK等是什么东西?

通常在函数名前面(返回值后面)会有APIENTRY或WINAPI或CALLBACK修饰,这其实与调用约定有关.

调用约定      
调用约定(Calling     convention)决定以下内容:函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法。MFC支持以下调用约定:     

_cdecl         

按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于“C”函数或者变量,修饰名是在函数名前加下划线。对于“C++”函数,有所不同。     
如函数void     test(void)的修饰名是_test;对于不属于一个类的“C++”全局函数,修饰名是?test@@ZAXXZ。     
这是MFC缺省调用约定。由于是调用者负责把参数弹出栈,所以可以给函数定义个数不定的参数,如printf函数。     

_stdcall       

按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。对于“C”函数或者变量,修饰名以下划线为前缀,然后是函数名,然后是符号“@”及参数的字节数,如函数int     func(int     a,     double     b)的修饰名是_func@12。对于“C++”函数,则有所不同。     
所有的Win32     API函数都遵循该约定。     

_fastcall   

头两个DWORD类型或者占更少字节的参数被放入ECX和EDX寄存器,其他剩下的参数按从右到左的顺序压入栈。由被调用者把参数弹出栈,对于“C”函数或者变量,修饰名以“@”为前缀,然后是函数名,接着是符号“@”及参数的字节数,如函数int     func(int     a,     double     b)的修饰名是@func@12。对于“C++”函数,有所不同。     
未来的编译器可能使用不同的寄存器来存放参数。     

thiscall   

仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压栈。thiscall不是关键词,因此不能被程序员指定。     

naked call     

采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked     call不产生这样的代码。     
naked     call不是类型修饰符,故必须和_declspec共同使用,如下:     
__declspec(naked) int func(formal_parameters)     
{     
//     Function     body     
}     

过时的调用约定    

原来的一些调用约定可以不再使用。它们被定义成调用约定_stdcall或者_cdecl。例如:    

#define     CALLBACK     __stdcall     
#define     WINAPI          __stdcall     
#define     WINAPIV        __cdecl     
#define     APIENTRY      WINAPI     
#define     APIPRIVATE    __stdcall     
#define     PASCAL          __stdcall     

.

2、HINSTANCE 、HANDLE 、 HWND 、LPCTSTR是什么?

HINSTANCE   是进程句柄;
HANDLE         是对象句柄;
HWND          是窗口的句柄。

LPCTSTR一个指向常固定地址的可以根据一些宏定义改变语义的字符串

其实句柄是一个32位的整数,WINDOWS操作系统用来标志一个对象,是进程、图像图标资源等对象的ID。在Windows这样的多任务操作系统中,一个程序可以同时运行多个实例。不同的实例间需要彼此区别,句柄就是干这个的。
而在WinMain函数中,带有4个参数,分别是:hInstance, hPrevInstance, lpCmdLine, nShowCmd。hInstance是程序的当前实例的句柄。在Windows这样的多任务操作系统中,一个程序可以同时运行多个实例。不同的实例间需要彼此区别,句柄就是干这个的。
微软喜欢将内核对象标识,称为句柄。如

  进      程: HINSTANCE 

  文件句柄: HANDLE

  窗口句柄: HWND

  画笔句柄: HPEN等等。

     

LPCTSTRL实际是一个指向常固定地址的可以根据一些宏定义改变语义的字符串。L表示long指针, 这是为了兼容Windows 3.1等16位操作系统遗留下来的, 在win32中以及其他的32为操作系统中, long指针和near指针及far修饰符都是为了兼容的作用。没有实际意义。 

P表示这是一个指针,C表示是一个常量T在Win32环境中, 有一个_T宏,这个宏用来表示你的字符是否使用UNICODE, 如果你的程序定义了UNICODE或者其他相关的宏,那么这个字符或者字符串将被作为UNICODE字符串,否则就是标准的ANSI字符串。STR表示这个变量是一个字符串。

 所以LPCTSTR就表示一个指向常固定地址的可以根据一些宏定义改变语义的字符串。同样, LPCSTR就只能是一个ANSI字符串,在程序中我们大部分时间要使用带T的类型定义。LPCTSTR == const TCHAR *
LP和P在win32中是等效的,都是指针的意思。   PTSTR的定义 typedef LPWSTR PTSTR, LPTSTR;    STR表示字符串。 问题就出在T上面. T是一个宏,当没定义unicode时为空,定义unicode后表示为宽字符。       

所以当定义unicode后,PTSTR转换为PSTR(LPSTR,一样意思)就不能直接转换了,因为一个是unicode,一个是ascii       结论:unicode下,PTSTR转换为PSTR是个编码转换问题。 编码转换可以用MS的函数完成。

WideCharToMultiByte将unicode转换成ascii     

MultiByteToWideChar将ascii转换成unicode

⑤ Win32 API 调用小例子
(注意,如果用VS类编译器,在新建项目时要选择"Win32项目",而不是控制台程序,如下图:)
复制代码
   
   
// #include "stdafx.h" // #include "First.h" #include < windows.h > #include < tchar.h > int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(hInstance); UNREFERENCED_PARAMETER(nCmdShow); // 获取桌面的句柄。 HWND hWnd = GetDesktopWindow(); // 显示一行消息。 MessageBox(hWnd,_T( " 第一个应用程序 " ),_T( " 例子 " ), MB_OK); return 0 ; }
复制代码

上面的例子将会会弹出一个小提示框,如图:

从上面这段程序就可以看到, _tWinMain 是应用程序的入口函数,这里是使用它的宏,定义在 tchar.h 头文件里,为什么要这样作宏定义的呢?由于 Windows 的应用程序要适应 UNICODE 和以前单字符的应用程序,由于 Windows 这两个 API 的定义是不一样的,如下:
UNICODE 的定义:
    
    
#define _tWinMain wWinMain
单字符的定义:
    
    
#define _tWinMain WinMain
只要经过这样的宏定义后,就可以适应不同字符宽度的函数接口了。由于我是采用 UNICODE 编译的,所以这里使用 wWinMain 函数定义,下面再把它的定义找出来,如下:
    
    
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd );
这里要详细地解释一下函数 wWinMain 的参数,它有四个参数。
hInstance 是当前应用程序的实例句柄,一般用来区分不同的资源使用。
hPrevInstance 是以前 Win98 使用的句柄,在 Win2000 以后的操作系统里都是空值 NULL 。
lpCmdLine 是命令行参数,比如你在 Windows 开始菜单里运行一个程序,并添加参数在后面,就会传递给应用程序,后面再详细讨论。
nShowCmd 是窗口的显示方式,比如最大化显示,最小化显示,还是正常显示。
 
Windows 运行程序时,是通过运行库里的启动代码来调用 wWinMain 函数,它是在启动文件里如下调用:
复制代码
    
    
#ifdef WPRFLAG mainret = wWinMain( #else /* WPRFLAG */ mainret = WinMain( #endif /* WPRFLAG */ (HINSTANCE) & __ImageBase, NULL, lpszCommandLine, StartupInfo.dwFlags & STARTF_USESHOWWINDOW ? StartupInfo.wShowWindow : SW_SHOWDEFAULT );
复制代码
这就是操作系统传递给应用程序的值,现在就来演示使用第一个参数 hInstance 。
请看下面的例子:
复制代码
   
   
#include " stdafx.h " #include " First.h " int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); // 使用应用程序句柄 const int MAXSIZE_APPBUF = 256 ; 19 TCHAR wAppTile[MAXSIZE_APPBUF]; 20 LoadString(hInstance,IDS_APP_TITLE,wAppTile,MAXSIZE_APPBUF); // 获取桌面的句柄。 HWND hWnd = GetDesktopWindow(); // 显示一行消息。 MessageBox(hWnd, _T( " 第一个应用程序 " ), wAppTile, MB_OK); return 0 ; }
复制代码
这个例子是在前面的基础上修改的,主要添加了使用应用程序实例句柄。在第 19 行里定义了一个保存应用程序标题的缓冲区,然后在第 20 行里调用函数 LoadString 从应用程序的资源里加载字符串,它的第一个参数就使用到 hInstance 句柄。因此应用程序句柄是表示程序在资源上唯一的标识符.

.

3、CWnd 、 CDC 与 HDC?

CWnd  是MFC的一个类,所有窗口类从其派生。

CWnd是提供窗口处理的一个类,里面有HWND m_hWnd成员,CWnd对象一般和一个窗口句柄绑定,但提供了很多窗口操作,如SetWindowText,GetWindowText,...。

CDC  是MFC的DC的一个绘图类,所有跟绘图相关的操作都被封装在CDC类中。 CClientDC类、CWindowDC类皆派生自CDC类
 HDC       HDC是DC的句柄,API中的一个类似指针的数据类型 

(MFC类的前缀都以C开头,H开头的大多是句柄,CDC等设备上下分类,都含有一个类的成员变量:m_nHdc;即HDC类型的句柄;MFC的类,是在用window API语句开发出来的有一定功能的小程序.(也可称为类).使用它的默认方法,就是,记住它的名字与参数)
 HDC与CDC的转换
方法一:

   
   
     
     
HDC hdc; CDC cdc; // cdc到hdc hdc = cdc.GetSafeHdc(); // hdc到cdc cdc.Attach(hdc)
方法二:


    
    
      
      
/* CDC to hdc 用成员变量m_hDC hdc to CDC 用FromHandle */ CDC dc; HDC hDC = dc.m_hDC dc.FromHandle(hDC);
复制代码
注意:


     
     
dc.FromHandle(hDC)产生一个dc,但是是临时的,mfc不保证系统在什么时候删除dc. 
dc.Attach(hDC)是永久的,直到这个dc的生命正常结束。
一些有关HDC与CDC使用的代码


       
       
         
         
// 使用HDC绘图 HDC hdc; hdc = ::GetDC(m_hWnd); MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL); LineTo(hdc,point.x,point.y); ::ReleaseDC(m_hWnd,hdc);


        
        

//所有跟窗口相关的操作都被封装在CWnd类中
//所有跟绘图相关的操作都被封装在CDC类中


         
         
           
           
// 使用CDC绘图 CDC * pDC = CWnd::GetDC(); // 此处直接使用CWnd类的GetDC,其返回值为CDC* // SDK中的同名函数使用返回的是HDC pDC -> MoveTo(m_ptOrigin); pDC -> LineTo(point); CWnd::ReleaseDC(pDC);



            
            
// 使用CClientDC绘图 /* CClientDC派生自CDC,在构造的时候调用GetDC, 在析构的时候调用ReleaseDC。使得我们不必 显示调用GetDC与ReleaseDC。 */ CClientDC dc( this ); dc.MoveTo(m_ptOrigin); dc.LineTo(point);



            
            
// 使用CWindowDC绘图 /* 同样派生自CDC,构造调用GetWindowDC, 析构时调用ReleaseDC()。 可以访问整个屏幕区域,包括客户区和非 客户区。 */ CWindowDC dc( this ); dc.MoveTo(m_ptOrigin); dc.LineTo(point);
复制代码

复制代码
            
            
// 通过GetDesktopWindow获得桌面DC /* 注意:用平台SDK同名函数获得的是句柄,CWnd中 的同名函数获得的是CWnd指针。可以通过该函数获得 桌面窗口的CWnd指针,使得我们可以对桌面进行操作。 */ CWindowDC dc(GetDesktopWindow()); dc.MoveTo(m_ptOrigin); dc.LineTo(point);

DC 桌面画图实例
           
           
             
             
#include < windows.h > int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { HDC hdc; HWND hwnd; COLORREF clr; TCHAR szBuffer[] = TEXT( " Http://Www.ProgramLife.Net " ); hwnd = GetDesktopWindow(); // 获取桌面窗口句柄 hdc = GetWindowDC(hwnd); // 获取桌面窗口DC SetBkMode(hdc, TRANSPARENT); // 背景色透明 clr = SetTextColor(hdc, RGB( 255 , 0 , 0 )); // 设置颜色并输出文字 TextOut(hdc, 0 , 0 , szBuffer, lstrlen(szBuffer)); SetTextColor(hdc, RGB( 0 , 255 , 0 )); TextOut(hdc, 0 , 20 , szBuffer, lstrlen(szBuffer)); SetBkMode(hdc, OPAQUE); // 背景色不透明 SetTextColor(hdc, RGB( 0 , 0 , 255 )); TextOut(hdc, 0 , 40 , szBuffer, lstrlen(szBuffer)); SetTextColor(hdc, clr); // 还原颜色 ReleaseDC(hwnd, hdc); // 释放句柄DC Sleep( 5000 ); return 0 ; }

运行后界面如图:


            
            
.
4、COLORREF 与 RGB 、LPVOID 与 LPCVOID ?


            
            

             
             
①  COLORREF 与 RGB
RGB(r,g,b)是一个宏
实际上它做得事是((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
  rgb(r,g,b)    =    一个整型值    =    r    +    g    *    256    +  b*255*256    
COLORREF 是 一 个 32-bit 整 型 数 值,它 代 表 了 一 种 颜 色。你 可以  使 用 RGB 函 数 来 初 始 化 COLORREF
它的定义
typedef DWORD    COLORREF;
COLORREF变量有两种赋值方法
●第一种
COLORREF cf = RGB(,,);
●第二种
                 
                 
CColorDialog colorDialog; COLORREF color; if ( colorDialog.DoModal() == IDOK ) { color = colorDialog.GetColor(); }
这 段 代 码 使 用 了 MFC 中 的 颜 色 对 话 框
如何从 COLORREF中取出RGB分量值?
可以使用宏 GetRValueGetGValueGetBValue
他们的定义如下
                 
                 
#define GetRValue(rgb) ((BYTE)(rgb)) #define GetGValue(rgb) ((BYTE)(((WORD)(rgb)) >> 8)) #define GetBValue(rgb) ((BYTE)((rgb)>>16))


              
              

.

5、char、wchar_t、TCHAR、WCHAR、std::string、std::wstring、CString、LPCTSTR、DWORD

① char、wchar_t

char        :  8 bit 表示,最基本的类型。

wchar_t  :  typedef unsigned short wchar_t ,它是16位的。 wchar_t类型的字符串应该这样写:L”Hello”,因此可定义一个宽字符指针:wchar_t  *pw = L”Hello”

                
                
::MessageBoxW(NULL,L " 你好 " ,L " OK " , 0 ); // L 表示为 wchar_t 型字符串 wchar_t * p = L”你好”; Setlocale(LC_ALL,””); // 必须有这句话,下面的才可以输出汉字 wcout << p << endl;

② TCHAR、WCHAR、CHAR

复制代码
                
                
#ifdef UNICODE   typedef wchar_t TCHAR; #else   typedef unsigned char TCHAR; #endif typedef unsigned char CHAR; typedef unsigned wchar_t WCHAR;
复制代码

由此看出,CHAR实际上是unsigned char,WCHAR为宽字符。而TCHAR根据是否支持unicode而不同。在程序使用sizeof(TCHAR),当默认设置时为1,当定义UNICODE为2

③ std::string、std::wstring

                
                
typedef basic_string < char > string ; typedef basic_string < wchar_t > wstring;

 在C++中字符串类string是基于basic_string模板的在C++中定义了两个字符串:string和wstring

String 可以看做char[] 而wstring 使用的是wchar_t 类型,这是宽字符类型,用于满足非ASCII要求,wstring有要使用对应的wcout、wcin、wcerr等函数。实际上,string也可以使用中文,但是它将一个汉字写在两个char中,而如果将一个汉字看做一个单位wchar_t的话,那么在wstring中就只占一个单元,其他的非英文文字和编码也是如此,这样才真正的满足字符串操作的要求,尤其是国际化的要求。

④ CString

CString是基于TCHAR数据类型的对象。如果在你的程序中定义了符号_UNICODE,则TCHAR被定义为wchar_t,即16位组成。非_UNICODE模式下,CString对象由8位字符组成。当不使用_UNICODE时,CString是多字节字符集(MBCS,也被认为是双字节字符集,DBCS)。注意,对于MBCS字符串,CString仍然基于8位字符来计算,返回,以及处理字符串,并且你的应用程序必须自己解释MBCS的开始和技术字符。

⑤ DWORD

int、long是有符号的,而DWORD是无符号的;

typedef unsigned long DWORD

.

6、SelectObject()和SelectStockObject()有何区别?

  SelectObject能选任何pen,brush,font等GDI对象,包括vc本身的和自己定义的   
  SelectStockObject一般用来选vc本身定义的GDI对象。   
  SelectStockObject选择的是系统预定义的GDI对象   
  SelectObject选择你自己自定义的的GDI对象  
说白了,Stock   Object就系统已创建好的,大家共用,不用你去delete,你也delete不了   
  而你自己建的东东,用完后要从HDC中选出来(选出也用SelectObject),delete掉,否   
  则致使资源泄露!
    
  SelectObject是将GDI资源选入设备,后者将选择系统设备   
  因为GDI资源选入设备后需要释放,因此经常这样做。   
  ...   
                
                
CBrush brush( 0x123456 ) pDC -> SelectObject( & brush); // CBrush *pOldBrush=(CBrush *)pDC->SelectObject(&brush); pDC -> FillRect(rect,brush); pDC -> SelectStockObject(WRITE_BRUSH); // pDC->SelectObject(pOldBrush);

SelectObject   is   more   powerful   than   SelectStockObject.

.

1、spy++ 可进行一些辅助功能,确实很有用,比如可以获取消息、类名、窗口标题等等,VC++ 自带的小工具。

2、获取窗口句柄。HWND FindWindow(LPCTSTR IpClassName,LPCTSTR IpWindowName); 用spy++获取了窗体的类名或者窗口标题后,就可以调用该函数了

                
                
HWND gameH = FindWindowA(窗口类名,窗口标题);

(注意,windows api函数有两套,一套ANSI接口(函数后面有A标志),一套UNICODE接口(函数后面有W标志))

  另外注意,如果在C++类函数中调用API,要在函数前面加上"::"前缀,否则会找不到函数,不加"::"前缀,编译器会以为该函数是类中的。

3、获取窗口坐标信息。BOOL GetWindowRect(HWND,LPRECT lpRect); 有了窗体句柄gameH,就可以通过该句柄获取窗口的一些信息,这里用

                
                
RECT r; // RECT类型为一个结构体,包括四个属性:top、bottom、left、right ::GetWindowRect(gameH, & r); // 这里将r结构体的地址当做参数,最终结果会返回给r,Win32 API基本上都是这样返回数据的,具体什么时候用,查看MSDN看函数原型。
4、获取鼠标位置信息。 BOOL GetCursorPos(LPPOlNT IpPoint);
                
                
POINT point; // POINT结构体类型,包含x、y属性 GetCursorPos( & point);

5、移动鼠标位置。 BOOL SetCursorPOS(int X,int Y);

                
                
SetCursorPos(point.left,point.top);

6、模拟鼠标事件。VOID mouse_event(DWORD dwFlags,DWORD dx,DWORD dwFlags,OWORD dx,DWORD dy,DWORD dwData,DWORD dwExtralnfo);

                
                
mouse_event(MOUSEEVENT_LEFTDOWN, 0 , 0 , 0 , 0 ); // 模拟左键按下事件 mouse_event(MOUSEEVENT_LEFTUP, 0 , 0 , 0 , 0 ); // 模拟右键按下事件
7、 获取窗口对应进程ID。 DWORD GetWindowThreadProcessld(HWND hwnd,LPDWORD lpdwProcessld);
                
                
DWORD processId; GetWindowThreadProcessId(gameH, & processId); // 获取窗口对应进程ID
8、根据进程ID打开进程。 HANDLE OpenProcess(DWORD dwDesiredAccess,BOOL bInheritHandle, DWORD dwProcessId);
                
                
HANDLE processH = OpenProcess(PROCESS_ALL_ACCESS, 0 ,processId);

9、读取进程内存BOOL ReadProcessMemory(HANDLE hProcess,LPCVOID lpBaseAddress,LPVOID lpBuffer,DWORD nSize,LPDWORD lpNumberOfBytesRead);

                
                
DWORD byread; // 实际读取的字节,当读取失败,可来看看到底正确读取了多少字节 LPCVOID pbase = (LPCVOID) 0x00B808E0 ; // 读取从0x00B808E0开始的数据 unsigned int m_num; // 缓冲区(相当于DWORD m_num;) LPVOID nbuffer = (LPVOID) & m_num; ReadProcessMemory(processH,pbase,nbuffer, 4 , & byread); // 读4字节数据
复制代码
               
               

1、GetWindowRect

BOOL GetWindowRect(

  HWND hWnd,      // handle to window

  LPRECT lpRect   // 存放返回值的首地址 RECT

);

2、SetCursorPos

BOOL SetCursorPos(

  int X,  //X

  int Y   //Y

);

3、mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);//MOUSEEVENTF_LEFTUP

4、FindWindow               //获取窗口句柄

HWND FindWindow(

  LPCTSTR lpClassName,  //窗口类名 NULL

  LPCTSTR lpWindowName  //窗口标题 NULL

);

5、GetWindowThreadProcessId //获取窗口进程ID

DWORD GetWindowThreadProcessId(

  HWND hWnd,             // handle to window

  LPDWORD lpdwProcessId  // 指向变量的指针 用来返回进程PID

);

6、OpenProcess              //打开指定进程

HANDLE OpenProcess(

  DWORD dwDesiredAccess,  // 访问权限 标记

  BOOL bInheritHandle,    // false;

  DWORD dwProcessId       // lpdwProcessId  进程ID标识

);

7、ReadProcessMemory        //读指定进程 内存数据

BOOL ReadProcessMemory(

  HANDLE hProcess,  //  HANDLE OpenProcess返回值

  LPCVOID lpBaseAddress,

                    // 读取 进程起始地址 基址

  LPVOID lpBuffer,  // 存放数据的缓冲区

  DWORD nSize,      // 要读出的字节数

  LPDWORD lpNumberOfBytesRead  // 实际读出字节数

);

8、WriteProcessMemory       

9、SendMessage //可以软模拟 鼠标 键盘操作

10、SetTimer

UINT SetTimer(

  HWND hWnd,              // 指向窗口的句柄

  UINT nIDEvent,          // 定时器 标识ID

  UINT uElapse,           // 时间间隔(毫秒)

  TIMERPROC lpTimerFunc   //回调函数

);

VOID CALLBACK TimerProc(

  HWND hwnd,     // handle of window for timer messages

  UINT uMsg,     // WM_TIMER message

  UINT idEvent,  // timer identifier

  DWORD dwTime   // 当前系统时间

);

11、KillTimer()

BOOL KillTimer(

  HWND hWnd,      // 指向窗口的句柄

  UINT uIDEvent   // 定时器 标识ID

);

12、SetWindowPos //HWND_TOPMOST 窗口置顶




              
              


              
              


              
              


              
              


              
              


              
              


              
              


              
              
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值