[.NET] 查询当前已安装所有 Win32 与 UWP 应用

news2024/12/22 11:20:01

为了获取当前设备用户已安装的所有应用程序,
一般来讲有两种方案. 一种是通过查询 “shell:AppsFolder” 目录下所有项,
一种是从开始菜单中获取所有快捷方式, 然后加上查询所有已安装的 UWP 应用, 最后得到总列表.

如需代码参考, 请看 github.com/SlimeNull/WindowsAppsQuery 以及 github.com/OrgEleCho/CurvaLauncher 中的 ‘Run Applictions’ 部分.

查询 Win32 应用

查询已安装 Win32 应用程序, 目前有两种常用方式.

  • 查询注册表中 “卸载” 的子键
  • 搜寻开始菜单目录

通过注册表查询

注册表中, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall 中列举了当前计算机的所有可卸载应用程序
如果是仅安装在当前用户中的应用程序, 则存储在 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall 下.

根据微软官方文档 Win32/MSI/卸载注册表键 中介绍, 上述注册表键的子键中, 属性与 MSI 中的属性有以下对应关系:

Windows Installer 属性
DisplayNameProductName 属性
DisplayVersion派生自 ProductVersion 属性
PublisherManufacturer 属性
VersionMinor派生自 ProductVersion 属性
VersionMajor派生自 ProductVersion 属性
Version派生自 ProductVersion 属性
HelpLinkARPHELPLINK 属性
HelpTelephoneARPHELPTELEPHONE 属性
InstallDate此产品最后一次接受服务的时间。 每次从产品应用或移除修补程序或使用 /v 命令行选项修复产品时,都会替换此属性的值。 如果产品未接受任何修复或修补,则此属性包含在此计算机上安装该产品的时间。
InstallLocationARPINSTALLLOCATION 属性
InstallSourceSourceDir 属性
URLInfoAboutARPURLINFOABOUT 属性
URLUpdateInfoARPURLUPDATEINFO 属性
AuthorizedCDFPrefixARPAUTHORIZEDCDFPREFIX 属性
CommentsARPCOMMENTS 属性
提供给“添加或删除程序”控制面板的注释。
ContactARPCONTACT 属性
提供给“添加或删除程序”控制面板的联系人。
EstimatedSize由 Windows Installer 确定和设置。
LanguageProductLanguage 属性
ModifyPath由 Windows Installer 确定和设置。
ReadmeARPREADME 属性
提供给“添加或删除程序”控制面板的自述文件。
UninstallString由 Windows Installer 确定和设置。
SettingsIdentifierMSIARPSETTINGSIDENTIFIER 属性

通过开始菜单目录查询

如果是搜寻开始菜单目录, 则是以下路径:

当前用户的开始菜单程序列表: C:\Users\slime\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
所有用户公共的开始菜单程序列表: C:\ProgramData\Microsoft\Windows\Start Menu\Programs

通过 Environment.GetFolderPath 方法即可获取上述两个路径. 参数分别是 Programs 枚举和 CommonPrograms 枚举

所有正常安装的 Win32 程序一般都会创建开始菜单项, 所以可以通过这个粗略的获取所有 Win32 应用程序列表.

然后, 所有的应用程序都是通过快捷方式存在于开始菜单中的, 所以你需要有解析快捷方式的方案, 第一种是通过 Shell API, 但这挺麻烦的.
笔者这边推荐的方式是通过 Securify.ShellLink 这个库来解析.

// 从文件读取快捷方式信息
var shortcut = Shortcut.ReadFromFile(@"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Paint.lnk");

需要注意的是, 快捷方式的结构是很复杂的, 你想要的 “目标路径” 可能在快捷方式中不一定哪个属性中, 可以参考以下的 Fallback 顺序:

  • ExtraData.EnvironmentVariableDataBlock.TargetUnicode
  • LinkTargetIDList.Path
  • LinkTargetIDList.DisplayName

在获取这些属性时, 记得对属性进行判空, 以免遇到 NullReferenceException

如果要获取其他信息, 诸如命令行参数, 工作目录, 图标此类, 直接获取快捷方式的对应属性即可.
至于以管理员权限身份运行, 这个直接判断 LinkFlags 是否有 RunAsUser 这个标志就可以了.


查询 UWP 应用

查询已安装的 UWP 应用程序, 有你能轻易在网上找到的三种查询方式, 以及笔者自己找到的第四种方式.

  • 使用 Windows SDK 中的 PackageManager 类进行最正规的查询
  • 使用防火墙的 API, 通过查询 UWP 容器列表, 实现查找所有 UWP 应用
  • 调用 PowerShell, 执行 Get-AppxPackage
  • 查注册表

通过 Windows SDK 查询

第一种方案, PackageManager 并不是你可以直接使用到的类, 而是需要使用 Windows SDK 才可以用的. 对项目的配置方式也简单.

如果你是 .NET CoreCLR 的项目, 例如 .NET8 的, 那么直接右键项目, 打开属性, 将目标 OS 改为 Windows, 目标 OS 版本改为你的目标系统版本, 支持的 OS 版本则是最低 OS 版本.
这里建议 1904 什么的, 例如 10.0.19041.0, 选择更低的版本, 可以支持更多系统.

如果你是 .NET Framework 的项目, 那么你需要知道你的 Windows SDK 安装在哪, 在它下面会有一个 Windows.winmd 动态链接库, 它大概在一个叫 ‘UnionMetadata’ 的目录下, 右键你的项目, 添加引用, 然后把这个 winmd 库添加进去即可.

配置完之后, 你就可以使用 ‘PackageManager’ 进行查询了, 查询当前用户的所有已安装的包不需要什么特殊权限, 代码如下:

using System.Security.Principal;
using Windows.Management.Deployment;

PackageManager packageManager = new PackageManager();
var packages = packageManager.FindPackagesForUser(WindowsIdentity.GetCurrent().User!.Value);

通过防火墙 API 查询

第二种方案, 通过 WinAPI NetworkIsolationEnumAppContainers 枚举所有的应用程序容器, 进而实现查询 UWP 应用程序.

非托管函数声明以及结构体定义

public static class NativeDll
{
    /// <summary>
    /// 加载资源文本
    /// </summary>
    /// <param name="pszSource">资源标识符</param>
    /// <param name="pszOutBuf">输出的缓冲区</param>
    /// <param name="cchOutBuf">缓冲区大小</param>
    /// <param name="ppvReserved">保留, 固定0</param>
    /// <returns></returns>
    [DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, ThrowOnUnmappableChar = true)]
    public static extern unsafe uint SHLoadIndirectString(string pszSource, ref char pszOutBuf, int cchOutBuf, IntPtr ppvReserved);

    [DllImport("FirewallAPI.dll")]
    public static extern uint NetworkIsolationEnumAppContainers(uint Flags, out uint pdwCntPublicACs, out IntPtr ppPublicACs);

    [DllImport("FirewallAPI.dll")]
    public static extern void NetworkIsolationFreeAppContainers(IntPtr pACs);

    public static string? GetIndirectString(string str)
    {
        Span<char> buffer = stackalloc char[4096];
        if (SHLoadIndirectString(str, ref buffer[0], buffer.Length, 0) != 0)
        {
            return null;
        }
        return new string(buffer.TrimEnd('\0'));
    }
}

internal struct INET_FIREWALL_APP_CONTAINER
{
    internal IntPtr appContainerSid;

    internal IntPtr userSid;

    [MarshalAs(UnmanagedType.LPWStr)]
    internal string appContainerName;

    [MarshalAs(UnmanagedType.LPWStr)]
    internal string displayName;

    [MarshalAs(UnmanagedType.LPWStr)]
    internal string description;

    internal INET_FIREWALL_AC_CAPABILITIES capabilities;

    internal INET_FIREWALL_AC_BINARIES binaries;

    [MarshalAs(UnmanagedType.LPWStr)]
    internal string workingDirectory;

    [MarshalAs(UnmanagedType.LPWStr)]
    internal string packageFullName;
}

internal struct INET_FIREWALL_AC_CAPABILITIES
{
    public uint count;

    public IntPtr capabilities;
}

internal struct INET_FIREWALL_AC_BINARIES
{
    public uint count;

    public IntPtr binaries;
}

简单封装一下应用容器类:

using System.Runtime.InteropServices;

internal record class AppContainer
{
    public string DisplayName { get; set; }
    public string Description { get; set; }
    public string PackageFullName { get; set; }
    public string[] Binaries { get; set; } = [];
    public string WorkingDirectory { get; set; }

    public AppContainer(INET_FIREWALL_APP_CONTAINER info)
    {
        PackageFullName = info.packageFullName;
        WorkingDirectory = info.workingDirectory;
        uint HRESULT = 0;

        Span<char> buffer = stackalloc char[4096];
        HRESULT = NativeDll.SHLoadIndirectString(info.displayName, ref buffer[0], buffer.Length, 0);
        if (HRESULT == 0)
            DisplayName = new string(buffer.TrimEnd('\0'));
        else
            DisplayName = info.displayName;
        buffer.Clear();

        if (NativeDll.SHLoadIndirectString(info.description, ref buffer[0], buffer.Length, 0) == 0)
            Description = new string(buffer.TrimEnd('\0'));
        else
            Description = info.description;
        buffer.Clear();

        INET_FIREWALL_AC_BINARIES inet_FIREWALL_AC_BINARIES = info.binaries;
        if (inet_FIREWALL_AC_BINARIES.count > 0 && inet_FIREWALL_AC_BINARIES.binaries != 0)
        {
            Binaries = new string[inet_FIREWALL_AC_BINARIES.count];
            for (int i = 0; i < inet_FIREWALL_AC_BINARIES.count; i++)
            {
                var str = Marshal.PtrToStringUni(Marshal.ReadIntPtr(inet_FIREWALL_AC_BINARIES.binaries + nint.Size * i));
                ;
                if (str?.StartsWith(@"\\?\") == true)
                {
                    str = str[4..];
                }

                if (str != null)
                {
                    Binaries[i] = str;
                }
            }
        }
    }
}

调用:

var enumResult = NativeDll.NetworkIsolationEnumAppContainers(0, out var num, out var ptr);
if (enumResult != 0)
    throw new Win32Exception((int)enumResult, Marshal.GetLastPInvokeErrorMessage());

List<AppContainer> appContainers = new((int)num);
for (int i = 0; i < num; i++)
{
    var info = Marshal.PtrToStructure<INET_FIREWALL_APP_CONTAINER>(ptr + Marshal.SizeOf<INET_FIREWALL_APP_CONTAINER>() * i);
    appContainers.Add(new AppContainer(info));
}

NativeDll.NetworkIsolationFreeAppContainers(ptr);

foreach (var appContainer in appContainers)
    Console.WriteLine(appContainer);

这种方式调用也有一点缺点, 你无法获取应用程序的图标.
以上代码来自于 Ilyfairy

通过注册表查询

第三种方案, 在注册表的 “HKEY_CURRENT_USER\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages” 下, 你可以看到所有已安装包的部分信息

其中 “PackageId” 是该包的 ID, 而 “PackageRootFolder” 直接给出了包的根文件夹, 而这个文件夹中, 我们可以找到包的清单文件 “AppxManifest.xml”

清单中列出了所有该包内所有应用程序的信息, 包括应用名称, LOGO, 以及该应用是否对用户可见(展示在开始菜单中)

部分应用程序, 在 Manifest 中, 应用程序的显示名称是直接以文本形式存在的, 但是也有一些是通过资源路径的形式存在的. 我们需要加载这个文本资源, 来获取其实际文本.

这里再次用到了 “SHLoadIndirectString” 这个 API:

[DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, ThrowOnUnmappableChar = true)]
static extern unsafe uint SHLoadIndirectString(string pszSource, ref char pszOutBuf, int cchOutBuf, IntPtr ppvReserved);

通过一个键获取其对应所有应用程序的逻辑如下:

// 类型定义
public abstract record class AppInfo
{
    private string? _queryRoot;

    public string Name { get; set; } = string.Empty;

    public string QueryRoot { get => _queryRoot ?? Name; set => _queryRoot = value; }

    public string[]? AlterQueryRoots { get; set; }
}

public record class UwpAppInfo : AppInfo
{
    public string PackageId { get; set; } = string.Empty;
    public string FamilyID { get; set; } = string.Empty;
    public string PackageRootFolder { get; set; } = string.Empty;
    public string AppxManifestPath => Path.Combine(PackageRootFolder, "AppxManifest.xml");

    public string ApplicationId { get; set; } = string.Empty;
    public UwpAppLogo[] AppLogos { get; set; } = Array.Empty<UwpAppLogo>();

    public required string OriginRegistryKeyName { get; set; } = string.Empty;
    
    public record struct UwpAppLogo(int Size, string Path);
}

// 方法定义
private IEnumerable<UwpAppInfo> GetUwpApps(RegistryKey subKey)
{
    if (subKey.GetValue("PackageID") is not string packageId)
        yield break;

    string packageFamilyId = Regex.Replace(packageId, "_.*__", "_");

    if (subKey.GetValue("PackageRootFolder") is not string packageRootFolder)
        yield break;

    string packageManifestPath = Path.Combine(packageRootFolder, "AppxManifest.xml");

    if (!System.IO.File.Exists(packageManifestPath))
        yield break;

    var appxManifestContent = File.ReadAllText(packageManifestPath);

    XmlDocument xml = new();
    xml.LoadXml(appxManifestContent);

    XmlNamespaceManager nsManager = new XmlNamespaceManager(xml.NameTable);//这一步实例化一个xml命名空间管理器
    nsManager.AddNamespace("ns", "http://schemas.microsoft.com/appx/manifest/foundation/windows10");
    nsManager.AddNamespace("uap", "http://schemas.microsoft.com/appx/manifest/uap/windows10");

    var idNode = xml.SelectSingleNode("/ns:Package/ns:Identity", nsManager);
    var appNodes = xml.SelectNodes("/ns:Package/ns:Applications/ns:Application", nsManager);

    if (appNodes == null)
        yield break;

    foreach (XmlNode appNode in appNodes)
    {
        var visualElementsNode = appNode.SelectSingleNode("uap:VisualElements", nsManager);

        if (visualElementsNode == null)
            continue;

        if (visualElementsNode?.Attributes?["AppListEntry"]?.Value is string appListEntry &&
            appListEntry.Equals("none", StringComparison.OrdinalIgnoreCase))
            continue;

        if (visualElementsNode?.Attributes?["DisplayName"]?.Value is not string displayName)
            continue;

        UwpAppInfo info = new()
        {
            OriginRegistryKeyName = Path.GetFileName(subKey.Name)
        };

        info.PackageId = packageId;
        info.FamilyID = packageFamilyId;
        info.PackageRootFolder = packageRootFolder;

        var logoNode = xml.SelectSingleNode("/ns:Package/ns:Properties/ns:Logo", nsManager);

        if (appNode == null ||
            appNode.Attributes?["Id"]?.Value is not string appId)
            continue;

        info.ApplicationId = appId;

        if (displayName.StartsWith(resourcePrefix))
        {
            string resourcePath = displayName.Substring(resourcePrefix.Length);

            // 非绝对路径, 且能够找到 Identity 节点
            if (!resourcePath.StartsWith("//") &&
                idNode?.Attributes?["Name"]?.Value is string id)
            {
                // 不是引用其他资源, 则添加 'Resource' 前缀
                if (!resourcePath.Contains("Resources"))
                    resourcePath = $"Resources/{resourcePath}";

                // 转为绝对资源
                resourcePath = $"//{id}/{resourcePath}";
            }

            string resourceStr = $"@{{{packageId}?ms-resource:{resourcePath}}}";
            uint errCode = SHLoadIndirectString(resourceStr, ref displayNameBuffer[0], displayNameBuffer.Length, 0);

            int endIndex = Array.IndexOf(displayNameBuffer, '\0');
            displayName = new string(displayNameBuffer, 0, endIndex);

            if (errCode != 0)
                continue;
        }

        if (string.IsNullOrWhiteSpace(displayName))
            continue;

        info.Name = displayName;

        string? logoPath = logoNode?.InnerText;

        if (logoPath is not null)
        {
            var logoFullPath = Path.Combine(info.PackageRootFolder, logoPath);

            var logoFileName = Path.GetFileNameWithoutExtension(logoPath);
            var logoExtension = Path.GetExtension(logoPath);
            var logoFilesDir = Path.GetDirectoryName(logoFullPath) ?? ".";
            var logoFilesPattern = $"{logoFileName}*{logoExtension}";

            if (Directory.Exists(logoFilesDir))
            {
                var regex = new Regex($@"{Regex.Escape(logoFileName)}(\.scale-(?<scale>\d+))?{Regex.Escape(logoExtension)}");

                List<UwpAppInfo.UwpAppLogo> logos = new();
                foreach (var searchedLogoFile in Directory.EnumerateFiles(logoFilesDir, logoFilesPattern))
                {
                    var searchedLogoFileName = Path.GetFileName(searchedLogoFile);
                    var match = regex.Match(searchedLogoFileName);

                    if (!match.Success)
                        continue;

                    int scale = 1;
                    if (int.TryParse(match.Groups["scale"].Value, out var parsedScale))
                        scale = parsedScale;

                    logos.Add(new UwpAppInfo.UwpAppLogo(44 * scale, searchedLogoFile));
                }

                info.AppLogos = logos.ToArray();
            }
        }

        yield return info;
    }
}

上述代码来自于 github.com/OrgEleCho/CurvaLauncher 中 ‘Run Application’ 的 UWP 应用索引逻辑


查询 AppsFolder

使用 ‘运行’ 对话框, 打开 shell:AppsFolder, 你可以打开一个同时包含 Win32 和 UWP 应用的文件夹,
但这个文件夹实际上是不存在的, 它是 Shell 抽象出来的, 本质上是开始菜单的 CommonProgramsPrograms 目录, 以及所有 UWP 应用程序加起来, 构成的一个虚拟目录.

不过, 我们仍然可以通过编程手段来遍历这个 “目录”, 获取所有已安装的应用程序. 下面是通过 COM 查询 AppsFolder 的 C++ 代码.

#include <Windows.h>
#include <ShlObj.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
using namespace std;

int main() {
    if (_setmode(_fileno(stdout), _O_U16TEXT) == -1) {
        wcout << L"Failed to set stdout to UTF-16" << endl;
        return -1;
    }

    HRESULT result;

    result = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (FAILED(result)) {
        wcout << L"CoInitializeEx failed: " << hex << result << endl;
        return -1;
    }

    IShellItem* appsFolder;
    result = SHGetKnownFolderItem(FOLDERID_AppsFolder, KF_FLAG_DEFAULT, nullptr, IID_PPV_ARGS(&appsFolder));
    if (FAILED(result)) {
        wcout << L"SHGetKnownFolderItem failed: " << hex << result << endl;
        return -1;
    }

    IEnumShellItems* appsFolderEnum;
    result = appsFolder->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&appsFolderEnum));
    if (FAILED(result)) {
        wcout << L"BindToHandler failed: " << hex << result << endl;
        return -1;
    }

    for (IShellItem* app; appsFolderEnum->Next(1, &app, nullptr) == S_OK; app->Release()) {
        // 获取应用程序的名称
        PWSTR name;
        result = app->GetDisplayName(SIGDN_NORMALDISPLAY, &name);
        if (FAILED(result)) {
            wcout << L"GetDisplayName failed: " << hex << result << endl;
            return -1;
        }
        wcout << name << L" -> ";
        CoTaskMemFree(name);

        // 获取应用程序的路径
        PWSTR path;
        result = app->GetDisplayName(SIGDN_PARENTRELATIVEPARSING, &path);
        if (FAILED(result)) {
            wcout << L"GetDisplayName failed: " << hex << result << endl;
            return -1;
        }
        wcout << path << L" = ";
        CoTaskMemFree(path);

        // 获取应用程序的图标
        IShellItemImageFactory* imageFactory;
        result = app->QueryInterface(IID_PPV_ARGS(&imageFactory));
        if (FAILED(result)) {
            wcout << L"QueryInterface failed: " << hex << result << endl;
            return -1;
        }
        HBITMAP bitmap;
        result = imageFactory->GetImage(SIZE { 64, 64 }, SIIGBF_ICONONLY, &bitmap);
        if (FAILED(result)) {
            wcout << L"GetImage failed: " << hex << result << endl;
            return -1;
        }
        wcout << bitmap << endl;
        DeleteObject(bitmap);
        imageFactory->Release();
    }

    appsFolderEnum->Release();
    appsFolder->Release();
    CoUninitialize();

    // 原神, 启动!
    // ShellExecuteW(nullptr, L"open", L"explorer", L"shell:AppsFolder\\Kingsoft.Office.ET", nullptr, SW_NORMAL);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1429180.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

拥抱个人成长与社会进步:自我认知与开放心态的相互影响

拥抱个人成长与社会进步&#xff1a;自我认知与开放心态的相互影响 Embracing Personal Growth and Societal Progress: The Interplay of Self-Awareness and Open-mindedness 一、引言 I. Introduction 在当今急速发展的时代&#xff0c;个人成长与社会进步交织在一起&…

必收藏面试题:什么是SQL注入?以及Mybatis中#号和$号之间的区别?

本文大纲&#xff1a; 先分析什么是SQL注入漏洞&#xff1f;再分析#{}和${}之间的区别再基于MybatisPlus做验证再介绍#{}和${}的使用场景 什么是SQL注入&#xff1f; 先看两段代码&#xff0c;假如id的值为字符串"100"&#xff0c;大家可以顺便想想每段代码最后拼…

玩美移动为花西子海外官网打造AR虚拟试妆决方案

全球领先的增强现实&#xff08;AR&#xff09;及人工智能&#xff08;AI&#xff09;美妆科技领导者及玩美系列APP开发商——玩美移动&#xff08;纽交所代码&#xff1a;PERF&#xff09;于近日宣布携手知名美妆品牌花西子&#xff0c;在其线海外官方网页提供多项彩妆虚拟试妆…

链表——C语言——day17

链表 链表是一种常见的重要的数据结构。它是动态地进行存储分配的一种结构。在用数组存放数据时&#xff0c;必须事先定义固定的长度&#xff08;即元素个数&#xff09;。链表则没有这种缺点&#xff0c;它根据需要开辟内存单元。 链表有一个“头指针“变量&#xff0c;图中…

电脑怎么录屏?打造专业级视频内容!

随着科技的进步&#xff0c;电脑已经深入到我们的日常生活和工作中。而在这个数字时代&#xff0c;录制屏幕内容变得日益重要。无论是制作教程、分享游戏技巧&#xff0c;还是记录重要的演示&#xff0c;录屏都是一个不可或缺的功能。可是电脑怎么录屏呢&#xff1f;本文将深入…

ESU毅速丨3D打印随形水路在模具制造中应用越来越多

在模具制造领域&#xff0c;冷却水路的设计和制造至关重要&#xff0c;它直接影响到产品的质量和生产效率。3D打印随形水路在设计和制造上相比传统模具水路有哪些优势&#xff0c;为什么越来越受到企业追捧&#xff1f; 传统模具水路通常是直线或规则形状的通道&#xff0c;设计…

高宇辰:打造“π”型人才 | 提升之路系列(七)

导读 为了发挥清华大学多学科优势&#xff0c;搭建跨学科交叉融合平台&#xff0c;创新跨学科交叉培养模式&#xff0c;培养具有大数据思维和应用创新的“π”型人才&#xff0c;由清华大学研究生院、清华大学大数据研究中心及相关院系共同设计组织的“清华大学大数据能力提升项…

ADAS感知摄像头的分辨率与帧率选择分析

说明&#xff1a;可以作为对智能驾驶爱好者对摄像头参数理解或者从业工程人员对设计硬件选型参考 前言 在当前智能驾驶中&#xff0c;基于摄像头的 ADAS 因其应用、更高的可靠性和对新要求的适应性而被广泛采用。 ADAS 摄像头通常部署在汽车的前部、侧面和后部&#xff0c;提…

计算视图里的projection和aggregation节点区别

Projection 和 Aggregation到底有什么区别&#xff1f; 看名字就能看出来的。 那么在什么场景下用呢&#xff1f; 1. Projection就是投影&#xff0c;也就是说你本来的源里有什么&#xff0c;就直接给你拿出来。 除了这个&#xff0c;它使用的场景就是&#xff1a; 只映射需…

基于JAVA+SpringBoot+Vue的前后端分离的仓库管理系统(进销存)系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着全球经济的不断发…

linux 下mongodb7版本怎么连?

概述&#xff1a;linux下的mongodb7版本默认是没有安装客户端的&#xff0c;需要下载shell客户端才能连&#xff0c;下载之后解压&#xff0c;不需要编译&#xff0c;进入bin目录就能自己运行&#xff0c;。 安装&#xff1a; linux 下mongodb7版本没有安装客户端需要当地下载…

vscode实时预览markdown效果

安装插件 Markdown Preview Enhanced 上面是搜索框 启动预览 右键->Open Preview On the Side 效果如下&#xff1a; 目录功能 目录功能还是使用gitee吧 push后使用gitee&#xff0c;gitee上markdown支持侧边生成目录

数据结构篇-05:哈希表解决字母异位词分组

本文对应力扣高频100 ——49、字母异位词分组 哈希表最大的特点就是它可以把搜索元素的时间复杂度降到O(1)。这一题就是要我们找到 “字母异位词” 并把它们放在一起。 “字母异位词”就是同一个单词中字母的不同组合形式。判断“字母异位词”有两个视角&#xff1a;1、所含字…

UE4 C++ 静态加载类和资源

静态加载类和资源&#xff1a;指在编译时加载&#xff0c;并且只能在构造函数中编写代码 .h //增加所需组件的头文件 #include "Components/SceneComponent.h" //场景组件 #include "Components/StaticMeshComponent.h" //静态网格体组件 #include &qu…

XXE基础知识整理(附加xml基础整理)

全称&#xff1a;XML External Entity 外部实体注入攻击 原理 利用xml进行读取数据时过滤不严导致嵌入了恶意的xml代码&#xff1b;和xss一样 危害 外界攻击者可读取商户服务器上的任意文件&#xff1b; 执行系统命令&#xff1b; 探测内网端口&#xff1b; 攻击内网网站。 商…

arcgis javascript api4.x加载非公开或者私有的arcgis地图服务

需求&#xff1a; 加载arcgis没有公开或者私有的地图服务&#xff0c;同时还想实现加载时不弹出登录窗口 提示&#xff1a;​ 下述是针对独立的arcgis server&#xff0c;没有portal的应用场景&#xff1b; 如果有portal可以参考链接&#xff1a;https://mp.weixin.qq.com/s/W…

麒麟系统—— openKylin 安装 Maven

麒麟系统—— openKylin 安装 Maven 一、准备工作1. 确保麒麟系统 openKylin 已经安装完毕。2. 确保 java 已经安装完毕 二、下载Maven三、解压 Maven 与环境配置解压配置环境变量验证 最终&#xff1a;介绍配置的其他参数使用 本文将分享如何在麒麟操作系统 openKylin 上安装…

【持续更新】2024牛客寒假算法基础集训营1题解 | JorbanS

文章目录 [A - DFS搜索](https://ac.nowcoder.com/acm/contest/67741/A)[B - 关鸡](https://ac.nowcoder.com/acm/contest/67741/B)[C - 按闹分配](https://ac.nowcoder.com/acm/contest/67741/C)[D - 数组成鸡](https://ac.nowcoder.com/acm/contest/67741/D)[E - 本题又主要考…

go_view同后端集成时的注意事项

goview是一个不错的可视化大屏配置工具;提供了丰富的功能可供调用。 官方地址和文档: https://gitee.com/dromara/go-view https://www.mtruning.club/guide/start/ 同nodejs集成可参考;https://gitee.com/qwdingyu/led (建议–后端集成有api功能,可直接配置sql)同dotne…

1 初识JVM

JVM&#xff08;Java Virtual Machine&#xff09;&#xff0c;也就是 “Java虚拟机”。 对于第三点功能&#xff1a;即时编译 常见的JVM 默认安装在JDK中的虚拟机为HotSpot&#xff1a;可以用“java -version”进行查看