SDL3_API分类参考_共享对象(CategorySharedObject)

2026-3-7 / 0 评论 / 1 阅读

共享对象子系统(CategorySharedObject)

SDL 提供了一套与系统无关的共享库加载接口,用于在运行时动态加载可执行代码模块——这类模块在不同系统中有不同的称呼:Windows 下称为「DLL」,Linux 下称为「共享库(.so)」,macOS 下称为「动态库(.dylib)」等。

使用流程:

  1. 编译生成共享库文件;
  2. 调用 SDL_LoadObject() 加载该共享库;
  3. 通过 SDL_LoadFunction() 从加载的共享库中查找并获取导出符号(函数)的地址;
  4. 使用完毕后,调用 SDL_UnloadObject() 卸载共享库并释放资源。

注意事项

  1. 这些函数仅支持 C 风格函数名:其他语言(如 C++)的函数名会发生「名字改编(name mangling)」,且不同编译器的处理方式不同,无法直接加载;
  2. 函数指针调用约定需匹配:声明的函数指针必须与共享库中实际函数的调用约定(如 cdecl、stdcall)一致,否则程序会莫名崩溃;
  3. 避免命名空间冲突:从共享库加载的符号是否进入应用全局命名空间是未定义行为,若与代码或其他共享库的符号冲突,会导致预期外的结果;
  4. 卸载后指针失效:共享库卸载后,所有通过 SDL_LoadFunction() 获取的函数指针都会失效(即使后续重新加载该库);若计划后续使用这些指针,请勿卸载库(尤其注意不要将这类指针传给 atexit(),可能导致库卸载后调用失效指针)。

函数

  • SDL_LoadObject:加载指定路径的共享库(DLL/.so/.dylib),返回 SDL_SharedObject 句柄(加载失败返回 NULL,可通过 SDL_GetError() 获取错误信息)
  • SDL_LoadFunction:从已加载的共享库中查找指定名称的导出函数,返回函数指针(查找失败返回 NULL)
  • SDL_UnloadObject:卸载已加载的共享库,释放相关资源(传入 NULL 无操作)

数据类型

  • SDL_SharedObject:共享库句柄类型(本质是平台相关的指针类型,用于标识已加载的共享库)

结构体

  • (无)

枚举

  • (无)

  • (无)

FreeBASIC 示例代码

' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库)
#Include "SDL.bi"

' 补充类型和函数声明(FreeBASIC 绑定可能缺失)
Type SDL_SharedObject As Any Ptr
Declare Function SDL_LoadObject CDecl (ByVal path As ZString Ptr) As SDL_SharedObject
Declare Function SDL_LoadFunction CDecl (ByVal so_handle As SDL_SharedObject, ByVal name As ZString Ptr) As Any Ptr
Declare Sub SDL_UnloadObject CDecl (ByVal so_handle As SDL_SharedObject)

' 定义共享库中导出函数的指针类型(需与实际函数签名一致)
' 示例1:无参数无返回值的函数
Type FuncVoid_Void As Sub()
' 示例2:int 参数 + int 返回值的函数
Type FuncInt_Int As Function(ByVal value As Integer) As Integer
' 示例3:字符串参数 + 字符串返回值的函数
Type FuncStr_Str As Function(ByVal str As ZString Ptr) As ZString Ptr

' 加载共享库并调用导出函数
Sub LoadAndCallSharedLib()
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "【加载并调用共享库】")

    ' 1. 定义共享库路径(根据平台适配)
    Dim As ZString * 256 libPath
    #If Defined(SDL_PLATFORM_WINDOWS)
        libPath = "mylib.dll"  ' Windows 下的 DLL 路径
    #Elif Defined(SDL_PLATFORM_LINUX)
        libPath = "./libmylib.so"  ' Linux 下的共享库路径
    #Elif Defined(SDL_PLATFORM_MACOS)
        libPath = "./libmylib.dylib"  ' macOS 下的动态库路径
    #Else
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "不支持的平台")
        Exit Sub
    #EndIf

    ' 2. 加载共享库
    Dim As SDL_SharedObject libHandle = SDL_LoadObject(@libPath)
    If (libHandle = NULL) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "加载共享库失败:%s", SDL_GetError())
        Exit Sub
    End If
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "共享库 %s 加载成功", libPath)

    ' 3. 查找并调用第一个导出函数:void HelloWorld()
    Dim As Any Ptr funcPtr1 = SDL_LoadFunction(libHandle, StrPtr("HelloWorld"))
    If (funcPtr1 = NULL) Then
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "未找到函数 HelloWorld:%s", SDL_GetError())
    Else
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "调用 HelloWorld():")
        Dim As FuncVoid_Void helloFunc = Cast(FuncVoid_Void, funcPtr1)
        helloFunc()  ' 调用共享库中的函数
    End If

    ' 4. 查找并调用第二个导出函数:int Add(int a)
    Dim As Any Ptr funcPtr2 = SDL_LoadFunction(libHandle, StrPtr("Add"))
    If (funcPtr2 = NULL) Then
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "未找到函数 Add:%s", SDL_GetError())
    Else
        Dim As FuncInt_Int addFunc = Cast(FuncInt_Int, funcPtr2)
        Dim As Integer result = addFunc(100)
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "调用 Add(100) → 结果:%d", result)
    End If

    ' 5. 查找并调用第三个导出函数:char* Greet(char* name)
    Dim As Any Ptr funcPtr3 = SDL_LoadFunction(libHandle, StrPtr("Greet"))
    If (funcPtr3 = NULL) Then
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "未找到函数 Greet:%s", SDL_GetError())
    Else
        Dim As FuncStr_Str greetFunc = Cast(FuncStr_Str, funcPtr3)
        Dim As ZString Ptr greetResult = greetFunc(StrPtr("SDL 用户"))
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "调用 Greet(""SDL 用户"") → 结果:%s", *greetResult)
    End If

    ' 6. 卸载共享库(注意:卸载后所有函数指针失效)
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "卸载共享库 %s", libPath)
    SDL_UnloadObject(libHandle)

    ' 测试:卸载后调用函数指针(会导致崩溃,此处仅作警示)
    ' If (funcPtr1 <> NULL) Then
    '     Dim As FuncVoid_Void helloFunc = Cast(FuncVoid_Void, funcPtr1)
    '     helloFunc()  ' 危险:指针已失效!
    ' End If
End Sub

' 演示:处理加载失败的情况(容错逻辑)
Sub HandleLoadFailure()
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "【共享库加载容错处理】")

    ' 尝试加载不存在的库
    Dim As SDL_SharedObject libHandle = SDL_LoadObject(StrPtr("nonexistent_lib.dll"))
    If (libHandle = NULL) Then
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "加载不存在的库失败(预期行为):%s", SDL_GetError())

        ' 容错逻辑:使用内置实现替代
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "使用内置替代函数:")
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "HelloWorld (内置):你好,这是内置实现!")
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Add(100) (内置) → 结果:%d", 100 + 50)
    Else
        SDL_UnloadObject(libHandle)
    End If
End Sub

' 共享库开发示例(供参考:编译为共享库的代码)
' 以下代码需单独编译为 DLL/.so/.dylib,示例中仅作注释说明
' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' ' mylib.c (C 语言共享库源码)
' #include <stdio.h>
' #include <string.h>
' 
' // 导出函数1:无参数无返回值
' __declspec(dllexport) void HelloWorld() {
'     printf("Hello from shared library!\n");
' }
' 
' // 导出函数2:int 参数 + int 返回值
' __declspec(dllexport) int Add(int a) {
'     return a + 50;
' }
' 
' // 导出函数3:字符串参数 + 字符串返回值
' __declspec(dllexport) char* Greet(char* name) {
'     static char buffer[100];
'     snprintf(buffer, sizeof(buffer), "你好,%s!", name);
'     return buffer;
' }
' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''

' 主程序
Sub Main()
    ' 初始化 SDL
    If (SDL_Init(0) < 0) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError())
        Exit Sub
    End If

    ' 运行示例
    LoadAndCallSharedLib()
    HandleLoadFailure()

    ' 清理 SDL
    SDL_Quit()

    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, vbCrLf & "程序正常退出")
End Sub

' 运行主程序
Main()

核心知识点补充

  1. 平台适配要点 平台 共享库后缀 加载路径规则 编译共享库的关键参数
    Windows .dll 优先查找当前目录,再查找系统路径 MinGW: -shared -o mylib.dll
    Linux .so 需指定绝对路径或 LD_LIBRARY_PATH GCC: -shared -fPIC -o libmylib.so
    macOS .dylib 需指定绝对路径或 @rpath Clang: -shared -fPIC -o libmylib.dylib
  2. 函数导出规则

    • C 语言函数默认可导出(Windows 需加 __declspec(dllexport),Linux/macOS 无需额外修饰);
    • C++ 函数需用 extern "C" 避免名字改编,否则无法通过原函数名查找;
    • 函数调用约定需统一(如 Windows 下 DLL 函数常用 stdcall,需在函数指针声明时匹配)。
  3. 常见错误及解决

    • 加载库失败:检查路径是否正确、库文件是否存在、库依赖是否完整(Windows 可通过 Dependency Walker 检查,Linux 用 ldd,macOS 用 otool -L);
    • 查找函数失败:检查函数名是否正确(区分大小写)、是否导出、C++ 函数是否加 extern "C"
    • 调用函数崩溃:检查函数指针类型/调用约定是否匹配、参数个数/类型是否一致。
  4. 内存管理注意

    • 共享库内部分配的内存,需由库自身提供释放函数(避免跨库内存管理导致崩溃);
    • 不要将共享库中的静态变量指针返回给主程序后卸载库(静态变量会随库卸载失效)。

总结

  1. 核心优势

    • 跨平台统一的共享库加载接口,无需适配 Windows/Linux/macOS 的原生 API(如 LoadLibrary/dlopen);
    • 简化动态插件系统开发,支持运行时加载/卸载功能模块;
    • 完善的错误处理机制,可通过 SDL_GetError() 获取详细的加载/查找失败原因。
  2. 使用建议

    • 优先加载 C 风格导出函数,避免 C++ 名字改编问题;
    • 声明函数指针时严格匹配原函数的参数、返回值和调用约定;
    • 实现容错逻辑:共享库加载/查找失败时,使用内置替代函数保证程序可用;
    • 避免频繁加载/卸载共享库,卸载前确保所有函数指针不再使用;
    • 共享库路径优先使用绝对路径,避免平台相关的路径查找问题。
  3. 关键点回顾

    • SDL_LoadObject 加载共享库返回句柄,失败返回 NULL;SDL_LoadFunction 查找函数返回指针,失败返回 NULL;
    • 共享库卸载后,所有通过 SDL_LoadFunction 获取的指针立即失效,禁止调用;
    • 仅支持 C 风格函数名,C++ 函数需加 extern "C" 避免名字改编;
    • 函数调用约定必须匹配,否则会导致程序崩溃。

评论一下?

OωO
取消