【UWP】让 UWP 自己托管自己 —— Windows SDK 篇

news/2025/1/17 16:06:06/文章来源:https://www.cnblogs.com/wherewhere/p/18446824

众所周知,UWP 使用的窗口模型是 CoreWindow,但是 UWP 本身只是一个应用模型,所以完全可以创建 win32 窗口,那么我们可以不可以创建一个 win32 窗口,然后像 XAML 岛 (XAML Islands) 一样把 XAML 托管上去呢?本篇将讲述如何在 UWP 创建一个 XAML 岛窗口。

示例

首先,XAML 岛会判断当前的应用模型是否为ClassicDesktop,所以我们需要利用Detours劫持AppPolicyGetWindowingModel方法。具体内容如下:

#r "nuget:Detours.Win32Metadata"
#r "nuget:Microsoft.Windows.CsWin32"using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Storage.Packaging.Appx;
using Detours = Microsoft.Detours.PInvoke;/// <summary>
/// Represents a hook for the <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function.
/// </summary>
public sealed partial class HookWindowingModel : IDisposable
{/// <summary>/// The value that indicates whether the class has been disposed./// </summary>private bool disposed;/// <summary>/// The reference count for the hook./// </summary>private static int refCount;/// <summary>/// The value that represents the current process token./// </summary>private const int currentProcessToken = -6;/// <remarks>The original <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function.</remarks>/// <inheritdoc cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/>private static unsafe delegate* unmanaged[Stdcall]<HANDLE, AppPolicyWindowingModel*, WIN32_ERROR> AppPolicyGetWindowingModel;/// <summary>/// Initializes a new instance of the <see cref="HookWindowingModel"/> class./// </summary>public HookWindowingModel(){refCount++;StartHook();}/// <summary>/// Finalizes this instance of the <see cref="HookWindowingModel"/> class./// </summary>~HookWindowingModel(){Dispose();}/// <summary>/// Gets the value that indicates whether the hook is active./// </summary>public static bool IsHooked { get; private set; }/// <summary>/// Gets or sets the windowing model to use when the hooked <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function is called./// </summary>internal static AppPolicyWindowingModel WindowingModel { get; set; } = AppPolicyWindowingModel.AppPolicyWindowingModel_ClassicDesktop;/// <summary>/// Starts the hook for the <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function./// </summary>private static unsafe void StartHook(){if (!IsHooked){using FreeLibrarySafeHandle library = PInvoke.GetModuleHandle("KERNEL32.dll");if (!library.IsInvalid && NativeLibrary.TryGetExport(library.DangerousGetHandle(), nameof(PInvoke.AppPolicyGetWindowingModel), out nint appPolicyGetWindowingModel)){void* appPolicyGetWindowingModelPtr = (void*)appPolicyGetWindowingModel;delegate* unmanaged[Stdcall]<HANDLE, AppPolicyWindowingModel*, WIN32_ERROR> overrideAppPolicyGetWindowingModel = &OverrideAppPolicyGetWindowingModel;_ = Detours.DetourRestoreAfterWith();_ = Detours.DetourTransactionBegin();_ = Detours.DetourUpdateThread(PInvoke.GetCurrentThread());_ = Detours.DetourAttach(ref appPolicyGetWindowingModelPtr, overrideAppPolicyGetWindowingModel);_ = Detours.DetourTransactionCommit();AppPolicyGetWindowingModel = (delegate* unmanaged[Stdcall]<HANDLE, AppPolicyWindowingModel*, WIN32_ERROR>)appPolicyGetWindowingModelPtr;IsHooked = true;}}}/// <summary>/// Ends the hook for the <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function./// </summary>private static unsafe void EndHook(){if (--refCount == 0 && IsHooked){void* appPolicyGetWindowingModelPtr = AppPolicyGetWindowingModel;delegate* unmanaged[Stdcall]<HANDLE, AppPolicyWindowingModel*, WIN32_ERROR> overrideAppPolicyGetWindowingModel = &OverrideAppPolicyGetWindowingModel;_ = Detours.DetourTransactionBegin();_ = Detours.DetourUpdateThread(PInvoke.GetCurrentThread());_ = Detours.DetourDetach(&appPolicyGetWindowingModelPtr, overrideAppPolicyGetWindowingModel);_ = Detours.DetourTransactionCommit();AppPolicyGetWindowingModel = null;IsHooked = false;}}/// <param name="policy">A pointer to a variable of the <a href="https://docs.microsoft.com/windows/win32/api/appmodel/ne-appmodel-apppolicywindowingmodel">AppPolicyWindowingModel</a> enumerated type./// When the function returns successfully, the variable contains the <see cref="WindowingModel"/> when the identified process is current; otherwise, the windowing model of the identified process.</param>/// <remarks>The overridden <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function.</remarks>/// <inheritdoc cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/>[UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]private static unsafe WIN32_ERROR OverrideAppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel* policy){if ((int)processToken.Value == currentProcessToken){*policy = WindowingModel;return WIN32_ERROR.ERROR_SUCCESS;}return AppPolicyGetWindowingModel(processToken, policy);}/// <inheritdoc/>public void Dispose(){if (!disposed && IsHooked){EndHook();}GC.SuppressFinalize(this);disposed = true;}
}

准备工作完成,接下来我们就可以创建窗口了,首先我们需要新创建一个线程,CoreWindow 线程无法新建 XAML 岛,新建线程只需要用Thread就行了。

new Thread(() => { ... });

首先我们需要创建 XAML 岛,这时我们就需要利用上面劫持器来劫持获取应用模型的方法了。

DesktopWindowXamlSource source;
using (HookWindowingModel hook = new())
{source = new DesktopWindowXamlSource();
}

微软并没有单独提供一个 Win32 窗口管理的轮子,如果引用 Windows Forms 就太臃肿了,于是我们需要手动制作一个 Window 类:

/// <summary>
/// Represents a system-managed container for the content of an app.
/// </summary>
public partial class DesktopWindow
{private bool m_bIsClosed = false;private DesktopWindowXamlSource m_source;private IDesktopWindowXamlSourceNative m_native;private readonly HWND m_hwnd;private readonly WNDCLASSEXW m_wndClassEx;/// <summary>/// Initializes a new instance of the <see cref="DesktopWindow"/> class./// </summary>public DesktopWindow(){m_wndClassEx = RegisterDesktopWindowClass(WNDPROC);m_hwnd = CreateDesktopWindow();}/// <summary>/// Gets the event dispatcher for the window./// </summary>public CoreDispatcher Dispatcher { get; private set; }/// <summary>/// Gets the <see cref="DesktopWindowXamlSource"/> to provide XAML for this window./// </summary>public DesktopWindowXamlSource WindowXamlSource{get => m_source;private init{if (m_source != value){m_source = value;if (value != null){Dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;m_native = value.As<IDesktopWindowXamlSourceNative>();m_native.AttachToWindow(m_hwnd);ResizeWindowToDesktopWindowXamlSourceWindowDimensions();}else{m_native = null;}}}}/// <summary>/// Shows the window and activates it./// </summary>public void Show() => _ = PInvoke.ShowWindow(m_hwnd, SHOW_WINDOW_CMD.SW_NORMAL);private LRESULT WNDPROC(HWND hWnd, uint message, WPARAM wParam, LPARAM lParam){switch (message){case PInvoke.WM_PAINT:HDC hdc = PInvoke.BeginPaint(hWnd, out PAINTSTRUCT ps);_ = PInvoke.GetClientRect(hWnd, out RECT rect);_ = PInvoke.FillRect(hdc, rect, new DefaultSafeHandle(PInvoke.GetStockObject(GET_STOCK_OBJECT_FLAGS.WHITE_BRUSH)));_ = PInvoke.EndPaint(hWnd, ps);return new LRESULT();case PInvoke.WM_CLOSE when m_bIsClosed:goto default;case PInvoke.WM_CLOSE:m_bIsClosed = true;goto default;case PInvoke.WM_SIZE:ResizeWindowToDesktopWindowXamlSourceWindowDimensions();return new LRESULT();case PInvoke.WM_CREATE:return new LRESULT();case PInvoke.WM_DESTROY:PInvoke.PostQuitMessage(0);return new LRESULT();default:return PInvoke.DefWindowProc(hWnd, message, wParam, lParam);}}private void ResizeWindowToDesktopWindowXamlSourceWindowDimensions(){if (m_bIsClosed) return;_ = PInvoke.GetClientRect(m_hwnd, out RECT rect);_ = PInvoke.SetWindowPos(m_native.WindowHandle,new HWND(),0, 0,rect.Width, rect.Height,SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE | SET_WINDOW_POS_FLAGS.SWP_NOZORDER | SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW);}
}public partial class DesktopWindow
{private static readonly unsafe HINSTANCE g_hInstance = new((void*)Process.GetCurrentProcess().Handle);// win32 window class name for top-level WinUI desktop windowsprivate const string s_windowClassName = "WinUIDesktopWin32WindowClass";// Default window title for top-level WinUI desktop windowsprivate const string s_defaultWindowTitle = "WinUI Desktop";private static unsafe WNDCLASSEXW RegisterDesktopWindowClass(WNDPROC lpfnWndProc){if (!PInvoke.GetClassInfoEx(new DefaultSafeHandle(g_hInstance), s_windowClassName, out WNDCLASSEXW wndClassEx)){wndClassEx.cbSize = (uint)Marshal.SizeOf(wndClassEx);wndClassEx.style = WNDCLASS_STYLES.CS_HREDRAW | WNDCLASS_STYLES.CS_VREDRAW;wndClassEx.cbClsExtra = 0;wndClassEx.cbWndExtra = 0;wndClassEx.hCursor = PInvoke.LoadCursor(new HINSTANCE(), PInvoke.IDC_ARROW);wndClassEx.hbrBackground = (HBRUSH)((nint)SYS_COLOR_INDEX.COLOR_WINDOW + 1);wndClassEx.hInstance = g_hInstance;fixed (char* lps_windowClassName = s_windowClassName){wndClassEx.lpszClassName = lps_windowClassName;}wndClassEx.lpfnWndProc = lpfnWndProc;_ = PInvoke.RegisterClassEx(wndClassEx);return wndClassEx;}return default;}private static unsafe HWND CreateDesktopWindow() =>PInvoke.CreateWindowEx(0,                                  // Extended Styles_windowClassName,                  // name of window classs_defaultWindowTitle,               // title-bar stringWINDOW_STYLE.WS_OVERLAPPEDWINDOW | WINDOW_STYLE.WS_VISIBLE,  // top-level windowint.MinValue,                       // default horizontal position(int)SHOW_WINDOW_CMD.SW_HIDE,       // If the y parameter is some other value,// then the window manager calls ShowWindow with that value as the nCmdShow parameterint.MinValue,                       // default widthint.MinValue,                       // default heightnew HWND(),                         // no owner windownull,                               // use class menunew DefaultSafeHandle(g_hInstance),null);private partial class DefaultSafeHandle(nint invalidHandleValue, bool ownsHandle) : SafeHandle(invalidHandleValue, ownsHandle){public DefaultSafeHandle(nint handle) : this(handle, true) => SetHandle(handle);public override bool IsInvalid => handle != nint.Zero;protected override bool ReleaseHandle() => true;}
}

然后我们就可以初始化一个窗口了。

DesktopWindow window = new() { WindowXamlSource = source };

最后不要忘了用消息循环保持当前线程,不然这里跑完了窗口就退出了。

MSG msg = new();
while (msg.message != PInvoke.WM_QUIT)
{if (PInvoke.PeekMessage(out msg, new HWND(), 0, 0, PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE)){_ = PInvoke.DispatchMessage(msg);}
}

最后把之前的东西组合起来,再加点东西:

/// <summary>
/// Represents a system-managed container for the content of an app.
/// </summary>
public partial class DesktopWindow
{private bool m_bIsClosed = false;private DesktopWindowXamlSource m_source;private IDesktopWindowXamlSourceNative m_native;private readonly HWND m_hwnd;private readonly WNDCLASSEXW m_wndClassEx;/// <summary>/// Initializes a new instance of the <see cref="DesktopWindow"/> class./// </summary>public DesktopWindow(){m_wndClassEx = RegisterDesktopWindowClass(WNDPROC);m_hwnd = CreateDesktopWindow();}/// <summary>/// Get the handle of the window./// </summary>public nint Hwnd => m_hwnd;/// <summary>/// Gets the event dispatcher for the window./// </summary>public CoreDispatcher Dispatcher { get; private set; }/// <summary>/// Gets or sets the visual root of an application window./// </summary>public UIElement Content{get => WindowXamlSource.Content;set => WindowXamlSource.Content = value;}/// <summary>/// Gets or sets the XamlRoot in which this element is being viewed./// </summary>[SupportedOSPlatform("Windows10.0.18362.0")]public XamlRoot XamlRoot{get => WindowXamlSource.Content.XamlRoot;set => WindowXamlSource.Content.XamlRoot = value;}/// <summary>/// Gets or sets a string used for the window title./// </summary>public unsafe string Title{get{int windowTextLength = PInvoke.GetWindowTextLength(m_hwnd);char* windowText = stackalloc char[windowTextLength + 1];_ = PInvoke.GetWindowText(m_hwnd, windowText, windowTextLength + 1);return new string(windowText);}set => _ = PInvoke.SetWindowText(m_hwnd, value);}/// <summary>/// Gets the <see cref="DesktopWindowXamlSource"/> to provide XAML for this window./// </summary>public DesktopWindowXamlSource WindowXamlSource{get => m_source;private init{if (m_source != value){m_source = value;if (value != null){Dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;m_native = value.As<IDesktopWindowXamlSourceNative>();m_native.AttachToWindow(m_hwnd);ResizeWindowToDesktopWindowXamlSourceWindowDimensions();}else{m_native = null;}}}}/// <summary>/// Occurs when the window has closed./// </summary>public event TypedEventHandler<DesktopWindow, object> Closed;/// <summary>/// Shows the window and activates it./// </summary>public void Show() => _ = PInvoke.ShowWindow(m_hwnd, SHOW_WINDOW_CMD.SW_NORMAL);/// <summary>/// Sets the icon for the window, using the specified icon path./// </summary>/// <param name="iconPath">The path of the icon.</param>public unsafe void SetIcon(string iconPath){fixed (char* ptr = iconPath){HANDLE icon = PInvoke.LoadImage(new HINSTANCE(), ptr, GDI_IMAGE_TYPE.IMAGE_ICON, 0, 0, IMAGE_FLAGS.LR_LOADFROMFILE);_ = PInvoke.SendMessage(m_hwnd, PInvoke.WM_SETICON, PInvoke.ICON_BIG, new LPARAM((nint)icon.Value));}}private LRESULT WNDPROC(HWND hWnd, uint message, WPARAM wParam, LPARAM lParam){switch (message){case PInvoke.WM_PAINT:HDC hdc = PInvoke.BeginPaint(hWnd, out PAINTSTRUCT ps);_ = PInvoke.GetClientRect(hWnd, out RECT rect);_ = PInvoke.FillRect(hdc, rect, new DefaultSafeHandle(PInvoke.GetStockObject(GET_STOCK_OBJECT_FLAGS.WHITE_BRUSH)));_ = PInvoke.EndPaint(hWnd, ps);return new LRESULT();case PInvoke.WM_CLOSE when m_bIsClosed:goto default;case PInvoke.WM_CLOSE:m_bIsClosed = true;Closed?.Invoke(this, null);goto default;case PInvoke.WM_SIZE:ResizeWindowToDesktopWindowXamlSourceWindowDimensions();return new LRESULT();case PInvoke.WM_CREATE:return new LRESULT();case PInvoke.WM_DESTROY:PInvoke.PostQuitMessage(0);return new LRESULT();default:return PInvoke.DefWindowProc(hWnd, message, wParam, lParam);}}private void ResizeWindowToDesktopWindowXamlSourceWindowDimensions(){if (m_bIsClosed) return;_ = PInvoke.GetClientRect(m_hwnd, out RECT rect);_ = PInvoke.SetWindowPos(m_native.WindowHandle,new HWND(),0, 0,rect.Width, rect.Height,SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE | SET_WINDOW_POS_FLAGS.SWP_NOZORDER | SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW);}
}public partial class DesktopWindow
{private static readonly unsafe HINSTANCE g_hInstance = new((void*)Process.GetCurrentProcess().Handle);// win32 window class name for top-level WinUI desktop windowsprivate const string s_windowClassName = "WinUIDesktopWin32WindowClass";// Default window title for top-level WinUI desktop windowsprivate const string s_defaultWindowTitle = "WinUI Desktop";private static unsafe WNDCLASSEXW RegisterDesktopWindowClass(WNDPROC lpfnWndProc){if (!PInvoke.GetClassInfoEx(new DefaultSafeHandle(g_hInstance), s_windowClassName, out WNDCLASSEXW wndClassEx)){wndClassEx.cbSize = (uint)Marshal.SizeOf(wndClassEx);wndClassEx.style = WNDCLASS_STYLES.CS_HREDRAW | WNDCLASS_STYLES.CS_VREDRAW;wndClassEx.cbClsExtra = 0;wndClassEx.cbWndExtra = 0;wndClassEx.hCursor = PInvoke.LoadCursor(new HINSTANCE(), PInvoke.IDC_ARROW);wndClassEx.hbrBackground = (HBRUSH)((nint)SYS_COLOR_INDEX.COLOR_WINDOW + 1);wndClassEx.hInstance = g_hInstance;fixed (char* lps_windowClassName = s_windowClassName){wndClassEx.lpszClassName = lps_windowClassName;}wndClassEx.lpfnWndProc = lpfnWndProc;_ = PInvoke.RegisterClassEx(wndClassEx);return wndClassEx;}return default;}private static unsafe HWND CreateDesktopWindow() =>PInvoke.CreateWindowEx(0,                                  // Extended Styles_windowClassName,                  // name of window classs_defaultWindowTitle,               // title-bar stringWINDOW_STYLE.WS_OVERLAPPEDWINDOW | WINDOW_STYLE.WS_VISIBLE,  // top-level windowint.MinValue,                       // default horizontal position(int)SHOW_WINDOW_CMD.SW_HIDE,       // If the y parameter is some other value,// then the window manager calls ShowWindow with that value as the nCmdShow parameterint.MinValue,                       // default widthint.MinValue,                       // default heightnew HWND(),                         // no owner windownull,                               // use class menunew DefaultSafeHandle(g_hInstance),null);private partial class DefaultSafeHandle(nint invalidHandleValue, bool ownsHandle) : SafeHandle(invalidHandleValue, ownsHandle){public DefaultSafeHandle(nint handle) : this(handle, true) => SetHandle(handle);public override bool IsInvalid => handle != nint.Zero;protected override bool ReleaseHandle() => true;}
}public partial class DesktopWindow
{/// <summary>/// Create a new <see cref="DesktopWindow"/> instance./// </summary>/// <param name="launched">Do something after <see cref="DesktopWindowXamlSource"/> created.</param>/// <returns>The new instance of <see cref="DesktopWindow"/>.</returns>public static Task<DesktopWindow> CreateAsync(Action<DesktopWindowXamlSource> launched){TaskCompletionSource<DesktopWindow> taskCompletionSource = new();new Thread(() =>{try{DesktopWindowXamlSource source;using (HookWindowingModel hook = new()){source = new DesktopWindowXamlSource();}DesktopWindow window = new() { WindowXamlSource = source };launched(source);taskCompletionSource.SetResult(window);MSG msg = new();while (msg.message != PInvoke.WM_QUIT){if (PInvoke.PeekMessage(out msg, new HWND(), 0, 0, PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE)){_ = PInvoke.DispatchMessage(msg);}}}catch (Exception e){taskCompletionSource.SetException(e);}}){Name = nameof(DesktopWindowXamlSource)}.Start();return taskCompletionSource.Task;}
}

这样创建的窗口还存在一些问题,但是我不知道该怎么解决,所以该方法还是仅供参考。

最后附上示例应用:https://github.com/wherewhere/CoreAppUWP/tree/wuxc

(由于微软迟迟不正式发布 .NET Core App 对 UWP 的支持,所以本文章实际仍处于未完成状态,源码也暂时未上传至 Github,作者会在微软正式发布之后完善文章并更新源码。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5 分钟复刻你的声音,一键实现 GPT-Sovits 模型部署

本文将详细介绍如何利用函数计算平台部署 GPT-Sovits 模型,以构建一个高效、可扩展的 AI 语音交互系统。通过这一部署方案,开发者和企业能够快速集成语音合成功能,实现从文本到语音的无缝转换,进而推动智能语音应用的创新和发展。想象一下,只需简单几步操作,就能生成逼真…

sam模型迁移昇腾训练loss不收敛问题的解决办法

一、问题描述:在进行sam模型迁移到昇腾的时候存在精度问题,模型链接: https://github.com/facebookresearch/segment-anything 两台机器上训练loss图对比,发现从一开始训练的时候就出现了差别,从图中对比看出来npu第一步 就开始没有向下收敛,而gpu是向下收敛。二、问题分…

Ansible - 自动化利器

Ansible 概念介绍 Ansible是自由开源的配置和自动化工具。官方网站 https://www.ansible.com/ 官方文档 https://docs.ansible.com/ansible/latest/ 安装包 https://releases.ansible.com/主要特点 - 无客户端模式(agentless),无侵入性,只需在主控端部署Ansible环境,被控端…

多光谱火焰识别摄像机

多光谱火焰识别摄像机是一种别摄像机具有以下几个显著优势:具有高度智能化技术的设备,能够. 更高的准确性:多光谱技术可以同时观测不同的火焰特征,从而准确、快速地检测火灾并定位火焰提高了火焰检测的准确性。不同物质燃烧时产生的光谱特征是不同的的位置和辐射强度。这种…

阿里二面和面试官争论Spring程序配置优先级

0 前言 一般用application.yml实现Spring Boot应用参数配置。但Spring配置有优先级,避免重复配置项的覆盖,须清晰优先级。 Spring通过Environment抽象出:Profile:规定场景。定义诸如dev、test、prod等环境 Property:PropertySources,各种配置源。一个环境中可能有多个配置…

yappi,Python性能分析库

yappi是为Python设计的线程感知型性能分析器,不仅支持CPU时间分析,还能够准确追踪线程级别的性能问题。 安装pip install yappi -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com基本用法import yappi import time import threadingdef func1()…

HTML4、HTML5 和 HTML6 之间的主要区别对比,以表格形式展示:

HTML4、HTML5 和 HTML6 之间的主要区别对比,以表格形式展示:特性 / 版本 HTML4 HTML5 HTML6 (预期)发布年份 1999 2014 尚未发布(预计未来发布)文档类型声明 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd…

Jenkins - 分布式构建

1 - 简介 Jenkins支持分布式多节点(Master-Slave)运行模式。将安装Jenkins的主机作为Master节点,然后通过界面配置(或远端启动服务)来添加Slave节点。在这种部署结构下,Master通常只充当管理者的角色,负责任务的调度,slave节点的管理,任务状态的收集等工作,具体的构建任…

Jenkins - 安装并启动Jenkins

1 - 关于Jenkins 构建流水线(build pipeline)工具Jenkins可以轻松地定义和管理各种各样的操作(构建、测试等),并将这些操作像管道pipe一样自由地进行组合,从而自动、流畅地执行一系列处理。将操作以项目project为单位整合并运行,操作简洁 消除手工操作,安全可靠 保留执…

Jenkins - 初识

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 通常企业都会将持续集成和持续交付的过程标准化一个单独的生态系统,将涉及的特定工具标…

10分钟搞懂大模型备案

大模型备案作为合规化运营的重要资质,办理难度大,申报周期长。为了方便大家快速弄懂大模型备案,我将大量涉及文件和繁杂的流程进行了精简和总结,力求让大家能快速理解重点。一、申报材料 大模型备案主要涉及语料安全评估、模型安全评估、安全措施评估三部分,主要申报材料如…

视频为什么会糊(二)?B站的码率变化

原文链接:https://tingxuanr.github.io/note/视频为什么会糊二b站的码率变化/ 摘要:流媒体平台(不只B站,所有流媒体都在干,画质最差的还得是优爱腾三家)为了节约成本,不断的降低自家视频的码率。很多所谓的4k、蓝光清晰度还不如六七年前的,纯粹是自欺欺人。上一篇对比 …