pylaunch 是一个基于嵌入式 Python(Embedded Python)的 Windows 启动器,允许开发者将 Python 项目与运行时一起打包并分发,从而无需在目标系统上安装 Python 环境。本教程将介绍 pylaunch 的原理、使用方法和打包方式。

项目特点

  • 独立启动器:通过纯 C 语言编写,默认不显示命令行窗口,可通过参数配置启用无窗口模式。
  • 嵌入式 Python 支持:自动加载本地目录中的 python3.dll(来自官方嵌入式 Python 发行版)。
  • 环境隔离:通过设置环境变量和动态路径,构建项目的私有运行环境。
  • 主脚本识别:自动查找并运行与可执行文件的同名模块,与 Python 脚本解耦。
  • 自动路径配置:动态将 packagessource 目录添加到 sys.path

项目结构

your_project/
├── source/
│   └── pylaunch.py          # 与 exe 同名模块
├── packages/                # 可选:项目依赖(纯 Python 库)
├── runtime/                 # 嵌入式 Python 运行时(解压官方 zip 包)
│   ├── python3.dll
│   └── ...
└── pylaunch.exe             # 编译好的 C 启动器

C 语言程序源码

#include <stdlib.h>
#include <wchar.h>
#include <windows.h>

// 主函数:启动 Python 运行时
int wmain() {
  int argc;
  LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc);
  if (!argv)
    return 1;

  // 获取可执行路径
  wchar_t exePath[MAX_PATH];
  if (!GetModuleFileNameW(NULL, exePath, MAX_PATH)) {
    LocalFree(argv);
    return 1;
  }

  // 构建 Python 初始化脚本
  wchar_t initScript[4096];
  _snwprintf(initScript, 4096,
             L"import sys,os,runpy\n"
             L"exe_path=r'%s'\n"
             L"home=os.path.dirname(exe_path)\n"
             L"sys.path[0:0]=[os.path.join(home,'source'),os.path.join(home,'"
             L"packages')]\n"
             L"module_name=os.path.splitext(os.path.basename(exe_path))[0]\n"
             L"sys.argv=[module_name]+sys.argv[1:]\n"
             L"runpy.run_module(module_name,run_name='__main__')\n",
             exePath);

  // 构造 Py_Main 参数
  int pyArgc = argc + 5;
  wchar_t **pyArgv = malloc(sizeof(wchar_t *) * pyArgc);
  if (!pyArgv) {
    LocalFree(argv);
    return 1;
  }
  pyArgv[0] = argv[0];
  pyArgv[1] = L"-I";
  pyArgv[2] = L"-s";
  pyArgv[3] = L"-S";
  pyArgv[4] = L"-c";
  pyArgv[5] = initScript;
  for (int i = 1; i < argc; ++i)
    pyArgv[5 + i] = argv[i];

  // 设置 DLL 目录
  wchar_t runtimeDir[MAX_PATH], pythonDll[MAX_PATH];
  wcsncpy(runtimeDir, exePath, MAX_PATH);
  wchar_t *lastSlash = wcsrchr(runtimeDir, L'\\');
  if (lastSlash)
    *lastSlash = 0;
  wcscat(runtimeDir, L"\\runtime");
  SetDllDirectoryW(runtimeDir);
  wcscpy(pythonDll, runtimeDir);
  wcscat(pythonDll, L"\\python3.dll");

  // 加载 Python DLL
  HMODULE hDLL = LoadLibraryW(pythonDll);
  if (!hDLL) {
    free(pyArgv);
    LocalFree(argv);
    return 1;
  }

  // 调用 Py_Main
  int (*Py_Main)(int, wchar_t **) = (void *)GetProcAddress(hDLL, "Py_Main");
  if (!Py_Main) {
    FreeLibrary(hDLL);
    free(pyArgv);
    LocalFree(argv);
    return 1;
  }

  int ret = Py_Main(pyArgc, pyArgv);
  FreeLibrary(hDLL);
  free(pyArgv);
  LocalFree(argv);
  return ret;
}

编译和构建

使用 MinGW 编译 C 启动器

gcc pylaunch.c -o pylaunch.exe -municode -static-libgcc -Os -s -flto -fno-exceptions

支持无窗口模式(可选)

gcc pylaunch.c -o pylaunch.exe -municode -mwindows -static-libgcc -Os -s -flto -fno-exceptions

添加应用图标

  1. 创建资源文件 pylaunch.rc
IDI_ICON1 ICON "icon.ico"
  1. 编译资源文件为 .o
windres pylaunch.rc -o pylaunch_res.o
  1. 编译最终可执行文件并链接资源:
gcc pylaunch.c pylaunch_res.o -o pylaunch.exe -municode -static-libgcc -Os -s -flto -fno-exceptions

嵌入式 Python 运行时

获取 Python 运行时

Python 官网 下载嵌入式 Python 包(embeddable package)。

配置 runtime/ 目录

解压缩下载的文件到 runtime/ 目录:

your_project/
└── runtime/
    ├── python3.dll
    ├── python312.zip
    ├── _asyncio.pyd
    └── ...

可选优化

  • 删除不需要的 .pyd 文件和 .exe 文件。
  • 保留最小运行子集(如 .dll.zip 和部分 .pyd)。

总结

通过 pylaunch 启动器,你可以轻松将 Python 项目打包为独立的 Windows 应用程序,无需在目标机器上安装 Python 环境。只需要一个可执行文件,你的 Python 脚本即可在没有 Python 安装的环境中运行,极大地简化了项目的分发与部署流程。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐