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

为了获取当前设备用户已安装的所有应用程序,
一般来讲有两种方案. 一种是通过查询 “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.mfbz.cn/a/366859.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Opencv(C++)学习 之RV1126平台的OPENCV交叉编译

本文特点&#xff1a;网上已经有了很多opencv移植RV1106的文章&#xff0c;本文主要记录基于cmake-gui编译&#xff0c;碰到的报错&#xff0c;及解决报错问题的方法&#xff0c;同时简单总结一些配置项相关的知识。 一、环境&#xff1a; ubuntu18 x64 RV1126交叉编译工具链 …

用HTML5 + JavaScript实现下雪效果

用HTML5 JavaScript实现下雪效果 下面是用HTML5 JavaScript实现下雪效果示例&#xff0c;展示了如何使用 HTML5 的 <canvas> 元素以及 JavaScript 来创建下雪效果。效果如下&#xff1a; 源码如下&#xff1a; <!DOCTYPE html> <html lang"en">…

逸学区块链【solidity】真随机数

参考Get a Random Number | Chainlink Documentation 但是很贵&#xff0c;价格 Gas Price&#xff1a;当前gas价格&#xff0c;根据网络状况而波动。Callback gas &#xff1a;返回您所请求的随机值时&#xff0c;回调请求消耗的gas 量。验证gas &#xff1a;量gas 用于验证…

应用层协议 ——— HTTP协议

应用层协议 ——— HTTP协议 HTTP简介认识URL二、登录信息三、服务器地址四、服务器端口号五、带层次的文件路径六、查询字符串七、片段标识符urlencode和urldecodeHTTP协议格式HTTP请求协议格式HTTP的方法HTTP的状态码HTTP常见的HeaderHTTPS VS HTTP对称加密 VS 非对称加密 HT…

Unity | YooAssetV2.1.0 + HybridCLR热更新

目录 一、项目更改 二、使用YooAsset热更 1.资源配置 2.资源构建 3.将两个文件夹下的资源上传CDN服务器 4.修改代码 5.运行效果 本文记录利用YooAssetHybridCLR来进行资源和dll的更新。YooAsset使用的是新版V2.1.0。相比于旧版&#xff0c;dll(原生文件)和资源要建两个p…

zabbix添加主机

zabbix添加主机 查看ip地址 [rootershi ~]# yum -y install net-tools [rootershi ~]# ifconfig eth0 |grep netmask |cut -d " " -f 10 192.168.88.20被监控主机安装zabbix-agent [root20 ~]# mount /dev/cdrom /mnt [root20 ~]# yum -y install wget [root20 ~]…

conda虚拟环境基础

【一文搞定最新版Anaconda】Win11 安装 Anaconda&#xff08;2023.9&#xff09;详解&#xff08;不删除旧版情况下下载、安装、注册、登录、设置环境变量、迁移旧环境、配置修改换源等&#xff09;连接Pycharm_win11安装anaconda-CSDN博客 conda命令大全&#xff08;create/in…

消息总线在微服务中的应用

直连式配置中心 上一篇文章介绍了 Spring Cloud 中的分布式配置组件 Config&#xff0c;每个服务节点可以从Config Server 拉取外部配置信息。但是似乎还有一个悬而未决的问题&#xff0c;那就是当服务节点数量非常庞大的时候&#xff0c;我们不可能一台一台服务器挨个去手工触…

2024Node.js零基础教程(小白友好型),nodejs新手到高手,(四)NodeJS入门——网络基础概念

041_网络基础概念_IP的介绍 hello&#xff0c;大家好&#xff0c;我们来一起认识一下IP。 在开始介绍 IP 之前&#xff0c;我们首先来介绍一个场景&#xff0c;方便大家去理解 IP 这个概念。比如这会儿强哥正在成都&#xff0c;然后还有另外一个小伙伴&#xff0c;谁呢&#x…

CodeFuse成功支持通义千问算法大赛,评测方案已开源

前段时间&#xff0c; 首届通义千问AI挑战赛成功举办&#xff0c;CodeFuse 为大赛提供技术支持&#xff0c;模型微调框架 MFTCoder 和 CodeFuseEval 评测框架为大赛保驾护航&#xff0c;助力大赛圆满完成。我们基于leetcode 阿里和蚂蚁最新面试题库建设了“模型赛马”在线打榜的…

25.云原生之ArgoCD-app of apps模式

文章目录 app of apps 模式介绍app如何管理apphelm方式管理kustomize方式管理 app of apps 模式介绍 通过一个app来管理其他app&#xff0c;当有多个项目要发布创建多个app比较麻烦&#xff0c;此时可以创建一个管理app&#xff0c;管理app创建后会创建其他app。比较适合项目环…

Ansible基础及常用模块

目录 1.前言 Ansible Ansible的特性 2.ansible环境安装部署 管理端安装ansible(192.168.88.22) ansible目录结构 配置主机清单 配置密钥对验证 3.ansible命令行模块 command 模块 shell 模块 ​编辑cron 模块 user 模块 group 模块 copy 模块 file 模块 hostn…

爱上算法:每日算法(24-2月2号)

&#x1f31f;坚持每日刷算法&#xff0c;将其变为习惯&#x1f91b; 题目链接&#xff1a;101. 对称二叉树 最开始肯定是比较简单的想法&#xff0c;就是遍历左右节点呀&#xff0c;不相等我就直接返回false。 但是这样错了&#xff0c;我们要的是以根节点为轴&#xff0c;而…

如何保证MySQL和Redis中的数据一致性?

文章目录 前言一、缓存案例1.1 缓存常见用法1.2 缓存不一致产生的原因 二、解决方案2.1 先删除缓存&#xff0c;再更新数据库2.2 先更新数据库&#xff0c;删除缓存2.3 只更新缓存&#xff0c;由缓存自己同步更新数据库2.4 只更新缓存&#xff0c;由缓存自己异步更新数据库2.5 …

Unity_使用Shader实现玻璃和镜面效果

效果图如下&#xff1a; 玻璃效果图 镜面效果图 Step1 搭建场景→镜子使用Quad代替&#xff0c;放置在需要反射的墙面→创建新的材质和Shader Step2 墙壁外创建Camera&#xff0c;用来渲染物体后方的视图→创建RenderTexture&#xff0c;赋于该相机 Step3 Shader的编写如下…

如何使用本地私有NuGet服务器

写在前面 上一篇介绍了如何在本地搭建一个NuGet服务器&#xff0c; 本文将介绍如何使用本地私有NuGet服务器。 操作步骤 1.新建一个.Net类库项目 2.打包类库 操作后会生成一个.nupkg文件&#xff0c;当然也可以用dotnet pack命令来执行打包。 3.推送至本地NuGet服务器 打开命…

来看看Tomcat和Web应用的目录结构

在前面两篇大致了解了Tomcat的架构和运行流程&#xff0c;以及Tomcat应用中的web.xml。 聊一聊Tomcat的架构和运行流程&#xff0c;尽量通俗易懂一点-CSDN博客 来吧&#xff0c;好好理解一下Tomcat下的web.xml-CSDN博客 那接下来&#xff0c;再看看Tomcat的目录&#xff0c;…

el-table点击某一行选中改变背景色且执行方法

elementUI table表格点击某一行选中并且改变背景色 使用:row-style"rowStyle"及row-click“selectRow”&#xff1a; 其中 selectRow 方法中&#xff1a; row 输出&#xff1a;当前行的内容 column 输出&#xff1a;当前列的信息 event 输出&#xff1a;当前事件 …

Unknown custom element:<xxx>-did you register the component correctly解决方案

如图所示控制台发现了爆红&#xff08;大哭&#xff09;&#xff1a; 报错解释&#xff1a; 当我们看到报错时&#xff0c;我们需要看到一些关键词&#xff0c;比如显眼的“component”和“name”这两个单词&#xff0c; 因此我们就从此处切入&#xff0c;大概与组件有关系。…

洛谷 B3635 硬币问题(DP入门)

[题目概述] 今有面值为 1、5、11 元的硬币各无限枚。 想要凑出 n 元&#xff0c;问需要的最少硬币数量。 输入格式 仅一行&#xff0c;一个正整数 n。 输出格式 仅一行&#xff0c;一个正整数&#xff0c;表示需要的硬币个数。 输入 15输出 3输入 12输出 2样例解释 …
最新文章