漫谈WinCE输入法的编写--以DLL替代CIMWnd

//========================================================================
//TITLE:
// 漫谈WinCE输入法的编写(五)--以DLL替代CIMWnd
//AUTHOR:
// norains
//DATE:
// Friday 12-October-2007
//Environment:
// EVC4.0 + Windows CE 5.0 Standard SDK
//========================================================================
之前的一系列文章(恩,好久以前的文章),輸入法的功能實現都是在CIMWnd中,而本文则是以外部的DLL为替代CIMWnd.以DLL替代CIMWnd类有什么好处呢?恩,这个~其实我也说不上,不过嘛,知道一种新方式,有多一种选择,毕竟不是一件坏事,不是么?何况,这个方法,以我个人观点,还更为灵活些.更换输入法部件时,只需要更新dll即可.

我们首先来回顾一下之前一系列文章中定义的CIMWnd接口功能:
class CIMWnd
{
public:
//获取窗口句柄
HWND GetWindow();;
//显示输入法的设置窗口
void ShowUserOptionsDlg(HWND hWndParent,HINSTANCE hInst = NULL);
//显示输入法界面
void ShowWindow(BOOL bShow);
//销毁输入法界面
void DestroyWindow();
//初始化窗口,hWndSip是输入法管理器的句柄
BOOL Initialize(HINSTANCE hInst, HWND hWndSip);

...

}

也就是说,为了和CInputMethod(漫谈WinCE输入法的编写(二))衔接起来,我们在dll中也必须实现这五个功能接口.我们新建一个名为DllWnd工程的DLL工程,并且为了避免和系统的函数重复从而导致出现获取函数入口失败,我们将这五个功能接口分别添上IM前缀:
//The interface
//获取窗口句柄
HWND IMGetWindow();
//显示输入法的设置窗口
void IMShowUserOptionsDlg(HWND hWndParent,HINSTANCE hInst);
//显示输入法界面
void IMShowWindow(BOOL bShow);
//销毁输入法界面
void IMDestroyWindow();
//初始化窗口
BOOL IMInitialize(HINSTANCE hInstSip, HWND hWndSip);


下面是一个完整的DllWnd代码,其功能是实现一个输入法窗口的创建,并且当点击该窗口时会输出一串测试字符.

#include "sip.h"
#include "resource.h"

//===================================================================
#define MYMSG_REGCALLBACK (WM_USER+100)
//Description:
// The message is using for registering the input method callback function
//
//Parameters:
// pImCallback = (IIMCallback *)wParam
// 0 = lParam

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


//====================================================================
//Macro define
#define WINDOW_CLASS TEXT("IMWND_Class")
#define WINDOW_TITLE TEXT("IMWND_Title")

//The taskbar height
#define TASKBAR_HEIGHT_AUTOHIDE 5
#define TASKBAR_HEIGHT 26

#define IMG_IMWND_WIDTH 370
#define IMG_IMWND_HEIGHT 206

#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 480

//==================================================================
//Forward Declare

//------------------------------------------------------------------------
//The interface

HWND IMGetWindow();
void IMShowUserOptionsDlg(HWND hWndParent,HINSTANCE hInst);
void IMShowWindow(BOOL bShow);
void IMDestroyWindow();
BOOL IMInitialize(HINSTANCE hInstSip, HWND hWndSip);

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


void OnDestroy(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
void OnPaint(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
void OnRegCallback(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
void OnLButtonUp(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
LRESULT WndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam);
//---------------------------------------------------------------------
//The global member
HWND g_hWnd = NULL; //输入法功能窗口
HWND g_hWndSip = NULL; //IM父窗口
HMODULE g_hInst = NULL; //dll的实例句柄
HINSTANCE g_hInstSip = NULL; //sip dll的实例句柄
IIMCallback *g_pIMCallback; //回调函数指针


//-------------------------------------------------------------------
//Description:
// The DLLMAIN
//
//------------------------------------------------------------------
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
g_hInst = (HMODULE)hModule;
break;
}
}

return TRUE;
}

//-------------------------------------------------------------------
//Description:
// Get the window handle
//
//------------------------------------------------------------------
HWND IMGetWindow()
{
return g_hWnd;
}


//-------------------------------------------------------------------
//Description:
// Show the user options dialog
//
//------------------------------------------------------------------
void IMShowUserOptionsDlg(HWND hWndParent,HINSTANCE hInst)
{
MessageBox(NULL,TEXT("控制面板可以調出的輸入法設置"),TEXT("設置"),MB_OK);
}


//-------------------------------------------------------------------
//Description:
// Show the window
//
//------------------------------------------------------------------
void IMShowWindow(BOOL bShow)
{
if(bShow == TRUE)
{
::ShowWindow(g_hWnd,SW_SHOW);
}
else
{
::ShowWindow(g_hWnd,SW_HIDE);
}
}


//-------------------------------------------------------------------
//Description:
// Destroy the window
//
//------------------------------------------------------------------
void IMDestroyWindow()
{
::DestroyWindow(g_hWnd);
}


//-------------------------------------------------------------------
//Description:
// Initialize the window
//
//------------------------------------------------------------------
BOOL IMInitialize(HINSTANCE hInstSip, HWND hWndSip)
{
g_hInstSip = hInstSip;
g_hWndSip = hWndSip;


UnregisterClass(WINDOW_CLASS,g_hInst);

WNDCLASS wc = {0};
wc.style = CS_VREDRAW | CS_HREDRAW;;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hInst;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = WINDOW_CLASS;
if(RegisterClass(&wc) == 0)
{
return FALSE;
}


//Set the window area to suit with the image
RECT rcClientWnd = {0};
GetClientRect (hWndSip, &rcClientWnd);
RECT rcSipWnd = {0};
GetWindowRect(hWndSip,&rcSipWnd);
//It may be the frame for the parent window,so we must hold it.
int iWidth = (rcSipWnd.right - rcSipWnd.left ) - (rcClientWnd.right - rcClientWnd.left) + IMG_IMWND_WIDTH;
int iHeight = (rcSipWnd.bottom - rcSipWnd.top) - (rcClientWnd.bottom - rcClientWnd.top) + IMG_IMWND_HEIGHT;
rcSipWnd.left = SCREEN_WIDTH - iWidth;
rcSipWnd.top = SCREEN_HEIGHT - iHeight - TASKBAR_HEIGHT;
SetWindowPos(hWndSip,hWndSip,rcSipWnd.left,rcSipWnd.top,iWidth,iHeight,NULL);

// Create SIP window.
g_hWnd = CreateWindowEx(0,
WINDOW_CLASS,
WINDOW_TITLE,
WS_CHILD | WS_BORDER ,
rcClientWnd.left,
rcClientWnd.top,
IMG_IMWND_WIDTH,//rcWnd.right - rcWnd.left,
IMG_IMWND_HEIGHT,//rcWnd.bottom - rcWnd.top,
hWndSip,
NULL,
g_hInst,
NULL
);


if(IsWindow(g_hWnd)==FALSE)
{
return FALSE;
}

return TRUE;
}

//-------------------------------------------------------------------
//Description:
// The window process
//
//------------------------------------------------------------------
LRESULT WndProc(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
{
switch(wMsg)
{
case WM_PAINT:
OnPaint(hWnd,wMsg,wParam,lParam);
return 0;
case MYMSG_REGCALLBACK:
OnRegCallback(hWnd,wMsg,wParam,lParam);
return 0;
case WM_ERASEBKGND:
//Do nothing in order not to flash the window
return 0;
case WM_LBUTTONUP:
OnLButtonUp(hWnd,wMsg,wParam,lParam);
return 0;

}
return DefWindowProc(hWnd,wMsg,wParam,lParam);
}

//-------------------------------------------------------------------
//Description:
// On message WM_PAINT
//
//------------------------------------------------------------------
void OnPaint(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
{

PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd,&ps);
//Create a DC that matches the device
HBITMAP hBitmap = CreateCompatibleBitmap(hdc,IMG_IMWND_WIDTH,IMG_IMWND_HEIGHT);
HDC hdcMem = CreateCompatibleDC(hdc);
//Select the bitmap into to the compatible device context
HGDIOBJ hOldSel = SelectObject(hdcMem,hBitmap);

//Create a DC that matches the device
HDC hdcBmp = CreateCompatibleDC(hdc);
//Load the bitmap
HANDLE hBmp= LoadImage(g_hInst,MAKEINTRESOURCE(IDB_PANNEL),IMAGE_BITMAP,0,0,0);
//Select the bitmap into to the compatible device context
HGDIOBJ hOldBmpSel = SelectObject(hdcBmp,hBmp);
//Copy the bitmap image to the memory DC.
BitBlt(hdcMem,0,0,IMG_IMWND_WIDTH,IMG_IMWND_HEIGHT,hdcBmp,0,0,SRCCOPY);


//Draw to the screen
BitBlt(hdc,0,0,IMG_IMWND_WIDTH,IMG_IMWND_HEIGHT,hdcMem,0,0,SRCCOPY);

//Restore original bitmap selection and destroy the memory DC
SelectObject(hdcBmp,hOldBmpSel);
SelectObject(hdcMem,hOldSel);
DeleteObject(hBitmap);
DeleteDC(hdcBmp);
DeleteDC(hdcMem);
EndPaint(hWnd,&ps);
}

//-------------------------------------------------------------------
//Description:
// On message MYMSG_REGCALLBACK
//
//------------------------------------------------------------------
void OnRegCallback(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
{
g_pIMCallback = (IIMCallback *)wParam;

}

//-------------------------------------------------------------------
//Description:
// On message WM_LBUTTONUP
//
//------------------------------------------------------------------
void OnLButtonUp(HWND hWnd,UINT wMsg,WPARAM wParam,LPARAM lParam)
{
if(g_pIMCallback != NULL)
{
g_pIMCallback->SendString(TEXT("輸入法測試程序 "),8);
}
}

当然,上部接口CInputMethod也需要更改.为了方便和CIMWnd契合,我们设置两个宏定义:IMWND_FROM_CODE和IMWND_FROM_DLL.

IMWND_FROM_CODE定义时,我们采用CIMWnd作为功能实现窗口;而IMWND_FROM_DLL则是采用上文所说的dllwnd.dll.


///
// InputMethod.h: interface for the CInputMethod class.
//
//

#ifndef IMPUTMETHOD_H
#define IMPUTMETHOD_H

#include "stdafx.h"
#include "sip.h"
#include "IMWnd.h"


//-----------------------------------------------------------------
class CInputMethod : public IInputMethod
{
public:

//IUnknown methods
STDMETHODIMP_(ULONG) Release(THIS);
STDMETHODIMP_(ULONG) AddRef(THIS);
STDMETHODIMP QueryInterface(THIS_ REFIID riid, LPVOID *ppv);

//IInputMethod
HRESULT STDMETHODCALLTYPE SetImData (DWORD dwSize, void *pvImData);
HRESULT STDMETHODCALLTYPE GetImData (DWORD dwSize, void *pvImData);
HRESULT STDMETHODCALLTYPE RegisterCallback(IIMCallback *pIMCallback);
HRESULT STDMETHODCALLTYPE ReceiveSipInfo(SIPINFO *psi);
HRESULT STDMETHODCALLTYPE GetInfo(IMINFO *pimi);
HRESULT STDMETHODCALLTYPE Hiding();
HRESULT STDMETHODCALLTYPE Showing();
HRESULT STDMETHODCALLTYPE Deselect();
HRESULT STDMETHODCALLTYPE Select(HWND hWndSip);
HRESULT STDMETHODCALLTYPE UserOptionsDlg (HWND hwndParent);
CInputMethod(long *plDllCnt,HINSTANCE hInst);
virtual ~CInputMethod();

protected:

HINSTANCE m_hInst; //The dll instance
long *m_plDllCnt;//point to the Global DLL reference count
long m_lRef;


#ifdef IMWND_FROM_CODE
CIMWnd *m_pIMWnd; //The input method window pointer
#define GETINSTANCE() (m_pIMWnd = CIMWnd::GetInstance())
#define GETWINDOW() (m_pIMWnd->GetWindow())
#define SHOWUSEROPTIONSDLG(x,y) (m_pIMWnd->ShowUserOptionsDlg(x,y))
#define SHOWWINDOW(x) (m_pIMWnd->ShowWindow(x))
#define DESTROYWINDOW() (m_pIMWnd->DestroyWindow())
#define INITIALIZE(x,y) (m_pIMWnd->Initialize(x,y))
#endif //#ifdef IMWND_FROM_CODE

#ifdef IMWND_FROM_DLL

BOOL m_bLoadLib;

//I don't need get instance from the dll.
#define GETINSTANCE()

typedef HWND (WINAPI *DLL_GETWINDOW)(void);
typedef void (WINAPI *DLL_SHOWUSEROPTIONSDLG)(HWND,HINSTANCE = NULL);
typedef void (WINAPI *DLL_SHOWWINDOW)(BOOL);
typedef void (WINAPI *DLL_DESTROYWINDOW)(void);
typedef BOOL (WINAPI *DLL_INITIALIZE)(HINSTANCE,HWND);


DLL_GETWINDOW GETWINDOW;
DLL_SHOWUSEROPTIONSDLG SHOWUSEROPTIONSDLG;
DLL_SHOWWINDOW SHOWWINDOW;
DLL_DESTROYWINDOW DESTROYWINDOW;
DLL_INITIALIZE INITIALIZE;

#endif //#ifdef IMWND_FROM_DLL
};

//--------------------------------------------------------------------------------
#endif // IMPUTMETHOD_H

//
// InputMethod.cpp: implementation of the CInputMethod class.
//
//
#include "InputMethod.h"

//--------------------------------------------------------------------
//Macro define
#define SIP_WND_WIDTH 200
#define SIP_WND_HEIGHT 180

#define IMWND_DLL_PATH TEXT("window/dllwnd.dll")
//----------------------------------------------------------------------
//
// Construction/Destruction
//

CInputMethod::CInputMethod(long *plDllCnt, HINSTANCE hInst)
{
GETINSTANCE();
m_hInst = hInst;
m_plDllCnt = plDllCnt;
(*m_plDllCnt)++;
m_lRef = 1; // Set ref count to 1 on create.


#ifdef IMWND_FROM_DLL

GETWINDOW = NULL;
SHOWUSEROPTIONSDLG = NULL;
SHOWWINDOW = NULL;
DESTROYWINDOW = NULL;
INITIALIZE = NULL;

HINSTANCE hInstDll;
hInstDll = LoadLibrary(IMWND_DLL_PATH);
if(hInstDll != NULL)
{
GETWINDOW = (DLL_GETWINDOW) GetProcAddress(hInstDll,TEXT("IMGetWindow"));
SHOWUSEROPTIONSDLG = (DLL_SHOWUSEROPTIONSDLG) GetProcAddress(hInstDll,TEXT("IMShowUserOptionsDlg"));
SHOWWINDOW = (DLL_SHOWWINDOW) GetProcAddress(hInstDll,TEXT("IMShowWindow"));
DESTROYWINDOW = (DLL_DESTROYWINDOW) GetProcAddress(hInstDll,TEXT("IMDestroyWindow"));
INITIALIZE = (DLL_INITIALIZE) GetProcAddress(hInstDll,TEXT("IMInitialize"));
}

if(GETWINDOW == NULL ||
SHOWUSEROPTIONSDLG == NULL ||
SHOWWINDOW == NULL ||
DESTROYWINDOW == NULL ||
INITIALIZE == NULL )
{
m_bLoadLib = FALSE;
}
else
{
m_bLoadLib = TRUE;
}

#endif

}

CInputMethod::~CInputMethod()
{
(*m_plDllCnt)--;
}


//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to create the windows and image list for the input method (IM).
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::Select(HWND hWndSip)
{

if(INITIALIZE(m_hInst,hWndSip) == FALSE)
{
return E_FAIL;
}

SHOWWINDOW(TRUE);


return S_OK;
}

//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to select the input method (IM) out of the software-based
//input panel window and to destroy the IM windows.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::Deselect()
{
DESTROYWINDOW();
return S_OK;
}

//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to perform any initialization before the software-based
//input panel window is displayed
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::Showing()
{
SHOWWINDOW(TRUE);
return S_OK;
}

//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to perform any saving routines before the software-based
//input panel is hidden.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::Hiding()
{
SHOWWINDOW(FALSE);

return S_OK;
}


//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to return information about the current input
//method (IM) to the operating system.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::GetInfo(IMINFO *pimi)
{

pimi->cbSize = sizeof (IMINFO);
pimi->hImageNarrow = 0;
pimi->hImageWide = 0;
pimi->iNarrow = 0;
pimi->iWide = 0;
pimi->fdwFlags = SIPF_DOCKED;

pimi->rcSipRect.left = 0;
pimi->rcSipRect.top = 0;
pimi->rcSipRect.right = SIP_WND_WIDTH;
pimi->rcSipRect.bottom = SIP_WND_HEIGHT;
return S_OK;
}

//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented for the IM to receive information about the size,
//placement, and docked status of the software-based input panel.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::ReceiveSipInfo(SIPINFO *psi)
{
return S_OK;
}

//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to receive a pointer to an IIMCallback interface.
//An input method (IM) uses the IIMCallback interface to send keystrokes to applications
//and to change the icons on the Input Panel button.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::RegisterCallback(IIMCallback *pIMCallback)
{

//Tell the IM window to register the callback
HWND hWnd = GETWINDOW();
SendMessage(hWnd,MYMSG_REGCALLBACK,(WPARAM)pIMCallback,0);
return S_OK;
}

//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to send data from the current
//input method (IM) to the current application.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::GetImData(DWORD dwSize, void *pvImData)
{
return S_OK;
}

//-------------------------------------------------------------------------------------------
//Description:
// This method is implemented to respond to an application's request to
//set input method (IM)-specific data within the IM.
//----------------------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::SetImData(DWORD dwSize, void *pvImData)
{
return S_OK;
}

//---------------------------------------------------------------------
//Description:
// Increment object ref count
//----------------------------------------------------------------------
STDMETHODIMP CInputMethod::QueryInterface(REFIID riid, LPVOID *ppv)
{


#ifdef IMWND_FROM_DLL
if(m_bLoadLib == FALSE)
{
return E_NOINTERFACE;
}
#endif //#ifdef IMWND_FROM_DLL


// If caller wants our IUnknown or IID_IInputMethod2 object,
// return a pointer to the object.
if (IsEqualIID (riid, IID_IUnknown) || IsEqualIID (riid, IID_IInputMethod) || IsEqualIID (riid, IID_IInputMethod2))
{
// Return ptr to object.
*ppv = (IInputMethod *)this;
AddRef(); // Increment ref to prevent delete on return.
return NOERROR;
}
*ppv = NULL;
return (E_NOINTERFACE);

}


//---------------------------------------------------------------------
//Description:
// Increment object ref count
//----------------------------------------------------------------------
STDMETHODIMP_(ULONG) CInputMethod::AddRef()
{

ULONG cnt;
cnt = (ULONG)InterlockedIncrement (&m_lRef);
return cnt;
}

//---------------------------------------------------------------------
//Description:
// Increment object ref count
//----------------------------------------------------------------------
STDMETHODIMP_(ULONG) CInputMethod::Release()
{
ULONG cnt;

cnt = (ULONG)InterlockedDecrement (&m_lRef);
if (cnt == 0)
{
delete this;
return 0;
}
return cnt;
}


//---------------------------------------------------------------------
//Description:
// The SIP Control Panel applet is asking for a configuration dialog box to be displayed.
//----------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CInputMethod::UserOptionsDlg(HWND hwndParent)
{

SHOWUSEROPTIONSDLG(hwndParent,m_hInst);
return S_OK;
}


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/norains/archive/2007/10/12/1822541.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值