C# wpf 使用GDI实现截屏

news2024/11/16 13:32:28

wpf截屏系列

第一章 使用GDI实现截屏(本章)
第二章 使用GDI+实现截屏
第三章 使用DockPanel制作截屏框
第四章 实现截屏框热键截屏
第五章 实现截屏框实时截屏
第六章 使用ffmpeg命令行实现录屏


文章目录

  • wpf截屏系列
  • 前言
  • 一、导入gdi32
    • 方法一、NuGet获取
      • (1)、获取gdi32
      • (2)、获取user32
    • 方法二、Dllimport
  • 二、实现步骤
    • 1、创建兼容DC
    • 2、创建位图
    • 3、获取位图信息
    • 4、BitBlt
    • 5、获取数据
    • 6、销毁资源
  • 三、封装成对象
  • 四、完整代码
  • 五、使用示例
    • 1、快照
      • (1)比例值区域截取
      • (2)实际值区域截取
      • (3)WPF中使用
    • 2、采集
      • (1)、异步
      • (2)、同步
      • (3)、WPF中使用(异步)
  • 总结
  • 附录


前言

wpf截屏时通常可以采用gdi+,调用起来比较方便。使用gdi也能实现截屏,截屏数据也能转成BitmapSource对象,当然调用流程会复杂一些,而且需要引入win32方法,唯一比较容易的就是可以直接绘制异或鼠标。


一、导入gdi32

方法一、NuGet获取

这种方法好处是简单方便,缺点是增加了依赖dll,生成的程序容量大一些且附带一些dll。

(1)、获取gdi32

在这里插入图片描述

(2)、获取user32

在这里插入图片描述

方法二、Dllimport

使用DllImport将需要的win32 api导入。这样做工作量比较大,但是好处是无依赖,生成程序很小。
示例如下:

[DllImport(User32, SetLastError = false, ExactSpelling = true)]
public static extern IntPtr GetDC([In, Optional] IntPtr ptr);

完整的gdi需要导入的所有接口见附录。


二、实现步骤

1、创建兼容DC

IntPtr srcHdc = IntPtr.Zero;
IntPtr dstHdc = IntPtr.Zero;
srcHdc = GetDC(hWnd);
dstHdc = CreateCompatibleDC(srcHdc);

2、创建位图

BITMAPINFO bmi = new BITMAPINFO();
IntPtr hBitmap = IntPtr.Zero;
IntPtr oldBitmap = IntPtr.Zero;
bmi.bmiHeader.biSize = (uint)Marshal.SizeOf<BITMAPINFOHEADER>();
bmi.bmiHeader.biWidth = capWidth;
bmi.bmiHeader.biHeight = -capHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24;
bmi.bmiHeader.biCompression = BitmapCompressionMode.BI_RGB;
bmi.bmiHeader.biSizeImage = 0;
bmi.bmiHeader.biXPelsPerMeter = 0;
bmi.bmiHeader.biYPelsPerMeter = 0;
bmi.bmiHeader.biClrUsed = 0;
bmi.bmiHeader.biClrImportant = 0;
hBitmap = CreateDIBSection(dstHdc, in bmi, DIBColorMode.DIB_RGB_COLORS, out pvBits, IntPtr.Zero, 0);
oldBitmap = SelectObject(dstHdc, hBitmap);

3、获取位图信息

需要获取位图的行对齐stride

BITMAP bitmap;
temp = Marshal.AllocHGlobal(Marshal.SizeOf<BITMAP>());
if (GetObject(hBitmap, Marshal.SizeOf<BITMAP>(), temp) == 0)
{
    throw new Exception("GetObject Failed");
}
bitmap = Marshal.PtrToStructure<BITMAP>(temp);

4、BitBlt

BitBlt(dstHdc, capX, capY, capWidth, capHeight, srcHdc, capX, capY, RasterOperationMode.SRCCOPY | RasterOperationMode.CAPTUREBLT)

5、获取数据

//行对齐
int stride=bitmap.bmWidthBytes;
//宽
int width=bitmap.bmWidth;
//高
int height=bitmap.bmHeight;
//位图数据
IntPtr pBuffer=bitmap.bmBits;

BITMAP转成WriteableBitmap(BitmapSource)

public static WriteableBitmap ToWriteableBitmap(this BITMAP bitmap)
{
    var wb = new WriteableBitmap(bitmap.bmWidth, bitmap.bmHeight, 0, 0, bitmap.bmBitsPixel == 32 ? PixelFormats.Bgra32 : PixelFormats.Bgr24, null);
    wb.WritePixels(new Int32Rect(0, 0, bitmap.bmWidth, bitmap.bmHeight), bitmap.bmBits, bitmap.bmHeight * bitmap.bmWidthBytes, bitmap.bmWidthBytes, 0, 0);
    return wb;
}

6、销毁资源

if (dstHdc != IntPtr.Zero)
{
    if (oldBitmap != IntPtr.Zero)
    {
        var ret = SelectObject(dstHdc, oldBitmap);
        if (ret == IntPtr.Zero)
        {
            errors += "SelectObject Failed";
        }
    }
    if (!DeleteDC(dstHdc))
    {
        errors += "DeleteDC Failed";
    }
}
if (srcHdc != IntPtr.Zero)
{
    if (!ReleaseDC(hWnd, srcHdc))
    {
        errors += "ReleaseDC Failed";
    }
}
if (!DeleteObject(hBitmap))
{
    errors += "DeleteObject Failed";
}
if (temp != IntPtr.Zero) Marshal.FreeHGlobal(temp);


三、封装成对象

/************************************************************************
* @Project:  	GdiGrabber
* @Decription:  gdi图片采集
* 可以根据提供的句柄采集图片或者获取快照。提供了采集区域提供了实际值和比例值
* 两种接口。采集提供了同步和异步两种方式,在主线程或者UI线程建议用异步,在非
* UI线程建议用同步。
* @Verision:  	v1.0.0
* @Author:  	Xin Nie
* @Create:  	2024/03/13 23:57:00
* @LastUpdate:  2024/03/13 23:57:00
************************************************************************
* Copyright @ 2024. All rights reserved.
************************************************************************/
public static class GdiGrabber
{
    /// <summary>
    /// 快照
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="hwnd"></param>
    /// <returns></returns>
    public static WriteableBitmap? Snapshot(int x, int y, int width, int height, nint hWnd = 0, bool isPaintMouse = true);
    /// <summary>
    /// 快照
    /// 按比例,在任意分辨率,比如0,0,1,1就是全屏。
    /// </summary>
    /// <param name="x">比例,0-1</param>
    /// <param name="y">比例,0-1</param>
    /// <param name="width">比例,0-1</param>
    /// <param name="height">比例,0-1</param>
    /// <param name="hwnd"></param>
    /// <returns></returns>
    public static WriteableBitmap? Snapshot(double x, double y, double width, double height, nint hWnd = 0, bool isPaintMouse = true);
    /// <summary>
    /// 采集,异步
    /// 按比例,在任意分辨率,比如0,0,1,1就是全屏。
    /// 用法 await foreach(var i in GdiGrabber.Capture){}
    /// 注意,在UI线程可以直接使用。
    /// 在非UI线程需要确保Dispatcher的运行,比如在线程最后调用Dispatcher.Run()、或 Dispatcher.PushFrame。
    /// </summary>
    /// <param name="x">比例,0-1</param>
    /// <param name="y">比例,0-1</param>
    /// <param name="width">比例,0-1</param>
    /// <param name="height">比例,0-1</param>
    /// <param name="hWnd">句柄,为0则采集桌面</param>
    /// <param name="isPaintMouse">是否绘制鼠标</param>
    /// <returns>采集的数据对象</returns>
    public static IAsyncEnumerable<BITMAP> Capture(double x, double y, double width, double height, nint hWnd = 0, bool isPaintMouse = true);
    /// <summary>
    /// 采集,异步
    /// 用法 await foreach(var i in GdiGrabber.Capture){}
    /// 注意,在UI线程可以直接使用。
    /// 在非UI线程需要确保Dispatcher的运行,比如在线程最后调用Dispatcher.Run()、或 Dispatcher.PushFrame。
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="hWnd">句柄,为0则采集桌面</param>
    /// <param name="isPaintMouse">是否绘制鼠标</param>
    /// <returns>采集的数据对象</returns>
    /// <exception cref="Exception"></exception>
    public static async IAsyncEnumerable<BITMAP> Capture(int x, int y, int width, int height, nint hWnd = 0, bool isPaintMouse = true);
    public static /*IEnumerable<BITMAP>*/void CaptureSync(double x, double y, double width, double height, Func<BITMAP, bool> onGrab, nint hWnd = 0, bool isPaintMouse = true);
    /// <summary>
    /// 采集,同步
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="hWnd">句柄,为0则采集桌面</param>
    /// <param name="isPaintMouse">是否绘制鼠标</param>
    /// <param name="onGrab">采集回调,返回是否继续采集。之所以采用回调是因为,更好的设计应该是使用yeild return,但是会出现内存异常读写问题,暂时无法解决。
    /// </param>
    /// <returns>采集的数据对象</returns>
    /// <exception cref="Exception"></exception>
    public static /*IEnumerable<BITMAP>*/void CaptureSync(int x, int y, int width, int height, Func<BITMAP, bool> onGrab, nint hWnd = 0, bool isPaintMouse = false);
    /// <summary>
    /// 将BITMAP转换成WriteableBitmap
    /// 作者的设备测试此操作1080p耗时8ms
    /// </summary>
    /// <param name="bitmap">this</param>
    /// <returns>WriteableBitmap</returns>
    public static WriteableBitmap ToWirteableBitmap(this BITMAP bitmap);
    /// <summary>
    /// 将BITMAP数据拷贝到riteableBitmap
    /// 作者的设备测试此操作1080p耗时2ms
    /// </summary>
    /// <param name="bitmap">this</param>
    /// <param name="wb">WriteableBitmap</param>
    public static void CopyToWriteableBitmap(this BITMAP bitmap, WriteableBitmap wb);
}

四、完整代码

vs2022 .net6.0 wpf项目,采用DllImport的方式无任何依赖。
之后上传


五、使用示例

1、快照

(1)比例值区域截取

截取全屏(任意分辨率)

WriteableBitmap? wb=  GdiGrabber.Snapshot(0,0,1.0,1.0);

(2)实际值区域截取

WriteableBitmap? wb=  GdiGrabber.Snapshot(0,0,600,600);

(3)WPF中使用

WpfGdiGrabber.xaml

<Window x:Class="WpfGdiGrabber.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfGdiGrabber"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid  >
        <Image x:Name="img"></Image>
    </Grid>
</Window>

WpfGdiGrabber.cs

using AC;
using System.Windows;
using System.Windows.Media.Imaging;
namespace WpfGdiGrabber
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            WriteableBitmap? wb = GdiGrabber.Snapshot(0, 0, 1.0, 1.0);
        }
    }
}

效果预览
在这里插入图片描述

2、采集

(1)、异步

UI线程使用

await foreach (var i in GdiGrabber.Capture(0, 0, 1.0, 1.0, 0))
{   
    //img为Image控件
    if (img.Source == null)
        img.Source = i.ToWriteableBitmap();
    else
        i.CopyToWriteableBitmapp(img.Source as WriteableBitmap);
}

非UI线程使用,需要启动一个Dispatcher用于调度消息以及阻塞线程避免结束。

new Thread(async () =>
    {
        bool isExit = false;
        var frame = new DispatcherFrame();
        var func = async () =>
        {
            //循环采集
            await foreach (var i in GdiGrabber.Capture(0, 0, 1.0, 1.0, 0))
            {
                //Dispatcher将操作切换到UI线程执行
                Dispatcher.Invoke(() =>
                {
                    //WriteableBitmap是和线程绑定的,需要在UI线程创建此对象。
                    WriteableBitmap? wb = i.ToWriteableBitmap();
                });
                //退出采集
                if (isExit) break;
            }
            //退出消息循环
            frame.Continue = false;
        };
        func();
        //启动Dispatcher消息循环,此行阻塞
        Dispatcher.PushFrame(frame);
    })
{ IsBackground = true }.Start();

(2)、同步

同步的方式会阻塞,建议在非UI线程中使用。但要注意WriteableBitmap 需要在UI线程中创建才能被控件使用。

GdiGrabber.CaptureSync(0, 0, 1.0, 1.0,  (bitmap) =>
{
    //获取到WriteableBitmap对象
    WriteableBitmap wb = bitmap.ToWriteableBitmap();
    //返回true为继续截屏,false为停止。
    return true;
});

(3)、WPF中使用(异步)

WpfGdiGrabber.xaml

<Window x:Class="WpfGdiGrabber.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfGdiGrabber"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid  >
        <Image x:Name="img"></Image>
    </Grid>
</Window>

WpfGdiGrabber.cs

using AC;
using System.Windows;
using System.Windows.Media.Imaging;
namespace WpfGdiGrabber
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Capture();
        }
        async void Capture()
        {
            await foreach (var i in GdiGrabber.Capture(0, 0, 1.0, 1.0, 0))
            {
                if (img.Source == null)
                    img.Source = i.ToWriteableBitmap();
                else
                    i.CopyToWriteableBitmap(img.Source as WriteableBitmap);
            }
        }
    }
}

效果预览
在这里插入图片描述


总结

以上就是今天要讲的内容,本文实现了的GDI截屏与GDI+对比性能略差一些,但也是可以一定程度满足使用,比如用来截屏或者制作放大镜。而且有一个好处是可以做到无额外依赖。总的来说,可以作为一种备选方案或者测试方案。


附录

DllImport

    [StructLayout(LayoutKind.Sequential)]
    public struct BITMAP
    {
        public int bmType;
        public int bmWidth;
        public int bmHeight;
        public int bmWidthBytes;
        public ushort bmPlanes;
        public ushort bmBitsPixel;
        public IntPtr bmBits;
    }

    class WinApiImport
    {
        const string Gdi32 = "gdi32.dll";
        const string User32 = "user32.dll";
        [StructLayout(LayoutKind.Sequential), Serializable]
        public struct POINT
        {
            public int X;
            public int Y;
        }
        [StructLayout(LayoutKind.Sequential), Serializable]
        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        [StructLayout(LayoutKind.Sequential, Size = 4)]
        public struct RGBQUAD
        {
            public byte rgbBlue;
            public byte rgbGreen;
            public byte rgbRed;
            public byte rgbReserved;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            public BITMAPINFOHEADER bmiHeader;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
            public RGBQUAD[] bmiColors;
        }

        public enum BitmapCompressionMode : uint
        {
            BI_RGB = 0,
            BI_RLE8 = 1,
            BI_RLE4 = 2,
            BI_BITFIELDS = 3,
            BI_JPEG = 4,
            BI_PNG = 5
        }

        [StructLayout(LayoutKind.Sequential, Pack = 2)]
        public struct BITMAPINFOHEADER
        {
            public uint biSize;
            public int biWidth;
            public int biHeight;
            public ushort biPlanes;
            public ushort biBitCount;
            public BitmapCompressionMode biCompression;
            public uint biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public uint biClrUsed;
            public uint biClrImportant;
        }

        public enum DIBColorMode : int
        {
            DIB_RGB_COLORS = 0,
            DIB_PAL_COLORS = 1
        }
        public enum CursorState
        {
            CURSOR_HIDDEN = 0,
            CURSOR_SHOWING = 0x00000001,
            CURSOR_SUPPRESSED = 0x00000002,
        }
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct CURSORINFO
        {
            public uint cbSize;
            public CursorState flags;
            public IntPtr hCursor;
            public POINT ptScreenPos;
        }
        [StructLayout(LayoutKind.Sequential)]
        public sealed class ICONINFO
        {
            public bool fIcon;
            public int xHotspot;
            public int yHotspot;
            public IntPtr hbmMask;
            public IntPtr hbmColor;
        }
        public enum RasterOperationMode
        {
            SRCCOPY = 0x00CC0020,
            SRCPAINT = 0x00EE0086,
            SRCAND = 0x008800C6,
            SRCINVERT = 0x00660046,
            SRCERASE = 0x00440328,
            NOTSRCCOPY = 0x00330008,
            NOTSRCERASE = 0x001100A6,
            MERGECOPY = 0x00C000CA,
            MERGEPAINT = 0x00BB0226,
            PATCOPY = 0x00F00021,
            PATPAINT = 0x00FB0A09,
            PATINVERT = 0x005A0049,
            DSTINVERT = 0x00550009,
            BLACKNESS = 0x00000042,
            WHITENESS = 0x00FF0062,
            NOMIRRORBITMAP = -2147483648,
            CAPTUREBLT = 0x40000000
        }
        [DllImport(User32, CharSet = CharSet.Auto, ExactSpelling = true, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        [System.Security.SecurityCritical]
        public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
        [DllImport(User32, SetLastError = false, ExactSpelling = true)]
        public static extern IntPtr GetDesktopWindow();
        [DllImport(User32, SetLastError = false, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);
        [DllImport(User32, SetLastError = false, ExactSpelling = true)]
        public static extern IntPtr GetDC([In, Optional] IntPtr ptr);
        [DllImport(Gdi32, ExactSpelling = true, SetLastError = true)]
        public static extern IntPtr CreateCompatibleDC([Optional] IntPtr hDC);
        [DllImport(Gdi32, SetLastError = false, ExactSpelling = true)]
        public static extern IntPtr CreateDIBSection([In, Optional] IntPtr hdc, in BITMAPINFO pbmi, DIBColorMode usage, out IntPtr ppvBits, [In, Optional] IntPtr hSection, [In, Optional] uint offset);
        [DllImport(Gdi32, SetLastError = false, CharSet = CharSet.Auto)]
        public static extern int GetObject(IntPtr hgdiobj, int cbBuffer, IntPtr lpvObject);
        [DllImport(Gdi32, ExactSpelling = true, SetLastError = true)]
        public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
        [DllImport(Gdi32, ExactSpelling = true, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, RasterOperationMode dwRop);
        [DllImport(Gdi32, ExactSpelling = true, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        [System.Security.SecurityCritical]
        public static extern bool DeleteDC(IntPtr hdc);
        [DllImport(User32, SetLastError = false, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
        [DllImport(Gdi32, ExactSpelling = true, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DeleteObject(IntPtr hObject);
        [DllImport(User32, SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetCursorInfo(ref CURSORINFO pci);
        [DllImport(User32, SetLastError = true, ExactSpelling = true)]
        public static extern IntPtr CopyIcon(IntPtr hIcon);
        [DllImport(User32, SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetIconInfo(IntPtr hIcon, [In, Out] ICONINFO piconinfo);
        [DllImport(User32, SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon);
        [DllImport(User32, SetLastError = true, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DestroyCursor(IntPtr hCursor);
        [DllImport(User32, SetLastError = true, CharSet = CharSet.Auto)]
        public static extern IntPtr LoadCursor(IntPtr hInstance, string lpCursorName);
    }

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

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

相关文章

【LeetCode: 102. 二叉树的层序遍历 + bfs】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

国产化三防笔记本丨亿道国产加固笔记本FT-2000/4处理器

国产化加固笔记本是指采用国产操作系统和处理器&#xff0c;通过技术手段对其进行硬件加固、软件加密、数据安全等多方面加强处理的产品。这种笔记本电脑通常被用于政府项目、金融行业等对安全性要求极高的领域。 在国产化加固笔记本中&#xff0c;硬件加固是重要的一环。为了保…

架构实战--以海量存储系统讲解热门话题:分布式概念

关注我&#xff0c;持续分享逻辑思维&管理思维&#xff1b; 可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导&#xff1b; 有意找工作的同学&#xff0c;请参考博主的原创&#xff1a;《面试官心得--面试前应该如何准备》&#xff0c;《面试官心得--面试时如何进行自…

腾讯云轻量服务器地域选择教程,2024最新地域选择攻略

腾讯云服务器地域怎么选择&#xff1f;不同地域之间有什么区别&#xff1f;腾讯云哪个地域好&#xff1f;地域选择遵循就近原则&#xff0c;访客距离地域越近网络延迟越低&#xff0c;速度越快。腾讯云百科txybk.com告诉大家关于地域的选择还有很多因素&#xff0c;地域节点选择…

Github主页设置贪吃蛇详细教程

先看最终实现结果&#xff1a; 有条贪吃蛇放在主页还是蛮酷的哈哈哈。接下来我来讲一讲怎么在Github主页添加一条贪吃蛇。 首先要修改自己的Github的主页&#xff0c;我们得有一个特殊的仓库——这个仓库必须与你的Github用户名保持一致&#xff0c;并且需要公开&#xff0c…

ArcGIS全系列实战视频教程——9个单一课程组合+系列直播回放

《ArcGIS全系列实战视频教程》是由9个单一课程组合合成。组成一条ArcGIS入门实战各项专题深入应用学习全链条&#xff0c;让你学有方向、学有目的&#xff0c;系统全面掌握ArcGIS。 ArcGIS全系列实战视频教程——9个单一课程组合https://edu.csdn.net/combo/detail/2569 《Ar…

unity2D生成9*9格子

1.创建一个空对象和格子 2将格子做成预制体&#xff08;直接将格子拖到这里即可&#xff0c;拖了过后删掉原来的格子&#xff09; 3.创建脚本并将脚本拖到空对象上 using System.Collections; using System.Collections.Generic; using UnityEngine;public class CreateMap : M…

增删卜易——八宫六十四卦

之前看倪海厦的《天纪》笔记里面提到了六十四卦世应,觉得不知道这个世应是啥意思。很长时间就没看了,偶然间看到了张文江教授写的一本书《潘雨廷先生谈话录》提到了《卜筮正宗》,“卜筮最后的判断是非理性转义,其他一切都只是形式”,“明人的著作,从京氏易出,如今天几日…

GitHub 服务器

GitHub 服务器 公司中&#xff0c;我们可以搭建中央服务器让项目组开发人员共享代码&#xff0c;但是如果我们的开发人员都是通过互联网进行协作&#xff0c;而不是在同一个地方&#xff0c;那么开发时&#xff0c;程序文件代码的版本管理就显得更加重要&#xff0c;这就需要搭…

企业数据流动安全管理软件(深度解析文章)

企业数据重要性不言而喻&#xff0c;而同时数据的流动和共享也带来了安全风险&#xff0c;如何确保企业数据在流动过程中的安全性&#xff0c;也成为了企业需要面临的重要问题。 企业数据流动安全管理软件的主要功能是监控和管理企业数据的流动过程。 它能够对企业内部的数据…

OpenCASCADE开发指南<八>:OCC 数据结构分析之二三维几何数据

数据结构,指的是数据元素之间的相互关系,尤其是数据的逻辑结构。选择数据结构的主要依据是数据的逻辑结构[6]。 因此&#xff0c; 本章将主要描述三种数据的逻辑结构。这三种数据包括&#xff1a;二维几何数据、三维几何数据和拓扑数据。 1 数据结构模块的整体框架 OCC 的第二…

GPT实战系列-如何让LangChain的Agent选择工具

GPT实战系列-如何让LangChain的Agent选择工具 LangChain GPT实战系列-LangChain如何构建基通义千问的多工具链 GPT实战系列-构建多参数的自定义LangChain工具 GPT实战系列-通过Basetool构建自定义LangChain工具方法 GPT实战系列-一种构建LangChain自定义Tool工具的简单方法…

ts文件怎么无损转换mp4?这样设置转换模式~

TS格式&#xff08;Transport Stream&#xff09;的起源可追溯到数字电视广播领域。设计初衷是解决视频、音频等多媒体数据在传输和存储中的问题。采用一系列标准技术&#xff0c;TS格式让视频信号能够以流的形式传输&#xff0c;因此在数字电视、广播等领域得到广泛应用。 MP4…

ChatGPT提问技巧——对抗性提示

ChatGPT提问技巧——对抗性提示 对抗性提示是一种允许模型生成能够抵御某些类型的攻击或偏差的文本的技术。这种技术可用于训练更健壮、更能抵御某些类型的攻击或偏差的模型。 要在 ChatGPT 中使用对抗性提示&#xff0c;应为模型提供一个提示&#xff0c;该提示的设计应使模…

Python数据分析-4

1.对于一组电影数据&#xff0c;呈现出rating,runtime的分布情况&#xff1a; #encodingutf-8 import pandas as pd import numpy as np from matplotlib import pyplot as plt file_path "./youtube_video_data/IMDB-Movie-Data.csv" df pd.read_csv(file_path) …

基于centos7的k8s最新版v1.29.2安装教程

k8s概述 Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态&#xff0c;其服务、支持和工具的使用范围相当广泛。 Kubernetes 这个名字源于希腊语&…

x86_64架构栈帧以及帧指针FP

文章目录 一、x86_64架构寄存器简介二、x86_64架构帧指针FP三、示例四、保存帧指针参考资料 一、x86_64架构寄存器简介 在x86架构中&#xff0c;有8个通用寄存器可用&#xff1a;eax、ebx、ecx、edx、ebp、esp、esi和edi。在x86_64&#xff08;x64&#xff09;扩展中&#xff…

StarRocks——滴滴的极速多维分析实践

背景 滴滴集团作为生活服务领域的头部企业&#xff0c;其中橙心优选经过一年多的数据体系建设&#xff0c;逐渐将一部分需要实时交互查询&#xff0c;即席查询的多维数据分析需求由ClickHouse迁移到了StarRocks中&#xff0c;接下来以StarRocks实现的漏斗分析为例介绍StarRocks…

对OceanBase进行 sysbench 压测前,如何用 obdiag巡检

有一些用户想对 OceanBase 进行 sysbench 压测&#xff0c;并向我询问是否需要对数据库的各种参数进行调整。我想起有一个工具 obdiag &#xff0c;具备对集群进行巡检的功能。因此&#xff0c;我正好借此机会试用一下这个工具。 obdiag 功能的比较丰富&#xff0c;详细情况可参…

如何正确地设置Outlook SMTP发送电子邮件?

Outlook SMTP发送邮件配置方法&#xff1f;Outlook怎么开启SMTP&#xff1f; 在使用Outlook发送邮件时&#xff0c;正确设置SMTP服务器是确保邮件能够顺利发送的关键步骤。接下来&#xff0c;就让AokSend一起探讨如何正确地设置Outlook SMTP发送电子邮件吧&#xff01; Outlo…