因为以前没用过USB,对USB也不了解,于是上网查了很多资料,不过网上的资料都是零零散散,不清不楚的,于是我自己总结了一下,下面几个链接是网上这么多零散资料里,我觉得比较有参考意义的。

  USB设备连接思路参考:https://www.cnblogs.com/xyzyx/articles/2959610.html#undefined

  代码参考:http://www.cnblogs.com/xidongs/archive/2011/11/28/2266100.html#

  收发数据参考:https://blog.csdn.net/zouwen198317/article/details/5814212

  整个思路概况为3步:1、识别设备;2、连接设备;3、数据传输。

  而我这里所有的步骤都是基于直接调用Windos的DLL实现的,调用了hid.dll、setupapi.dll、kernel32.dll,建立好工程后,可以从你自己的电脑里复制出这几个文件到工程目录下使用,调用DLL思路如下:

            /*          使用USB传输数据思路
* 0.连接DALL的库函数做好准备工作
* 1.调用HidD_GetHidGuid获取到GUID
* 2.调用SetupDiGetClassDevs获取全部的HID值
* 3.调用SetupDiEnumDeviceInterfaces,填写PSP_DEVICE_INTERFACE_DATA结构数据项,
* 该结构用于识别一个HID设备接口,结构具体作用参考函数说明
* 4.调用SetupDiGetDeviceInterfaceDetail获取特定的HID路径名
* 5.调用CreateFile获得设备HID句柄
* 6.调用HidD_GetAttributes,填写HID_ATTRIBUTES结构的数据项,
* 该结构包含设备的厂商ID、产品ID和产品序列号,程序可比照这些获取到的参数值确定该设备是否是查找的设备
* 7.查找成功完成,未成功重复3 4 5 6 步
* 8.与自己需要的USB设备连接成功后,调用WriteFile和ReadFile进行数据传输
* 9.关闭时调用SetupDiDestroyDeviceInfoList(hDevInfo)和CloseHandle(HidHandle)断开连接并关闭USB设备
*/

  接下来开始按上面的步骤来操作。

0、连接DALL的库函数做好准备工作
/*  ======================声明dll文件中需要调用的函数,以下是调用windows的API的函数======================= */

        //获得USB设备的GUID
[DllImport("hid.dll")]
public static extern void HidD_GetHidGuid(ref Guid HidGuid);
Guid guidHID = Guid.Empty; //获得一个包含全部HID信息的结构数组的指针,具体配置看函数说明:https://docs.microsoft.com/zh-cn/windows/desktop/api/setupapi/nf-setupapi-setupdigetclassdevsw
[DllImport("setupapi.dll", SetLastError = true)]
public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, uint Enumerator, IntPtr HwndParent, DIGCF Flags);
IntPtr hDevInfo; public enum DIGCF
{
DIGCF_DEFAULT = 0x1,
DIGCF_PRESENT = 0x2,
DIGCF_ALLCLASSES = 0x4,
DIGCF_PROFILE = 0x8,
DIGCF_DEVICEINTERFACE = 0x10
} //该结构用于识别一个HID设备接口,获取设备,true获取到
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, UInt32 memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);
//SetupDiEnumDeviceInterfaces 识别出来的SP_DEVICE_INTERFACE_DATA结构,该结构标识满足搜索参数的接口
public struct SP_DEVICE_INTERFACE_DATA
{
public int cbSize; //SP_DEVICE_INTERFACE_DATA结构的大小
public Guid interfaceClassGuid; //设备接口所属的类的GUID
public int flags; //接口转态标记
public int reserved; //保留,不做使用
} // 获得一个指向该设备的路径名,接口的详细信息 必须调用两次 第1次返回长度 第2次获取数据
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData,
int deviceInterfaceDetailDataSize, ref int requiredSize, SP_DEVINFO_DATA deviceInfoData); [StructLayout(LayoutKind.Sequential)]
public class SP_DEVINFO_DATA
{
public int cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
public Guid classGuid = Guid.Empty; // temp
public int devInst = ; // dumy
public int reserved = ;
} [StructLayout(LayoutKind.Sequential, Pack = )]
internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
internal int cbSize;
internal short devicePath;
} [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern Boolean SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet); //获取设备文件(获取句柄)
[DllImport("kernel32.dll", SetLastError = true)]
//根据要求可在下面设定参数,具体参考参数说明:https://docs.microsoft.com/zh-cn/windows/desktop/api/fileapi/nf-fileapi-createfilea
private static extern int CreateFile
(
string lpFileName, // file name 文件名
uint dwDesiredAccess, // access mode 访问模式
uint dwShareMode, // share mode 共享模式
uint lpSecurityAttributes, // SD 安全属性
uint dwCreationDisposition, // how to create 如何创建
uint dwFlagsAndAttributes, // file attributes 文件属性
uint hTemplateFile // handle to template file 模板文件的句柄
); [DllImport("Kernel32.dll", SetLastError = true)] //接收函数DLL
private static extern bool ReadFile
(
IntPtr hFile,
byte[] lpBuffer,
uint nNumberOfBytesToRead,
ref uint lpNumberOfBytesRead,
IntPtr lpOverlapped
); [DllImport("kernel32.dll", SetLastError = true)] //发送数据DLL
public static extern Boolean WriteFile
(
IntPtr hFile,
byte[] lpBuffer,
uint nNumberOfBytesToWrite,
ref uint nNumberOfBytesWrite,
IntPtr lpOverlapped
); [DllImport("hid.dll")]
/*HidDeviceObject:指定顶级集合的打开句柄
Attributes:指向调用者分配的HIDD_ATTRIBUTES结构的指针,该结构返回由HidDeviceObject指定的集合的属性*/
private static extern Boolean HidD_GetAttributes(IntPtr hidDeviceObject, out HIDD_ATTRIBUTES HIDD_ATTRIBUTES);
//HidD_GetAttributes的调用者使用此结构来对比查找设备
public unsafe struct HIDD_ATTRIBUTES
{
public int Size; //指定HIDD_ATTRIBUTES结构的大小(以字节为单位)
public ushort VendorID; //指定HID设备的供应商ID( VID )
public ushort ProductID; //指定HID设备的产品ID( PID )
public ushort VersionNumber; //指定HIDClass设备的制造商版本号
} //自定义的结构体,用来存放自己要操作的设备信息
public unsafe struct my_usb_id
{
public ushort my_vid;
public ushort my_Pid;
public ushort my_number;
} /**/
[DllImport("hid.dll")]
/*HidP_GetCaps返回一个顶级集合的 HIDP_CAPS结构,获取设备具体信息,这里暂时用不上
PreparsedData:指向顶级集合的预分析数据的指针; Capabilities:指向调用程序分配的缓冲区的指针,该缓冲区用于返回集合的HIDP_CAPS结构*/
private static extern uint HidP_GetCaps(IntPtr PreparsedData, out HIDP_CAPS Capabilities);
[StructLayout(LayoutKind.Sequential)]
public unsafe struct HIDP_CAPS
{
public ushort UsagePage; //指定顶级集合的使用情况
public uint Usage; //指定顶级集合的 使用ID
public ushort InputReportByteLength; //指定所有输入报告的最大大小(以字节为单位)
public ushort OutputReportByteLength; //指定所有输出报告的最大大小(以字节为单位)
public ushort FeatureReportByteLength; //指定所有功能报告的最大长度(以字节为单位)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = )] //保留供内部系统使用数组
public ushort NumberLinkCollectionNodes; //指定的数量HIDP_LINK_COLLECTION_NODE了为这个顶级集合返回的结构HidP_GetLinkCollectionNodes
public ushort NumberInputButtonCaps; //指定HidP_GetButtonCaps返回的输入HIDP_BUTTON_CAPS结构的数量
public ushort NumberInputValueCaps; //指定HidP_GetValueCaps返回的输入HIDP_VALUE_CAPS结构的数量
public ushort NumberInputDataIndices; //指定分配给所有输入报告中的按钮和值的数据索引数
public ushort NumberOutputButtonCaps; //指定HidP_GetButtonCaps返回的输出HIDP_BUTTON_CAPS结构的数量
public ushort NumberOutputValueCaps; //指定HidP_GetValueCaps返回的输出HIDP_VALUE_CAPS结构的数量
public ushort NumberOutputDataIndices; //指定分配给所有输出报告中的按钮和值的数据索引数
public ushort NumberFeatureButtonCaps; //指定HidP_GetButtonCaps返回的功能HIDP_BUTTONS_CAPS结构的总数
public ushort NumberFeatureValueCaps; //指定HidP_GetValueCaps返回的功能HIDP_VALUE_CAPS结构的总数
public ushort NumberFeatureDataIndices; //指定分配给所有要素报告中的按钮和值的数据索引数
} [DllImport("hid.dll")]
private static extern Boolean HidD_GetPreparsedData(IntPtr hidDeviceObject, out IntPtr PreparsedData); //释放设备
[DllImport("hid.dll")]
static public extern bool HidD_FreePreparsedData(ref IntPtr PreparsedData); //关闭访问设备句柄,结束进程的时候把这个加上保险点
[DllImport("kernel32.dll")]
static public extern int CloseHandle(int hObject); //查看数据传输异常函数
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress", SetLastError = true)]
public static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
  上面的DLL调用在 public partial class Form1 : Form{}函数里直接声明就好了
1~7连接USB设备
  连接前先定义一些要用到的变量,再调用 UsBMethod 函数连接,这个函数里面包含了1~7的所有步骤:
        //定于句柄序号和一些参数,具体可以去网上找这些API的参数说明
int HidHandle = -;
int sele = ;
int usb_flag = ;
bool result;
string devicePathName; //CreateFile参数配置
public const uint GENERIC_READ = 0x80000000;
public const uint GENERIC_WRITE = 0x40000000;
public const uint FILE_SHARE_READ = 0x00000001;
public const uint FILE_SHARE_WRITE = 0x00000002;
public const int OPEN_EXISTING = ; private void UsBMethod(int index)
{
//获取USB设备的GUID
HidD_GetHidGuid(ref guidHID); //Console.WriteLine(" GUID_HID = "+ guidHID); //输出guid信息调试用 //获取系统中存在的所有设备的列表,这些设备已从存储卷设备接口类启用了接口
hDevInfo = SetupDiGetClassDevs(ref guidHID, , IntPtr.Zero, DIGCF.DIGCF_PRESENT | DIGCF.DIGCF_DEVICEINTERFACE); int bufferSize = ;
ArrayList HIDUSBAddress = new ArrayList(); while (true)
{ //获取设备,true获取到
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
DeviceInterfaceData.cbSize = Marshal.SizeOf(DeviceInterfaceData); for (int i = ; i < ; i++)
{
//识别HID设备接口,获取设备,返回true成功
result = SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guidHID, (UInt32)index, ref DeviceInterfaceData);
} //Console.WriteLine(" 识别HID接口\t"+result); //识别接口打印信息查看 //第一次调用出错,但可以返回正确的Size
SP_DEVINFO_DATA strtInterfaceData = new SP_DEVINFO_DATA();
//获得一个指向该设备的路径名,接口的详细信息 必须调用两次 第1次返回路径长度
result = SetupDiGetDeviceInterfaceDetail(hDevInfo, ref DeviceInterfaceData, IntPtr.Zero, , ref bufferSize, strtInterfaceData); //第二次调用传递返回值,调用即可成功 , 第2次获取路径数据
IntPtr detailDataBuffer = Marshal.AllocHGlobal(bufferSize);
SP_DEVICE_INTERFACE_DETAIL_DATA detailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();
detailData.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DETAIL_DATA)); Marshal.StructureToPtr(detailData, detailDataBuffer, false);
result = SetupDiGetDeviceInterfaceDetail(hDevInfo, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, strtInterfaceData); if (result == false)
{
break;
} //获取设备路径访
IntPtr pdevicePathName = (IntPtr)((int)detailDataBuffer + );
devicePathName = Marshal.PtrToStringAuto(pdevicePathName);
HIDUSBAddress.Add(devicePathName); //Console.WriteLine(" Get_DvicePathName = "+ devicePathName); //打印路径信息,调试用 //连接USB设备文件
int aa = CT_CreateFile(devicePathName);
usb_flag = aa;
if (aa == ) //设备连接成功
{
//获取设备VID PID 出厂编号信息判断是否跟自定义的USB设备匹配,匹配返回 1
usb_flag = HidD_GetAttributes(HidHandle);
if (usb_flag == ) break;
else usb_flag = ;
}
else usb_flag = ;
index++;
} }

  上面的函数调用到了一个建立和设备的连接的函数,该函数主要是用来跟设备建立连接,函数如下:

  /*  =================建立和设备的连接==================    */
public unsafe int CT_CreateFile(string DeviceName)
{
HidHandle = CreateFile
(
DeviceName,
//GENERIC_READ | // | GENERIC_WRITE,//读写,或者一起
GENERIC_READ | GENERIC_WRITE,
//FILE_SHARE_READ | // | FILE_SHARE_WRITE,//共享读写,或者一起
FILE_SHARE_READ | FILE_SHARE_WRITE,
,
OPEN_EXISTING,
, ); //Console.WriteLine(" IN_DeviceName = " + DeviceName); //查看参数是否传入 if (HidHandle == -) //INVALID_HANDLE_VALUE实际值等于-1,连接失败
{ //Console.WriteLine(" 失败 HidHandle = 0x" + "{0:x}",HidHandle ); //查看状态,打印调试用
return ;
}
else //连接成功
{
//Console.WriteLine(" 成功 HidHandle = 0x" + "{0:x}",HidHandle); //查看状态,打印调试用
return ;
}
}

  调用函数建立好连接后,就要判断是否是自己需要的USB设备了,这里就调用了一个判断函数:

 /*  ==============获取设备的VID PID 出厂编号等信息,存放到HIDD_ATTRIBUTES==============   */
public unsafe int HidD_GetAttributes(int handle)
{
HIDD_ATTRIBUTES HIDD_ATTRIBUTE = new HIDD_ATTRIBUTES();
//handle是CreateFile函数返回一个有效的设备操作句柄,HIDD_ATTRIBUTES是函数返回的结构体信息(VID PID 设备号)
bool sel = HidD_GetAttributes((IntPtr)handle, out HIDD_ATTRIBUTE); /*//打印VID、PID信息以16进制显示调试用,打印数据前不能接+号,不然打印不出来,信息为0
Console.Write("\t" + "VID:{0:x}", HIDD_ATTRIBUTE.VendorID );
Console.Write("\t" + "PID:{0:x}", HIDD_ATTRIBUTE.ProductID);
Console.WriteLine("\r\n"); */ if (sel == true) //获取设备信息成功
{
//对自己定义的my_usb_id结构体赋值,输入自己要操作的设备参数,用来跟读取出来的设备参数比较
my_usb_id my_usb_id = new my_usb_id();
my_usb_id.my_vid = ; //自定义的USB设备VID 0x10c4=4292
my_usb_id.my_Pid = ; //自定义的USB设备PID 0x82cd=33485 if (my_usb_id.my_vid == HIDD_ATTRIBUTE.VendorID && my_usb_id.my_Pid == HIDD_ATTRIBUTE.ProductID) //判断识别出来的是不是自定义的USB设备
{
//Console.WriteLine("获取VID PID成功"); //打印信息调试用
sele = ;
}
else sele = ;
return sele;
}
else
{
//Console.WriteLine("获取VID PID失败"); //打印信息调试用
return sele = ;
}
}

  到这里就已经连接上USB设备了,接下来可以收发数据了。

8、调用WriteFile和ReadFile进行数据传输
  这里的发送函数使用的是同步发送,至于怎样同步异步各位可以自行到https://docs.microsoft.com/zh-cn/windows/desktop/api/fileapi/nf-fileapi-writefile查看,或者看我前面的收发数据的参考链接
 private void button2_Click(object sender, EventArgs e)
{
if (usb_flag == ) //USB识别成功后发送数据
{
uint read = ;
byte[] src = { , , };
bool isread = WriteFile((IntPtr)HidHandle, src, (uint), ref read, IntPtr.Zero);
if (isread == false)
{
int errCode = Marshal.GetLastWin32Error();
// Console.WriteLine("数据发送失败!错误代码:" + errCode);
}
}
}
注意这里缓存区要比你的报文描述符多一个字节,不然会出错,至于接收,使用异步接收,在接收前要做一个调用DLL声明:
        /*构建一个Overlapped结构,异步通信用,
internal是错误码,internalHigh是传输字节,这个两个是IO操作完成后需要填写的内容*/
[StructLayout(LayoutKind.Sequential)]
public struct OVERLAPPED
{
public IntPtr Internal; //I/O请求的状态代码
public IntPtr InternalHigh; //传输I/O请求的字节数
public int Offset; //文件读写起始位置
public int OffsetHigh; //地址偏移量
public IntPtr hEvent; //操作完成时系统将设置为信号状态的事件句柄
} /*监听异步通信函数*/
[DllImport("Kernel32.dll")]
public static extern unsafe long WaitForSingleObject(IntPtr hHandle, long dwMilliseconds);
  声明好后就开始写接收函数了,接收函数怎么异步接收各位可以自行百度,或者官网查阅,也可以看穷前面的收发函数参考链接。
        public void redata()//USB异步接收数据
{
//初始化Overlapped
OVERLAPPED overlap = new OVERLAPPED();
overlap.Offset = ;
overlap.OffsetHigh = ; //创建事件对象
overlap.hEvent = CreateEvent(IntPtr.Zero, false, false, null); //接收数据缓存区:接收到的数据如果比这个小,则按实际数据大小,接收到一个ID+64个数据
byte[] buffer = new byte[];
int dwRead = ;
Console.WriteLine("read... ");
//while (true)
{
//读设备
bool re = ReadFile(HidHandle, buffer, , out dwRead, out overlap);
if (re != false)
{
SetupDiDestroyDeviceInfoList(hDevInfo);
for (int i = ; i < ; i++)
{
Console.WriteLine("i = " + i + " \t buffer = " + buffer[i]);
}
MessageBox.Show(" READ OK !");
//break;
}
}
// long cc=WaitForSingleObject(overlap.hEvent, 5000);
}

  这样异步接收可以开一个定时器时不时检测是否有数据接收,来实现,开定时是可以参考我的隐藏窗体功能的那篇文章:https://www.cnblogs.com/xingboy/p/10110443.html 不过用定时器做不定时检测可能会出现丢包情况,最好的方法是多开一个线程一直等待接收,接收到立刻跳转处理接收处理函数。

  收发完函数后,就可以关闭通道了,以便节约资源嘛。

9、关闭USB设备

  /*  释放关闭USB设备   */
public void Dispost()
{
//释放设备资源(hDevInfo是SetupDiGetClassDevs获取的)
SetupDiDestroyDeviceInfoList(hDevInfo);
//关闭连接(HidHandle是Create的时候获取的)
CloseHandle(HidHandle);
}
//===================================================

以上就是USB通信上位机部分的内容了,这些函数都放在 public partial class Form1 : Form{ } 完成,至于下位机的部分各位可以参考:https://www.cnblogs.com/xingboy/p/9913963.html

补充一点:如果生成的程序在你的电脑可正常使用,在别的电脑不可以用的话,那可能是windows系统的DLL出了问题,解决方法可以参考我另一个文章:https://www.cnblogs.com/xingboy/p/9876812.html
 
 

C# 实现自定义的USB设备与上位机进行通信(上位机部分)的更多相关文章

  1. 基于libUSB的USB设备固件更新程序(下载数据)(转)

    源:基于libUSB的USB设备固件更新程序(下载数据) 本文紧接上一篇日志:基于libUSB-Win32的USB设备固件更新程序(前言),相关背景以及起因等,此处不再赘述,如感兴趣请移步. libU ...

  2. Linux下的硬件驱动——USB设备(转载)

    usb_bulk_msg函数 当对usb设备进行一次读或者写时,usb_bulk_msg 函数是非常有用的; 然而, 当你需要连续地对设备进行读/写时,建议你建立一个自己的urbs,同时将urbs 提 ...

  3. C# 上位机的USB设备拔插检测

    我们做USB通信时,通信成功后,往往要检测USB设备的拔插状态,这里就USB拔插进行一下说明. 参考:https://www.imooc.com/article/17438 先说明一下,我这里只是用C ...

  4. 厂商自定义USB设备固件程序及特性

    通过前面的学习,大家应该对USB固件程序结构有了比较深的认识,现在再来详细说说固件里决定设备识别成厂商自定义USB设备的地方有哪些,或者说厂商自定义USB设备的固件特性有哪些. 之前不止一次说过学习U ...

  5. 厂商自定义USB设备类概述

    USB协会将常用具有相同/相似功能的设备归为一类,并制定了相关的设备类规范,这样就能保障只要依照同样的规范标准,即使不同的厂商开发的USB设备也可以使用同样的驱动程序,而且操作系统中无须为每种设备提供 ...

  6. Linux usb子系统(二):USB设备驱动usb-skeleton.c

    usb驱动分为通过usbfs操作设备的用户空间驱动,内核空间的内核驱动.两者不能同时进行,否则容易引发对共享资源访问的问题,死锁!使用了内核驱动,就不能在usbfs里驱动该设备. 下面转载的一篇分析u ...

  7. Linux下usb设备驱动详解

    USB驱动分为两块,一块是USB的bus驱动,这个东西,Linux内核已经做好了,我们可以不管,我们只需要了解它的功能.形象的说,USB的bus驱动相当于铺出一条路来,让所有的信息都可以通过这条USB ...

  8. 利用mass storage class 做免驱动usb设备.

    当需要使用usb bulk传输,想让设备像串口通讯那样和PC主机通信, 通常需要自己做一个PC端的驱动,比较麻烦. 为避免在pc上编写usb设备驱动的麻烦,可以将设备做成mass storage 类的 ...

  9. 在树莓派下对多个串口转USB设备进行设备名称绑定操作

    在开发过程中,需要用一个树莓派链接多个串口转USB设备(GPS模块,数传模块等),在树莓派linux系统环境下,USB串口设备的命名规则是 /dev/ttyUSB0 ,/dev/ttyUSB1,/de ...

随机推荐

  1. js限制checkbox选中个数

    今天在做项目时,碰到一个问题,我须要展示多个checkbox复选框,而仅仅能同意最多选6个.调试了老半天.最终出来了,代码例如以下: <SCRIPT LANGUAGE="JavaScr ...

  2. Lua 与C/C++ 交互系列:注冊枚举enum到Lua Code中

    在Lua Code中注冊C/C++的枚举很easy,就像注冊全局变量一样.我们使用枚举名称作为命名空间,来避免注冊的枚举发生冲突.注冊的枚举存储在全局环境(线程环境)中. 当在Lua Code中訪问枚 ...

  3. poj3621 Sightseeing Cows

    01分数规划 二分+spfa负环(SLF优化) #include<cstdio> #include<iostream> #include<cstring> #inc ...

  4. python 读取二进制文件 转为16进制输出

    示例: #!/usr/bin/env python #encoding: utf-8 import binascii fh = open(r'C:\Temp\img\2012517165556.png ...

  5. spark 按照key 分组 然后统计每个key对应的最大、最小、平均值思路——使用groupby,或者reduceby

    What you're getting back is an object which allows you to iterate over the results. You can turn the ...

  6. ES6和Node容易搞混淆的点

    ES6 import  模块名 from XX  '模块标识符'     -----导入模块 import '路径 ' -----导入CSS样式 export default { }  和export ...

  7. pip换源简易方法

    安装pqi >>> pip install pqi 列出pip源 >>> pqi ls 使用pip源 >>> pqi use <name&g ...

  8. 28. Implement strStr()[E]实现strStr()

    题目 Implement strStr(). Return the index of the first occurrence of needle in haystack, or -1 if need ...

  9. Orcal的JDBC数据连接方式

    package cn.com.db; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Prepar ...

  10. LeetCode Weekly Contest 19

    1.504. Base 7 水题,直接写.但是我错了一次,忘了处理0的情况. 没有考虑边界条件.写完代码,一般需要自己想几个边界测试用例进行测试. class Solution { public: s ...