因为以前没用过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. C算法与数据结构-线性表的应用,多项式求和---ShinePans

    /*---上机作业作业,二项式加法---*/ /*---By 潘尚 ---*/ /*---日期: 2014-5-8 . ---*/ /*---题目:---*/ //如果有两个稀疏多项式A和B,设计算法 ...

  2. HDU 5063 Operation the Sequence(暴力)

    HDU 5063 Operation the Sequence 题目链接 把操作存下来.因为仅仅有50个操作,所以每次把操作逆回去执行一遍,就能求出在原来的数列中的位置.输出就可以 代码: #incl ...

  3. 11gR2 Database Services for &quot;Policy&quot; and &quot;Administrator&quot; Managed Databases (文档 ID 1481647.1)

    In this Document   Purpose   _afrLoop=1459311711568804&id=1481647.1&displayIndex=6&_afrW ...

  4. wpf datagridtemplatecolumn visibility binding

    因为datagridtemplatecolumn不在Virsual Tree中,不能继承DataGrid的DataContext, 所以想要绑定到datagridtemplatecolumn的 vis ...

  5. Git 工具 - 子模块

    子模块 有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目. 也许是第三方库,或者你独立开发的,用于多个父项目的库. 现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使 ...

  6. C++ 指针 引用 变量引用

    变量引用: 引用的作用就是给变量起个别名,假如有一个变量a,想给它起个别名b,         可以这么写:int a;//定义a是整型变量.int &b=a;//声明b是a的引用. 上面就是 ...

  7. java语言的运行机制

    计算机高级编程语言按其程序的执行方式可分为编译型语言和解释型语言. 编译型语言是指使用专门的编译器,针对特定的操作系统将源程序代码一次性翻译成计算机能识别的机器指令.例如C.C++等都属于编译型语言. ...

  8. WPF中ListBox ListView数据翻页浏览笔记(强调:是数据翻页,非翻页动画)

    ListBox和ListView在应用中,常常有需求关于每页显示固定数量的数据,然后通过Timer自动或者手动翻页操作,本文介绍到的就是该动作的实现. 一.重点 对于ListBox和ListView来 ...

  9. HTML不熟悉方法总结

    1. onblur   属性在元素失去焦点时触发. 2. onfocus 属性在元素获得焦点时触发. 3.addEventlistener 事件监听 4.focus() 方法用于给予该元素焦点.这样用 ...

  10. thrift RPC 框架的自我搭建

    安装thrift rpc   安装的系统是Centos 7 未成功的方法 :(原因没找到,但是还是要记录下) 安装依赖库 yum install automake libtool flex bison ...