Windows 上配 SDL2 只有四处要动,但第四处「DLL 放哪」最坑—— 编过了、一运行就闪退,还没有任何提示。
上一节认识了 SDL,这一节把它装进 VS。 沙箱里没有 VS,所以这一节给的是清单加原理,能真验的只有「DLL 在哪、多大」这两件事。 2026 年了,新项目直接上 VS 2022;这一节当「看得懂老项目」用。
一、四处要动的地方
头文件、库目录、依赖项、DLL——就这四处。
| 项 | 填什么 | 填错的表现 | |---|---|---|---| | 附加包含目录 | SDL2 的 include | 找不到 SDL.h | | 附加库目录 | SDL2 的 lib\x64 | LNK1104 打不开 SDL2.lib | | 附加依赖项 | SDL2.lib、SDL2main.lib | LNK2019 未解析符号 | | DLL | 和 exe 同目录 | 能编过,一运行就退 |
前三个错的报错都在编译期,看得见;第四个错在加载期,程序自己的日志一个字都来不及打。 这就是为什么「配环境」里最难查的永远是第四处。
二、SDL2main 是干什么的
SDL2main.lib 不是给功能用的,是给「入口函数」用的。
| 情况 | 入口 | 要不要 SDL2main |
|---|---|---|
| 控制台程序 | main |
要,它负责转给 SDL_main |
| Windows 程序 | WinMain |
要 |
| 自己写 WinMain | WinMain |
不要,定义 SDL_MAIN_HANDLED |
为什么需要它:SDL 要在 main 之前做平台初始化,而 main 的签名是固定的,插不进去。
所以它用宏把 main 改名成 SDL_main,自己提供真正的 main。
这就是为什么漏了 SDL2main.lib 会报「找不到 main」——你的 main 已经被改名了,没人接手。
三、DLL 放哪,三种选择
找 DLL 的顺序是有讲究的,知道顺序就知道该放哪。
| 放哪 | 优先级 | 什么时候用 |
|---|---|---|
| exe 同目录 | 最高 | 发布时最稳 |
| System32 | 其次 | 不推荐,会污染系统 |
| PATH 里某个目录 | 再次 | 开发期省事 |
第一行是唯一推荐的做法:拷一个 SDL2.dll 到 exe 旁边,整包发给别人就能跑。
第二行别做:一旦系统里装了两个版本,你自己的程序会被别人的 DLL 顶掉,
而且不报错,行为诡异。
四、VS 2015 的两个额外麻烦
这两个坑现在还会遇到(因为老项目还在):
| 麻烦 | 表现 | 修法 |
|---|---|---|
| 默认 C89 | for 循环变量未声明 |
改语言标准为 C11 |
| 没装 Windows SDK | 找不到 windows.h | 装 SDK 或改平台工具集 |
第一行在新版 SDL 头文件上必然出现:SDL 用了 C99 的行内声明。 报错信息指向的是 SDL 的头文件,容易被误认成「SDL 装错了」——其实是你的编译器太老。
五、动手 Demo:验证本机的 SDL2 DLL:在哪、多大、能不能加载
没有 VS,但 DLL 是现成的(pysdl2-dll 带的)。 先确认这个文件真实存在、体积多少,再验一次「加载成功」是什么样。 这一节能给的就是这两件事,编译链接的部分只能在真机上做。
import ctypes, ctypes.util, os
dll = ctypes.util.find_library("SDL2")
print(dll) # 多半给出完整路径
print(os.path.getsize(dll) // 1024, "KB")
# 加载成功就是这一句不抛异常
try:
ctypes.WinDLL(dll)
print("加载成功")
except OSError as e:
print("加载失败", e)
实跑输出:
--------------------------------------------------------------------
VS 2015 配 SDL2,要动的是四处
--------------------------------------------------------------------
**Windows 上配 SDL2 出问题,九成是四件事之一:头文件路径、库文件路径、要链接的库名、DLL 放哪。**
项 填什么 填错了的表现
------------------------------------------------------------------------
附加包含目录 SDL2 的 include 目录 找不到 SDL.h
附加库目录 SDL2 的 lib\x64 LNK1104 打不开 SDL2.lib
附加依赖项 SDL2.lib、SDL2main.lib LNK2019 未解析的外部符号
DLL 和 exe 放在一起 能编过,一运行就退出
**第四行最坑**:编译链接全过,双击就闪退,
什么提示都没有——**因为缺 DLL 是操作系统在加载阶段报的,程序自己的日志一个字都来不及打。**
本机没有 VS,但**有 SDL2 的 DLL**(pysdl2-dll 带的)。先把它找出来:
C:\Users\admin\.workbuddy\binaries\python\envs\default\Lib\site-packages\sdl2…
大小 1549 KB
**这个文件就是要跟 exe 放在一起的那个。** 名字必须是 `SDL2.dll`,改个名系统就找不到。
**再用 ctypes 验一遍「加载成功」是什么样**:
ctypes.util.find_library("SDL2") → 给了路径
它在 Windows 上经常给不出路径,**给不出不代表没装**——
只代表「标准搜索路径里没有」。Windows 上让 DLL 和 exe 同目录最省事。
**VS 2015 现在还有个额外麻烦**:它默认的 C 标准是 C89 风格,而新版 SDL2 的头文件用了 C99 的语法。
报错是「`for` 循环变量未声明」这一类,**改项目属性里的 C 语言标准**就好。
**2026 年了,新项目直接上 VS 2022**,这一节当「看得懂老项目」用。
SDL2.dll 有 1.5 MB 左右——这个体积就是「静态链接」和「动态链接」的分界线:
静态链进去的 exe 会胖一截,但不用带 DLL。
两种都行,看发布方式:单文件工具用静态,需要换版本用动态。
find_library 在 Windows 上经常给不出路径,这次给出来了,是因为 pysdl2-dll 目录在 PATH 里。
给不出不代表没装——只代表标准搜索路径里没有。这句话要记住,能省很多「明明装了却说没装」的困惑。
ctypes.WinDLL 加载成功说明这个 DLL 的位数和 Python 一致(都是 64 位)。
位数不一致时加载会失败,提示「不是有效的 Win32 程序」。
这也是配环境时的高频错:64 位编译器配 32 位库,报错却说「无效的应用程序」。
30 秒实验
- 用
ctypes.WinDLL(dll, mode=ctypes.RTLD_LOCAL)再加载一次,看有没有区别。 - 查一下
SDL_GetRevision(),把你本机的源码版本打出来,和SDL_GetVersion对比。 - 把这个 DLL 拷到另一个目录,再用全路径加载,验证「放哪都行,路径对就行」。
常见误区
- 以为「能编过」就配好了。 缺 DLL 时编译链接全过,一运行就闪退,因为加载期失败, 程序来不及打日志。
- 以为 SDL2main 是可选的功能库。 它是入口函数, 漏了就报「找不到 main」——因为你的 main 已经被宏改名了。
- 以为把 DLL 放 System32 更方便。 系统里两个版本时会互相顶掉,而且不报错。 发布一律放 exe 同目录。
自测三问(点开对答案)
1. 为什么漏了 SDL2main.lib 会报「找不到 main」? —— SDL 的宏把你的 `main` 改名成 `SDL_main`,真正的 `main` 由 SDL2main 提供。 漏了它就没有入口了。 2. DLL 应该放哪?为什么? —— exe 同目录。这是搜索优先级最高的位置,而且发布时整体打包就能跑,不会和系统里别的版本冲突。 3. 编译报「for 循环变量未声明」,是什么原因? —— VS 2015 默认按 C89 编译,而 SDL 的头文件用了 C99 的行内声明。把语言标准改成 C11 就好。下一节换 Qt:配置本身不难,难的是两个框架都想当主循环的主人。
- ☐ (本节完,下一节 03.03.03 Qt 5.9平台搭建SDL2开发环境)