1. 核心原理

要实现“注入并显示”,DLL 必须能把自己挂载到主程序的渲染循环里。在 raylib 中,由于它是顺序执行的,最简单的“注入”方式是:

  1. 主程序提供一个入口(类似一个“插件槽位”)。

2. DLL 端代码:

searchbox_dll.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include "IPlugin.h"
#include <vector>

// 插件内部私有变量(被封存在 DLL 内存空间中)
static char inputText[64] = { 0 };
static int letterCount = 0;
static bool active = false;

// --- 接口实现函数 ---

void SearchOnInit() {
// 可以在这里初始化资源,比如加载搜索框专用的字体或音效
TraceLog(LOG_INFO, "[SearchBox DLL] Plugin Initialized.");
}

void SearchOnTick() {
if (IsKeyPressed(KEY_F2)) active = !active;
if (!active) return;

// --- 原有的输入处理逻辑 ---
int key = GetCharPressed();
while (key > 0) {
if (key >= 32 && key <= 125 && letterCount < 63) {
inputText[letterCount++] = (char)key;
inputText[letterCount] = '\0';
}
key = GetCharPressed();
}
if (IsKeyPressed(KEY_BACKSPACE) && letterCount > 0) inputText[--letterCount] = '\0';

// --- 新增:回车事件处理 ---
if (IsKeyPressed(KEY_ENTER)) {
if (letterCount > 0) {
// 1. 打印输出(会显示在主程序的控制台里)
TraceLog(LOG_INFO, "[SearchBox] Searching for: %s", inputText);

// 2. 这里可以添加更多反应,比如:
// - 改变一个全局状态
// - 触发搜索逻辑
// - 甚至是调用主程序传进来的回调函数

// 3. 执行完后清空并关闭
letterCount = 0;
inputText[0] = '\0';
active = false;
}
}

// --- 绘图逻辑保持不变 ---
Rectangle box = { GetScreenWidth() / 2.0f - 200, 20, 400, 40 };
DrawRectangleRounded(box, 0.3f, 8, Fade(BLACK, 0.8f));
DrawRectangleRoundedLines(box, 0.3f, 8, active ? SKYBLUE : GRAY);
DrawText(inputText, (int)box.x + 15, (int)box.y + 10, 20, RAYWHITE);
}

void SearchOnUnload() {
// 清理资源
TraceLog(LOG_INFO, "[SearchBox DLL] Plugin Unloaded.");
}

// --- 唯一的导出函数:将上述函数打包交给宿主 ---

extern "C" __declspec(dllexport) PluginInterface* GetPluginAPI() {
static PluginInterface api = {
{"Global Search", "1.0"},
SearchOnInit,
SearchOnTick,
SearchOnUnload
};
return &api;
}

IPlugin.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef IPLUGIN_H
#define IPLUGIN_H

#include "raylib.h"

// 插件元数据
struct PluginInfo {
const char* name;
const char* version;
};
typedef struct {
PluginInfo info;
void (*OnInit)();
void (*OnTick)();
void (*OnUnload)();
} PluginInterface;

typedef PluginInterface* (*GetPluginAPI_Func)();

#endif

dllmain.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// dllmain.cpp : Defines the entry point for the DLL application.
#include <windows.h>

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}


3. 主程序端代码:预留“注入接口”

在主程序渲染中调用dll

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include "raylib.h"
#include "PluginLoader.h"

int main() {
InitWindow(800, 600, "Clean Plugin System");
SetTargetFPS(60);

PluginInstance searchBox("searchbox.dll");
searchBox.Load();

while (!WindowShouldClose()) {
if (IsKeyPressed(KEY_F5)) searchBox.Reload();

BeginDrawing();
ClearBackground(DARKGRAY);

// 状态检查
if (searchBox.IsLoaded()) {
DrawCircle(780, 20, 10, GREEN); // 右上角绿灯
searchBox.Update();
}
else {
DrawCircle(780, 20, 10, RED); // 右上角红灯
DrawText("PLUGIN NOT LOADED!", 600, 40, 10, RED);
}
EndDrawing();
}

CloseWindow();
return 0;
}

PluginLoader.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef PLUGIN_LOADER_H
#define PLUGIN_LOADER_H

#include "IPlugin.h"
#include <string>

class PluginInstance {
public:
explicit PluginInstance(const std::string& path);
~PluginInstance();

bool Load();
void Unload();
void Reload();
void Update();
bool IsLoaded() const;
private:
std::string dllPath;
void* hModule; // 内部封装 HMODULE
PluginInterface* api;
};

#endif

PluginLoader.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include "PluginLoader.h"

// 只有在这里才包含 Windows 头文件
#define NOGDI
#define NOUSER
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

PluginInstance::PluginInstance(const std::string& path)
: dllPath(path), hModule(nullptr), api(nullptr) {
}

PluginInstance::~PluginInstance() {
Unload();
}

bool PluginInstance::Load() {
hModule = (void*)LoadLibraryA(dllPath.c_str());

if (!hModule) {
DWORD error = GetLastError();
TraceLog(LOG_ERROR, "[Loader] LoadLibraryA failed. Error Code: %lu. Path: %s", error, dllPath.c_str());
// 常见错误码:126 (找不到模块), 193 (不是有效的 Win32 应用程序/位数不匹配)
return false;
}

auto GetAPI = (GetPluginAPI_Func)GetProcAddress((HMODULE)hModule, "GetPluginAPI");
if (!GetAPI) {
TraceLog(LOG_ERROR, "[Loader] GetProcAddress failed. Could not find 'GetPluginAPI' in %s", dllPath.c_str());
Unload();
return false;
}

api = GetAPI();
if (api && api->OnInit) {
api->OnInit();
TraceLog(LOG_INFO, "[Loader] Plugin %s loaded and initialized.", dllPath.c_str());
}
return true;
}

void PluginInstance::Unload() {
if (api && api->OnUnload) api->OnUnload();
if (hModule) {
FreeLibrary((HMODULE)hModule);
hModule = nullptr;
}
api = nullptr;
}

void PluginInstance::Reload() {
Unload();
Load();
}

void PluginInstance::Update() {
if (api && api->OnTick) api->OnTick();
}

bool PluginInstance::IsLoaded() const {
// 只要模块句柄不为空,且 API 函数表拿到了,就认为加载成功
return (hModule != nullptr && api != nullptr);
}

IPlugin.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef IPLUGIN_H
#define IPLUGIN_H

#include "raylib.h"

// 插件元数据
struct PluginInfo {
const char* name;
const char* version;
};
typedef struct {
PluginInfo info;
void (*OnInit)();
void (*OnTick)();
void (*OnUnload)();
} PluginInterface;

typedef PluginInterface* (*GetPluginAPI_Func)();

#endif