开源WebRTC库放大器模式在采集桌面图像时遇到的DPI缩放与内存泄漏问题排查
目录
1、在非100%的显示比例下放大器采集到的桌面图像不全问题
1.1、通过manifest文件禁止系统对软件进行缩放
1.2、调用SetThreadDpiAwarenessContext函数,禁止系统对目标线程中的窗口进行缩放
1.3、使用winver命令查看Windows的年月版本
2、使用放大器模式遇到的内存泄漏问题
2.1、使用Windbg动态调试发现软件因为申请内存失败抛出bad_alloc异常导致程序闪退
2.2、进一步分析发现时内存泄漏导致进程内存不足,引发申请内存失败抛出bad_alloc异常
2.3、排查桌面共享模块内存泄漏的原因
3、最后
VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具案例集锦(专栏文章正在更新中...)https://blog.csdn.net/chenlycly/category_12279968.html WebRTC开源库中实现桌面图像采集的方式有多种,为了支持过滤部分窗口的功能,我们采用了magnification放大器方式,但在使用放大器这种采集方式时遇到了一些问题,在这里大概地总结一下,给大家提供一个借鉴或参考。
1、在非100%的显示比例下放大器采集到的桌面图像不全问题
我们软件为了支持过滤窗口,采用了开源WebRTC库中支持的放大镜采集模式,但使用放大器模式后测试发现,当系统的显示比例调成非100%的显示比例(比如150%、200%等)后,放大器组件采集出来的桌面图像不全,只采集到桌面的一部分。应该是系统DPI显示缩放引起的,默认情况下,系统会根据当前的显示比例自动对程序进行缩放。
1.1、通过manifest文件禁止系统对软件进行缩放
一般再高分辨率的电脑上,均需要将系统的显示比例设置成100%以上的比例。设置入口是,在桌面空白处点击右键,在弹出的右键菜单中点击显示设置,然后在打开的窗口中找到缩放与布局栏,就可以更改系统的显示比例了,如下所示:
默认情况下,系统会根据当前的显示比例自动对程序进行缩放,除非我们想禁用一下系统的缩放,让程序始终保持100%的显示效果,看看放大器组件采集出来的桌面图像是否完整。直接到我们程序桌面快捷方式的属性中设置禁止系统对我们程序进行缩放,设置入口如下所示:
即不管系统设置了什么显示比例,我们的程序始终保持100%的显示效果,重新运行程序,发现采集出来的桌面图像就完整了。
上述设置是手动修改的,有没有办法通过代码去设置呢?告诉系统不要对我的程序进行缩放呢?以前我们研究过系统API函数SetProcessDPIAware,这个函数可以禁止系统对我们程序进行缩放,但将程序放到Win10系统中运行就没有效果了,系统还是对程序进行了缩放。后来查看SetProcessDPIAware函数在MSDN上说明,提示不要再使用这接口了,应该使用嵌入manifest文件的方式,相关说明如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"><asmv3:application><asmv3:windowsSettings><dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware><dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness></asmv3:windowsSettings></asmv3:application>
</assembly>
我们新建txt文件,然后把上述内容拷贝到该文件中,然后再将文件重命名为.mannifest文件即可(注意manifest文件的名称要和进程名称一致)。然后直接将manifest文件像添加文件一样添加到工程中,编译后启动程序就生效了,Win10系统中就能实现禁止系统缩放了。
1.2、调用SetThreadDpiAwarenessContext函数,禁止系统对目标线程中的窗口进行缩放
如果要让放大器组件采集到完整的桌面图像,就需要禁用系统对程序的缩放。但要禁用系统的缩放,让程序始终显示100%大小效果,会导致程序在一些高分率的电脑上(比如2K或4K的微软Surface平板电脑),显示太小,根本就没法看没法操作了。腾讯系的很多软件都禁用了系统缩放,软件自己实现了跟随系统显示比例的缩放,所以他们没有这样的困扰。但程序自己去实现缩放是有难度的,我们目前做不到,所以还是需要依赖系统缩放的。
后来找到了一个针对线程设置DPI缩放属性的API函数SetThreadDpiAwarenessContext,可以禁止系统对某个线程中创建的窗口禁止缩放。我们可以将放大器组件的操作放到一个线程中,然后调用这个函数禁止系统对该线程中放大器窗口进行缩放,这样放大器组件就能采集到完整的桌面图像了。
这个API接口是放置在系统库user32.dll中的:
Win10以前的版本是不支持的,即便是Win10系统也要1607(2016年7月发布)之后的版本才支持。所以,要从user32.dll库中动态加载,如果找不到SetThreadDpiAwarenessContext接口,则直接返回;如果找到接口再去调用,相关代码如下:
bool SetThreadDpiAware()
{//设置本线程为DPI感知模式,解决缩放时屏幕采集不全的问题HMODULE user32_module = GetModuleHandle(TEXT("user32.dll"));if (nullptr == user32_module) {return false;}decltype(&SetThreadDpiAwarenessContext) set_thread_dpi_awareness_context_func =(decltype(&SetThreadDpiAwarenessContext))GetProcAddress(user32_module, "SetThreadDpiAwarenessContext");if (nullptr == set_thread_dpi_awareness_context_func) {return false;}DPI_AWARENESS_CONTEXT original_dpi_awareness_context = set_thread_dpi_awareness_context_func(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);return true;
}
1.3、使用winver命令查看Windows的年月版本
上面说到的SetThreadDpiAwarenessContext函数只有Win10系统才支持,并且只有“Windows 10, version 1607”以后的版本才支持,此处的版本号1607是2016年7月发布的意思。这个version 1607是年月版本,在命令行中使用systeminfo命令查看的版本是系统内部版本:
如果要查看Windows发布的年月版本,则需要使用winver命令,执行该命令后会打开如下的版本窗口:
如图所示,当前机器的年月版本为1909,即2019年9月发布的版本。
2、使用放大器模式遇到的内存泄漏问题
测试同事反馈,在多次发起桌面共享后,软件会出现闪退问题,异常捕获模块没有捕获到,没有生成dump文件。
2.1、使用Windbg动态调试发现软件因为申请内存失败抛出bad_alloc异常导致程序闪退
对于这种没有捕获到异常的场景,就需要使用Windbg进行动态调试了。于是让同事重新将软件启动起来,然后将Windbg附加上去,让Wingdbg和软件一起跑,然后按照操作步骤将闪退问题复现出来。问题复现时,Windbg第一时间感知到并中断下来,使用kn命令查看此时的函数的函数调用堆栈,如下所示:
通过堆栈看出当前是在用new动态申请内存时抛出了bad_alloc异常,所以引发了异常崩溃。
2.2、进一步分析发现时内存泄漏导致进程内存不足,引发申请内存失败抛出bad_alloc异常
应该是内存不足导致内存申请失败,进而抛出bad_alloc异常。此时的进程还在的,使用Process Explorer工具查看进程的虚拟内存占用,看到进程的虚拟内存已经占用了1.7GB。注意,Window任务管理器看不到进程占用的虚拟内存,只能看到与物理内存相关的内容,需要借助Process Explorer工具去查看。Process Explorer中显示的是用户态虚拟内存。
我们的程序是32位的,系统给进程分配了4GB的虚拟内存,其中用户态虚拟内存占2GB,内核态虚拟内存占2GB。当前程序进程的用户态虚拟内存占用达到1.7GB:
按将离上限2GB还有300MB空闲,为啥用new申请内存时会失败呢?可能申请的是一段较长的buffer,而空闲的300MB虚拟内存是分散在不同地方的零零散散的小块内存(这就是我们经常讲的内存碎片),找不到一段很大的连续内存去分配了,所以出现内存分配失败了。为啥程序的虚拟内存占用会达到1.7GB之多呢?估计是程序中有内存泄漏了。
鉴于当前的操作场景,可能是桌面共享功能模块有内存泄漏,于是让测试发起桌面共享之前记录一下总的虚拟内存大小,然后发起桌面共享,然后再关闭桌面共享,看看内存有没有明显增长。经测试发现,发起共享时会申请内存,但停止共享后没有将申请的内存释放掉,所以这个操作有内存泄漏。后来使用Windbg分析内存泄漏,分析出发生泄漏内存的函数调用堆栈就指向桌面共享的模块代码中。具体如何使用Windbg分析内存泄漏,可以参见我之前写的文章:
使用Windbg定位Windows C++程序中的内存泄漏https://blog.csdn.net/chenlycly/article/details/121295720
2.3、排查桌面共享模块内存泄漏的原因
2.3.1、怀疑是放大器组件回调上来的buffer没有释放导致内存泄漏的
桌面共享模块为了实现窗口过滤,选用了上面讲到的magnification放大器模式,于是详细去排查操作放大器的相关代码,看看为啥会有内存泄漏。代码中主要使用了MagInitialize、MagUninitialize、MagSetWindowSource、MagSetWindowFilterList、MagSetImageScalingCallback这几个系统API函数,到MSDN上详细查看了这几个函数的详细说明,但说明都比较少,没有找到线索,也没有说需要额外释放哪些资源。我们在结束桌面共享时也调用MagUninitialize去释放资源,但还是存在内存泄漏。
排查至此,陷入了僵局,没法进行下去了。后来想,难道是放大器组件调用设置进去的回调函数回调出来的buffer需要外部去释放?这个回调函数是调用MagSetImageScalingCallback函数设置的,函数声明如下:
ypedef BOOL (CALLBACK* MagImageScalingCallback)(HWND hwnd, void * srcdata, MAGIMAGEHEADER srcheader, void * destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty );
回调出来的buffer地址就存放在void * srcdata指针变量中,通过加打印发现回调出来的buffer首地址一个不变的地址,估计在放大器组件内部在开始时申请的一段buffer,存放抓取的桌面共享图像数据的,多次抓取的图像数据都是保存在该buffer中,然后每次将该buffer的地址回调出去。
怀疑这个放大器组件内部申请的buffer内存没有释放,导致了内存泄漏。于是将回调函数回调出来的buffer地址保存到成员变量void* m_srcdata中,在结果共享时上层去主动将这个bufer内存给释放掉。最开始尝试用delete去释放,结果执行到delete时产生了异常。
2.3.2、参考API函数GetAdaptersAddresses的Remarks部分的说明,决定使用HeapFree去释放
动态申请内存的方式有多种,比如使用new(要用delete去释放),比如使用malloc(要用free去释放),再比如调用系统API函数HeapCreate或者HeapAlloc(要用HeapFree去释放),还有可以调用API函数VirtualAlloc(要用VirtualFree去释放),还有其他的API函数。
到底这个buffer使用哪种方式呢?以前在写读取多个网卡信息时,需要调用系统API函数GetAdaptersAddresses,在调用该接口时传入用来存放网卡信息的buffer需要外部申请好,看MSDN上GetAdaptersAddresses函数的Remarks部分说明:
建议使用HeapAlloc去申请内存,然后使用完后使用HeapFree将内存释放掉。调用系统API函数GetAdaptersAddresses的示例代码如下:
#include <winsock2.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>// Link with Iphlpapi.lib
#pragma comment(lib, "IPHLPAPI.lib")void PrintAdapterInfo()
{// Declare and initialize variablesDWORD dwSize = 0;DWORD dwRetVal = 0;unsigned int i = 0;// Set the flags to pass to GetAdaptersAddressesULONG flags = GAA_FLAG_INCLUDE_PREFIX;// default to unspecified address family (both)ULONG family = AF_UNSPEC;// AF_INET - ipv4, AF_INET6 - ipv6LPVOID lpMsgBuf = NULL;PIP_ADAPTER_ADDRESSES pAddresses = NULL;ULONG outBufLen = 0;ULONG Iterations = 0;PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL;IP_ADAPTER_PREFIX *pPrefix = NULL;// Allocate a 15 KB buffer to start with.outBufLen = 15000;do {pAddresses = (IP_ADAPTER_ADDRESSES *)HeapAlloc(GetProcessHeap(), 0, outBufLen);if (pAddresses == NULL) {printf("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");return;}dwRetVal = GetAdaptersAddresses( family, flags, NULL, pAddresses, &outBufLen );if (dwRetVal == ERROR_BUFFER_OVERFLOW) {HeapFree( GetProcessHeap(), 0, pAddresses );pAddresses = NULL;}else {break;}Iterations++;} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < 3));if (dwRetVal == NO_ERROR) {// If successful, output some information from the data we receivedpCurrAddresses = pAddresses;while (pCurrAddresses) {printf("\tLength of the IP_ADAPTER_ADDRESS struct: %ld\n",pCurrAddresses->Length);printf("\tIfIndex (IPv4 interface): %u\n", pCurrAddresses->IfIndex);printf("\tAdapter name: %s\n", pCurrAddresses->AdapterName);pUnicast = pCurrAddresses->FirstUnicastAddress;if (pUnicast != NULL) {for (i = 0; pUnicast != NULL; i++)pUnicast = pUnicast->Next;printf("\tNumber of Unicast Addresses: %d\n", i);}elseprintf("\tNo Unicast Addresses\n");pAnycast = pCurrAddresses->FirstAnycastAddress;if (pAnycast) {for (i = 0; pAnycast != NULL; i++)pAnycast = pAnycast->Next;printf("\tNumber of Anycast Addresses: %d\n", i);}elseprintf("\tNo Anycast Addresses\n");pMulticast = pCurrAddresses->FirstMulticastAddress;if (pMulticast) {for (i = 0; pMulticast != NULL; i++)pMulticast = pMulticast->Next;printf("\tNumber of Multicast Addresses: %d\n", i);}elseprintf("\tNo Multicast Addresses\n");pDnServer = pCurrAddresses->FirstDnsServerAddress;if (pDnServer) {for (i = 0; pDnServer != NULL; i++)pDnServer = pDnServer->Next;printf("\tNumber of DNS Server Addresses: %d\n", i);}elseprintf("\tNo DNS Server Addresses\n");printf("\tDNS Suffix: %wS\n", pCurrAddresses->DnsSuffix);printf("\tDescription: %wS\n", pCurrAddresses->Description);printf("\tFriendly name: %wS\n", pCurrAddresses->FriendlyName);if (pCurrAddresses->PhysicalAddressLength != 0) {printf("\tPhysical address: ");for (i = 0; i < (int)pCurrAddresses->PhysicalAddressLength;i++) {if (i == (pCurrAddresses->PhysicalAddressLength - 1))printf("%.2X\n",(int)pCurrAddresses->PhysicalAddress[i]);elseprintf("%.2X-",(int)pCurrAddresses->PhysicalAddress[i]);}}printf("\tFlags: %ld\n", pCurrAddresses->Flags);printf("\tMtu: %lu\n", pCurrAddresses->Mtu);printf("\tIfType: %ld\n", pCurrAddresses->IfType);printf("\tOperStatus: %ld\n", pCurrAddresses->OperStatus);printf("\tIpv6IfIndex (IPv6 interface): %u\n",pCurrAddresses->Ipv6IfIndex);printf("\tZoneIndices (hex): ");for (i = 0; i < 16; i++)printf("%lx ", pCurrAddresses->ZoneIndices[i]);printf("\n");printf("\tTransmit link speed: %I64u\n", pCurrAddresses->TransmitLinkSpeed);printf("\tReceive link speed: %I64u\n", pCurrAddresses->ReceiveLinkSpeed);pPrefix = pCurrAddresses->FirstPrefix;if (pPrefix) {for (i = 0; pPrefix != NULL; i++)pPrefix = pPrefix->Next;printf("\tNumber of IP Adapter Prefix entries: %d\n", i);}elseprintf("\tNumber of IP Adapter Prefix entries: 0\n");printf("\n");pCurrAddresses = pCurrAddresses->Next;}}else {printf("Call to GetAdaptersAddresses failed with error: %d\n",dwRetVal);if (dwRetVal == ERROR_NO_DATA)printf("\tNo addresses were found for the requested parameters\n");else {if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),// Default language(LPTSTR)& lpMsgBuf, 0, NULL)) {printf("\tError: %s", lpMsgBuf);LocalFree(lpMsgBuf);if (pAddresses)HeapFree(GetProcessHeap(), 0, pAddresses);}}}if (pAddresses) {HeapFree(GetProcessHeap(), 0, pAddresses);}return;
}
根据此处的提示,估计Windows API内部比较喜欢实用HeapAlloc去动态申请堆内存,所以此处决定试试HeapFree去释放放大器组件回调上来的buffer内存。经测试,使用HeapFree是有效的,使用新版本测试,每次发桌面共享停止后不再有明显的内存泄漏了,那基本确定就是没有释放回调出来的buffer内存引起内存泄漏的。
2.4、为啥MagSetImageScalingCallback接口设置的回调函数回调上来的buffer需要外部释放呢?
在MSDN的MagSetImageScalingCallback函数说明页面:
已经显示MagSetImageScalingCallback函数被微软废弃了,应用程序不要再使用了。但对于桌面图像采集,必须要用到这个接口去设置回调的,通过设置的回调将采集到的桌面图像回调上来的。至于回调函数回调上来的buffer需要外部释放的问题,不知道是否与这点有关系。
2.5、解决了放大器模式下的内存泄漏,但WebRTC相关模块还是有小的内存泄漏
但详细测试下来,发起桌面共享并停止后还是有轻微的内存泄漏,每次大概泄漏5MB左右,用以前的老版本测试也存在同样的问题,每次泄漏5MB左右。这应该是开源的WebRTC库中有内存泄漏,应该和使用放大器模式没关系,因为老版本的桌面共享使用的不是桌面共享模式。这个5MB的泄漏,可以容忍,不会直接引发问题,但这是个隐患。比如客户长时间电脑不关机,软件长时间运行,如果多次发起桌面共享,内存泄漏会持续累计,比如发起100次桌面共享就会泄漏100*5=500MB的内存,这个影响就比较大了。
但开源WebRTC内部的代码比较复杂,排查起来比较困难,等后面有时间的时候再去详细研究,目前这个放大器模式引发的明显内存泄漏算是告一段落了。
3、最后
之前在遇到上述问题时,在网上一直也没搜到有用的信息,这里将使用magnification放大器方式遇到的这两个典型的问题做个详细的总结,以便给大家提供一个借鉴或参考。
相关文章:
开源WebRTC库放大器模式在采集桌面图像时遇到的DPI缩放与内存泄漏问题排查
目录 1、在非100%的显示比例下放大器采集到的桌面图像不全问题 1.1、通过manifest文件禁止系统对软件进行缩放 1.2、调用SetThreadDpiAwarenessContext函数,禁止系统对目标线程中的窗口进行缩放 1.3、使用winver命令查看Windows的年月版本 2、使用放大器模式遇…...
敲黑板!java反射机制和原理
获取Class对象:首先,你需要获取表示要操作的类的Class对象。可以使用以下三种方式之一来获取Class对象: Class.forName()方法:使用类的全限定名获取Class对象,例如:Class<? Class<?> clazz MyC…...
【大数据工具】HBase 集群搭建与基本使用
HBase 集群搭建 HBase 安装包下载地址:https://archive.apache.org/dist/hbase/ 安装 HBase 的前提: ZooKeeper 集群 OKHadoop 集群 OK 1. HBase 集群安装 1. 将 HBase 软件包上传至 Hadoop0 解压并重命名 使用 FileZilla 将 hbase-1.3.1-bin.tar.g…...
【Java】数组详解
文章目录 一、数组的基本认识1.1 数组的概念1.2数组的创建与初始化1.3 数组的使用 二、数组的类型 — 引用类型2.1 JVM 内存分布2.2 什么是引用类型2.3 基本类型变量与引用类型变量的区别2.4 Java 中的 null 三、数组的应用3.1 保存数据3.2 函数参数3.3 函数返回值 一、数组的基…...
NumPy库的学习
本文主要记录的是笔者在B站自学Numpy库的学习笔记。 引入numpy库 import numpy as np矩阵的创建 创建一个二行三列的矩阵。 array np.array([[1,2,3],[2,3,4]])查看array的行数、形状、元素数量 print("number of dim:",array.ndim) print("shape:"…...
CentOS安装IRIS
最近电脑提搞了,可以无顾虑创建虚拟机了,试一下在Linux安装IRIS,适用CentOS7.6上安装Intersystem公司的IRIS数据库,资料基本是空白,分享一下。 首先安装解压软件unzip和libicu,最小化安装的缺,…...
华为OD机试真题 JavaScript 实现【最多几个直角三角形】【2023Q1 100分】
一、题目描述 有 N 条线段,长度分别为 a[1]-a[n]。 现要求你计算这 N 条线段最多可以组合成几个直角三角形,每条线段只能使用一次,每个三角形包含三条线段。 二、输入描述 第一行输入一个正整数 T (1< T< 100) ,表示有…...
vue3中的reactive、ref、toRef和toRefs
目录 reactivereactive的实现原理使用reactive的注意事项 refref的实现原理使用ref的注意事项 toRef和toRefsref和reactive的使用比较 reactive reactive用于创建响应式对象,它返回一个对象的响应式代理。即:它返回的对象以及其中嵌套的对象都会通过 Pr…...
数字图像处理与Python实现-图像增强经典算法汇总
图像增强经典算法汇总 文章目录 图像增强经典算法汇总1、像素变换2、图像逆变换3、幂律变换4、对数变换5、图像均衡化6、对比度受限自适应直方图均衡(CLAHE)7、对比度拉伸8、Sigmoid校正9、局部对比度归一化10、总结本文将对图像增强经典算法做一个简单的汇总。图像增强的经典…...
tag提示词总结
顺序的权重 越靠前的tag权重越大,越靠后的tag权重越小经验来讲,将图像质量相关的tag放在前面,例如masterpiece,best quality等;接着添加主体画风等;最后添加一些不太重要的细节 权重增减 (tag):…...
微信小程序原生开发功能合集二十:导航栏、tabbar自定义及分包功能介绍
本章实现导航栏及tabbar的自定义处理的相关方法介绍及效果展示。 另外还提供小程序开发基础知识讲解课程,包括小程序开发基础知识、组件封装、常用接口组件使用及常用功能实现等内容,具体如下: 1. CSDN课程: https://edu.csdn.net/course/detail/37977 2. 5…...
高通 Camera HAL3:项目开发技术点总结
做高通 Camera HAL3开发的一些技术点的总结、整理。 做个记录,方便后续查阅。 1.目录、so、配置文件 productName是项目名 out Target路径:\out\target\product\productName\chi-cdk:\vendor\qcom\proprietary\chi-cdk\ldc node࿱…...
chatgpt赋能python:Python怎么删除列表中的最大值和最小值
Python怎么删除列表中的最大值和最小值 在Python中,一个列表(List)是一种非常常见的数据结构,它允许我们以有序的方式存储和访问数据。但是,有时候我们需要从列表中删除最大或最小的值,以满足我们的特定需…...
简述Vue的生命周期以及每个阶段做的事情
03_简述Vue的生命周期以及每个阶段做的事情 思路 给出概念 列举出生命周期各个阶段 阐述整体流程 结合实际 扩展:vue3变化 回答范例 每个vue组件实例被创建后都会经过一系列步骤。比如它需要数据观测、模板编译、挂载实例到dom、以及数据变化的时候更新dom、…...
LeetCode-C#-0004.寻找两个正序数组的中位数
0.声明 该题目来源于LeetCode 如有侵权,立马删除。 解法不唯一,如有新解法可一同讨论。 1.题目 0004寻找两个正序数组的中位数 给定两个大小分别为m和n的正序(从小到大)数组nums1和nums2。 请你找出并返回着两个正序数组的中位…...
Vue.js 中的 $emit 和 $on 方法有什么区别?
Vue.js 中的 $emit 和 $on 方法有什么区别? 在 Vue.js 中,$emit 和 $on 方法是两个常用的方法,用于实现组件间的通信。它们可以让我们在一个组件中触发一个自定义事件,并在另一个组件中监听这个事件,从而实现组件间的…...
LAZADA平台的商品评论Python封装API接口接入文档和参数说明
LAZADA是一个位于东南亚的电商平台,成立于2012年。该平台覆盖的国家包括新加坡、马来西亚、印尼、菲律宾、泰国和越南等地。它提供了一个多样化的产品选择,包括时尚、美容、数码、母婴等商品,并且拥有许多知名品牌的官方旗舰店。同时…...
云原生Docker镜像管理
docker是什么? docker是一个go语言开发的应用容器引擎。 docker的作用? ①运行容器里的应用; ②docker是用来管理容器和镜像的一种工具。 #容器 与 虚拟机 的区别? 容器虚拟机所有容器共享宿主机内核每个虚拟机都有独立的操…...
ChatGPT+小红书的8种高级玩法
掌握了这套万能命令,让你快速做出小红书爆款文案! 一、用ChatGPT做定位 我是一个大龄的普通人,没有什么特殊的技能,接下来,请你作为一位小红书的账号定位专家,通过与我对话的方式,为我找到我的小红书账号定…...
shell脚本学习记录1(运算符)
Shell 传递参数 我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推…… 以下实例我们向脚本传递三个…...
vector 迭代器失效问题
vector 迭代器失效 迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向…...
docker使用与服务器上的可视化(ROS rviz等)
1.安装docker 安装docker:官网教程,按照官网命令一步步来即可。 添加当前用户到docker用户组: 【docker】添加用户到docker组,这样后面运行docker的时候前面不需要加sudo命令,否则运行docker的时候一直需要在前面加su…...
最新版本Portraiture4.1中文版ps磨皮滤镜插件安装包
在Portraiture有非常强大的手动功能,可以为用户进行手动调整照片中的皮肤区域以达到更加完美的效果,软件还支持同时导入上千张照片,用户可以通过自动识别照片中的人脸从而依照自己的风格进行批量处理十分的方便快捷。 最新版本Portraiture 4…...
仓储WMS对接淘宝奇门详细说明【亲测可用】
文章目录 简介名词解释奇门对接方案前期准备系统调用流程代码实现思路关键点(个人观点)奇门对接关键代码可能遇到的问题 简介 淘宝奇门项目支持 ERP、WMS 之间的系统标准化对接,通过构建 ERP、WMS 系统之间标准通信协议来实现不同系统之间的打通;对商家…...
RFID软件:简介、功能和应用范围
在当今快节奏的商业环境中,RFID(射频识别)技术已经成为物流、供应链和库存管理等领域中不可或缺的工具。本文将向您介绍RFID软件的基本知识,探讨其功能和广泛应用的范围。 第一部分:RFID软件简介 RFID软件是一种应用…...
Android 逆向之安全防护基本策略
对抗反编译 混淆 使用混淆主要可以减小包的大小。混淆对于安全保护来说,只是增加了阅读难度而已。混淆不会把关键代码混淆掉,比如MainActivity,Application等,可以通过分析smali和阅读jar包定位代码。 资源混淆也是换汤不换药&…...
基站机房:保障通信网络稳定,如何解决安全隐患?
基站机房作为无线通信网络的关键组成部分,承载着大量的网络设备和通信设施,对于运营商和通信服务提供商来说具有重要意义。 无论是大型运营商还是通信服务提供商,动环监控系统都将成为他们成功运营和管理通信网络的关键工具。 客户案例 案例…...
sqlmap -os-shell 使用方法
一、burp suite抓包。 如上图所示,红框处很明显是一个传参点,我们就在这个页面抓包。 抓到包之后将内容保存到桌面的1000.txt文件下。 二、sqlmap跑包。 打开sqlmap跑包。 python sqlmap.py -r C:\Users\16434\Desktop\1000.txt -dbmsmysql --os-shell…...
Go语言并发之Select多路选择操作符
1、Go语言并发之Select多路选择操作符 select 是类 UNIX 系统提供的一个多路复用系统 API,Go 语言借用多路复用的概念,提供了 select 关键字,用 于多路监听多个通道。当监听的通道没有状态是可读或可写的,select 是阻塞的&#…...
黄金回收小程序开发功能有哪些?
一、用户端: 1、实时查询:通过对接三方接口实现实时金价动态查看; 2、多种类珠宝实时回收:小程序支持多品类珠宝的实时回收包含黄金饰品、金条、铂金、K金、白银等,同步实现价格实时更新; …...
网站建设个人总结/在百度平台如何做营销
小编觉得吧证件照这种东西就有一种“让人露出真面目”的魔力身份证照片更是证件照里的重灾区超过八成的网友表示身份证照是世界上最丑的照片它掀起你的减龄刘海将卡姿兰大眼睛照成死鱼眼让你的脸看起来像被车碾过一样又平又宽很多人对身份证照深恶痛绝绞尽脑汁想换照片某市民谎…...
宁波企业做网站哪家好/英文网站seo
问题现象: 请一段开发个项目 程序调试全部通过但测试时出现个问题 “out of memory” 在长时间运行时!后来终于解决 :很简单其实就是object.create时对象没有释放。 代码如下: function SplitString(const Source,ch:string):TStr…...
百度网站客服电话/广告接单平台app
一:dijkstra算法时间复杂度,用优先级队列优化的话,O((MN)logN)求单源最短路径,要求所有边的权值非负。若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的。 设road[i][j]表示相邻的…...
网站怎么做全屏滚动/网络优化工程师吃香吗
一、标准制修订 2020年,完成标准制修订数量418项,较上年增加47项。其中,国家标准31项,较上年减少7项;行业标准103项,较上年增加6项;地方标准204项,较上年增加39项;团体标…...
自己做h5网站/全国疫情高峰感染高峰
与Non-mutating Algorithms相比,变易算法能修改容器元素数据,可进行序列数据的复制、交换、替换、填充、移除、旋转、随机抖动、分割。还是参考叶至军的那本书以及网站Cplusplus.com copy 元素复制。该函数用于容器间元素拷贝,将迭代器区间[…...
可以做旅行计划的网站/网络宣传
当我们平时清理电脑文件时误删了文件,而想要恢复误删文件就不知道怎么处理了。下面与大家分享用diskgenius分区工具来恢复删除文件,一起来看看如何 恢复文件 吧。1、首先,我们提前制作好u启动u盘启动盘。接着将该u盘启动盘插入电脑usb接口&am…...