国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

USB系列-LibUSB使用指南(1)-Windows下的報(bào)錯(cuò)與踩坑

這篇具有很好參考價(jià)值的文章主要介紹了USB系列-LibUSB使用指南(1)-Windows下的報(bào)錯(cuò)與踩坑。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

成就更好的自己

時(shí)隔一年再次開(kāi)始撰寫博客,這一年的時(shí)間經(jīng)歷了很多,現(xiàn)在終于穩(wěn)定下來(lái)。以后很長(zhǎng)一段時(shí)間都能夠穩(wěn)定的學(xué)習(xí)和更新。時(shí)間將會(huì)聚焦于USB和PCIe的開(kāi)發(fā)進(jìn)行,能和大家共同進(jìn)步真的很高興。

本篇為USB系列的LibUSB使用指南的第一篇。

USB系列主要圍繞USB的知識(shí)、協(xié)議、開(kāi)發(fā)總結(jié)、使用說(shuō)明等進(jìn)行。

LibUSB使用指南主要圍繞LibUSB庫(kù)的使用進(jìn)行。


LibUSB中的描述符結(jié)構(gòu)分析

LibUSB中的描述符結(jié)構(gòu)主要分為一下幾種層次:

設(shè)備描述符->配置描述符->接口描述符(備用接口描述符)->端點(diǎn)描述符

看到這篇博客的都應(yīng)該知道上述描述符的包含關(guān)系和可能的存在數(shù)量,基礎(chǔ)知識(shí)不再贅述,到時(shí)候會(huì)專門寫對(duì)應(yīng)的基礎(chǔ)知識(shí)博客。

LibUSB中的描述符包含關(guān)系與結(jié)構(gòu)如下圖所示:

libusb,USB開(kāi)發(fā)系列,USB,Windows,LibUSB

?

從圖中可以看出是沒(méi)有設(shè)備描述符libusb_device_descriptor的,因?yàn)樵O(shè)備描述符一般可以看做是以設(shè)備為對(duì)象的一種描述結(jié)構(gòu),因此每個(gè)設(shè)備只有一個(gè)而且地位特殊。

圖中最高層是配置描述符libusb_config_descripto,使用libusb_get_config_descriptor函數(shù)傳入索引可以獲得該設(shè)備中的某一個(gè)配置描述符。配置描述符中有一項(xiàng)bNumInterfaces為接口描述符的個(gè)數(shù),可通過(guò)該項(xiàng)獲得在該配置描述符中接口描述符的個(gè)數(shù)。

在配置描述符中還有一項(xiàng)為libusb_interface *interface,該項(xiàng)就是接口描述符的結(jié)構(gòu)體數(shù)組。這個(gè)接口描述符與我們廣義上理解的接口描述符不同,這里的接口描述符是由備用接口描述符結(jié)構(gòu)體數(shù)組和備用描述符個(gè)數(shù)組成的,也就是libusb_interface_descriptor *altsetting和num_altsetting這兩項(xiàng)。

要注意的是bNumInterfaces所指是接口描述符數(shù)組的個(gè)數(shù),num_altsetting所指的是每個(gè)接口描述符數(shù)組成員中的備用接口描述符的數(shù)量。因此實(shí)際的配置描述符是由備用接口描述符組成的一個(gè)二維數(shù)組,這個(gè)二維數(shù)組的主索引是bNumInterfaces,副索引是num_altsetting。

在備用接口描述符數(shù)組中的每個(gè)成員都攜帶著在該備用接口下所屬的若干端點(diǎn)的描述符結(jié)構(gòu)體。

整個(gè)上面的過(guò)程中只有這個(gè)備用接口的概念和廣義理解上的不太一樣,剩下各個(gè)描述符中的各個(gè)字段均與協(xié)議棧中我們所通識(shí)的一樣。實(shí)際上,這些結(jié)構(gòu)體的存在方式和使用LibUSB函數(shù)獲取描述符的方式與USB設(shè)備的枚舉過(guò)程和方式的有著本質(zhì)上的關(guān)聯(lián)(后期博客提到)。

下面就是以博主電腦上為例,輸出能探測(cè)到設(shè)備的詳細(xì)信息(太長(zhǎng)了,部分截圖):

libusb,USB開(kāi)發(fā)系列,USB,Windows,LibUSB

Windows下的USB驅(qū)動(dòng)問(wèn)題

Windows下LibUSB產(chǎn)生的很多報(bào)錯(cuò)與驅(qū)動(dòng)有著很大關(guān)系。

我先言簡(jiǎn)意駭?shù)暮?jiǎn)要介紹一下在你的Windows中可能存在的各種USB驅(qū)動(dòng),我暫時(shí)驅(qū)動(dòng)分為高級(jí)的功能性驅(qū)動(dòng)和低級(jí)的設(shè)備性驅(qū)動(dòng):

高級(jí)的功能性驅(qū)動(dòng):比如插入MSD類設(shè)備、移動(dòng)硬盤、藍(lán)牙適配器、HID類設(shè)備、網(wǎng)卡等具有實(shí)際功能性意義的設(shè)備時(shí),Windows底層會(huì)自動(dòng)的識(shí)別該設(shè)備的類和功能,從而找到一款能夠直接適配該設(shè)備功能的通用高級(jí)驅(qū)動(dòng),這樣就實(shí)現(xiàn)了目前Windows下多數(shù)陌生設(shè)備的免驅(qū)。

低級(jí)的設(shè)備性驅(qū)動(dòng):當(dāng)插入的設(shè)備極為特殊,Windows認(rèn)不出來(lái)這個(gè)玩意是干嘛的(比如設(shè)備描述符和接口描述符中的類字段與子類字段全部為0xff,即廠家定義),這個(gè)時(shí)候僅僅只是能夠進(jìn)行設(shè)備的基礎(chǔ)枚舉操作和通信。此時(shí)能夠支撐設(shè)備枚舉和通信的驅(qū)動(dòng)就是設(shè)備性驅(qū)動(dòng),比如libusb-win32或WinUSB等。

假如我現(xiàn)在插入一個(gè)U盤就開(kāi)始調(diào)用LibUSB庫(kù)進(jìn)行Open操作,這樣是一定會(huì)返回錯(cuò)誤的,因?yàn)長(zhǎng)ibUSB本身不是驅(qū)動(dòng),實(shí)際上也是要調(diào)用驅(qū)動(dòng)留出的系統(tǒng)調(diào)用接口進(jìn)行數(shù)據(jù)和指令傳遞的(這里類比linux的特性,表述可能些許不合適)。當(dāng)這個(gè)設(shè)備底層根本不是LibUSB需要的接口的時(shí)候,當(dāng)然是無(wú)法正常調(diào)用。

此時(shí)需要將該設(shè)備的驅(qū)動(dòng)換成合適的低級(jí)驅(qū)動(dòng)(linux的優(yōu)勢(shì)就出來(lái)了),LibUSB庫(kù)在Windows平臺(tái)上是基于WinUSB底層驅(qū)動(dòng)去開(kāi)發(fā)的。因此最適合LibUSB開(kāi)發(fā)的底層驅(qū)動(dòng)當(dāng)然是WinUSB。博主嘗試過(guò)使用linusb-win32等其他的驅(qū)動(dòng),他們可以open設(shè)備但是在claim接口的時(shí)候會(huì)有掉設(shè)備的情況(一claim就聽(tīng)到Windows掉USB設(shè)備的聲音,正常后不會(huì)這樣),直接導(dǎo)致transfer的時(shí)候返回LIBUSB_ERROR_IO(-1)。因此,在進(jìn)行Windows下的LibUSB開(kāi)發(fā)之前先解決你的驅(qū)動(dòng)問(wèn)題。

驅(qū)動(dòng)問(wèn)題的解決方式是通過(guò)zadig工具進(jìn)行驅(qū)動(dòng)替換,首先插入你的設(shè)備,list一下獲得設(shè)備名單,選擇你的設(shè)備,選擇winUSB驅(qū)動(dòng),replace即可。前提是要有網(wǎng)(公司的機(jī)器沒(méi)有網(wǎng),GG)。如圖(博主使用的SMT32USBD虛擬為com):

libusb,USB開(kāi)發(fā)系列,USB,Windows,LibUSB

注意,使用此種方式他只會(huì)根據(jù)選擇設(shè)備的設(shè)備號(hào)進(jìn)行定向更換,這樣就產(chǎn)生了兩個(gè)問(wèn)題:第一,替換的效果只能適用同一VID和PID的設(shè)備。第二,被替換的設(shè)備從此以后只能使用這個(gè)低級(jí)驅(qū)動(dòng),原來(lái)高級(jí)驅(qū)動(dòng)的功能將不能使用。若不小心將重要設(shè)備的驅(qū)動(dòng)進(jìn)行重置和降級(jí),那就去zadig工具的官網(wǎng)上尋找答案吧(當(dāng)時(shí)無(wú)知把移動(dòng)硬盤驅(qū)動(dòng)給降級(jí)了,人都裂開(kāi)了)。

zadig官網(wǎng)與技術(shù)支持:

??????https://www.baidu.com/link?url=UuDz3G0f6UbanYtYLC8SIC6sDu-aTCuBlW1BkriYHqK&wd=&eqid=cd9600c2000f6899000000046358207b

Open_device過(guò)程

這一節(jié)咱們直接面向?qū)ο髞?lái)說(shuō):

  1. libusb_open_device_with_vid_pid或者open_device:打開(kāi)設(shè)備,得到一個(gè)libusb_device_handle,沒(méi)啥說(shuō)的,驅(qū)動(dòng)沒(méi)安好你就是開(kāi)不了,氣不氣。
  2. libusb_get_device:由libusb_device_handle結(jié)構(gòu)體反推libusb_device_descriptor結(jié)構(gòu)體用的(open_device的不用)。
  3. libusb_get_device_descriptor:獲取端點(diǎn)信息的第一步—獲取設(shè)備描述符。
  4. libusb_get_config_descriptor:獲取端點(diǎn)信息的第二步—獲取配置描述符。、
  5. 使用上一步的信息,通過(guò)一系列if篩選,找到你想要建立通信的備用接口和備用接口中的端點(diǎn)地址。
  6. libusb_claim_interface和libusb_set_interface_alt_setting:聲明接口和備用接口,傳入?yún)?shù)為索引值,上一步得到的。驅(qū)動(dòng)沒(méi)安好你就是claim不了(ret=-5),氣不氣。
  7. libusb_clear_halt:清除端點(diǎn)和對(duì)應(yīng)通道中的殘存的數(shù)據(jù),為下一次通信做好準(zhǔn)備。
  8. 該free的free,該release的release。

到此為止,正常情況下你就應(yīng)該得到一個(gè)已經(jīng)open的設(shè)備和一些(>=1)準(zhǔn)備傳輸?shù)亩它c(diǎn)。注意:第六第七步的順序千萬(wàn)不能倒過(guò)來(lái),否則你的libusb_clear_halt會(huì)報(bào)錯(cuò)LIBUSB_ERROR_NOT_FOUND (ret=-5)。而且在Windows開(kāi)發(fā)中,LibUSB中的detach_kernel系列的函數(shù)都是多余的,人家Windows的原理就不需要這些操作。

Transfer過(guò)程

博主使用的是libusb_bulk_transfer傳輸?shù)?,中間出了點(diǎn)小問(wèn)題,簡(jiǎn)要說(shuō)一下:

  1. libusb_bulk_transfer返回LIBUSB_ERROR_IO(-1)大概率是因?yàn)槟愕尿?qū)動(dòng)沒(méi)有裝對(duì),libusb0 (v2.6.0)的驅(qū)動(dòng)能夠open和claim不報(bào)錯(cuò),但是就是在傳輸?shù)臅r(shí)候報(bào)錯(cuò)。
  2. libusb_bulk_transfer返回LIBUSB_ERROR_NOT_FOUND (-5)大概率是因?yàn)槟鉩laim這一步?jīng)]有正常claim。注意如果你的端點(diǎn)出自備用接口,記得claim備用接口(claim標(biāo)準(zhǔn)接口好像是默認(rèn)claim該接口下索引為0的備用接口,保險(xiǎn)起見(jiàn)不管備用接口索引是幾,記得claim一下備用接口)。

貼一下程序(bulk)

main.c

#include <stdio.h>
#include <main.h>
#include <libusb.h>
#include "log.h"
#include <stdint.h>
#include "usb_device_opt.h"

const uint16_t VID = 0x1234;
const uint16_t PID = 0x1234;
//const uint16_t VID = 0x0bda;
//const uint16_t PID = 0x8179;
int main(void)
{
	libusb_device **devs;
	USB_MSD_ST msd = {NULL, NULL, 0x00, 0x00};
	uint8_t err = 0;
	int16_t dev_num;

    Log_Init(3);
	err = libusb_init(NULL);
	if(err > 0)
    {
        LOG_ERROR("libusb_init() err with %d", err);
        goto init_err;
    }
    //libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL , LIBUSB_LOG_LEVEL_INFO);

	dev_num = libusb_get_device_list(NULL, &devs);
	if(dev_num < 0)
    {
        LOG_ERROR("libusb_get_device_list() err with %d", err);
        goto getlist_err;
    }

	USB_Dev_Scan_A_Print(dev_num, devs);

    err = USB_MSD_Open(VID, PID, &msd);
    if(err > 0)
    {
        USB_MSD_Close(&msd);
        goto getlist_err;
    }

    uint8_t buff[512];
    int size = 0;
    memset(buff, 9, 512);
    err = USB_MSD_Bulk_Write(&msd, buff, 64, &size, 1000);
    if(err > 0)
    {
        USB_MSD_Close(&msd);
        goto getlist_err;
    }
    else
    {
        LOG_INFO("Send OK!");
    }
    err = USB_MSD_Close(&msd);

getlist_err:
	libusb_free_device_list(devs, 1);
init_err:
	libusb_exit(NULL);
	return 0;
}

?usb_device_opt.c

#include <stdio.h>
#include <libusb.h>
#include "log.h"
#include <stdint.h>
#include "usb_device_opt.h"

uint8_t USB_Dev_Scan_A_Print(int16_t dev_num, libusb_device **devs)
{
    struct libusb_device_descriptor usb_dev_desc;
    struct libusb_config_descriptor *usb_cfg_desc;
    uint8_t err = 0;


    LOG_INFO("Dev_Num:%d", dev_num);
    for(uint16_t i = 0;i < dev_num; i++)
    {
        err = libusb_get_device_descriptor(devs[i], &usb_dev_desc);
        if(err > 0)
        {
            LOG_ERROR("libusb_get_device_descriptor()  err with %d", err);
            goto getdevdesc_err;
        }

        printf("|--[Vid:0x%04x, Pid:0x%04x]-[Class:0x%02x, SubClass:0x%02x]-[bus:%d, device:%d, port:%d]-[cfg_desc_num:%d]\n",
            usb_dev_desc.idVendor, usb_dev_desc.idProduct, usb_dev_desc.bDeviceClass, usb_dev_desc.bDeviceSubClass,
            libusb_get_bus_number(devs[i]), libusb_get_device_address(devs[i]), libusb_get_port_number(devs[i]), usb_dev_desc.bNumConfigurations);
        //printf()
        for(uint8_t j = 0;j < usb_dev_desc.bNumConfigurations; j++)
        {
            err = libusb_get_config_descriptor(devs[i], j, &usb_cfg_desc);
            if(err > 0)
            {
                LOG_ERROR("libusb_get_config_descriptor(cfg_index:%d)  err with %d", j, err);
                goto getcfgdesc_err;
            }
            printf("|  |--cfg_desc:%02d-[cfg_value:0x%01x]-[infc_desc_num:%02d]\n",
                j, usb_cfg_desc->bConfigurationValue, usb_cfg_desc->bNumInterfaces);
            for(uint8_t l = 0;l < usb_cfg_desc->bNumInterfaces; l++)
            for(uint8_t n = 0;n < usb_cfg_desc->interface[l].num_altsetting; n++)
            {
                printf("|  |  |--intfc_desc: %02d:%02d-[Class:0x%02x, SubClass:0x%02x]-[ep_desc_num:%02d]\n",
                    l, n, usb_cfg_desc->interface[l].altsetting[n].bInterfaceClass, usb_cfg_desc->interface[l].altsetting[n].bInterfaceSubClass,
                    usb_cfg_desc->interface[l].altsetting[n].bNumEndpoints);
                 for(uint8_t m = 0;m < usb_cfg_desc->interface[l].altsetting[n].bNumEndpoints; m++)
                 {
                    printf("|  |  |  |--ep_desc:%02d-[Add:0x%02x]-[Attr:0x%02x]-[MaxPkgLen:%02d]\n",
                        m, usb_cfg_desc->interface[l].altsetting[n].endpoint[m].bEndpointAddress,
                        usb_cfg_desc->interface[l].altsetting[n].endpoint[m].bmAttributes,
                        usb_cfg_desc->interface[l].altsetting[n].endpoint[m].wMaxPacketSize);
                 }
            }
        }
    }
    return 0;
getdevdesc_err:
    return 0xff;
getcfgdesc_err:
    return 0xff;
}

uint8_t USB_MSD_Open(uint16_t VID, uint16_t PID, USB_MSD_ST *msd)
{
    struct libusb_device_descriptor usb_dev_desc;
    struct libusb_config_descriptor *usb_cfg_desc;
    uint8_t intfc_index = 0;
    uint8_t err = 0;

    msd->msd_handle = libusb_open_device_with_vid_pid(NULL, VID, PID);
    if(msd->msd_handle == NULL)
    {
        LOG_ERROR("[0x%04x:0x%04x] MSD Open failed!", VID, PID);
        goto opendev_err;
    }
    msd->msd_dev = libusb_get_device(msd->msd_handle);
    if(msd->msd_dev == NULL)
    {
        LOG_ERROR("[0x%04x:0x%04x] get dev failed!", VID, PID);
        goto getdev_err;
    }
    err = libusb_get_device_descriptor(msd->msd_dev, &usb_dev_desc);
    if(err > 0)
    {
        LOG_ERROR("[0x%04x:0x%04x] get dev_desc failed err with %d", VID, PID, err);
        goto getdevdesc_err;
    }
    err = libusb_get_config_descriptor(msd->msd_dev, 0, &usb_cfg_desc);
    if(err > 0)
    {
        LOG_ERROR("[0x%04x:0x%04x] get cfg_desc failed err with %d", VID, PID, err);
        goto getcfgdesc_err;
    }
    for(uint8_t m = 0;m < usb_cfg_desc->bNumInterfaces; m++)
    {
        for(uint8_t n = 0;n < usb_cfg_desc->interface[m].num_altsetting;n++)
        {
            if(usb_cfg_desc->interface[m].altsetting[n].bInterfaceClass == 0x0a && usb_cfg_desc->interface[m].altsetting[n].bInterfaceSubClass == 0x00)
            {
                for(uint8_t i = 0;i < usb_cfg_desc->interface[m].altsetting[n].bNumEndpoints;i++)
                {
                    if((usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK)
                    {
                        if((usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN)
                        {
                            msd->endpoint_in = usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress;
                        }
                        if((usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT)
                        {
                            msd->endpoint_out = usb_cfg_desc->interface[m].altsetting[n].endpoint[i].bEndpointAddress;
                        }
                    }
                }
                if(msd->endpoint_in != 0x00 && msd->endpoint_out != 0x00)
                {
                    intfc_index = m;
                }
                else
                {
                    msd->endpoint_in = 0x00;
                    msd->endpoint_out = 0x00;
                }
            }
        }
    }
    if(msd->endpoint_in == 0x00 || msd->endpoint_out == 0x00)
    {
        LOG_ERROR("[0x%04x:0x%04x] get ep_addr failed!", VID, PID);
        goto getepaddr_err;
    }

    err = libusb_claim_interface(msd->msd_handle, 1);
    err = libusb_set_interface_alt_setting(msd->msd_handle, 1, 0);
    err = libusb_claim_interface(msd->msd_handle, 0);
    err = libusb_set_interface_alt_setting(msd->msd_handle, 0, 0);
    if(err > 0)
    {
        LOG_ERROR("[0x%04x:0x%04x] claim intfc failed err with %d", VID, PID, err);
        goto claimintfc_err;
    }

    err = libusb_clear_halt(msd->msd_handle, msd->endpoint_out);
    if(err > 0)
    {
        LOG_ERROR("[0x%04x:0x%04x] ep_out:%x clear halt failed err with %d", VID, PID, msd->endpoint_out, (int8_t)err);
        goto epclrhalt_err;
    }

    err = libusb_clear_halt(msd->msd_handle, msd->endpoint_in);
    if(err > 0)
    {
        LOG_ERROR("[0x%04x:0x%04x] ep_in:%x clear halt failed err with %d", VID, PID, msd->endpoint_in, (int8_t)err);
        goto epclrhalt_err;
    }

    libusb_free_config_descriptor(usb_cfg_desc);
    libusb_reset_device(msd->msd_handle);
    LOG_INFO("[0x%04x:0x%04x]-[EP_IN:0x%02x, EP_OUT:0x%02x] Open success!", VID, PID, msd->endpoint_in, msd->endpoint_out);

    return 0;
//óD?êìa

claimintfc_err:
    libusb_release_interface(msd->msd_handle, 1);
    libusb_release_interface(msd->msd_handle, 0);

detachkernel_err:

epclrhalt_err:

getepaddr_err:

getcfgdesc_err:

getdevdesc_err:

getdev_err:

opendev_err:
    libusb_free_config_descriptor(usb_cfg_desc);
    libusb_close(msd->msd_handle);
    return 0xff;
}

uint8_t USB_MSD_Close(USB_MSD_ST *msd)
{
    if(msd->msd_handle != NULL)
    {
        libusb_release_interface(msd->msd_handle, 0);
        libusb_close(msd->msd_handle);
    }
    return 0;
}

uint8_t USB_MSD_Bulk_Write(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms)
{
	int err = 0;

	err = libusb_bulk_transfer(msd->msd_handle, msd->endpoint_out, buffer, len, size, ms);
	if (err < 0)
	{
		LOG_ERROR("Write:%d", err);
		return -1;
	}

	return 0;
}

uint8_t USB_MSD_Bulk_Read(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms)
{
	int err = 0;

	err = libusb_bulk_transfer(msd->msd_handle, msd->endpoint_in, buffer, len, size, ms);
	if (err < 0)
	{
		LOG_ERROR("Read:%d", err);
		return -1;
	}

	return 0;
}


usb_device_opt.h

#ifndef _USB_DEVICE_OPT_
#define _USB_DEVICE_OPT_
#include <stdio.h>
#include <libusb.h>
#include "log.h"
#include <stdint.h>

typedef struct USB_MSD
{
    libusb_device* msd_dev;
    libusb_device_handle* msd_handle;
    uint8_t endpoint_in;
    uint8_t endpoint_out;
} USB_MSD_ST;

extern uint8_t USB_Dev_Scan_A_Print(int16_t dev_num, libusb_device **devs);
extern uint8_t USB_MSD_Open(uint16_t VID, uint16_t PID, USB_MSD_ST *msd);
extern uint8_t USB_MSD_Bulk_Write(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms);
extern uint8_t USB_MSD_Bulk_Read(USB_MSD_ST* msd, uint8_t* buffer, int len, int* size, uint32_t ms);
extern uint8_t USB_MSD_Close(USB_MSD_ST *msd);

#endif // _USB_DEVICE_OPT_

Good Night!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-836972.html

到了這里,關(guān)于USB系列-LibUSB使用指南(1)-Windows下的報(bào)錯(cuò)與踩坑的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 去掉鼠標(biāo)系列之一: 語(yǔ)雀快捷鍵使用指南

    去掉鼠標(biāo)系列之一: 語(yǔ)雀快捷鍵使用指南

    其實(shí)應(yīng)該是系列之二了,因?yàn)榍懊鎸懥艘粋€(gè)關(guān)于Interlij IDEA的快捷鍵了。 為什么要寫這個(gè)了,主要是覺(jué)得一會(huì)兒用鼠標(biāo),一會(huì)兒鍵盤,一點(diǎn)兒不酷,我希望可以一直用鍵盤,拋開(kāi)鼠標(biāo)。后面陸續(xù)記錄一下各個(gè)軟件的快捷鍵,一步步減少鼠標(biāo)的使用。 gogogo! 1,搜索 Ctrl+J 2, 撤

    2024年02月12日
    瀏覽(33)
  • 16-3_Qt 5.9 C++開(kāi)發(fā)指南_使用QStyle 設(shè)置界面外觀_實(shí)現(xiàn)不同系統(tǒng)下的界面效果的匹配

    16-3_Qt 5.9 C++開(kāi)發(fā)指南_使用QStyle 設(shè)置界面外觀_實(shí)現(xiàn)不同系統(tǒng)下的界面效果的匹配

    Qt 是一個(gè)跨平臺(tái)的類庫(kù),相同的界面組件在不同的操作系統(tǒng)上顯示效果是不一樣的。QStyle是封裝了 GUI 界面組件外觀的抽象類,Qt 定義了 QStyle 類的一些子類,應(yīng)用于不同的操作系統(tǒng)如QWindowsStyle和QMacStyle 等。這些樣式是 QtGUI 模塊自帶的,在不同的平臺(tái)上編譯運(yùn)行的程序具有缺

    2024年02月13日
    瀏覽(25)
  • grasscutter 使用指南——Android/Windows/IOS端均已支持

    grasscutter是某二次元手游的開(kāi)源后端,目前功能并不完整,但正在contributers正在全速開(kāi)發(fā)中,未來(lái)可期。可以部署在linux和windows下,通過(guò)代理與各種平臺(tái)的客戶端進(jìn)行交互。本文提供grasscutter的linux端部署(windows端比較簡(jiǎn)單)、windows/andriod/ios端的客戶端連接指導(dǎo),其中安卓提

    2023年04月09日
    瀏覽(73)
  • 新機(jī)開(kāi)荒攻略及新手電腦使用指南不完全手冊(cè):Windows 電腦

    新機(jī)開(kāi)荒攻略及新手電腦使用指南不完全手冊(cè):Windows 電腦

    介紹一下 Windows 的新機(jī)開(kāi)荒、使用指南、軟件推薦。 如果您沒(méi)接觸過(guò)電腦請(qǐng)先于 Bilibili 搜索零基礎(chǔ)電腦入門教學(xué),學(xué)習(xí)電腦基本操作。 我這里介紹的內(nèi)容較多,有些功能的作用也不是必須,所以您可以酌情學(xué)習(xí)。 卸載多余的無(wú)用UWP應(yīng)用 您可以使用 Geek Uninstaller工具卸載不需

    2024年02月04日
    瀏覽(21)
  • C語(yǔ)言調(diào)用libusb訪問(wèn)USB驅(qū)動(dòng)

    C語(yǔ)言調(diào)用libusb訪問(wèn)USB驅(qū)動(dòng)

    目錄 一、環(huán)境搭建 1. 下載庫(kù)文件 2. 解壓 3. 配置VS工程 3.1 頭文件的配置 3.2 靜態(tài)庫(kù)文件的處理 3.3 配置運(yùn)行時(shí)庫(kù) 二、生成自定義設(shè)備的驅(qū)動(dòng) 1. 禁用Windows驅(qū)動(dòng)程序強(qiáng)制簽名 2. 設(shè)備描述符的設(shè)計(jì) 3. 設(shè)備枚舉 4. 如何為自己的設(shè)備安裝WinUSB驅(qū)動(dòng) 三、測(cè)試 1. 測(cè)試代碼 2. 小結(jié) 開(kāi)發(fā)環(huán)

    2024年02月07日
    瀏覽(21)
  • 網(wǎng)安工具 | Windows便攜式滲透測(cè)試環(huán)境PentestBox入門到進(jìn)階使用指南

    網(wǎng)安工具 | Windows便攜式滲透測(cè)試環(huán)境PentestBox入門到進(jìn)階使用指南

    [ 點(diǎn)擊 ?? 關(guān)注「 全棧工程師修煉指南」公眾號(hào) ] 微信改版了,現(xiàn)在看到我們?nèi)珣{緣分,為了不錯(cuò)過(guò)【全棧工程師修煉指南】重要內(nèi)容及福利,大家記得按照上方步驟設(shè)置「接收文章推送」哦~ 希望各位看友多多支持【關(guān)注、點(diǎn)贊、評(píng)論、收藏、投幣】,助力每一個(gè)夢(mèng)想。 【

    2024年02月08日
    瀏覽(66)
  • 測(cè)試員進(jìn)階必看系列 “ python自動(dòng)化測(cè)試工具selenium使用指南 ”

    測(cè)試員進(jìn)階必看系列 “ python自動(dòng)化測(cè)試工具selenium使用指南 ”

    概述 python+selenium環(huán)境安裝 使用selenium啟動(dòng)瀏覽器 selenium頁(yè)面加載等待和檢測(cè) 使用time.sleep()等待 使用implicitly_wait設(shè)置最長(zhǎng)等待時(shí)間 使用WebDriverWait設(shè)置等待條件 檢測(cè)document是否加載完成 selenium元素定位和讀取 查找元素 dom元素交互 查找元素失敗處理 selenium交互控制 ActionChains動(dòng)

    2024年02月05日
    瀏覽(118)
  • Spark初學(xué)者指南:使用指南和示例

    本文介紹了如何使用Spark處理大規(guī)模數(shù)據(jù)集,并提供了一個(gè)Scala編寫的Word Count示例,指導(dǎo)您從安裝和配置到編寫和運(yùn)行Spark應(yīng)用程序。無(wú)需擔(dān)心,即使您是Spark初學(xué)者,也可以按照本文的步驟來(lái)學(xué)習(xí)和使用Spark。 Spark是一個(gè)流行的分布式計(jì)算框架,用于處理大規(guī)模數(shù)據(jù)集。它使

    2024年02月06日
    瀏覽(35)
  • 【12】Git工具 協(xié)同工作平臺(tái)使用教程 Gitee使用指南 騰訊工蜂使用指南【Gitee】【騰訊工蜂】【Git】

    【12】Git工具 協(xié)同工作平臺(tái)使用教程 Gitee使用指南 騰訊工蜂使用指南【Gitee】【騰訊工蜂】【Git】

    tips:少量的git安裝和使用教程,更多講快速使用上手Gitee和工蜂平臺(tái) ? ???Git - Downloads (git-scm.com) 找到對(duì)應(yīng)操作系統(tǒng),對(duì)應(yīng)版本,對(duì)應(yīng)的位數(shù) ? 下載后根據(jù)需求自己安裝,然后用git --version驗(yàn)證是否成功 ? 使用 SSH 密鑰可以讓你在與 Git 服務(wù)器進(jìn)行通信時(shí),實(shí)現(xiàn)更安全的身份

    2024年02月13日
    瀏覽(22)
  • Jmeter進(jìn)階使用指南-使用斷言

    Apache JMeter是一個(gè)流行的開(kāi)源負(fù)載和性能測(cè)試工具。在JMeter中,斷言(Assertions)是用來(lái)驗(yàn)證響應(yīng)數(shù)據(jù)是否符合預(yù)期的一個(gè)重要組件。它是對(duì)請(qǐng)求響應(yīng)的一種檢查,如果響應(yīng)不符合預(yù)期,那么斷言會(huì)標(biāo)記為失敗。 以下是如何在JMeter中使用斷言的基本步驟: 添加斷言 :首先,你

    2024年02月09日
    瀏覽(24)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包