SDK学习的资料及研华采集卡的例程研读

最近一段时间,在研究研华采集卡USB4711附带的example,发现VC++例子采用SDK所写,文件名后缀为.c,其中有些组织结构看不懂,所以首先看懂了孙鑫VC++详解的第一章,了解了windows程序的内部运行机制。但是例子上面没有怎么使用资源,如修改图标,添加菜单和对话框等。于是自己写,但是写的过程中遇到了好多的困难。首先是菜单的加载,其次是对话框的使用。再次总结一下

① FARPROC lpfnConfigDlgProc; // config. dialog procedure

lpfnConfigDlgProc = MakeProcInstance (ConfigDlgProc, hInstance) ;这是一句过时的调用

DialogBox (hInstance, MAKEINTRESOURCE(IDD_SETTING), hWnd, lpfnConfigDlgProc) ;

现在已经改成DialogBox (hInstance, MAKEINTRESOURCE(IDD_SETTING),hwnd, RunDlgproc) ;

② menu的几种加载方式和注意事项,前面的那篇文章已经写了。

③ 窗口过程函数的写法。主窗口过程和对话框子窗口过程

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HINSTANCE hInstance ;//获取本程序的句柄
switch (message)
{
case WM_CREATE :
hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;
return 0 ;
case WM_COMMAND :
switch (LOWORD (wParam))
{
case IDM_APP_ABOUT :
DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;
break ;
}
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}

return DefWindowProc (hwnd, message, wParam, lParam) ;

}

/* *********************************************************************** * Program : DIGOUT.C * * Description : Demo program for Win16 with digital output * * Revision : 1.00 * * Date : 9/5/1996 Advantech Co., Ltd. * *********************************************************************** * * FUNCTIONS: * * InitApplication() - Initializes window data and registers window class * * InitInstance() - Saves instance handle and creates main window * * MainWndProc() - Process main window messages * * ConfigDlgProc() - Process the dialog box messages for configuration * * CvtHex() - Convert string to hexadecimal integer */ #include <windows.h> /* required for all Windows applications */ #include <stdlib.h> #include <dos.h> #include <math.h> #include <stdio.h> #include <string.h> #include "../../../include/driver.h" #include "../../../include/os.h" #include "resource.h" // ------------------------------------------------------------------------ // CONSTANT DEFINITION // ------------------------------------------------------------------------ #define MAX_DEVICES 100 // ------------------------------------------------------------------------ // GLOBAL VARIABLES // ------------------------------------------------------------------------ HANDLE hInst; // current instance HWND hMainWnd; // main window handle static PT_DioWritePortByte ptDioWritePortByte; // DioWritePortByte table static PT_DioWriteBit ptDioWriteBit; // DioWriteBit table static PT_DioGetCurrentDOByte ptDioGetCurrentDOByte; char szErrMsg[80]; // used for MESSAGEBOX function LRESULT ErrCde; // return error code // // used for dynamic allocation memory for the installed devices // // static HGLOBAL hDeviceList,hSubDeviceList; // static LPDEVLIST DeviceList,SubDeviceList; // // // used for fixed memory for the installed devices // DEVLIST DeviceList[MAX_DEVICES]; DEVLIST SubDeviceList[MAX_DEVICES]; LONG DriverHandle = (LONG)NULL; // driver handle USHORT gwDevice = 0, gwSubDevice = 0; // device index int gwChannel = 0; // output channel USHORT gwMask = 0xff; // mask USHORT gwData = 0xaa; // output data USHORT gwOutBit = 0; // output bit USHORT gwDoState = 0; // current DO state SHORT gnNumOfDevices, gnNumOfSubdevices; // number of installed devices FARPROC lpfnConfigDlgProc; // config. dialog procedure FARPROC lpfnRunDlgProc; // run dialog procedure // ------------------------------------------------------------------------ // FUNCTION DECLARATION // ------------------------------------------------------------------------ BOOL InitApplication(HANDLE hInstance); BOOL InitInstance(HANDLE hInstance, int nCmdShow); long FTYPE MainWndProc(HWND, UINT, WPARAM, LPARAM); int FTYPE WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); WORD CvtHex(char *str); /*************************************************************************** FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int) PURPOSE: calls initialization function, processes message loop ****************************************************************************/ int PASCAL WinMain ( HANDLE hInstance, // current instance HANDLE hPrevInstance, // previous instance LPSTR lpCmdLine, // command line int nCmdShow // show-window type (open/icon) ) { MSG msg; // message if (!hPrevInstance) if (!InitApplication(hInstance)) // Initialize shared things return (FALSE); // Exits if unable to initialize // Perform initializations that apply to a specific instance if (!InitInstance(hInstance, nCmdShow)) return (FALSE); // Acquire and dispatch messages until a WM_QUIT message is received. while (GetMessage(&msg, // message structure (HWND)NULL, /* handle of window receiving the message */ (UINT)NULL, /* lowest message to examine */ (UINT)NULL)) /* highest message to examine */ { TranslateMessage(&msg); // Translates virtual key codes DispatchMessage(&msg); // Dispatches message to window } return (msg.wParam); // Returns the value from PostQuitMessage } /**************************************************************************** FUNCTION: InitApplication(HANDLE) PURPOSE: Initializes window data and registers window class ****************************************************************************/ BOOL InitApplication ( HANDLE hInstance // current instance ) { WNDCLASS wc; // Fill in window class structure with parameters that describe the // main window. wc.style = CS_HREDRAW | CS_VREDRAW; // Class style(s). wc.lpfnWndProc = (WNDPROC)MainWndProc; // window process procedure wc.cbClsExtra = 0; // No per-class extra data. wc.cbWndExtra = 0; // No per-window extra data. wc.hInstance = hInstance; // Application that owns the class. wc.hIcon = LoadIcon(hInstance, "IDI_ICON1"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MyMenu"; // Name of menu resource in .RC file. wc.lpszClassName = "MyClass"; // Name used in call to CreateWindow. // Register the window class and return success/failure code. return (RegisterClass(&wc)); } /*************************************************************************** FUNCTION: InitInstance(HANDLE, int) PURPOSE: Saves instance handle and creates main window ****************************************************************************/ BOOL InitInstance ( HANDLE hInstance, // Current instance identifier. int nCmdShow // Param for first ShowWindow() call. ) { // Save the instance handle in static variable, which will be used in // many subsequence calls from this application to Windows. hInst = hInstance; // Create a main window for this application instance. hMainWnd = CreateWindow( "MyClass", // See RegisterClass() call. "Advantech Driver Demo : Digital Output" , // Window title bar WS_OVERLAPPEDWINDOW, // Window style. CW_USEDEFAULT, // Default horizontal position. CW_USEDEFAULT, // Default vertical position. CW_USEDEFAULT, // Default width. CW_USEDEFAULT, // Default height. NULL, // Overlapped windows have no parent. NULL, // Use the window class menu. hInstance, // This instance owns this window. NULL // Pointer not needed. ); // If window could not be created, return "failure" if (!hMainWnd) return (FALSE); // Make the window visible; update its client area; and return "success" ShowWindow(hMainWnd, nCmdShow); // Show the window UpdateWindow(hMainWnd); // Sends WM_PAINT message return (TRUE); // Returns the value from PostQuitMessage } /*************************************************************************** FUNCTION: ConfigDlgProc(HWND, unsigned, WPARAM, LPARAM) PURPOSE: Processes dialog box messages for configuration ****************************************************************************/ BOOL FTYPE ConfigDlgProc ( HWND hDlg, // window handle unsigned message, // type of message WPARAM wParam, // additional information LPARAM lParam // additional information ) { int i; DWORD dwIndex; char szBuffer[40]; int nOutEntries; switch (message) { case WM_INITDIALOG : // -------------------------------- // Initialize Device List Combobox // -------------------------------- // get number of the installed devices if ((ErrCde = DRV_DeviceGetNumOfList((SHORT far *)&gnNumOfDevices)) != SUCCESS) { DRV_GetErrorMessage(ErrCde,(LPSTR)szErrMsg); MessageBox(hMainWnd,(LPCSTR)szErrMsg, "Driver Message", MB_OK); return TRUE; } // ---------------------------------------------------------------- // used for dynamic allocation memory for these installed devices // // if ((hDeviceList = GlobalAlloc(GHND, gnNumOfDevices * // sizeof(DEVLIST))) == NULL) // { // MessageBox(hMainWnd, (LPCSTR)"Not Enough Memory", // "Memory Allocation", MB_OK); // return TRUE; // } // DeviceList = (LPDEVLIST)GlobalLock(hDeviceList); // ---------------------------------------------------------------- if (gnNumOfDevices > MAX_DEVICES) gnNumOfDevices = MAX_DEVICES; // retrieve the information of all installed devices if ((ErrCde = DRV_DeviceGetList((DEVLIST far *)&DeviceList[0], (SHORT)gnNumOfDevices, (SHORT far *)&nOutEntries)) != (LONG)SUCCESS) { DRV_GetErrorMessage(ErrCde,(LPSTR)szErrMsg); MessageBox(hMainWnd,(LPCSTR)szErrMsg,"Driver Message",MB_OK); return TRUE; } // initialize the Device List Combobox with the retrieved information for (i = 0; i < gnNumOfDevices; i ++) SendDlgItemMessage(hDlg, IDC_DEVICE, CB_ADDSTRING, 0, (LPARAM)((LPSTR)DeviceList[i].szDeviceName)); SendDlgItemMessage(hDlg, IDC_DEVICE, CB_SETCURSEL,(WPARAM)gwDevice, (LPARAM)0); // ----------------------------------------------------------- // Initialize Module List Combobox for COM port or CAN devices // ----------------------------------------------------------- // check if there is any device attached on this COM port or CAN gnNumOfSubdevices = DeviceList[gwDevice].nNumOfSubdevices; if (gnNumOfSubdevices > MAX_DEVICES) gnNumOfSubdevices = MAX_DEVICES; // -------------------------------------------------------------- // used for dynamic allocation memory for these installed devices // // if ((hSubDeviceList = GlobalAlloc(GHND, gnNumOfSubdevices * // sizeof(DEVLIST))) == NULL) // { // MessageBox(hMainWnd, (LPCSTR)"Not Enough Memory", // "Memory Allocation", MB_OK); // return TRUE; // } // // SubDeviceList = (LPDEVLIST)GlobalLock(hSubDeviceList); // -------------------------------------------------------------- // retrieve the information of all installed devices if (gnNumOfSubdevices != 0) { if ((ErrCde = DRV_DeviceGetSubList( (DWORD)DeviceList[gwDevice].dwDeviceNum, (DEVLIST far *)&SubDeviceList[0], (SHORT)gnNumOfSubdevices, (SHORT far *)&nOutEntries)) != (LONG)SUCCESS) { DRV_GetErrorMessage(ErrCde,(LPSTR)szErrMsg); MessageBox(hMainWnd,(LPCSTR)szErrMsg,"Driver Message",MB_OK); return TRUE; } // initialize the Module List Combobox with the retrieved // information EnableWindow(GetDlgItem(hDlg, IDC_MODULE), TRUE); SendDlgItemMessage(hDlg, IDC_MODULE, CB_RESETCONTENT, 0, (LPARAM)((LPSTR)0)); for (i = 0; i < gnNumOfSubdevices; i++) SendDlgItemMessage(hDlg, IDC_MODULE, CB_ADDSTRING, 0, (LPARAM)((LPSTR)SubDeviceList[i].szDeviceName)); SendDlgItemMessage(hDlg, IDC_MODULE, CB_SETCURSEL, (WPARAM)gwSubDevice, (LPARAM)0); } else { EnableWindow(GetDlgItem(hDlg, IDC_MODULE), FALSE); SendDlgItemMessage(hDlg, IDC_MODULE, CB_RESETCONTENT, 0, (LPARAM)((LPSTR)0)); } // ----------------------------- // Initialize Output Channel // ----------------------------- itoa(gwChannel, szBuffer, 10); SendDlgItemMessage(hDlg, IDC_ECHANNEL, EM_REPLACESEL, 0, (LPARAM)((LPSTR)szBuffer)); // ----------------------------- // Initialize Mask // ----------------------------- ltoa((long)gwMask, szBuffer, 16); SendDlgItemMessage(hDlg, IDC_EMASK, EM_REPLACESEL, 0, (LPARAM)((LPSTR)szBuffer)); return TRUE; case WM_COMMAND : switch (LOWORD(wParam)) { case IDOK : // // get device selection // if ((dwIndex = SendDlgItemMessage(hDlg, IDC_DEVICE, CB_GETCURSEL, 0, 0)) != CB_ERR) gwDevice = (WORD)dwIndex; // // get sub-device selection // if ((dwIndex = SendDlgItemMessage(hDlg, IDC_MODULE, CB_GETCURSEL, 0, 0)) != CB_ERR) gwSubDevice = (WORD)dwIndex; // // Get channel number // if (SendDlgItemMessage(hDlg, IDC_ECHANNEL, EM_GETMODIFY, 0, 0)) { SendDlgItemMessage(hDlg, IDC_ECHANNEL, WM_GETTEXT, 10, (LPARAM)(LPSTR)szBuffer) ; gwChannel = atoi(szBuffer); } // // Get mask data // if (SendDlgItemMessage(hDlg, IDC_EMASK, EM_GETMODIFY, 0, 0)) { SendDlgItemMessage(hDlg, IDC_EMASK, WM_GETTEXT, 10, (LPARAM)(LPSTR)szBuffer) ; gwMask = CvtHex(szBuffer); } EndDialog(hDlg, 0); return TRUE; case IDCANCEL : EndDialog(hDlg, 0); return TRUE; case IDC_DEVICE : // // When device selection is changed, it needs to // re-initialize sub-device combobox and input range // combobox. // if (HIWORD(wParam) == CBN_SELCHANGE) { if ((dwIndex = SendDlgItemMessage(hDlg, IDC_DEVICE, CB_GETCURSEL, 0, 0)) != CB_ERR) gwDevice = (WORD)dwIndex; else return TRUE; // ------------------------------------------------------ // Initialize Module Combobox for COM port or CAN devices // ------------------------------------------------------ // check any device attached on this COM port or CAN gnNumOfSubdevices = DeviceList[gwDevice].nNumOfSubdevices; if (gnNumOfSubdevices > MAX_DEVICES) gnNumOfSubdevices = MAX_DEVICES; // retrieve the information of all installed devices if (gnNumOfSubdevices != 0) { if ((ErrCde = DRV_DeviceGetSubList( (DWORD)DeviceList[gwDevice].dwDeviceNum, (DEVLIST far *)&SubDeviceList[0], (SHORT)gnNumOfSubdevices, (SHORT far *)&nOutEntries)) != (LONG)SUCCESS) { DRV_GetErrorMessage(ErrCde,(LPSTR)szErrMsg); MessageBox(hMainWnd, (LPCSTR)szErrMsg, "Driver Message", MB_OK); return TRUE; } // initialize the Module List Combobox with the // retrieved information EnableWindow(GetDlgItem(hDlg, IDC_MODULE), TRUE); SendDlgItemMessage(hDlg, IDC_MODULE, CB_RESETCONTENT, 0, (LPARAM)((LPSTR)0)); for (i = 0; i < gnNumOfSubdevices; i++) SendDlgItemMessage(hDlg, IDC_MODULE, CB_ADDSTRING, 0, (LPARAM)((LPSTR)SubDeviceList[i].szDeviceName)); gwSubDevice = 0; SendDlgItemMessage(hDlg, IDC_MODULE, CB_SETCURSEL, (WPARAM)gwSubDevice, (LPARAM)0); } else { EnableWindow(GetDlgItem(hDlg, IDC_MODULE), FALSE); SendDlgItemMessage(hDlg, IDC_MODULE, CB_RESETCONTENT, 0, (LPARAM)((LPSTR)0)); } } return TRUE; } break; } return FALSE ; } /*************************************************************************** FUNCTION: RunDlgProc(HWND, unsigned, WPARAM, LPARAM) PURPOSE: Processes dialog box messages for IDD_RUN ****************************************************************************/ BOOL FTYPE RunDlgProc ( HWND hDlg, // window handle unsigned message, // type of message WPARAM wParam, // additional information LPARAM lParam // additional information ) { int i; DWORD dwIndex; char szBuffer[40]; switch (message) { case WM_INITDIALOG : // -------------------------------- // Initialize Output Bit Combobox // -------------------------------- for (i = 0; i < 8; i ++) { _itoa(i, szBuffer, 10); SendDlgItemMessage(hDlg, IDC_OUTBIT, CB_ADDSTRING, 0, (LPARAM)((LPSTR)szBuffer)); } _itoa(gwOutBit, szBuffer, 10); SendDlgItemMessage(hDlg, IDC_OUTBIT, CB_SELECTSTRING,(WPARAM)(-1), (LPARAM)((LPSTR)szBuffer)); // ----------------------------------------- // Initialize Output Byte Value Check Button // ----------------------------------------- CheckDlgButton(hDlg, IDC_D0, (gwData & 0x1)); CheckDlgButton(hDlg, IDC_D1, (gwData & 0x2)); CheckDlgButton(hDlg, IDC_D2, (gwData & 0x4)); CheckDlgButton(hDlg, IDC_D3, (gwData & 0x8)); CheckDlgButton(hDlg, IDC_D4, (gwData & 0x10)); CheckDlgButton(hDlg, IDC_D5, (gwData & 0x20)); CheckDlgButton(hDlg, IDC_D6, (gwData & 0x40)); CheckDlgButton(hDlg, IDC_D7, (gwData & 0x80)); // // open device // if (gnNumOfSubdevices == 0) ErrCde = DRV_DeviceOpen( DeviceList[gwDevice].dwDeviceNum, (LONG far *)&DriverHandle); else ErrCde = DRV_DeviceOpen( SubDeviceList[gwSubDevice].dwDeviceNum, (LONG far *)&DriverHandle); if (ErrCde != SUCCESS) { strcpy(szErrMsg,"Device open error !"); MessageBox(hDlg,(LPCSTR)szErrMsg,"Notice",MB_OK); return 0; } return TRUE; case WM_COMMAND : switch (LOWORD(wParam)) { case IDCANCEL : // // close device // DRV_DeviceClose((LONG far *)&DriverHandle); EndDialog(hDlg, 0); return TRUE; case IDC_WRITEBYTE : // // get output byte value // if (IsDlgButtonChecked(hDlg, IDC_D0) > 0) gwData = 1; else gwData = 0; if (IsDlgButtonChecked(hDlg, IDC_D1) > 0) gwData += 0x2; if (IsDlgButtonChecked(hDlg, IDC_D2) > 0) gwData += 0x4; if (IsDlgButtonChecked(hDlg, IDC_D3) > 0) gwData += 0x8; if (IsDlgButtonChecked(hDlg, IDC_D4) > 0) gwData += 0x10; if (IsDlgButtonChecked(hDlg, IDC_D5) > 0) gwData += 0x20; if (IsDlgButtonChecked(hDlg, IDC_D6) > 0) gwData += 0x40; if (IsDlgButtonChecked(hDlg, IDC_D7) > 0) gwData += 0x80; // // output byte // ptDioWritePortByte.port = gwChannel; ptDioWritePortByte.mask = gwMask; ptDioWritePortByte.state = gwData; if((ErrCde = DRV_DioWritePortByte(DriverHandle, (LPT_DioWritePortByte)&ptDioWritePortByte)) != 0) { DRV_GetErrorMessage(ErrCde,(LPSTR)szErrMsg); MessageBox(hDlg,(LPCSTR)szErrMsg,"Driver Message",MB_OK); return 0; } return TRUE; case IDC_WRITEBIT : // // get output byte value // if (IsDlgButtonChecked(hDlg, IDC_D0) > 0) gwData = 1; else gwData = 0; if (IsDlgButtonChecked(hDlg, IDC_D1) > 0) gwData += 0x2; if (IsDlgButtonChecked(hDlg, IDC_D2) > 0) gwData += 0x4; if (IsDlgButtonChecked(hDlg, IDC_D3) > 0) gwData += 0x8; if (IsDlgButtonChecked(hDlg, IDC_D4) > 0) gwData += 0x10; if (IsDlgButtonChecked(hDlg, IDC_D5) > 0) gwData += 0x20; if (IsDlgButtonChecked(hDlg, IDC_D6) > 0) gwData += 0x40; if (IsDlgButtonChecked(hDlg, IDC_D7) > 0) gwData += 0x80; // // get output bit // if ((dwIndex = SendDlgItemMessage(hDlg, IDC_OUTBIT, CB_GETCURSEL, 0, 0)) != CB_ERR) gwOutBit = (WORD)dwIndex; else return TRUE; // // output bit // ptDioWriteBit.port = gwChannel; ptDioWriteBit.bit = gwOutBit; if ((gwData & (1 << gwOutBit))) ptDioWriteBit.state = 1; else ptDioWriteBit.state = 0; if((ErrCde = DRV_DioWriteBit(DriverHandle, (LPT_DioWriteBit)&ptDioWriteBit)) != 0) { DRV_GetErrorMessage(ErrCde,(LPSTR)szErrMsg); MessageBox(hDlg,(LPCSTR)szErrMsg,"Driver Message",MB_OK); return 0; } return TRUE; case IDC_READBACK : // // get current DO byte // ptDioGetCurrentDOByte.port = gwChannel; ptDioGetCurrentDOByte.value = (USHORT far *)&gwDoState; if((ErrCde = DRV_DioGetCurrentDOByte(DriverHandle, (LPT_DioGetCurrentDOByte)&ptDioGetCurrentDOByte)) != 0) { DRV_GetErrorMessage(ErrCde,(LPSTR)szErrMsg); MessageBox(hDlg,(LPCSTR)szErrMsg,"Driver Message",MB_OK); return 0; } // // display the value in Output State Edit // _itoa(gwDoState, szBuffer, 16); SendDlgItemMessage(hDlg, IDC_STATUS, WM_SETTEXT, 0, (LPARAM)((LPSTR)szBuffer)); return TRUE; } break; } return FALSE ; } /*************************************************************************** FUNCTION: MainWndProc(HWND, unsigned, WPARAM, LPARAM) PURPOSE: Processes messages MESSAGES: WM_CREATE - create window WM_COMMAND - application menu (About dialog box) WM_DESTROY - destroy window ****************************************************************************/ long FTYPE MainWndProc ( HWND hWnd, // window handle unsigned message, // type of message WPARAM wParam, // additional information LPARAM lParam // additional information ) { static HANDLE hInstance ; switch (message) { case WM_CREATE: hInstance = ((LPCREATESTRUCT) lParam)->hInstance ; lpfnConfigDlgProc = MakeProcInstance (ConfigDlgProc, hInstance) ; lpfnRunDlgProc = MakeProcInstance (RunDlgProc, hInstance) ; return 0 ; case WM_COMMAND: // message: from application menu switch (LOWORD(wParam)) { case IDM_SETTING : DialogBox (hInstance, MAKEINTRESOURCE(IDD_SETTING), hWnd, lpfnConfigDlgProc) ; return 0; case IDM_RUN : DialogBox (hInstance, MAKEINTRESOURCE(IDD_RUN), hWnd, lpfnRunDlgProc) ; return 0; } return 0; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, message, wParam, lParam)); } return ((LONG)NULL); } WORD CvtHex(char *str) { char temp; WORD data = 0; int i; for(i = 0; i < 4; ++i) { temp = str[i]; if(temp == 0) break; if(temp == ' ') continue; data <<= 4; if(temp >= '0' && temp <= '9') { data |= (temp - '0'); } else if(temp >= 'a' && temp <= 'f') { data |= (temp - 'a') + 10; str[i] = temp - 'a' + 'A'; } else if(temp >= 'A' && temp <= 'F') { data |= (temp - 'A') + 10; } else { break; } } return(data); }

总结,有关windows 32 SDK的学习资料最好的是《WINDOWS程序设计》这本书,作者是Charles Petzold;

劝学网的教程也相当不错http://www.quanxue.cn/JC_CLanguage/SdkIndex.html

windowSDK笔记也值得一看http://www.cnblogs.com/MS-Frank/archive/2009/03/13/1410325.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值