当前位置: 首页 > news >正文

.NET编程——利用C#调用海康机器人工业相机SDK实现回调取图与软触发取图【含免费源码】

目录

  • 引言
  • 前期准备
  • 演示视频
  • HikCamera类
  • 打开或关闭相机
  • 软触发单张取图

引言

海康机器人(HikRobot)作为机器视觉头部厂商之一,其工业相机被广泛应用于机器视觉领域,常见的Halcon和VisionPro这两个软件海康官方已经开发了原生的.dll,可以在MVS安装路径下ThirdPartyPlatformAdapter找到这两款软件更推荐使用官方提供的库,对于第三方开发的软件或者非常规的工业相机就会涉及到SDK取图的问题,本文教程主要介绍如何将调用海康SDK取图。

前期准备

开始前在海康机器人下载中心将最新版的MVS安装在默认路径下

演示视频

海康工业相机SDK取图

🔗免费下载链接

HikCamera类

#define MVCAMERAusing System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using MvCameraControl;
using System.Globalization;
using HalconDotNet;namespace MyCamera
{internal class HikCamera : BaseCamera{readonly DeviceTLayerType enumTLayerType = DeviceTLayerType.MvGigEDevice | DeviceTLayerType.MvUsbDevice| DeviceTLayerType.MvGenTLGigEDevice | DeviceTLayerType.MvGenTLCXPDevice | DeviceTLayerType.MvGenTLCameraLinkDevice | DeviceTLayerType.MvGenTLXoFDevice;public HikCamera() : base() { }#region param[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);List<IDeviceInfo> deviceInfoList = new List<IDeviceInfo>();List<string> deviceSNList = new List<string>();IDevice device = null;IInterface _ifInstance;IntPtr m_BufForDriver = IntPtr.Zero;UInt32 _bufferSize = 3072 * 2048 * 3;private byte[] _buffer;private uint _buffSizeForSaveImage = 3072 * 2048 * (16 * 3 + 4) + 2048;private byte[] _bufForSaveImage;private Object m_BufForSaveImageLock = new Object();public IntPtr m_pSaveImageBuf = IntPtr.Zero;#endregion#region operatepublic override List<string> GetListEnum(){List<string> deviceList = new List<string>();foreach (var item in GetListInfoEnum()){IDeviceInfo deviceInfo = item;if (deviceInfo.UserDefinedName != ""){//deviceList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.UserDefinedName + " (" + deviceInfo.SerialNumber + ")");deviceList.Add(deviceInfo.SerialNumber);}else{//deviceList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.ManufacturerName + " " + deviceInfo.ModelName + " (" + deviceInfo.SerialNumber + ")");deviceList.Add(deviceInfo.SerialNumber);}}return deviceList;}public override bool InitDevice(string CamSN){try{int nRet;if (string.IsNullOrEmpty(CamSN)) return false;var infolist = GetListInfoEnum();if (infolist.Count < 1) return false;IDeviceInfo deviceInfo = infolist[0];bool selectSNflag = false;foreach (var item in infolist){if (item.SerialNumber.Equals(CamSN)){deviceInfo = item;selectSNflag = true;break;}}if (!selectSNflag) return false;try{// ch:打开设备 | en:Open devicedevice = DeviceFactory.CreateDevice(deviceInfo);}catch (Exception ex){MessageBox.Show("Create Device fail!" + ex.Message);return false;}nRet = device.Open();if (nRet != MvError.MV_OK){ShowErrorMsg("Open Device fail!", nRet);return false;}// ch:注册回调函数 | en:Register image callback// 注册回调函数操作应放在打开采集流操作之前device.StreamGrabber.FrameGrabedEvent += CallBackEventHandler;// ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)if (device is IGigEDevice){int packetSize;nRet = (device as IGigEDevice).GetOptimalPacketSize(out packetSize);if (packetSize > 0){nRet = device.Parameters.SetIntValue("GevSCPSPacketSize", packetSize);if (nRet != MvError.MV_OK){Console.WriteLine("Warning: Set Packet Size failed {0:x8}", nRet);}else{Console.WriteLine("Set PacketSize to {0}", packetSize);}}else{Console.WriteLine("Warning: Get Packet Size failed {0:x8}", nRet);}}// ch:设置触发模式为On || en:set trigger mode as On// 设置为Off会一直触发回调SetTriggerMode(TriggerMode.On, TriggerSource.Software);device.Parameters.SetEnumValueByString("AcquisitionMode", "Continuous");if (nRet != MvError.MV_OK){Console.WriteLine("Set TriggerMode failed:{0:x8}", nRet);return false;}if (!StartGrabbing()){Console.WriteLine("开始采集失败");return false;}//ch: 设置合适的缓存节点数量 | en: Setting the appropriate number of image nodesdevice.StreamGrabber.SetImageNodeNum(5);SN = CamSN;return true;}catch { return false; }}public override bool CloseDevice(){try{if (m_BufForDriver != IntPtr.Zero){Marshal.Release(m_BufForDriver);}// ch:关闭设备 | en:Close devicevar nRet = device.Close();if (MvError.MV_OK == nRet){// ch:销毁设备 | en:Destroy devicedevice.Dispose();return true;}nRet = device.Close();if (MvError.MV_OK != nRet){Console.WriteLine("Close device failed:{0:x8}", nRet);return false;}// ch:销毁设备 | en:Destroy devicedevice.Dispose();return true;}catch{device = null;return false;}}/// <summary>/// ch:软触发执行一次 | en:Trigger once by software/// </summary>public override bool SoftTrigger(){int nRet;// ch:触发命令 | en:Trigger commandnRet = device.Parameters.SetCommandValue("TriggerSoftware");return (MvError.MV_OK == nRet);}#endregion#region SettingConfigpublic override bool SetTriggerMode(TriggerMode mode = TriggerMode.On, TriggerSource triggerEnum = TriggerSource.Software){int nRet;switch (mode){case TriggerMode.Off:nRet = device.Parameters.SetEnumValueByString("TriggerMode", "Off");break;case TriggerMode.On:nRet = device.Parameters.SetEnumValueByString("TriggerMode", "On");break;default:nRet = device.Parameters.SetEnumValueByString("TriggerMode", "On");break;}bool flag1 = (MvError.MV_OK == nRet);switch (triggerEnum){case TriggerSource.Software:nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Software");break;case TriggerSource.Line0:nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line0");break;case TriggerSource.Line1:nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line1");break;case TriggerSource.Line2:nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line2");break;case TriggerSource.Line3:nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line3");break;default:nRet = device.Parameters.SetEnumValueByString("TriggerSource", "Line4");break;}bool flag2 = (MvError.MV_OK == nRet);return flag1 && flag2;}public override bool GetTriggerMode(out TriggerMode mode, out TriggerSource source){int nRet;mode = TriggerMode.On;source = TriggerSource.Software;IEnumValue enumValue;nRet = device.Parameters.GetEnumValue("TriggerMode", out enumValue);bool flag1 = (MvError.MV_OK == nRet);if (flag1){switch (enumValue.CurEnumEntry.Symbolic){case "On":mode = TriggerMode.On;break;case "Off":mode = TriggerMode.Off;break;default:mode = TriggerMode.On;break;}}nRet = device.Parameters.GetEnumValue("TriggerSource", out enumValue);bool flag2 = (MvError.MV_OK == nRet);if (flag2){switch (enumValue.CurEnumEntry.Symbolic){case "TriggerSoftware":source = TriggerSource.Software;break;case "Line0":source = TriggerSource.Line0;break;case "Line1":source = TriggerSource.Line1;break;case "Line2":source = TriggerSource.Line2;break;default:source = TriggerSource.Line0;break;}}return flag1 && flag2;}public override bool SetExpouseTime(float value){int nRet = device.Parameters.SetFloatValue("ExposureTime", value);return (MvError.MV_OK == nRet);}public override bool GetExpouseTime(out float value){IFloatValue floatValue;int nRet = device.Parameters.GetFloatValue("ExposureTime", out floatValue);value = (float)floatValue.CurValue;return (MvError.MV_OK == nRet);}public override bool SetTriggerPolarity(TriggerPolarity polarity){IEnumValue enumValue;int nRet = _ifInstance.Parameters.GetEnumValue("TimerTriggerActivation", out enumValue);if (nRet != MvError.MV_OK){return false;}for (int i = 0; i < enumValue.SupportedNum; ++i){if (polarity.ToString().Equals(enumValue.SupportEnumEntries[i].Symbolic, StringComparison.OrdinalIgnoreCase)){nRet = _ifInstance.Parameters.SetEnumValue("TimerTriggerActivation", enumValue.SupportEnumEntries[i].Value);if (nRet != MvError.MV_OK){return false;}break;}}return (MvError.MV_OK == nRet);}public override bool GetTriggerPolarity(out TriggerPolarity polarity){polarity = TriggerPolarity.RisingEdge;IEnumValue enumValue;int nRet = _ifInstance.Parameters.GetEnumValue("TimerTriggerActivation", out enumValue);if (nRet != MvError.MV_OK){return false;}string activate = enumValue.CurEnumEntry.Symbolic;//1下降沿 0 上升沿if (activate == "RisingEdge"){ //上升沿polarity = TriggerPolarity.RisingEdge;}else if (activate == "FallingEdge"){ //下降沿polarity = TriggerPolarity.FallingEdge;}return (MvError.MV_OK == nRet);}public override bool SetTriggerFliter(float flitertime){int nRet = _ifInstance.Parameters.SetIntValue("LineDebouncerTimeNs", (uint)flitertime);nRet = _ifInstance.Parameters.SetIntValue("LineDebouncerTime", (uint)flitertime);return (MvError.MV_OK == nRet);}public override bool GetTriggerFliter(out float flitertime){flitertime = 1000;IFloatValue floatValue;int nRet = device.Parameters.GetFloatValue("ExposureTime", out floatValue);flitertime = (float)floatValue.CurValue;return (MvError.MV_OK == nRet);}public override bool SetTriggerDelay(float delay){int nRet = device.Parameters.SetFloatValue("TriggerDelay", delay);return (MvError.MV_OK == nRet);}public override bool GetTriggerDelay(out float delay){delay = 0;IFloatValue floatValue;int nRet = device.Parameters.GetFloatValue("TriggerDelay", out floatValue);delay = (float)floatValue.CurValue;return (MvError.MV_OK == nRet);}public override bool SetGain(float gain){int nRet = device.Parameters.SetFloatValue("Gain", (float)gain);return (MvError.MV_OK == nRet);}public override bool GetGain(out float gain){gain = 0;IFloatValue floatValue;int nRet = device.Parameters.GetFloatValue("Gain", out floatValue);gain = (float)floatValue.CurValue;return (MvError.MV_OK == nRet);}public override bool SetLineMode(IOLines line, LineMode mode){int nRet = device.Parameters.SetEnumValueByString(line.ToString(), mode.ToString());return (MvError.MV_OK == nRet);}public override bool SetLineStatus(IOLines line, LineStatus linestatus){int nRet = device.Parameters.SetBoolValue(line.ToString(), linestatus.Equals(LineStatus.Hight));return (MvError.MV_OK == nRet);}public override bool GetLineStatus(IOLines line, out LineStatus linestatus){bool resultsignal = false;int nRet = device.Parameters.GetBoolValue(line.ToString(), out resultsignal);linestatus = resultsignal ? LineStatus.Hight : LineStatus.Low;return (MvError.MV_OK == nRet);}public override bool AutoBalanceWhite(){int nRet = device.Parameters.SetEnumValueByString("BalanceWhiteAuto", "Once");return (MvError.MV_OK == nRet);}#endregion#region helper // ch:显示错误信息 | en:Show error messageprivate void ShowErrorMsg(string message, int errorCode){string errorMsg;if (errorCode == 0){errorMsg = message;}else{errorMsg = message + ": Error =" + String.Format("{0:X}", errorCode);}switch (errorCode){case MvError.MV_E_HANDLE: errorMsg += " Error or invalid handle "; break;case MvError.MV_E_SUPPORT: errorMsg += " Not supported function "; break;case MvError.MV_E_BUFOVER: errorMsg += " Cache is full "; break;case MvError.MV_E_CALLORDER: errorMsg += " Function calling order error "; break;case MvError.MV_E_PARAMETER: errorMsg += " Incorrect parameter "; break;case MvError.MV_E_RESOURCE: errorMsg += " Applying resource failed "; break;case MvError.MV_E_NODATA: errorMsg += " No data "; break;case MvError.MV_E_PRECONDITION: errorMsg += " Precondition error, or running environment changed "; break;case MvError.MV_E_VERSION: errorMsg += " Version mismatches "; break;case MvError.MV_E_NOENOUGH_BUF: errorMsg += " Insufficient memory "; break;case MvError.MV_E_UNKNOW: errorMsg += " Unknown error "; break;case MvError.MV_E_GC_GENERIC: errorMsg += " General error "; break;case MvError.MV_E_GC_ACCESS: errorMsg += " Node accessing condition error "; break;case MvError.MV_E_ACCESS_DENIED: errorMsg += " No permission "; break;case MvError.MV_E_BUSY: errorMsg += " Device is busy, or network disconnected "; break;case MvError.MV_E_NETER: errorMsg += " Network error "; break;}MessageBox.Show(errorMsg, "PROMPT");}public override bool StartGrabbing(){// Set default state after grabbing starts// Turn off real-time mode which is default// 0: real-time// 1: triggerint nRet = device.StreamGrabber.StartGrabbing();if (MvError.MV_OK != nRet) Debug.WriteLine("Grab start failed");isGrabbing = true;return (MvError.MV_OK == nRet);}public override bool StopGrabbing(){int nRet = device.StreamGrabber.StopGrabbing();if (MvError.MV_OK != nRet) Debug.WriteLine("Grab stop failed");isGrabbing = false;return (MvError.MV_OK == nRet);}public override void Dispose(){if(device != null){SN = string.Empty;device.Close();device.Dispose();}}private List<IDeviceInfo> GetListInfoEnum(){System.GC.Collect();deviceInfoList.Clear();// ch:创建设备列表 | en:Create Device Listint nRet = DeviceEnumerator.EnumDevices(enumTLayerType, out deviceInfoList);if (nRet != MvError.MV_OK){ShowErrorMsg("Enumerate devices fail!", nRet);return new List<IDeviceInfo>();}// ch:在窗体列表中显示设备名 | en:Display device name in the form listfor (int i = 0; i < deviceInfoList.Count; i++){IDeviceInfo deviceInfo = deviceInfoList[i];if (deviceInfo.UserDefinedName != ""){deviceSNList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.UserDefinedName + " (" + deviceInfo.SerialNumber + ")");}else{deviceSNList.Add(deviceInfo.TLayerType.ToString() + ": " + deviceInfo.ManufacturerName + " " + deviceInfo.ModelName + " (" + deviceInfo.SerialNumber + ")");}}// ch:选择第一项 | en:Select the first itemif (deviceInfoList.Count != 0){deviceSNList[0] = deviceSNList[0];}return deviceInfoList;}static bool IsMonoPixelFormat(MvGvspPixelType enType){switch (enType){case MvGvspPixelType.PixelType_Gvsp_Mono10:case MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:case MvGvspPixelType.PixelType_Gvsp_Mono12:case MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:return true;default:return false;}}static bool IsColorPixelFormat(MvGvspPixelType enType){switch (enType){case MvGvspPixelType.PixelType_Gvsp_BGR8_Packed:case MvGvspPixelType.PixelType_Gvsp_YUV422_Packed:case MvGvspPixelType.PixelType_Gvsp_YUV422_YUYV_Packed:case MvGvspPixelType.PixelType_Gvsp_BayerGR8:case MvGvspPixelType.PixelType_Gvsp_BayerRG8:case MvGvspPixelType.PixelType_Gvsp_BayerGB8:case MvGvspPixelType.PixelType_Gvsp_BayerBG8:case MvGvspPixelType.PixelType_Gvsp_BayerGB10:case MvGvspPixelType.PixelType_Gvsp_BayerGB10_Packed:case MvGvspPixelType.PixelType_Gvsp_BayerBG10:case MvGvspPixelType.PixelType_Gvsp_BayerBG10_Packed:case MvGvspPixelType.PixelType_Gvsp_BayerRG10:case MvGvspPixelType.PixelType_Gvsp_BayerRG10_Packed:case MvGvspPixelType.PixelType_Gvsp_BayerGR10:case MvGvspPixelType.PixelType_Gvsp_BayerGR10_Packed:case MvGvspPixelType.PixelType_Gvsp_BayerGB12:case MvGvspPixelType.PixelType_Gvsp_BayerGB12_Packed:case MvGvspPixelType.PixelType_Gvsp_BayerBG12:case MvGvspPixelType.PixelType_Gvsp_BayerBG12_Packed:case MvGvspPixelType.PixelType_Gvsp_BayerRG12:case MvGvspPixelType.PixelType_Gvsp_BayerRG12_Packed:case MvGvspPixelType.PixelType_Gvsp_BayerGR12:case MvGvspPixelType.PixelType_Gvsp_BayerGR12_Packed:return true;default:return false;}}/// <summary>/// 回调函数(不要在内部增加耗时操作)/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void CallBackEventHandler(object sender, FrameGrabbedEventArgs e){int nRet;Console.WriteLine("Get Image Buffer: Width[{0}] , Height[{1}] , FrameNum[{2}]", e.FrameOut.Image.Width, e.FrameOut.Image.Height, e.FrameOut.FrameNum);IImage inputImage = e.FrameOut.Image;IImage outImage = inputImage;MvGvspPixelType dstPixelType = MvGvspPixelType.PixelType_Gvsp_Undefined;if (IsColorPixelFormat(e.FrameOut.Image.PixelType)){dstPixelType = MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;}else if (IsMonoPixelFormat(e.FrameOut.Image.PixelType)){dstPixelType = MvGvspPixelType.PixelType_Gvsp_Mono8;}else{Console.WriteLine("Don't need to convert!");}if (dstPixelType != MvGvspPixelType.PixelType_Gvsp_Undefined){// ch:像素格式转换 | en:Pixel type convert nRet = device.PixelTypeConverter.ConvertPixelType(inputImage, out outImage, dstPixelType);if (nRet != MvError.MV_OK){Console.WriteLine("Image Convert failed:{0:x8}", nRet);return;}Console.WriteLine("Image Convert success!");}//ch: 释放图像缓存 | en: Release image bufferdevice.StreamGrabber.FreeImageBuffer(e.FrameOut);if (outImage.ToBitmap() == null) return;CallBackImg = outImage.ToBitmap();ActionGetImage?.Invoke(SN, outImage.ToBitmap());}#endregion}
}
读者自行断开继承关系使用
# 搜索可用相机
private void cmbSN_MouseDown(object sender, MouseEventArgs e)
{switch (cmbBrand.Text){case "Hik":camera = new HikCamera();foreach (var item in ((HikCamera)camera).GetListEnum()){if (!cmbSN.Items.Contains(item))cmbSN.Items.Add(item);}break;default:break;}
}

打开或关闭相机

private void btnOpen_Click(object sender, EventArgs e)
{switch (cmbBrand.Text){case "Hik":if (cmbSN.Items.Count > 0 && ((HikCamera)camera).InitDevice(cmbSN.Text.ToString())){((HikCamera)camera).ActionGetImage += GetImageBllComplete;MessageBox.Show(((HikCamera)camera).SN + "打开成功");}break;default:break;}
}private void btnClose_Click(object sender, EventArgs e)
{switch (cmbBrand.Text){case "Hik":if (((HikCamera)camera).CloseDevice()){MessageBox.Show(((HikCamera)camera).SN + "断开成功");}break;default:break;}
}

软触发单张取图

private void btnGrabOnce_Click(object sender, EventArgs e)
{switch (cmbBrand.Text){case "Hik":if(camera != null)((HikCamera)camera).GetImageWithSoftTrigger(out Bitmap bitmap);break;default:break;}
}/// <summary>
/// 软触发获取图像
/// </summary>
/// <param name="bitmap"></param>
/// <param name="outtime"></param>
/// <returns></returns>
public bool GetImageWithSoftTrigger(out Bitmap bitmap, int outtime = 3000)
{if (!isGrabbing)StartGrabbing();// 开始时间DateTime startTime = DateTime.Now; // 当前时间// 设置超时时间DateTime lastTime = startTime.AddMilliseconds(outtime);// 判断是否超时while (lastTime > DateTime.Now)// 设置超时时间为 3 秒{if (isGrabbing)break;}GetTriggerMode(out TriggerMode triggerMode, out TriggerSource triggerSource);SetTriggerMode(TriggerMode.On, TriggerSource.Software);bitmap = null;if (!SoftTrigger()) return false;GetImage(out bitmap, outtime);SetTriggerMode(triggerMode, triggerSource);return (bitmap != null);
}/// <summary>
/// 等待硬触发获取图像
/// </summary>
/// <param name="bitmap"></param>
/// <param name="outtime"></param>
/// <returns></returns>
public bool GetImage(out Bitmap bitmap, int outtime = 3000)
{bitmap = null;// 开始时间DateTime startTime = DateTime.Now; // 当前时间// 设置超时时间DateTime lastTime = startTime.AddMilliseconds(outtime);// 判断是否超时while (lastTime > DateTime.Now)// 设置超时时间为 3 秒{if (CallBackImg != null){bitmap = this.CallBackImg.Clone() as Bitmap;CallBackImg = null;break;}}return false;
}

相机取图前需要StartGrab()进入取流状态
建议进入取流状态前设置相机触发模式为On否则会一直触发回调函数
进入取流状态后软触发前需要设置触发源为Software
发送软触发指令即可单次取图

相机进入取流模式但没有触发回调函数需要检查相机触发模式和触发源

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 搭建Windows下的Rust开发环境
  • mapActions辅助函数的使用
  • 操作系统 --- 进程的状态和状态转换
  • 10个Python办公自动化案例
  • Docker容器的基础命令操作大全(入门必看)
  • 试商法除法器
  • 大和解!淘宝微信11年“屏蔽战”终落幕
  • NC 二进制中1的个数
  • 如何在Android 12 aosp系统源码中添加三指下滑截图功能
  • 流程图符号速查:快速掌握流程图绘制要点
  • 每天五分钟玩转深度学习PyTorch:获取神经网络模型的子网络模型
  • HarmonyOs 应用基础--ArkTS-核心-基础
  • css之雪碧图(精灵图)
  • [数据集][目标检测]西红柿缺陷检测数据集VOC+YOLO格式17318张3类别
  • VisualStudio环境搭建C++
  • JS 中的深拷贝与浅拷贝
  • 分享一款快速APP功能测试工具
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • Facebook AccountKit 接入的坑点
  • iOS编译提示和导航提示
  • JavaScript标准库系列——Math对象和Date对象(二)
  • Python学习笔记 字符串拼接
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • zookeeper系列(七)实战分布式命名服务
  • 动态规划入门(以爬楼梯为例)
  • 计算机常识 - 收藏集 - 掘金
  • 精彩代码 vue.js
  • 开发基于以太坊智能合约的DApp
  • 前端性能优化——回流与重绘
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • 设计模式走一遍---观察者模式
  • 深入浅出Node.js
  • 新手搭建网站的主要流程
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 用Canvas画一棵二叉树
  • 正则学习笔记
  • zabbix3.2监控linux磁盘IO
  • ​【原创】基于SSM的酒店预约管理系统(酒店管理系统毕业设计)
  • ‌移动管家手机智能控制汽车系统
  • # wps必须要登录激活才能使用吗?
  • $refs 、$nextTic、动态组件、name的使用
  • (js)循环条件满足时终止循环
  • (MATLAB)第五章-矩阵运算
  • (顶刊)一个基于分类代理模型的超多目标优化算法
  • (附源码)ssm高校运动会管理系统 毕业设计 020419
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (七)glDrawArry绘制
  • (三) diretfbrc详解
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (十五)使用Nexus创建Maven私服
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • **PHP二维数组遍历时同时赋值
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .net core 调用c dll_用C++生成一个简单的DLL文件VS2008