当前位置: 首页 > news >正文

13.ImGui-搭建内部绘制的ImGui项目框架(无消息循环的简单ImGui实例)

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

本次游戏没法给

内容参考于:微尘网络安全

上一个内容:12.ImGui-外部绘制独立的界面窗体

下面开始写代码了,代码看不懂没关系,要知道怎样去复制(写的很细全程傻瓜模式),代码的说明会放在最后(不放在最后,截图的时候不好截,看起来会很乱,用来说代码的来源会很不友好),说明用小白也能看懂的语言写的,看多了慢慢就知道了,不知道也没关系会复制会用就行

到这ImGui的基础就可以了,然后接下来是如何在某程序内部使用ImGui绘制窗口

首先打卡VisualStudio2022创建一个新项目

内部绘制就要把我们程序放到目标程序里,也就是要用动态链接库,所以就创建一个动态链接库的项目,如下图红框

随便输入一个项目名和保存位置,然后点击创建

创建完成

然后导入ImGui用到的库(文件) ,首先把下图红框的文件复制一下

把复制的上图红框里的文件放到下图红框的目录,也就是上方创建项目的时候设置的那个目录

然后创建一个文件夹

然后再复制下图红框的两个文件

把它们复制到刚刚创建的ImGui文件夹里,如下图红框

然后在把下图红框的两个文件复制到ImGui文件夹中

复制完后,文件相关的就完事了

然后来到vs2022里,首先新加一个筛选器

然后把下图红框里的4个文件拖到新加的筛选器里

然后把下图红框的文件拖到源文件里

然后右击头文件选择新建项

创建一个main.h文件

main.h里添加内容

#pragma once
#include <d3d11.h>
#include "ImGui/imgui_impl_dx11.h"
#include "ImGui/imgui_impl_win32.h"

然后设置包含目录,如下图鼠标右击选择属性

如下图设置包含目录

添加 $(ProjectDir),然后点击确定

$(ProjectDir)是vs中内置的目录,它表示.vcxproj文件所在的目录,也就是下图红框文件的所在目录,这样写换个电脑就不用重新设置目录了,让vs自己找目录

然后在给附加依赖项添加d3d11.lib; 如下图红框

然后点击运行,会出现错误

在预编译头里选择不使用预编译头,然后点击确定

然后点击下图红框运行代码,会出现下图蓝框的提示,这是因为我们的项目是一个dll,不是exe这是正常的

可以点击重新生成

然后编译成功

导致ImGui需要的东西就都搞完了,也都是之前的内容,然后接下来就开始写主要代码了,然后来到DllMain里,这是dll的入口,就是当dll放到目标程序里,就会执行DllMain里的代码

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
/*** DllMain:Windows动态链接库(DLL)的入口点函数* 当DLL被加载到进程、线程创建/销毁、DLL被卸载时,系统会自动调用该函数* 用于执行DLL的初始化、资源分配、清理等操作** 参数说明:* - hModule:DLL的模块句柄(HMODULE),可理解为DLL在内存中的唯一标识*            类似于EXE的实例句柄,可用于加载资源、获取路径等* - ul_reason_for_call:调用该函数的原因(触发事件),为DWORD类型* - lpReserved:保留参数,用于传递额外信息,不同事件含义不同** 返回值:BOOL类型* - 对于DLL_PROCESS_ATTACH事件:返回TRUE表示DLL加载成功;返回FALSE会导致加载失败* - 其他事件:返回值通常被忽略(系统不处理)*/
BOOL APIENTRY DllMain(HMODULE hModule,        // DLL模块句柄(当前DLL的标识)DWORD  ul_reason_for_call,  // 调用原因(触发的事件类型)LPVOID lpReserved       // 保留参数(不同事件含义不同)
)
{// 根据调用原因处理不同事件switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:// 事件含义:DLL被成功加载到某个进程的地址空间时触发// 触发时机:进程首次加载DLL时(如通过LoadLibrary函数)// 典型用途:初始化DLL的全局资源(如分配内存、创建对象、注册回调)// 注意:此时进程的主线程可能还未开始执行,避免在此创建UI或复杂操作// lpReserved:若为NULL,表示DLL是被显式加载(如LoadLibrary);非NULL表示隐式加载(如依赖项加载)break;case DLL_THREAD_ATTACH:// 事件含义:进程中创建新线程时,系统会通知所有已加载的DLL// 触发时机:新线程启动时(线程函数执行前)// 典型用途:为新线程分配专属资源(如线程局部存储TLS)// 注意:每个新线程创建都会触发,需避免资源泄露// lpReserved:始终为NULLbreak;case DLL_THREAD_DETACH:// 事件含义:进程中的线程结束时,系统会通知所有已加载的DLL// 触发时机:线程正常退出时(线程函数返回后)// 典型用途:释放该线程在DLL中分配的专属资源(如TLS中的数据)// 注意:若线程异常终止(如TerminateThread),此事件不会触发// lpReserved:始终为NULLbreak;case DLL_PROCESS_DETACH:// 事件含义:DLL从进程的地址空间中卸载时触发// 触发时机:进程调用FreeLibrary释放DLL,或进程退出时// 典型用途:释放DLL_PROCESS_ATTACH中分配的全局资源(如内存、文件句柄)// 注意:若进程退出(如ExitProcess),部分系统资源可能已失效,避免复杂操作// lpReserved:若为NULL,表示DLL被显式卸载(如FreeLibrary);非NULL表示进程正在退出break;}// 返回TRUE表示所有事件处理成功// 对于DLL_PROCESS_ATTACH,返回FALSE会导致DLL加载失败return TRUE;
}

然后创建一个MyDx.h和MyDx.cpp文件

然后在main.h文件中添加下图红框的内容 #include <windows.h>

MyDx.h的内容

#pragma once
// 声明一个函数叫做Go
DWORD Go(LPVOID lpThreadParameter
);

MyDx.pp的内容

#include "main.h"
// 实现Go函数
DWORD Go(LPVOID lpThreadParameter
) {return 0;
}

然后dllmain.cpp里的内容

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "main.h"
/*** DllMain:Windows动态链接库(DLL)的入口点函数* 当DLL被加载到进程、线程创建/销毁、DLL被卸载时,系统会自动调用该函数* 用于执行DLL的初始化、资源分配、清理等操作** 参数说明:* - hModule:DLL的模块句柄(HMODULE),可理解为DLL在内存中的唯一标识*            类似于EXE的实例句柄,可用于加载资源、获取路径等* - ul_reason_for_call:调用该函数的原因(触发事件),为DWORD类型* - lpReserved:保留参数,用于传递额外信息,不同事件含义不同** 返回值:BOOL类型* - 对于DLL_PROCESS_ATTACH事件:返回TRUE表示DLL加载成功;返回FALSE会导致加载失败* - 其他事件:返回值通常被忽略(系统不处理)*/
BOOL APIENTRY DllMain(HMODULE hModule,        // DLL模块句柄(当前DLL的标识)DWORD  ul_reason_for_call,  // 调用原因(触发的事件类型)LPVOID lpReserved       // 保留参数(不同事件含义不同)
)
{// 根据调用原因处理不同事件switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:// 事件含义:DLL被成功加载到某个进程的地址空间时触发// 触发时机:进程首次加载DLL时(如通过LoadLibrary函数)// 典型用途:初始化DLL的全局资源(如分配内存、创建对象、注册回调)// 注意:此时进程的主线程可能还未开始执行,避免在此创建UI或复杂操作// lpReserved:若为NULL,表示DLL是被显式加载(如LoadLibrary);非NULL表示隐式加载(如依赖项加载)// 启动一个线程调用Go函数::CreateThread(0,0,Go,0,0,0);break;case DLL_THREAD_ATTACH:// 事件含义:进程中创建新线程时,系统会通知所有已加载的DLL// 触发时机:新线程启动时(线程函数执行前)// 典型用途:为新线程分配专属资源(如线程局部存储TLS)// 注意:每个新线程创建都会触发,需避免资源泄露// lpReserved:始终为NULLbreak;case DLL_THREAD_DETACH:// 事件含义:进程中的线程结束时,系统会通知所有已加载的DLL// 触发时机:线程正常退出时(线程函数返回后)// 典型用途:释放该线程在DLL中分配的专属资源(如TLS中的数据)// 注意:若线程异常终止(如TerminateThread),此事件不会触发// lpReserved:始终为NULLbreak;case DLL_PROCESS_DETACH:// 事件含义:DLL从进程的地址空间中卸载时触发// 触发时机:进程调用FreeLibrary释放DLL,或进程退出时// 典型用途:释放DLL_PROCESS_ATTACH中分配的全局资源(如内存、文件句柄)// 注意:若进程退出(如ExitProcess),部分系统资源可能已失效,避免复杂操作// lpReserved:若为NULL,表示DLL被显式卸载(如FreeLibrary);非NULL表示进程正在退出break;}// 返回TRUE表示所有事件处理成功// 对于DLL_PROCESS_ATTACH,返回FALSE会导致DLL加载失败return TRUE;
}

然后上方的Go函数实现还没完,我们的ImGui的代码要写在Go函数里,所以接下来开始复制ImGui的代码,下图蓝框的代码是创建一个窗口,现在我们是内部窗口目标程序已经有窗口了,所以现在不需要它们,直接从CreateDeviceD3D开始

把下图红框的代码全部复制,

复制过来后补齐代码,都是从ImGui源码里复制的

复制方式,来到ImGui源码里,然后找到缺少的代码,按着CTRL然后鼠标左键单击,就会给跳到它的声明位置(声明可以理解为创建)

就会跳到下图红框位置,把它复制过去就可以了

然后再补全CreateRenderTarget函数,按着CTRL鼠标左键单击下图红框位置

跳到CreateRenderTarget函数后,把下图红框的代码复制过去

复制过来后,可以看到它缺少 g_mainRenderTargetView,再找g_mainRenderTargetView

按着CTRL使用鼠标左键单击下图红框位置

然后它就跳转到下图红框位置,把下图红框代码复制过去

然后就不报错了

下方就不贴复制后的图了,只贴需要复制的代码的图,默认把它们依次复制过去就行

然后是创建ImGui的上下文,也就是让ImGui初始化它需要的数据(上下文可以理解为需要的数据)

然后是ImGui的样式

然后是初始化WIn32(连接ImGui与Windows窗口系统)和DirectX 11(连接ImGui与DirectX 11渲染系统)

然后是Begin和End

然后数渲染视图

然后绑定视图

连接DirectX 11

复制完的代码

重新生成,可以正常生成

以上就是一个从官方的实例中摘出来的最简单最基本的ImGui实例,想要运行还需要把渲染的代码放到虚表里,下一节hook虚表,然后把我们的代码放进去

带说明的代码,由Ai生成

// 包含程序所需的头文件(通常包含DirectX、ImGui等库的声明)
#include "main.h"// 获取目标窗口的句柄(HWND)
// FindWindowA参数说明:
// - 第一个参数:窗口类名(为空字符串表示不限制类名)
// - 第二个参数:窗口标题(NULL表示查找任何窗口)
// 作用:获取一个已存在窗口的句柄,后续DirectX渲染会输出到该窗口
HWND hWnd = FindWindowA("", NULL);// DirectX 11核心全局资源(整个程序共享)
static ID3D11Device* g_pd3dDevice = nullptr;               // D3D11设备对象,负责创建渲染资源
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr; // D3D11设备上下文,负责执行渲染命令
static IDXGISwapChain* g_pSwapChain = nullptr;             // 交换链对象,管理前后缓冲区实现双缓冲
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr; // 主渲染目标视图,绑定交换链的后台缓冲区/*** 线程函数:负责初始化DirectX 11和ImGui,并执行首次UI渲染* 函数签名符合Windows线程函数要求(LPVOID参数,返回DWORD)* 参数:* - lpThreadParameter:线程启动时传递的参数(此处未使用)* 返回值:DWORD类型,0通常表示执行成功*/
DWORD Go(LPVOID lpThreadParameter  // 线程参数(未使用,保留为兼容线程函数签名)
) {// 初始化交换链描述结构体(DXGI_SWAP_CHAIN_DESC),定义交换链的属性DXGI_SWAP_CHAIN_DESC sd;ZeroMemory(&sd, sizeof(sd));  // 清空结构体,避免未初始化内存导致的错误// 配置交换链核心参数sd.BufferCount = 2;                   // 缓冲区数量(双缓冲:1个前台显示,1个后台渲染)sd.BufferDesc.Width = 0;              // 缓冲区宽度(0表示自动匹配窗口宽度)sd.BufferDesc.Height = 0;             // 缓冲区高度(0表示自动匹配窗口高度)sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;  // 像素格式(32位RGBA,8位/通道)sd.BufferDesc.RefreshRate.Numerator = 60;  // 刷新率分子(60Hz)sd.BufferDesc.RefreshRate.Denominator = 1; // 刷新率分母(60/1=60Hz)sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;  // 允许切换显示模式(窗口/全屏)sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;   // 缓冲区用途(作为渲染目标)sd.OutputWindow = hWnd;               // 绑定的窗口句柄(渲染结果输出到该窗口)sd.SampleDesc.Count = 1;              // 多重采样数量(1表示无抗锯齿)sd.SampleDesc.Quality = 0;            // 采样质量(0表示默认)sd.Windowed = TRUE;                   // 窗口模式(TRUE为窗口化,FALSE为全屏)sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;  // 交换效果(交换后丢弃后台缓冲区数据)// 创建设备的标志(可添加调试标志)UINT createDeviceFlags = 0;// createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;  // 启用调试模式(需要安装DirectX SDK)D3D_FEATURE_LEVEL featureLevel;  // 存储实际支持的Direct3D特性级别(版本)// 支持的D3D版本列表(优先使用11.0,不支持则降级到10.0)const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0 };// 创建设备、设备上下文和交换链HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr,                       // 显卡适配器(nullptr使用默认显卡)D3D_DRIVER_TYPE_HARDWARE,      // 驱动类型(硬件加速,使用GPU)nullptr,                       // 软件渲染模块(未使用)createDeviceFlags,             // 创建设备的标志featureLevelArray,             // 支持的D3D版本列表2,                             // 版本列表数量D3D11_SDK_VERSION,             // SDK版本(使用当前版本)&sd,                           // 交换链描述结构体&g_pSwapChain,                 // 输出:创建的交换链&g_pd3dDevice,                 // 输出:创建的D3D设备&featureLevel,                 // 输出:实际支持的D3D版本&g_pd3dDeviceContext           // 输出:创建的设备上下文);// 若硬件加速失败(如显卡不支持D3D11),尝试使用WARP软件渲染驱动if (res == DXGI_ERROR_UNSUPPORTED)res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP,  // 软件渲染驱动(CPU模拟GPU)nullptr, createDeviceFlags,featureLevelArray, 2,D3D11_SDK_VERSION, &sd,&g_pSwapChain, &g_pd3dDevice,&featureLevel, &g_pd3dDeviceContext);// 若设备/交换链创建失败,返回错误(0表示成功,此处返回false可能是笔误,应为非0)if (res != S_OK)return false;// 获取交换链的后台缓冲区(用于创建渲染目标视图)ID3D11Texture2D* pBackBuffer;  // 临时指针,存储后台缓冲区g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));  // 0表示第一个缓冲区// 使用后台缓冲区创建渲染目标视图(告诉GPU渲染结果输出到这里)g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);pBackBuffer->Release();  // 释放临时缓冲区指针(渲染目标视图已引用该资源)// 初始化ImGui上下文(管理UI状态和资源的核心对象)ImGui::CreateContext();// 设置ImGui的UI风格为暗色主题(内置风格之一,还有亮色、经典风格等)ImGui::StyleColorsDark();// 初始化ImGui的Win32后端(连接ImGui与Windows窗口系统,处理输入事件)ImGui_ImplWin32_Init(hWnd);// 初始化ImGui的DX11后端(连接ImGui与DirectX 11,负责UI渲染)ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);// 定义一个ImGui窗口("Hello, world!"为窗口标题)// 注意:正常流程中,窗口定义应放在NewFrame()之后、Render()之前的主循环内// 此处直接调用Begin/End缺少帧初始化,可能导致UI无法正确显示ImGui::Begin("Hello, world!");  // 开始定义窗口内容(此处无实际内容)ImGui::End();  // 结束窗口定义// 生成UI绘制数据(将窗口/控件转换为GPU可识别的绘制命令)ImGui::Render();// 设置渲染目标(告诉GPU接下来的渲染输出到主渲染目标)g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);// 通过DX11后端执行绘制命令,将ImGui UI渲染到屏幕上ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());// 线程执行完成,返回0表示成功return 0;
}

AI生成的总结

上方代码的核心目的:在一个已有的 Windows 窗口上,用 DirectX11(负责画图的工具)和 ImGui(快速做 UI 的库),画一个简单的 “Hello, world!” 窗口。

一、先搞懂 3 个关键工具是干嘛的

在看代码前,先记 3 个 “小角色”:

  1. DirectX11:相当于 “画笔 + 画布”,负责把图像(比如按钮、文本)画到屏幕上;
  2. ImGui:相当于 “UI 模板库”,不用自己写按钮、窗口的底层代码,直接调用现成的功能;
  3. Windows 窗口句柄(hWnd):相当于 “贴画的地方”,UI 最终要画在这个窗口上。

二、代码分 5 步走:从 “准备工具” 到 “画出 UI”

第 1 步:找到要画 UI 的窗口(找 “贴画的地方”)

HWND hWnd = FindWindowA("", NULL);
  • 作用:找一个已经存在的 Windows 窗口(比如记事本、浏览器窗口),hWnd就是这个窗口的 “身份证号”;
  • 小白注意:这里参数是空的,可能会找到 “不对的窗口”(比如本来想画在游戏窗口,结果画在了桌面窗口),实际用的时候要填窗口标题或类名。

第 2 步:初始化 DirectX11(准备 “画笔 + 画布”)

这部分是最复杂的,但核心就干 3 件事:

  1. 创建 “设备(g_pd3dDevice)”:相当于 “画笔工厂”,负责造画图需要的各种工具(比如画 UI 的颜色、形状);
  2. 创建 “交换链(g_pSwapChain)”:相当于 “双画布”,一个画布显示当前画面,另一个画布偷偷画下一针,画完再切换,避免画面闪烁;
  3. 创建 “渲染目标(g_mainRenderTargetView)”:相当于 “指定画布的作画区域”,告诉 DirectX “UI 要画在交换链的这个区域里”。
  • 小白注意:如果显卡不支持 DirectX11,代码会自动用 “软件渲染”(用 CPU 模拟显卡,速度慢但能跑)。

第 3 步:初始化 ImGui(准备 “UI 模板”)

ImGui::CreateContext();       // 开一个ImGui的“工作环境”
ImGui::StyleColorsDark();     // 给UI选个风格(这里是深色主题,还有亮色、经典款)
ImGui_ImplWin32_Init(hWnd);   // 让ImGui能“认”这个Windows窗口,处理鼠标/键盘输入
ImGui_ImplDX11_Init(...)      // 让ImGui能“用”DirectX11的画笔,把UI画出来
  • 作用:把 ImGui 和 Windows 窗口、DirectX11 连起来,不然 ImGui 不知道该画在哪、用什么工具画。

第 4 步:定义要画的 UI(选 “UI 模板”)

ImGui::Begin("Hello, world!");  // 新建一个窗口,标题是“Hello, world!”
ImGui::End();                   // 窗口定义结束(这里窗口里没内容,只有个空标题栏)
  • 小白注意:这就像搭积木,Begin是 “开始搭一个窗口”,中间可以加按钮(ImGui::Button)、文本(ImGui::Text),End是 “搭完这个窗口”。

第 5 步:把 UI 画到屏幕上(动手 “贴画”)

ImGui::Render();                       // 让ImGui把定义好的UI转换成“画图指令”
g_pd3dDeviceContext->OMSetRenderTargets(...);  // 告诉DirectX“接下来画到这个区域”
ImGui_ImplDX11_RenderDrawData(...);    // 让DirectX执行画图指令,把UI画出来
  • 作用:从 “纸上谈兵”(定义 UI)到 “实际动手”(画到屏幕),这一步才是真正让 UI 显示出来。

三、小白要注意的 3 个 “坑”

  1. 没有 “循环”,UI 只显示一次:这段代码画完一次 UI 就结束了,没法互动(比如点按钮没反应)。实际用的时候要加个while循环,让代码反复画 UI、处理输入;
  2. 没 “清理垃圾”:代码创建的 DirectX 和 ImGui 资源(比如设备、交换链)没释放,程序关了可能还占内存。要在最后加Release()(释放 DirectX 资源)和Shutdown()(关闭 ImGui);
  3. 找窗口可能找错FindWindowA("", NULL)会找 “任意窗口”,如果开了多个程序,可能画错地方。要改成FindWindowA("窗口类名", "窗口标题")(比如找记事本窗口,标题是 “无标题 - 记事本”)。

四、整体逻辑一句话总结

找一个窗口 → 准备好画图工具(DirectX11) → 准备好 UI 模板(ImGui) → 选一个 UI 模板(空窗口) → 用工具把 UI 画到窗口上。


img


文章转载自:

http://mEbSHwaK.mgmyt.cn
http://fKtxTEOk.mgmyt.cn
http://sRYporUl.mgmyt.cn
http://kvVv2gVh.mgmyt.cn
http://TRyuA3wy.mgmyt.cn
http://DCndSaFL.mgmyt.cn
http://EURVtWIq.mgmyt.cn
http://3i2ijcf3.mgmyt.cn
http://I2NlVlWr.mgmyt.cn
http://Qo4eecQm.mgmyt.cn
http://wdm3yA0R.mgmyt.cn
http://N42ZDQ7p.mgmyt.cn
http://Te9BtfFQ.mgmyt.cn
http://AT4mIN6f.mgmyt.cn
http://BuUctWGs.mgmyt.cn
http://5p6HUhvG.mgmyt.cn
http://wVSDSMWn.mgmyt.cn
http://ESRj7tmd.mgmyt.cn
http://kromVxqs.mgmyt.cn
http://6ns4kb7C.mgmyt.cn
http://rz5325gQ.mgmyt.cn
http://t3TNlkXJ.mgmyt.cn
http://gAa5TkJB.mgmyt.cn
http://GVUYwiSG.mgmyt.cn
http://HDIyDxZI.mgmyt.cn
http://XqaWQCa7.mgmyt.cn
http://3Q4HKZKS.mgmyt.cn
http://xr3UkLuF.mgmyt.cn
http://RBQCa1R7.mgmyt.cn
http://gVut9pfs.mgmyt.cn
http://www.dtcms.com/a/382671.html

相关文章:

  • 工业互联网与数字孪生:解码产业数字化转型的核心支撑
  • 知识库内容冗余重复该怎么办
  • ScreenToGif:一款免费开源的屏幕录制与GIF制作工具
  • XHR与Fetch取消请求的方法及原理深度解析
  • 除了 transformer 还有哪些 新的 神经网络架构
  • 鸿蒙NEXT的Web组件网络安全与隐私保护实践
  • D. Coprime
  • 利用python pandas库清洗病例处方清洗步骤
  • 数据库在并发访问时,不同隔离级别下脏读幻读问题
  • Python核心技术开发指南(065)——with语句
  • Python核心技术开发指南(064)——析构方法
  • 20250913-01: Langchain概念:Runnable可运行接口
  • 记一次谷歌语法获取路径 针对空白页面
  • Java GC:从GC Roots到分代设计的哲学
  • 一款4000℃高温材料设计方案及性能预测
  • 【leetcode】64. 最小路径和
  • 2.10组件间的通信
  • MinerU学习
  • 网络安全学习
  • 如何用 Rust 重写 SQLite 数据库(一):项目探索
  • Qwen3-80B-A3B混合注意力机制
  • OBS使用教程:OBS多路推流插件如何下载?如何安装使用?
  • 禁用 vscode 的终端的粘滞滚动
  • 人工智能通识与实践 - 人工智能概述
  • Symantec卸载
  • 第34章 AI在文娱与内容创作领域的应用
  • 学生信息管理系统(面向对象初步接触)
  • LangChain 中 Output Parsers 是什么?
  • Wolfspeed重组计划已确认
  • 【C++】继承机制深度解析:多继承与菱形继承