在windows程序中嵌入Lua脚本引擎–编写自己的Lua库

在《在windows程序中嵌入lua脚本引擎–建立一个简易的“云命令”执行的系统》一文中,我提到了使用lua的ffi库,可以让我们像编写c代码一样编写lua程序。这对我们这些c程序员来说是一件令人兴奋的事。然而,使用ffi库编写的程序通常会比较大,因为我们可能需要声明一些api的原型和结构体。比如,在luajit的wiki中有一个关于使用ffi调用kernel32的例子。(转载请注明出自breaksoftware的csdn博客)如果我们想要使用

BOOL HeapWalk(HANDLE hHeap, PROCESS_HEAP_ENTRY * lpEntry);

在Lua中,我们需要进行如下声明:

ffi.cdef[[typedef struct _PROCESS_HEAP_ENTRY {    PVOID lpData;    DWORD cbData;    BYTE cbOverhead;    BYTE iRegionIndex;    WORD wFlags;    union {        struct {            HANDLE hMem;            DWORD dwReserved[ 3 ];        } Block;        struct {            DWORD dwCommittedSize;            DWORD dwUnCommittedSize;            LPVOID lpFirstBlock;            LPVOID lpLastBlock;        } Region;    } DUMMYUNIONNAME;} PROCESS_HEAP_ENTRY, *LPPROCESS_HEAP_ENTRY, *PPROCESS_HEAP_ENTRY;BOOL HeapWalk(HANDLE hHeap, PROCESS_HEAP_ENTRY * lpEntry);]]

也就是说,我们需要告诉Lua,我们要调用的函数的名字以及函数使用的结构体的原型。

看到这里,可能会让想使用ffi库的朋友感到有些退缩。那么,我们如何才能更简洁地调用这个函数呢?那就是:编写我们自己的Lua库”fl”。

我们可以参考Luajit中os库的声明方式。

在lualib.h中新增我们库的名字”fl”,并声明注册我们库的函数luaopen_fl

……#define LUA_FFILIBNAME"ffi"#define LUA_FLLIBNAME   "fl"
……LUALIB_API int luaopen_ffi(lua_State *L);LUALIB_API int luaopen_fl(lua_State *L);

在lib_init.c中,将我们的库名字和打开库的名字绑定

……  { LUA_JITLIBNAME,luaopen_jit },  { LUA_FLLIBNAME, luaopen_fl },  { NULL,NULL }

在Lualib目录下新建一个lib_fl.c文件

实现最基本的函数和结构。主要是实现注册我们库的luaopen_fl函数,以及函数名和函数地址绑定结构体数组luaL_Reg_fl_lib

#include "lua.h"#include "lauxlib.h"#include "lualib.h"#include "lj_lib.h"static const luaL_Reg fl_lib[] = {    { NULL, NULL }};LUALIB_API int luaopen_fl(lua_State *L){    luaL_register(L, LUA_FLLIBNAME, fl_lib);    return 1;}

将lib_fl.c加入《在Windows程序中嵌入Lua脚本引擎–使用VS IDE编译Luajit脚本引擎》中介绍的Lua工程。

修改《在Windows程序中嵌入Lua脚本引擎–使用VS IDE编译Luajit脚本引擎》中介绍的Buildvm工程的生成后事件。在事件中,将lib_fl.c加入ALL_LIB中

@set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c lib_fl.c

在ljamalg.c中新增#include “lib_fl.c”

这样我们便新增了一个名为fl的库,我们可以

fl = require "fl"fl.……

来调用它。

现在我们要扩展我的库:

A. 在fl库中新增一个获取系统版本信息的函数

LJLIB_CF(fl_GetSystemVersion){    OSVERSIONINFOA osver;    ZeroMemory(&osver, sizeof(OSVERSIONINFOA));    osver.dwOSVersionInfoSize = sizeof(osver);    if ( GetVersionExA(&osver) )    {        lua_pushnumber(L, osver.dwMajorVersion);        lua_pushnumber(L, osver.dwMinorVersion);        lua_pushnumber(L, osver.dwBuildNumber);        lua_pushnumber(L, osver.dwPlatformId);        lua_pushstring(L, osver.szCSDVersion);        return 5;    }    else    {        return 0;    }}

并在fl_lib数组中新增名字和该函数的地址的绑定

static const luaL_Reg fl_lib[] = {    { "GetSystemVersion",lj_cf_fl_GetSystemVersion },    { NULL, NULL }};

这样我们编译出来的Luajit便可以使用简单的方法调用获取系统版本了。

豆包AI编程 豆包AI编程

豆包推出的AI编程助手

豆包AI编程 483 查看详情 豆包AI编程

在windows程序中嵌入Lua脚本引擎--编写自己的Lua库

B. 在fl库中新增一个获取系统中所有进程的函数

为了让我们的这个例子尽可能复杂,我不准备使用快照的方法去获取进程信息。而是使用Windows未公开的函数NtQuerySystemInformation。我在之前的《使用APIHOOK实现进程隐藏》中介绍过该方法。

#include #define MAXLOOPCOUNT 5 #define MAXPROCSSNUM 1024#define STATUS_SUCCESS 0x00000000#define SystemProcessInformation 5#define STATUS_SUCCESS 0x00000000#define STATUS_INFO_LENGTH_MISMATCH ((ULONG)0xC0000004L)typedef DWORD (WINAPI * Fun_NtQuerySystemInformation)( DWORD, PVOID, DWORD, PDWORD );typedef struct _UNICODE_STRING {    USHORT Length;    USHORT MaximumLength;    PWSTR  Buffer;} UNICODE_STRING;typedef UNICODE_STRING *PUNICODE_STRING;typedef const UNICODE_STRING *PCUNICODE_STRING;typedef struct _SYSTEM_PROCESS_INFORMATION{    DWORD           dwNextEntryOffset;                  // 下段结构对象的偏移    DWORD           dwNumberOfThreads;                  // 线程数    LARGE_INTEGER   qSpareLi1;                              LARGE_INTEGER   qSpareLi2;    LARGE_INTEGER   qSpareLi3;    LARGE_INTEGER   qCreateTime;                        // 创建时间    LARGE_INTEGER   qUserTime;                          // 用户态时间    LARGE_INTEGER   qKernelTime;                        // 内核态时间    UNICODE_STRING  ImageName;                          // 文件名(非路径)    int             nBasePriority;                      // 基本优先级    DWORD           dwProcessId;                        // 进程ID    DWORD           dwInheritedFromUniqueProcessId;     // 父进程ID    DWORD           dwHandleCount;                      // 句柄数    DWORD           dwSessionId;    ULONG           dwSpareUl3;    SIZE_T          tPeakVirtualSize;    SIZE_T          tVirtualSize;    DWORD           dwPageFaultCount;    DWORD           dwPeakWorkingSetSize;    DWORD           dwWorkingSetSize;    SIZE_T          tQuotaPeakPagedPoolUsage;    SIZE_T          tQuotaPagedPoolUsage;    SIZE_T          tQuotaPeakNonPagedPoolUsage;    SIZE_T          tQuotaNonPagedPoolUsage;    SIZE_T          tPagefileUsage;    SIZE_T          tPeakPagefileUsage;    SIZE_T          tPrivatePageCount;    LARGE_INTEGER   qReadOperationCount;    LARGE_INTEGER   qWriteOperationCount;    LARGE_INTEGER   qOtherOperationCount;    LARGE_INTEGER   qReadTransferCount;    LARGE_INTEGER   qWriteTransferCount;    LARGE_INTEGER   qOtherTransferCount;}SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;BOOL GetNtQuerySystemInfoBuffer( IN DWORD SystemInformationClass, void** lpBuffer ){    BOOL bSuccess = FALSE;    HMODULE hNtDll =  NULL;    Fun_NtQuerySystemInformation NtQuerySystemInformation = NULL;    ULONG cbBuffer = 0x0001;    ULONG ulNeedBytes = cbBuffer;    int nloopcount = 0;    ULONG nstatus = 0;    void* pBuffer = NULL;    *lpBuffer = NULL;    hNtDll = GetModuleHandle( L"ntdll.dll" );    do {        if( NULL == hNtDll ) {            // 加载ntdll失败,返回FALSE            break;        }        NtQuerySystemInformation =             ( Fun_NtQuerySystemInformation ) GetProcAddress( hNtDll, "NtQuerySystemInformation" );        if ( NULL == NtQuerySystemInformation ) {            // 获取导出函数NtQuerySystemInformation失败,返回FALSE            break;        }        // 预分配的空间大小,先分配一个小空间,理论上使用这个空间去获取信息是失败的        cbBuffer = 0x0001;        ulNeedBytes = cbBuffer;        // 分配内存用于保存进程信息,只分配不释放,由外部释放        *lpBuffer = malloc( cbBuffer );        nloopcount = 0;        do {            // 为了防止无限循环,做个最大循环限制            if ( nloopcount > MAXLOOPCOUNT ) {                break;            }            nloopcount++;            if( NULL == *lpBuffer ) {                // 分配内存失败,返回FALSE                break;            }            // 理论上,第一次执行这个函数会失败的,因为分配的空间太小            nstatus = NtQuerySystemInformation(                 SystemInformationClass,                 *lpBuffer,                cbBuffer,                &ulNeedBytes );            if ( STATUS_INFO_LENGTH_MISMATCH == nstatus ) {                // 理论上,第一次执行NtQuerySystemInformation后会进入这里进行内存的再次扩容                cbBuffer = ulNeedBytes;                // 重新分配内存用于保存进程信息,只分配不释放,由外部释放                pBuffer = realloc( *lpBuffer, cbBuffer );                if (pBuffer != NULL)                    *lpBuffer = pBuffer;                continue;            }            else if ( STATUS_SUCCESS == nstatus ) {                // 成功                bSuccess = TRUE;                break;            }            else {                // 非内存大小分配不够导致的错误                bSuccess = FALSE;                break;            }        } while(1);    } while( 0 );    if ( NULL != hNtDll ) {        FreeLibrary( hNtDll );        hNtDll = NULL;    }    if ( FALSE == bSuccess ) {        // 如果获取信息失败,则释放分配的内存        if ( NULL !=  *lpBuffer ) {            free( *lpBuffer );            *lpBuffer = NULL;        }    }    return bSuccess;}

看到如上结构体,要是在Lua中用ffi去声明,岂不是很崩溃!我们再看下填充数据的辅助函数

BOOL GetProcessFullInfo(lua_State *L){    BOOL bSuccess = FALSE;    PSYSTEM_PROCESS_INFORMATION pInfo = NULL;    int nloopcount = 0;    do {        LPVOID pBuffer = NULL;        if ( FALSE == GetNtQuerySystemInfoBuffer( SystemProcessInformation, &pBuffer) ) {            // 获取失败,直接返回,不用释放pBuffer,因为GetZwQuerySystemInfoBuffer内部就释放了            break;        }        if ( NULL == pBuffer ) {            break;        }        pInfo = ( PSYSTEM_PROCESS_INFORMATION ) pBuffer;        nloopcount = 0;        lua_newtable( L );        while( NULL != pInfo ) {            // 为了防止无限循环,做个最大进程数限制            if ( nloopcount > MAXPROCSSNUM ) {                break;            }            nloopcount++;            if( 0 == pInfo->dwNextEntryOffset ) {                // 找到末尾最后一个了,就退出循环                break;            }            else {                lua_pushinteger( L, pInfo->dwProcessId);                //////////////////////////////////////////////////////////////////////////                lua_newtable( L );                lua_pushinteger( L, pInfo->dwInheritedFromUniqueProcessId );                lua_pushlstring( L, (char*)pInfo->ImageName.Buffer, pInfo->ImageName.Length );                lua_settable( L, -3 );                //////////////////////////////////////////////////////////////////////////                lua_settable( L, -3 );                // 利用偏移,找到下一个结构对象                pInfo = ( PSYSTEM_PROCESS_INFORMATION)(                     ( (PUCHAR) pInfo ) + pInfo->dwNextEntryOffset );            }        }        if ( NULL != pBuffer ) {            free( pBuffer );            pBuffer = NULL;        }        bSuccess = TRUE;    } while( 0 );    return bSuccess;}

该函数生成一个如同

struct PINFO{DWORD dwPPID; // 父进程IDwstring wstrProcessName; // 本进程名字};struct PINFOEX{DWORD dwPID;  // 本进程IDPINFO Info;}list PINFOLIST;

的结果。

如果我们执行如此简短的Lua脚本

fl = require "fl"allprocessinfo = fl.GetAllProcess()for PID,item in pairs(allprocessinfo) dofor PPID,PNAME in pairs(item) do  print(PPID,PNAME)endend

将把我们系统中进程信息打印出来。

在windows程序中嵌入Lua脚本引擎--编写自己的Lua库

介绍Luajit嵌入Win32程序的文章讲完了。

最后,该工程源码都在以下链接:https://www.php.cn/link/3244ef4c2aeb17c03511c93cb43caef0 密码:pknq

以上就是在windows程序中嵌入Lua脚本引擎–编写自己的Lua库的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/458506.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
新技术推出,IDEA研究院发布T-Rex模型,让用户能够直接在图像上选择“Prompt”提示
上一篇 2025年11月8日 02:32:50
VSCode如何提升代码审查效率?CodeReview插件简化代码评论流程
下一篇 2025年11月8日 02:32:54

相关推荐

  • Effidit的”个性化词库”怎么设置?如何添加行业术语或专有名词?

    Effidit的”个性化词库”怎么设置?如何添加行业术语或专有名词?Effidit的”个性化词库”怎么设置?如何添加行业术语或专有名词?Effidit的”个性化词库”怎么设置?如何添加行业术语或专有名词?Effidit的”个性化词库”怎么设置?如何添加行业术语或专有名词?

    设置effidit个性化词库的关键在于找到词库管理入口并添加专属词汇。1.进入设置或个人资料页面,选择添加新词或导入词库;2.手动输入术语或导入已有文本文件,并为词条添加标签或解释;3.词库生效后,effidit将优先使用这些词汇提供写作建议,提升专业性和效率;4.持续优化词库内容和标签结构,以适应…

    2026年9月29日 • 用户投稿
    100
  • 电脑怎么禁用自动装载外接移动设备

    电脑怎么禁用自动装载外接移动设备电脑怎么禁用自动装载外接移动设备电脑怎么禁用自动装载外接移动设备电脑怎么禁用自动装载外接移动设备

    相信大家在日常办公中都会使用u盘、移动硬盘等外接存储设备来传输和保存文件。但有时会遇到设备无法识别,或插入后导致系统运行缓慢的问题。这时,我们可以尝试关闭系统的自动装载功能,待问题修复后再重新启用。如果你还不了解具体操作,不妨跟着下面的步骤一起来学习。 操作步骤: 右键点击“开始”菜单图标,选择“W…

    2026年9月29日 • 用户投稿
    100
  • 安装 Windows 系统时,鼠标和键盘突然失灵,无法操作?

    安装 Windows 系统时,鼠标和键盘突然失灵,无法操作?安装 Windows 系统时,鼠标和键盘突然失灵,无法操作?安装 Windows 系统时,鼠标和键盘突然失灵,无法操作?安装 Windows 系统时,鼠标和键盘突然失灵,无法操作?

    鼠标键盘失灵因USB驱动缺失,解决方法是集成USB 3.0/3.1驱动到安装盘或启用BIOS中Legacy USB支持,优先使用主板官网驱动并通过NTLite等工具注入ISO,或改用PS/2设备完成安装。 在安装Windows系统时遇到鼠标和键盘突然失灵、无法操作的情况,这通常是由于安装介质中缺少了…

    2026年9月29日 • 用户投稿
    000
  • win10连接投影仪没反应怎么办_win10投影连接异常修复方法

    首先检查投影仪连接与输入源设置,确保线缆完好且设备正常开启;接着通过Win+P切换显示模式并检测第二屏幕;更新显卡驱动以排除兼容问题;重启设备恢复通信;最后确认无线投影功能已启用并处于同一网络。 如果您尝试将Windows 10电脑连接到投影仪,但屏幕没有信号或无法显示画面,可能是由于连接、设置或驱…

    2026年9月29日
    000
  • 豆包AI怎么生成报告 豆包AI报告生成方法

    豆包AI怎么生成报告 豆包AI报告生成方法豆包AI怎么生成报告 豆包AI报告生成方法豆包AI怎么生成报告 豆包AI报告生成方法豆包AI怎么生成报告 豆包AI报告生成方法

    豆包ai生成报告的方法主要包括四步。一、准备好输入内容,确保有清晰的数据或框架,如行业数据、课程内容等,数据越具体生成内容越精准;二、使用合适的提示词,明确告诉ai生成报告类型及具体要求,例如“请根据以下销售数据生成季度分析报告”;三、调整格式与细节,检查标题结构、数据准确性、语言风格,并补充图表以…

    2026年9月29日 • 用户投稿
    200
  • OPPO Find X9系列外观公布:告别圆形“奥利奥”模组

    OPPO Find X9系列外观公布:告别圆形“奥利奥”模组OPPO Find X9系列外观公布:告别圆形“奥利奥”模组OPPO Find X9系列外观公布:告别圆形“奥利奥”模组OPPO Find X9系列外观公布:告别圆形“奥利奥”模组

    今天,oppo官方通过其微博平台首次揭晓了oppo find x9系列的外观设计细节。 从发布的宣传海报可以看出,Find X9系列摒弃了自Find X6以来一直沿用的圆形“奥利奥”镜头模组设计,转而采用左上角布局的圆角矩形相机模块,整体风格更为方正且富有现代感。 在具体配置方面,Find X9标准…

    2026年9月29日 • 用户投稿
    000
  • win10窗口自定义调整的具体操作方法

    win10窗口自定义调整的具体操作方法win10窗口自定义调整的具体操作方法win10窗口自定义调整的具体操作方法win10窗口自定义调整的具体操作方法

    安装windows 10后,许多用户会发现系统中的控件和图标尺寸明显变大,使用起来不太顺手。那么,如何自定义调整win10窗口的大小呢?下面将详细介绍具体的操作步骤。 1、按下快捷键 Win + R 打开“运行”对话框,在输入框中键入 regedit,然后按回车键确认。 2、进入注册表编辑器后,依次…

    2026年9月29日 • 用户投稿
    000
  • 解决Vaadin Grid“同一属性多列”异常:理解自动列生成与手动添加

    解决Vaadin Grid“同一属性多列”异常:理解自动列生成与手动添加解决Vaadin Grid“同一属性多列”异常:理解自动列生成与手动添加解决Vaadin Grid“同一属性多列”异常:理解自动列生成与手动添加解决Vaadin Grid“同一属性多列”异常:理解自动列生成与手动添加

    Vaadin Grid在初始化时传入实体类(如new Grid(Audit.class))会自动为其所有属性创建列。若随后又手动调用grid.addColumns(“propertyName”)添加已存在的属性列,将导致“Multiple columns for the sa…

    2026年9月29日 • 用户投稿
    000
  • 如何让豆包AI实现Python文本分析

    如何让豆包AI实现Python文本分析如何让豆包AI实现Python文本分析如何让豆包AI实现Python文本分析如何让豆包AI实现Python文本分析

    想让豆包ai做python文本分析的关键在于明确目标、写好提示词并结合python自动化处理。1. 明确分析内容,如实体识别、情感分析、关键词提取或文本分类,并选择合适工具库如jieba、pandas和requests;2. 编写清晰提示词,避免模糊表达,提升结果准确性;3. 使用python调用a…

    2026年9月29日 • 用户投稿
    000
  • Kotlin/Native与JVM混合部署:性能优化与跨平台兼容性实现指南

    Kotlin/Native与JVM混合部署:性能优化与跨平台兼容性实现指南Kotlin/Native与JVM混合部署:性能优化与跨平台兼容性实现指南Kotlin/Native与JVM混合部署:性能优化与跨平台兼容性实现指南Kotlin/Native与JVM混合部署:性能优化与跨平台兼容性实现指南

    本文探讨了如何在Java/JVM应用中巧妙结合Kotlin/Native编译的原生可执行文件与纯JVM实现,以兼顾极致性能和广泛跨平台兼容性。核心策略是利用Java Native Interface (JNI) 作为桥梁,在运行时根据平台加载并调用Kotlin/Native生成的动态库,并在原生库不…

    2026年9月29日 • 用户投稿
    000
  • Sublime项目快速启动 Sublime常用工程收藏夹

    Sublime项目快速启动 Sublime常用工程收藏夹Sublime项目快速启动 Sublime常用工程收藏夹Sublime项目快速启动 Sublime常用工程收藏夹Sublime项目快速启动 Sublime常用工程收藏夹

    高效管理sublime text项目的核心在于利用.sublime-project文件和快速切换功能。1. 创建.sublime-project文件保存工作区快照,包括文件夹路径、视图布局、特定设置等;2. 将项目文件保存在项目根目录或集中存放于统一目录(如~/sublimeprojects)以便管…

    2026年9月29日 • 用户投稿
    000
  • win10镜像如何重装系统win7系统?一键重装win7系统教程

    win10镜像如何重装系统win7系统?一键重装win7系统教程win10镜像如何重装系统win7系统?一键重装win7系统教程win10镜像如何重装系统win7系统?一键重装win7系统教程win10镜像如何重装系统win7系统?一键重装win7系统教程

    有部分用户刚刚重装完win10系统,却开始怀念起win7的操作体验,觉得还是老系统更顺手。遇到这种情况该怎么办呢?别担心,小编为大家准备了多种解决方案,总有一款适合你。接下来就详细聊聊如何将win10系统更换为win7系统。 一键切换至win7系统 推荐使用一键重装工具,直接在当前系统下完成win7…

    2026年9月29日 • 用户投稿
    100
  • Room 数据库预填充数据为空的排查与解决方案

    Room 数据库预填充数据为空的排查与解决方案Room 数据库预填充数据为空的排查与解决方案Room 数据库预填充数据为空的排查与解决方案Room 数据库预填充数据为空的排查与解决方案

    本文深入探讨了Android Room数据库预填充数据后列表仍显示为空的常见原因与解决方案。核心问题在于RoomDatabase.Callback中的onCreate方法仅在数据库首次创建时执行一次。文章详细分析了这一生命周期行为,并提供了通过卸载应用或清除数据来强制数据库重新创建的直接方法,同时介…

    2026年9月29日 • 用户投稿
    000
  • 豆包AI编程操作指南 豆包AI代码生成技巧

    豆包AI编程操作指南 豆包AI代码生成技巧豆包AI编程操作指南 豆包AI代码生成技巧豆包AI编程操作指南 豆包AI代码生成技巧豆包AI编程操作指南 豆包AI代码生成技巧

    要提升豆包ai编程效率需掌握四个关键点:一、明确需求并写好提示词,包括语言类型、功能目标及具体要求;二、利用上下文逐步补充细节,提高生成准确性;三、检查代码逻辑与变量命名,测试边界条件并谨慎对待安全相关代码;四、将ai作为辅助工具,结合本地开发流程用于模板生成、文档理解与思路提供。 ☞☞☞AI 智能…

    2026年9月29日 • 用户投稿
    000
  • 广州新地标!小鹏汽车新总部正式亮灯 明天新车上市!

    据小鹏汽车官方消息,8月26日,小鹏汽车位于广州天河智慧城岑村片区的新总部——小鹏科技园正式亮灯启用。这座总建筑面积达36万平方米的科技园区,以步步高升的“x”造型设计成为广州又一科技新地标。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ …

    2026年9月29日
    000
  • [pcl][原创]python-pcl安装配置支持windows和ubuntu完美流程 2021版本

    [pcl][原创]python-pcl安装配置支持windows和ubuntu完美流程 2021版本[pcl][原创]python-pcl安装配置支持windows和ubuntu完美流程 2021版本[pcl][原创]python-pcl安装配置支持windows和ubuntu完美流程 2021版本[pcl][原创]python-pcl安装配置支持windows和ubuntu完美流程 2021版本

    目前在全网范围内,关于PCL的Python版本配置方案普遍存在各种兼容性与依赖问题,几乎没有一篇文章能够提供完整、可复现的解决方案。经过长达数月的反复尝试与调试,我最终成功打通了完整的安装流程,并在Windows与Ubuntu双系统上均完成了验证。以下是最终成果展示: 其中,Windows端的成功运…

    2026年9月29日 • 用户投稿
    100
  • Kotlin Native与JVM混合部署:通过JNI实现性能与跨平台兼顾的策略

    Kotlin Native与JVM混合部署:通过JNI实现性能与跨平台兼顾的策略Kotlin Native与JVM混合部署:通过JNI实现性能与跨平台兼顾的策略Kotlin Native与JVM混合部署:通过JNI实现性能与跨平台兼顾的策略Kotlin Native与JVM混合部署:通过JNI实现性能与跨平台兼顾的策略

    本文探讨了如何在单一JAR包中集成Kotlin Native多平台可执行文件与JVM回退实现,以兼顾高性能与极致跨平台能力。核心在于利用Java Native Interface (JNI) 作为桥梁,使JVM应用能动态加载并调用Kotlin Native编译生成的平台特定动态库,同时在原生库不可用…

    2026年9月29日 • 用户投稿
    100
  • 格子达知网查重入口在哪里—格子达毕业设计查重入口

    格子达知网查重入口在哪里—格子达毕业设计查重入口格子达知网查重入口在哪里—格子达毕业设计查重入口格子达知网查重入口在哪里—格子达毕业设计查重入口格子达知网查重入口在哪里—格子达毕业设计查重入口

    格子达查重入口为https://co.gocheck.cn/11653,提供论文重复率检测、AIGC风险评估及多轮自检功能,适配本科毕业设计全流程,支持Word格式直接上传,结果清晰标红便于修改。 格子达知网查重入口在哪里—这是不少网友都关注的,接下来由PHP小编为大家带来格子达毕业设计查重入口地址…

    2026年9月29日 • 用户投稿
    100
  • 理解标准输出缓冲:Python、C、Java与Go的行为差异解析

    理解标准输出缓冲:Python、C、Java与Go的行为差异解析理解标准输出缓冲:Python、C、Java与Go的行为差异解析理解标准输出缓冲:Python、C、Java与Go的行为差异解析理解标准输出缓冲:Python、C、Java与Go的行为差异解析

    本文深入探讨了不同编程语言在标准输出(stdout)缓冲机制上的差异,特别是在输出连接到终端(TTY)或管道时。我们将解析Python和C在管道场景下默认采用块缓冲,导致输出延迟,而Java和Go则倾向于实时刷新。文章将提供代码示例,并指导如何控制和管理输出缓冲,以确保在各种环境下都能获得预期的程序…

    2026年9月29日 • 用户投稿
    100
  • 如何用API调用Midjourney生成图像 Midjourney API使用与参数详解

    如何用API调用Midjourney生成图像 Midjourney API使用与参数详解如何用API调用Midjourney生成图像 Midjourney API使用与参数详解如何用API调用Midjourney生成图像 Midjourney API使用与参数详解如何用API调用Midjourney生成图像 Midjourney API使用与参数详解

    调用midjourney api可通过第三方平台集成到应用中,具体步骤为:注册账号并获取api密钥、阅读接口文档、发送请求;基本流程包括发送“/imagine”命令、等待任务完成、获取图片链接;常用参数有prompt、aspect、style、version、chaos、seed;生成图片后需注意临…

    2026年9月29日 • 用户投稿
    100

发表回复

登录后才能评论
关注微信