.NET获取硬盘序列号的几个方法

news/2024/12/4 16:46:15/

最近作软件注册,收集了很多.NET相关的获取硬盘物理序列号的方法,主要分为使用WMI方式和API方式。但这些方法均可能有问题。

1,使用WMI方式,有的机器根本取不到硬盘序列号,有的方式在Vista下面会报错。

常用的使用WMI的方式主要有下面一些方式:

  1. class HardDrive
  2.     {
  3.         private string model = null;
  4.         private string type = null;
  5.         private string serialNo = null;
  6.         public string Model
  7.         {
  8.             get {return model;}
  9.             set {model = value;}
  10.         }
  11.         public string Type
  12.         {
  13.             get {return type;}
  14.             set {type = value;}
  15.         }
  16.         public string SerialNo
  17.         {
  18.             get {return serialNo;}
  19.             set {serialNo = value;}
  20.         }
  21.     }
  22.     class TestProgram
  23.     {
  24.         /// <summary>
  25.         /// The main entry point for the application.
  26.         /// </summary>
  27.         [STAThread]
  28.         static void Main(string[] args)
  29.         {
  30.             //在Vista下面失败
  31.             ArrayList hdCollection = new ArrayList();
  32.             ManagementObjectSearcher searcher = new
  33.                 ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
  34.             foreach(ManagementObject wmi_HD in searcher.Get())
  35.             {
  36.                 HardDrive hd = new HardDrive();
  37.                 hd.Model    = wmi_HD["Model"].ToString();
  38.                 hd.Type     = wmi_HD["InterfaceType"].ToString();
  39.                 hdCollection.Add(hd);
  40.             }
  41.             searcher = new
  42.                 ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMedia");
  43.             int i = 0;
  44.             foreach(ManagementObject wmi_HD in searcher.Get())
  45.             {
  46.                 // get the hard drive from collection
  47.                 // using index
  48.                 HardDrive hd = (HardDrive)hdCollection[i];
  49.                 // get the hardware serial no.
  50.                 if (wmi_HD["SerialNumber"] == null)
  51.                     hd.SerialNo = "None";
  52.                 else
  53.                     hd.SerialNo = wmi_HD["SerialNumber"].ToString();
  54.                 ++i;
  55.             }
  56.             // Display available hard drives
  57.             foreach(HardDrive hd in hdCollection)
  58.             {
  59.                 Console.WriteLine("Model/t/t: " + hd.Model);
  60.                 Console.WriteLine("Type/t/t: " + hd.Type);
  61.                 Console.WriteLine("Serial No./t: " + hd.SerialNo);
  62.                 Console.WriteLine();
  63.             }
  64.             // Pause application
  65.             Console.WriteLine("Press [Enter] to exit...");
  66.             Console.ReadLine();
  67.         }
  68.     }

 上面的方式先查询Win32_DiskDrive,然后再查询 Win32_PhysicalMedia,经过测试,这种方式不能保证在所有机器上均取得硬盘序列号,而且在Vista下面还会出错,程序直接抛出无法处理的异常。

 

另外,还可以使用另外一WMI方式,就是查询 PNPDeviceID 的 signature,代码如下:

  1. /// <summary>
  2.        /// 获取硬盘唯一序列号(不是卷标号),可能需要以管理员身份运行程序
  3.        /// </summary>
  4.        /// <returns></returns>
  5.         public static string GetHdId()
  6.         {
  7.             ManagementObjectSearcher wmiSearcher = new ManagementObjectSearcher();
  8.             /*
  9.              * PNPDeviceID   的数据是由四部分组成的:   
  10.   1、接口,通常有   IDE,ATA,SCSI;   
  11.   2、型号   
  12.   3、(可能)驱动版本号   
  13.   4、(可能)硬盘的出厂序列号   
  14.              * 
  15.              * 
  16.              */
  17.             //signature 需要程序以管理员身份运行(经过测试,2003系统上非管理员身份也可以运行,查相关资料说,可能在2000系统上获取的值为空)
  18.             wmiSearcher.Query = new SelectQuery(
  19.             "Win32_DiskDrive",
  20.             "",
  21.             new string[] { "PNPDeviceID""signature" }
  22.             );
  23.             ManagementObjectCollection myCollection = wmiSearcher.Get();
  24.             ManagementObjectCollection.ManagementObjectEnumerator em =
  25.             myCollection.GetEnumerator();
  26.             em.MoveNext();
  27.             ManagementBaseObject mo = em.Current;
  28.             //string id = mo.Properties["PNPDeviceID"].Value.ToString().Trim();
  29.             string id = mo.Properties["signature"].Value.ToString().Trim();
  30.             return id;
  31.         }

 

有人说,使用 signature 需要程序以管理员身份运行(经过测试,2003系统上非管理员身份也可以运行),而且查询相关资料说,可能在2000系统上获取的值为空。

使用这种方式,在Vista上面工作良好。

经过测试,使用 signature  均能够取得硬盘序列号,但是跟 Win32_PhysicalMedia 查询出来的号不一样。目前我也不能肯定 使用 signature  能够100%取道硬盘序列号。

 

使用WMI方式需要客户机开启WMI服务,但这个往往不能保证,所以使用这种方式有一定局限性。

 

2,使用API方式。

在网上找到一片资料,说使用 RING3调用 API DeviceIoControl()来获取硬盘信息,下面是原话:

硬盘序列号(Serial Number)不等于卷标号(Volume Name),后者虽然很容易得到,但是格式化分区后就会重写,不可靠。遗憾的是很多朋友往往分不清这一点。

要得到硬盘的物理序列号,可以通过WMI,也就是Win32_PhysicalMedia.SerialNumber。可惜的是Windows 98/ME的WMI并不支持这个类,访问时会出现异常。

受陆麟的例子的启发,我们还可以通过S.M.A.R.T.接口,直接从RING3调用 API DeviceIoControl()来获取硬盘信息,而不需要写VXD或者DRIVER。这样这个问题就解决了,我对它进行了封装,大量使用了 P/Invoke技术,一个完整的Library。支持Windows 98-2003。

使用上很简单:

HardDiskInfo hdd = AtapiDevice.GetHddInfo(0); // 第一个硬盘
Console.WriteLine("Module Number: {0}", hdd.ModuleNumber);
Console.WriteLine("Serial Number: {0}", hdd.SerialNumber);
Console.WriteLine("Firmware: {0}", hdd.Firmware);
Console.WriteLine("Capacity: {0} M", hdd.Capacity);

 

感谢原文作者的贡献,(在这里我已经不知道原文作者是谁了,网上的文章都是转载的),经过测试,这种方式比较准确,但是需要管理员权限运行。

下面把代码分享:

  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Text;
  4. namespace HardwareUtility
  5. {
  6.     [Serializable]
  7.     public struct HardDiskInfo
  8.     {
  9.         /// <summary>
  10.         /// 型号
  11.         /// </summary>
  12.         public string ModuleNumber;
  13.         /// <summary>
  14.         /// 固件版本
  15.         /// </summary>
  16.         public string Firmware;
  17.         /// <summary>
  18.         /// 序列号
  19.         /// </summary>
  20.         public string SerialNumber;
  21.         /// <summary>
  22.         /// 容量,以M为单位
  23.         /// </summary>
  24.         public uint Capacity;
  25.     }
  26.     #region Internal Structs
  27.     [StructLayout(LayoutKind.Sequential, Pack = 1)]
  28.     internal struct GetVersionOutParams
  29.     {
  30.         public byte bVersion;
  31.         public byte bRevision;
  32.         public byte bReserved;
  33.         public byte bIDEDeviceMap;
  34.         public uint fCapabilities;
  35.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  36.         public uint[] dwReserved; // For future use.
  37.     }
  38.     [StructLayout(LayoutKind.Sequential, Pack = 1)]
  39.     internal struct IdeRegs
  40.     {
  41.         public byte bFeaturesReg;
  42.         public byte bSectorCountReg;
  43.         public byte bSectorNumberReg;
  44.         public byte bCylLowReg;
  45.         public byte bCylHighReg;
  46.         public byte bDriveHeadReg;
  47.         public byte bCommandReg;
  48.         public byte bReserved;
  49.     }
  50.     [StructLayout(LayoutKind.Sequential, Pack = 1)]
  51.     internal struct SendCmdInParams
  52.     {
  53.         public uint cBufferSize;
  54.         public IdeRegs irDriveRegs;
  55.         public byte bDriveNumber;
  56.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
  57.         public byte[] bReserved;
  58.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  59.         public uint[] dwReserved;
  60.         public byte bBuffer;
  61.     }
  62.     [StructLayout(LayoutKind.Sequential, Pack = 1)]
  63.     internal struct DriverStatus
  64.     {
  65.         public byte bDriverError;
  66.         public byte bIDEStatus;
  67.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
  68.         public byte[] bReserved;
  69.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
  70.         public uint[] dwReserved;
  71.     }
  72.     [StructLayout(LayoutKind.Sequential, Pack = 1)]
  73.     internal struct SendCmdOutParams
  74.     {
  75.         public uint cBufferSize;
  76.         public DriverStatus DriverStatus;
  77.         public IdSector bBuffer;
  78.     }
  79.     [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 512)]
  80.     internal struct IdSector
  81.     {
  82.         public ushort wGenConfig;
  83.         public ushort wNumCyls;
  84.         public ushort wReserved;
  85.         public ushort wNumHeads;
  86.         public ushort wBytesPerTrack;
  87.         public ushort wBytesPerSector;
  88.         public ushort wSectorsPerTrack;
  89.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
  90.         public ushort[] wVendorUnique;
  91.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
  92.         public byte[] sSerialNumber;
  93.         public ushort wBufferType;
  94.         public ushort wBufferSize;
  95.         public ushort wECCSize;
  96.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
  97.         public byte[] sFirmwareRev;
  98.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
  99.         public byte[] sModelNumber;
  100.         public ushort wMoreVendorUnique;
  101.         public ushort wDoubleWordIO;
  102.         public ushort wCapabilities;
  103.         public ushort wReserved1;
  104.         public ushort wPIOTiming;
  105.         public ushort wDMATiming;
  106.         public ushort wBS;
  107.         public ushort wNumCurrentCyls;
  108.         public ushort wNumCurrentHeads;
  109.         public ushort wNumCurrentSectorsPerTrack;
  110.         public uint ulCurrentSectorCapacity;
  111.         public ushort wMultSectorStuff;
  112.         public uint ulTotalAddressableSectors;
  113.         public ushort wSingleWordDMA;
  114.         public ushort wMultiWordDMA;
  115.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
  116.         public byte[] bReserved;
  117.     }
  118.     #endregion
  119.     /// <summary>
  120.     /// ATAPI驱动器相关
  121.     /// </summary>
  122.     public class AtapiDevice
  123.     {
  124.         #region DllImport
  125.         [DllImport("kernel32.dll", SetLastError = true)]
  126.         static extern int CloseHandle(IntPtr hObject);
  127.         [DllImport("kernel32.dll", SetLastError = true)]
  128.         static extern IntPtr CreateFile(
  129.             string lpFileName,
  130.             uint dwDesiredAccess,
  131.             uint dwShareMode,
  132.             IntPtr lpSecurityAttributes,
  133.             uint dwCreationDisposition,
  134.             uint dwFlagsAndAttributes,
  135.             IntPtr hTemplateFile);
  136.         [DllImport("kernel32.dll")]
  137.         static extern int DeviceIoControl(
  138.             IntPtr hDevice,
  139.             uint dwIoControlCode,
  140.             IntPtr lpInBuffer,
  141.             uint nInBufferSize,
  142.             ref GetVersionOutParams lpOutBuffer,
  143.             uint nOutBufferSize,
  144.             ref uint lpBytesReturned,
  145.             [Out] IntPtr lpOverlapped);
  146.         [DllImport("kernel32.dll")]
  147.         static extern int DeviceIoControl(
  148.             IntPtr hDevice,
  149.             uint dwIoControlCode,
  150.             ref SendCmdInParams lpInBuffer,
  151.             uint nInBufferSize,
  152.             ref SendCmdOutParams lpOutBuffer,
  153.             uint nOutBufferSize,
  154.             ref uint lpBytesReturned,
  155.             [Out] IntPtr lpOverlapped);
  156.         const uint DFP_GET_VERSION = 0x00074080;
  157.         const uint DFP_SEND_DRIVE_COMMAND = 0x0007c084;
  158.         const uint DFP_RECEIVE_DRIVE_DATA = 0x0007c088;
  159.         const uint GENERIC_READ = 0x80000000;
  160.         const uint GENERIC_WRITE = 0x40000000;
  161.         const uint FILE_SHARE_READ = 0x00000001;
  162.         const uint FILE_SHARE_WRITE = 0x00000002;
  163.         const uint CREATE_NEW = 1;
  164.         const uint OPEN_EXISTING = 3;
  165.         #endregion
  166.         #region GetHddInfo
  167.         /// <summary>
  168.         /// 获得硬盘信息
  169.         /// </summary>
  170.         /// <param name="driveIndex">硬盘序号</param>
  171.         /// <returns>硬盘信息</returns>
  172.         /// <remarks>
  173.         /// 参考lu0的文章:http://lu0s1.3322.org/App/2k1103.html
  174.         /// by sunmast for everyone
  175.         /// thanks lu0 for his great works
  176.         /// 在Windows 98/ME中,S.M.A.R.T并不缺省安装,请将SMARTVSD.VXD拷贝到%SYSTEM%/IOSUBSYS目录下。
  177.         /// 在Windows 2000/2003下,需要Administrators组的权限。
  178.         /// </remarks>
  179.         /// <example>
  180.         /// AtapiDevice.GetHddInfo()
  181.         /// </example>
  182.         public static HardDiskInfo GetHddInfo(byte driveIndex)
  183.         {
  184.             switch (Environment.OSVersion.Platform)
  185.             {
  186.                 case PlatformID.Win32Windows:
  187.                     return GetHddInfo9x(driveIndex);
  188.                 case PlatformID.Win32NT:
  189.                     return GetHddInfoNT(driveIndex);
  190.                 case PlatformID.Win32S:
  191.                     throw new NotSupportedException("Win32s is not supported.");
  192.                 case PlatformID.WinCE:
  193.                     throw new NotSupportedException("WinCE is not supported.");
  194.                 default:
  195.                     throw new NotSupportedException("Unknown Platform.");
  196.             }
  197.         }
  198.         #region GetHddInfo9x
  199.         private static HardDiskInfo GetHddInfo9x(byte driveIndex)
  200.         {
  201.             GetVersionOutParams vers = new GetVersionOutParams();
  202.             SendCmdInParams inParam = new SendCmdInParams();
  203.             SendCmdOutParams outParam = new SendCmdOutParams();
  204.             uint bytesReturned = 0;
  205.             IntPtr hDevice = CreateFile(
  206.                 @"//./Smartvsd",
  207.                 0,
  208.                 0,
  209.                 IntPtr.Zero,
  210.                 CREATE_NEW,
  211.                 0,
  212.                 IntPtr.Zero);
  213.             if (hDevice == IntPtr.Zero)
  214.             {
  215.                 throw new Exception("Open smartvsd.vxd failed.");
  216.             }
  217.             if (0 == DeviceIoControl(
  218.                 hDevice,
  219.                 DFP_GET_VERSION,
  220.                 IntPtr.Zero,
  221.                 0,
  222.                 ref vers,
  223.                 (uint)Marshal.SizeOf(vers),
  224.                 ref bytesReturned,
  225.                 IntPtr.Zero))
  226.             {
  227.                 CloseHandle(hDevice);
  228.                 throw new Exception("DeviceIoControl failed:DFP_GET_VERSION");
  229.             }
  230.             // If IDE identify command not supported, fails
  231.             if (0 == (vers.fCapabilities & 1))
  232.             {
  233.                 CloseHandle(hDevice);
  234.                 throw new Exception("Error: IDE identify command not supported.");
  235.             }
  236.             if (0 != (driveIndex & 1))
  237.             {
  238.                 inParam.irDriveRegs.bDriveHeadReg = 0xb0;
  239.             }
  240.             else
  241.             {
  242.                 inParam.irDriveRegs.bDriveHeadReg = 0xa0;
  243.             }
  244.             if (0 != (vers.fCapabilities & (16 >> driveIndex)))
  245.             {
  246.                 // We don't detect a ATAPI device.
  247.                 CloseHandle(hDevice);
  248.                 throw new Exception(string.Format("Drive {0} is a ATAPI device, we don't detect it", driveIndex + 1));
  249.             }
  250.             else
  251.             {
  252.                 inParam.irDriveRegs.bCommandReg = 0xec;
  253.             }
  254.             inParam.bDriveNumber = driveIndex;
  255.             inParam.irDriveRegs.bSectorCountReg = 1;
  256.             inParam.irDriveRegs.bSectorNumberReg = 1;
  257.             inParam.cBufferSize = 512;
  258.             if (0 == DeviceIoControl(
  259.                 hDevice,
  260.                 DFP_RECEIVE_DRIVE_DATA,
  261.                 ref inParam,
  262.                 (uint)Marshal.SizeOf(inParam),
  263.                 ref outParam,
  264.                 (uint)Marshal.SizeOf(outParam),
  265.                 ref bytesReturned,
  266.                 IntPtr.Zero))
  267.             {
  268.                 CloseHandle(hDevice);
  269.                 throw new Exception("DeviceIoControl failed: DFP_RECEIVE_DRIVE_DATA");
  270.             }
  271.             CloseHandle(hDevice);
  272.             return GetHardDiskInfo(outParam.bBuffer);
  273.         }
  274.         #endregion
  275.         #region GetHddInfoNT
  276.         private static HardDiskInfo GetHddInfoNT(byte driveIndex)
  277.         {
  278.             GetVersionOutParams vers = new GetVersionOutParams();
  279.             SendCmdInParams inParam = new SendCmdInParams();
  280.             SendCmdOutParams outParam = new SendCmdOutParams();
  281.             uint bytesReturned = 0;
  282.             // We start in NT/Win2000
  283.             IntPtr hDevice = CreateFile(
  284.                 string.Format(@"//./PhysicalDrive{0}", driveIndex),
  285.                 GENERIC_READ | GENERIC_WRITE,
  286.                 FILE_SHARE_READ | FILE_SHARE_WRITE,
  287.                 IntPtr.Zero,
  288.                 OPEN_EXISTING,
  289.                 0,
  290.                 IntPtr.Zero);
  291.             if (hDevice == IntPtr.Zero)
  292.             {
  293.                 throw new Exception("CreateFile faild.");
  294.             }
  295.             if (0 == DeviceIoControl(
  296.                 hDevice,
  297.                 DFP_GET_VERSION,
  298.                 IntPtr.Zero,
  299.                 0,
  300.                 ref vers,
  301.                 (uint)Marshal.SizeOf(vers),
  302.                 ref bytesReturned,
  303.                 IntPtr.Zero))
  304.             {
  305.                 CloseHandle(hDevice);
  306.                 throw new Exception(string.Format("Drive {0} may not exists.", driveIndex + 1));
  307.             }
  308.             // If IDE identify command not supported, fails
  309.             if (0 == (vers.fCapabilities & 1))
  310.             {
  311.                 CloseHandle(hDevice);
  312.                 throw new Exception("Error: IDE identify command not supported.");
  313.             }
  314.             // Identify the IDE drives
  315.             if (0 != (driveIndex & 1))
  316.             {
  317.                 inParam.irDriveRegs.bDriveHeadReg = 0xb0;
  318.             }
  319.             else
  320.             {
  321.                 inParam.irDriveRegs.bDriveHeadReg = 0xa0;
  322.             }
  323.             if (0 != (vers.fCapabilities & (16 >> driveIndex)))
  324.             {
  325.                 // We don't detect a ATAPI device.
  326.                 CloseHandle(hDevice);
  327.                 throw new Exception(string.Format("Drive {0} is a ATAPI device, we don't detect it.", driveIndex + 1));
  328.             }
  329.             else
  330.             {
  331.                 inParam.irDriveRegs.bCommandReg = 0xec;
  332.             }
  333.             inParam.bDriveNumber = driveIndex;
  334.             inParam.irDriveRegs.bSectorCountReg = 1;
  335.             inParam.irDriveRegs.bSectorNumberReg = 1;
  336.             inParam.cBufferSize = 512;
  337.             if (0 == DeviceIoControl(
  338.                 hDevice,
  339.                 DFP_RECEIVE_DRIVE_DATA,
  340.                 ref inParam,
  341.                 (uint)Marshal.SizeOf(inParam),
  342.                 ref outParam,
  343.                 (uint)Marshal.SizeOf(outParam),
  344.                 ref bytesReturned,
  345.                 IntPtr.Zero))
  346.             {
  347.                 CloseHandle(hDevice);
  348.                 throw new Exception("DeviceIoControl failed: DFP_RECEIVE_DRIVE_DATA");
  349.             }
  350.             CloseHandle(hDevice);
  351.             return GetHardDiskInfo(outParam.bBuffer);
  352.         }
  353.         #endregion
  354.         private static HardDiskInfo GetHardDiskInfo(IdSector phdinfo)
  355.         {
  356.             HardDiskInfo hddInfo = new HardDiskInfo();
  357.             ChangeByteOrder(phdinfo.sModelNumber);
  358.             hddInfo.ModuleNumber = Encoding.ASCII.GetString(phdinfo.sModelNumber).Trim();
  359.             ChangeByteOrder(phdinfo.sFirmwareRev);
  360.             hddInfo.Firmware = Encoding.ASCII.GetString(phdinfo.sFirmwareRev).Trim();
  361.             ChangeByteOrder(phdinfo.sSerialNumber);
  362.             hddInfo.SerialNumber = Encoding.ASCII.GetString(phdinfo.sSerialNumber).Trim();
  363.             hddInfo.Capacity = phdinfo.ulTotalAddressableSectors / 2 / 1024;
  364.             return hddInfo;
  365.         }
  366.         private static void ChangeByteOrder(byte[] charArray)
  367.         {
  368.             byte temp;
  369.             for (int i = 0; i < charArray.Length; i += 2)
  370.             {
  371.                 temp = charArray[i];
  372.                 charArray[i] = charArray[i + 1];
  373.                 charArray[i + 1] = temp;
  374.             }
  375.         }
  376.         #endregion
  377.     }
  378. }

http://www.ppmy.cn/news/640546.html

相关文章

全网采集工具(msray)-百度搜索引擎关键词全网采集

全网采集工具&#xff08;msray&#xff09;-百度搜索引擎进行全网采集 Msray-plus&#xff0c;是一款采用GO语言开发的企业级综合性爬虫/采集软件。 支持&#xff1a;搜索引擎结果采集、域名采集、URL采集、网址采集、全网域名采集、CMS采集、联系信息采集 支持亿级数据存储、…

CCPD数据集 不限速 夸克云盘下载 车牌数据集 40G

原始公开数据中提供了谷歌云与百度网盘的链接&#xff0c;在此补充夸克云盘下载链接&#xff08;不限速下载&#xff09; 我用夸克网盘分享了「CCPD2019.tar」&#xff0c;点击链接即可保存。打开「夸克APP」 链接&#xff1a;https://pan.quark.cn/s/e9b6cff18e7c 提取码&a…

怎样获取计算机硬盘号,电脑硬盘序列号读取工具

电脑硬盘序列号读取工具是一款电脑序列号获取工具&#xff0c;能一键获取支持各种电脑序列号获取&#xff0c;需要的获取序列号的朋友快来下载吧&#xff01; 软件介绍 靖源磁盘管理单元测试(电脑硬盘序列号读取工具)是一款非常实用的硬盘序列号获取软件。比较强大&#xff0c;…

单点登录原理

单点登录原理 一、什么是单点登录 单点登录英文全称Single Sign On&#xff0c;简称SSO。指在多系统应用群中登录一个系统&#xff0c;便可在其他所有系统中得到授权而无需再次登录&#xff0c;包括单点登录与单点注销两部分。 二、为什么需要单点登录 在一些子系统用户信息…

使用 ArrayList 应当避免的坑

大家都知道 ArrayList 是由数组实现&#xff0c;而数据的长度有限&#xff0c;需要在合适的时机对数组扩容。 当我们初始化一个长度为 2 的 ArrayList &#xff0c;并往里边写入三条数据时 ArrayList 就得扩容了&#xff0c;也就是将之前的数据复制一份到新的数组长度为 3 的数…

桃核做口哨

小时候,我们都很会玩,记得我们常常把桃核从一面的中间挖个圆形小孔,把里面的桃核仁一点一点弄出来.让里面成为中空,这样就可以当哨子用了,声音非常悦耳

微信小程序:王者荣耀出装与铭文推荐助手

这是一款王者荣耀助手的一款小程序源码 该小程序主要功能就是提供各个英雄出装和铭文查询 功能虽然单调但是对于玩王者的朋友来说还是挺实用的! 目前该小程序源码已支持多种流量主模式 小程序源码下载地址&#xff1a; 微信小程序&#xff1a;王者荣耀出装与铭文推荐助手-小程…

直播过程中的掌声,口哨,背景音等音效怎么实现

大家有没有想过在直播过程中的掌声&#xff0c;口哨&#xff0c;背景音等音效是怎么实现的吗?这些功能都是可以通过混音来实现的。本篇文章介绍即构科技音视频SDK高级功能第五篇&#xff0c;ZegoLiveRoom SDK 混音功能&#xff0c;还是以iOS环境为例。 混音 1、功能简介 Ze…