编译 PythonService.cpp来自 win32 Python 扩展构建



我需要一些帮助来构建pythonservice.exe(PythonService.cpp https://github.com/kovidgoyal/pywin32/tree/master/win32/src)。

据说(https://github.com/kovidgoyal/pywin32)需要VS2015,所以我下载了社区版本。我打开了一个新的win32控制台项目,并仅将PythonService.cpp导入源文件。

在项目设置中,我有:

包含目录:C:Python27_32bitinclude;C:pywin32-masterwin32src;$(VC_IncludePath);$(WindowsSDK_IncludePath);

库目录:C:Python27Libsite-packageswin32libs;C:Python27_32bitlibs;C:pywin32-masterwin32src;$(LibraryPath);C:pywin32-masterlibx32win32libs;

现在,在构建时,我收到未解决的外部符号错误:

1>PythonService.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) char * __cdecl GetPythonTraceback(struct _object *,struct _object *,struct _object *)" (__imp_?GetPythonTraceback@@YAPADPAU_object@@00@Z)

GetPythonTraceback是在PyWinTypesmodule中定义的.cpp但是当我将其添加到我的项目中时,它给出了其他错误,需要其他未解决的功能。我不想编译整个win32项目。我所需要的只是pythonservices.exe。这可以在不编译整个项目的情况下完成吗? 将不胜感激任何帮助!

谢谢! 阿列克谢

我最终找到了我的问题的解决方案(并非没有@CristiFati的答案的帮助!我下载了 https://github.com/mhammond/pywin32/tree/b222 并使用 setup.py 脚本构建了整个包。此外,我还安装了所需的SDK包(适用于python 2.6+Microsoft Windows SDK for Windows 7和.NET Framework 4(版本7.1))。经过一点挖掘,我发现编译和链接是用Python 9.0的Visual C++完成的,下面是pythonservice.pyd和pythonservice的编译和链接配方.exe(python列出了cmd参数到os.spawnv(os)的调用。P_WAIT,可执行文件,cmd):

对于pythonservice.pyd:

['"C:\Users\username\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\WinSDK\Bin\x64\mc.exe"','-h', 'win32/src', '-r', 'build\temp.win-amd64-2.7\Release\win32/src', 'win32/src/PythonServiceMessages.mc']
['"C:\Users\username\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\WinSDK\Bin\x64\rc.exe"','/fobuild\temp.win-amd64-2.7\Release\win32/src/PythonServiceMessages.res','build\temp.win-amd64-2.7\Release\win32/src\PythonServiceMessages.rc']
['"C:\Users\username\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe"','/c', '/nologo', '/Ox','/W3','/GS', '/DNDEBUG', '/MD', '-DDISTUTILS_BUILD', '-D_CRT_SECURE_NO_WARNINGS','-Icom/win32com/src/include', '-Iwin32/src', '-IC:\Python27\include', '-IC:\Python27\PC','"-IC:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\include"', '/Tpwin32/src/PythonService.cpp','/Fobuild\temp.win-amd64-2.7\Release\win32/src/PythonService.obj', '-DPYSERVICE_BUILD_DLL', '/Zi','/Fdbuild\temp.win-amd64-2.7\Release\servicemanager_vc.pdb', '/EHsc', '/DUNICODE', '/D_UNICODE', '/DWINNT']
['"C:\Users\username\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\link.exe"', '/DLL', '/nologo', '/INCREMENTAL:NO', '/LIBPATH:C:\Python27\libs', '/LIBPATH:C:\Python27\PCbuild\amd64', '/LIBPATH:C:\Python27\PC\VS9.0\amd64', '/LIBPATH:build\temp.win-amd64-2.7\Release', '"/LIBPATH:C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\lib\x64"', 'user32.lib', 'ole32.lib', 'advapi32.lib', 'shell32.lib', '/EXPORT:initservicemanager', 'build\temp.win-amd64-2.7\Release\win32/src/PythonServiceMessages.res', 'build\temp.win-amd64-2.7\Release\win32/src/PythonService.obj', '/OUT:build\lib.win-amd64-2.7\win32\servicemanager.pyd', '/IMPLIB:build\temp.win-amd64-2.7\Release\win32/src\servicemanager.lib', '/MANIFEST:NO', '/MACHINE:amd64', '/BASE:0x1e7d0000', '/DEBUG', '/PDB:build\temp.win-amd64-2.7\Release\servicemanager.pdb']

对于pythonservice.exe:

['"C:\Users\username\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe"','/c', '/nologo', '/Ox', '/W3', '/GS-', '/DNDEBUG', '/MD', '-DDISTUTILS_BUILD', '-D_CRT_SECURE_NO_WARNINGS','-Icom/win32com/src/include', '-Iwin32/src', '-IC:\Python27\include', '-IC:\Python27\PC','"-IC:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\include"','/Tpwin32\src\PythonService.cpp', '/Fobuild\temp.win-amd64-2.7\Release\pythonservice\win32\src\PythonService.obj','/Zi', '/Fdbuild\temp.win-amd64-2.7\Release\pythonservice_vc.pdb', '/EHsc', '/DUNICODE', '/D_UNICODE', '/DWINNT']
['"C:\Users\username\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\WinSDK\Bin\x64\rc.exe"','-DDISTUTILS_BUILD', '-D_CRT_SECURE_NO_WARNINGS', '-Icom/win32com/src/include', '-Iwin32/src', '-IC:\Python27\include','-IC:\Python27\PC', '"-IC:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\include"','/fobuild\temp.win-amd64-2.7\Release\pythonservice\win32\src\PythonService.res', 'win32\src\PythonService.rc']
['"C:\Users\username\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\link.exe"','/nologo', '/INCREMENTAL:NO', '/LIBPATH:C:\Python27\libs', '/LIBPATH:C:\Python27\PCbuild\amd64','/LIBPATH:C:\Python27\PC\VS9.0\amd64', '/LIBPATH:build\temp.win-amd64-2.7\Release','"/LIBPATH:C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\lib\x64"', 'user32.lib', 'advapi32.lib', 'ole32.lib','shell32.lib', 'build\temp.win-amd64-2.7\Release\pythonservice\win32\src\PythonService.obj','build\temp.win-amd64-2.7\Release\pythonservice\win32\src\PythonService.res','/OUT:build\lib.win-amd64-2.7\win32\pythonservice.exe','/MANIFESTFILE:build\temp.win-amd64-2.7\Release\pythonservice\win32\src\pythonservice.exe.manifest','/SUBSYSTEM:CONSOLE', '/MACHINE:amd64', '/DEBUG', '/PDB:build\temp.win-amd64-2.7\Release\pythonservice.pdb']
['mt.exe', '-nologo', '-manifest','build\temp.win-amd64-2.7\Release\pythonservice\win32\src\pythonservice.exe.manifest.orig','-outputresource:build\lib.win-amd64-2.7\win32\pythonservice.exe;1']
['C:\Python27\python.exe', 'C:\Python27\Lib\site-packages\win32\lib\win32verstamp.py', '--version=2.7.222.1', '--comments=https://github.com/mhammond/pywin32', '--original-filename=pythonservice.exe','--product=PyWin32', '--quiet', 'build\lib.win-amd64-2.7\win32\pythonservice.exe']

我在这里看到的第一件事是结合Python 2.7VStudio 2015。根据[Python.Wiki]:WindowsCompilers,对于VStudion 2015(或14.0),你应该使用Python3.5或Python 3.6(我有前者)。
检查[SO]:Windows中的Simstring(python)安装(@CristiFati的答案)-(第一部分),关于兼容性和限制:

  • 平台
  • 工具版本
  • 建筑

另外,有关在Win上构建C代码的一些(基本)信息:[SO]:CLR Windows表单中的LNK2005错误(@CristiFati的答案)

  • 从以下位置下载原始 PyWin32:[GitHub]: mhammond/pywin32 - (b222) (至pywin32-b222.zip)。">${PYWIN32_SRC_DIR}\win32\src\PythonService.cpp"与您提到的URL中的对应者相同
  • 为其创建一个目录和cd(应为空)。这将是%ROOT_DIR%,我将使用的所有路径都将相对于它(当然绝对路径除外),这将是默认目录(未指定时)
  • 将下载的.zip解压缩到某处。我们(直接)需要它:

    • ">${PYWIN32_SRC_DIR}\win32\src\PythonService.cpp" - 正如你提到的,源代码
    • ">${PYWIN32_SRC_DIR}\win32\src\PythonServiceMessages.mc" - 消息文本文件


    我将它们复制到一个名为src的目录中:

    E:WorkDevStackOverflowq048931801>dir /b
    src
    E:WorkDevStackOverflowq048931801>dir /b "src"
    PythonService.cpp
    PythonServiceMessages.mc
    
  • 由于构建涉及
  • 资源和自定义构建步骤(我不是很擅长),而且它不涉及很多文件,因此我不打算使用IDE
  • 下一个最好的方法是使用Makefiles,但是由于VStudio有一个错误[MS.MSDN]: ntwin32.mak 未找到:

    • 它也影响VStudio 2015
    • 它需要一次性修复
    • 我在我的机器上
    • 进行了修复并且它可以工作,但您很可能没有在您的机器上修复,我不想用此步骤给您带来不必要的负担


    我要手动完成所有操作

  • 设置虚拟主机环境

    • [女士。文档]:从命令行使用 MSVC 工具集
    • 你也可以检查[SO]:如何构建libjpeg 9b的DLL版本?(@CristiFati的回答)(第 1部分)了解有关手动构建的更多详细信息
    • 我正在使用vcvarsall.bat([MS.MSDN]:vcvarsall .bat 文件在哪里?从命令行

      E:WorkDevStackOverflowq048931801>"c:Installx86MicrosoftVisual Studio Community2015VCvcvarsall.bat" amd64
      
      • 您(显然)需要提供VStudio安装路径
      • amd64是因为我只安装了64 位(x64 或AMD64)版本的Python 3.5
  • 消息文件编译为资源文件 - 使用mc.exe([MS.文档]:消息编译器(MC.exe))

    E:WorkDevStackOverflowq048931801>mc "srcPythonServiceMessages.mc"
    MC: Compiling srcPythonServiceMessages.mc
    E:WorkDevStackOverflowq048931801>dir /b
    MSG00001.bin
    PythonServiceMessages.h
    PythonServiceMessages.rc
    src
    

    如前所述,它生成了 3 个新文件

  • 编译资源文件 - 使用rc.exe([MS.文档]:资源编译器)

    E:WorkDevStackOverflowq048931801>rc /NOLOGO /r "PythonServiceMessages.rc"
    E:WorkDevStackOverflowq048931801>dir /b
    MSG00001.bin
    PythonServiceMessages.h
    PythonServiceMessages.rc
    PythonServiceMessages.res
    src
    
  • 完成资源,转到代码(PythonService.cpp)。正如您已经提到的,它不会编译OOTB。我不打算列出所有失败的尝试,而是列出我采取的步骤,只显示最终(成功)编译。因此,该文件需要来自其他文件的代码。最简单的方法是从其他文件中复制它并将其放在这个文件中。我选择将其粘贴在第#1530行周围(就在">* 入口点"部分之前):

    • PyWinTypesModule.cpp-GetPythonTraceback函数(第 1142 - 1235 行)
    • PyUnicode.cpp-PyWin_AutoFreeBstr::PyWin_AutoFreeBstrPyWin_AutoFreeBstr::~PyWin_AutoFreeBstrPyWin_AutoFreeBstr::SetBstr(行 204 - 219)

    手动应用更改,或保存:

    --- PythonService.cpp.orig  2018-01-20 21:43:10.000000000 +0200
    +++ PythonService.cpp   2018-02-23 20:02:20.186966800 +0200
    @@ -1529,6 +1529,120 @@
    }
    }
    +// @TODO: cfati - copied from PyUnicode.cpp (lines 204 - 219)
    +PyWin_AutoFreeBstr::PyWin_AutoFreeBstr( BSTR bstr /*= NULL*/ )
    + : m_bstr(bstr)
    +{
    +   return;
    +}
    +
    +PyWin_AutoFreeBstr::~PyWin_AutoFreeBstr()
    +{
    +   SysFreeString(m_bstr);
    +}
    +
    +void PyWin_AutoFreeBstr::SetBstr( BSTR bstr )
    +{
    +   SysFreeString(m_bstr);
    +   m_bstr = bstr;
    +}
    +
    +
    +// @TODO: cfati - copied from PyWinTypesModule.cpp (lines 1142 - 1235)
    +// Function to format a python traceback into a character string.
    +#define GPEM_ERROR(what) {errorMsg = "<Error getting traceback - " ## what ## ">";goto done;}
    +char *GetPythonTraceback(PyObject *exc_type, PyObject *exc_value, PyObject *exc_tb)
    +{
    +   // Sleep (30000); // Time enough to attach the debugger (barely)
    +   char *result = NULL;
    +   char *errorMsg = NULL;
    +   PyObject *modStringIO = NULL;
    +   PyObject *modTB = NULL;
    +   PyObject *obFuncStringIO = NULL;
    +   PyObject *obStringIO = NULL;
    +   PyObject *obFuncTB = NULL;
    +   PyObject *argsTB = NULL;
    +   PyObject *obResult = NULL;
    +
    +   /* Import the modules we need - cStringIO and traceback */
    +#if (PY_VERSION_HEX < 0x03000000)
    +   modStringIO = PyImport_ImportModule("cStringIO");
    +#else
    +   // In py3k, cStringIO is in "io"
    +   modStringIO = PyImport_ImportModule("io");
    +#endif
    +
    +   if (modStringIO==NULL) GPEM_ERROR("cant import cStringIO");
    +   modTB = PyImport_ImportModule("traceback");
    +   if (modTB==NULL) GPEM_ERROR("cant import traceback");
    +
    +   /* Construct a cStringIO object */
    +   obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO");
    +   if (obFuncStringIO==NULL) GPEM_ERROR("cant find cStringIO.StringIO");
    +   obStringIO = PyObject_CallObject(obFuncStringIO, NULL);
    +   if (obStringIO==NULL) GPEM_ERROR("cStringIO.StringIO() failed");
    +
    +   /* Get the traceback.print_exception function, and call it. */
    +   obFuncTB = PyObject_GetAttrString(modTB, "print_exception");
    +   if (obFuncTB==NULL) GPEM_ERROR("cant find traceback.print_exception");
    +   argsTB = Py_BuildValue("OOOOO"
    +#if (PY_VERSION_HEX >= 0x03000000)
    +       "i"     
    +       // Py3k has added an undocumented 'chain' argument which defaults to True
    +       //  and causes all kinds of exceptions while trying to print a goddam exception
    +#endif
    +       ,
    +       exc_type ? exc_type : Py_None,
    +       exc_value ? exc_value : Py_None,
    +       exc_tb  ? exc_tb  : Py_None,
    +       Py_None,    // limit
    +       obStringIO
    +#if (PY_VERSION_HEX >= 0x03000000)
    +       ,0  // Goddam undocumented 'chain' param, which defaults to True
    +#endif
    +       );
    +   if (argsTB==NULL) GPEM_ERROR("cant make print_exception arguments");
    +
    +   obResult = PyObject_CallObject(obFuncTB, argsTB);
    +   if (obResult==NULL){
    +       // Chain parameter when True causes traceback.print_exception to fail, leaving no
    +       //  way to see what the original problem is, or even what error print_exc raises
    +       // PyObject *t, *v, *tb;
    +       // PyErr_Fetch(&t, &v, &tb);
    +       // PyUnicodeObject *uo=(PyUnicodeObject *)v;
    +       // DebugBreak();
    +       GPEM_ERROR("traceback.print_exception() failed");
    +       }
    +   /* Now call the getvalue() method in the StringIO instance */
    +   Py_DECREF(obFuncStringIO);
    +   obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue");
    +   if (obFuncStringIO==NULL) GPEM_ERROR("cant find getvalue function");
    +   Py_DECREF(obResult);
    +   obResult = PyObject_CallObject(obFuncStringIO, NULL);
    +   if (obResult==NULL) GPEM_ERROR("getvalue() failed.");
    +
    +   /* And it should be a string all ready to go - duplicate it. */
    +   if (PyString_Check(obResult))
    +       result = strdup(PyString_AsString(obResult));
    +#if (PY_VERSION_HEX >= 0x03000000)
    +   else if (PyUnicode_Check(obResult))
    +       result = strdup(_PyUnicode_AsString(obResult));
    +#endif
    +   else
    +       GPEM_ERROR("getvalue() did not return a string");
    +
    +done:
    +   if (result==NULL && errorMsg != NULL)
    +       result = strdup(errorMsg);
    +   Py_XDECREF(modStringIO);
    +   Py_XDECREF(modTB);
    +   Py_XDECREF(obFuncStringIO);
    +   Py_XDECREF(obStringIO);
    +   Py_XDECREF(obFuncTB);
    +   Py_XDECREF(argsTB);
    +   Py_XDECREF(obResult);
    +   return result;
    +}
    /*************************************************************************
    *
    

    作为">src\PythonService.diff",这是一个差异。参见 [SO]:在 PyCharm 社区版中从鼠标右键单击上下文菜单中运行/调试 Django 应用程序的单元测试?(@CristiFati的回答)(修补utrunner部分)了解如何在Win上应用补丁(基本上,以一个"+"号开头的每一行都进入,以一个"-">符号开头的每一行都出去)。我正在使用Cygwin

    E:WorkDevStackOverflowq048931801>"c:Installx64CygwinCygwinAllVersbinpatch.exe" "srcPythonService.cpp" "srcPythonService.diff"
    patching file 'srcPythonService.cpp'
    
  • 编译代码 - 使用cl.exe([MS.文档]:编译 C/C++ 程序)

    E:WorkDevStackOverflowq048931801>cl /GS /W1 /Zc:wchar_t /I"." /I"c:Installx64PythonPython3.5include" /I"e:WorkDevFatiWinBuildOPSWpython27srcpywin32-b222win32src" /Gm- /O2 /Zc:inline /fp:precise /D "UNICODE" /D "_UNICODE" /D "WIN32" /D "WIN64" /D "NDEBUG" /D "BUILD_PYWINTYPES" /errorReport:prompt /WX- /Zc:forScope /Gd /MD /c "srcPythonService.cpp"
    Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x64
    Copyright (C) Microsoft Corporation.  All rights reserved.
    PythonService.cpp
    E:WorkDevStackOverflowq048931801>dir /b "*.obj"
    PythonService.obj
    

    参数是标准参数([MS.Docs]:按字母顺序列出的编译器选项),特定于这种情况:

    • /D "BUILD_PYWINTYPES"(宏定义)
    • /D "WIN64"- 64位
    • 包括路径(/I) - 您必须调整它们以匹配您的路径:
      1. "c:\Install\x64\Python\Python\3.5\include">(Python路径)
      2. "e:\Work\Dev\Fati\WinBuild\OPSWpython27\src\pywin32-b222\win32\src" (pywin32path)
  • 将所有内容链接在一起 - 使用链接.exe([MS.文档]:链接)

    E:WorkDevStackOverflowq048931801>link /NOLOGO /MACHINE:X64 /SUBSYSTEM:CONSOLE /ERRORREPORT:PROMPT /LIBPATH:"c:Installx64PythonPython3.5libs" /NXCOMPAT /DYNAMICBASE "kernel32.lib" "user32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "PythonService.obj" "PythonServiceMessages.res" /OUT:"PythonService.exe"
    Creating library PythonService.lib and object PythonService.exp
    E:WorkDevStackOverflowq048931801>dir /b "*.exe"
    PythonService.exe
    

    同样,参数是标准的([MS.文档]:链接器选项),特定于这种情况:

    • /MACHINE:X64- 64位
    • 库路径(/LIBPATH) - 调整它以匹配您的路径:
      1. ">c:\Install\x64\Python\Python\3.5\libs(Pythonpath)
  • 运行服务之前的最后一步。由于它依赖于python35.dll操作系统必须知道在哪里查找它。最简单的方法是将其路径添加到%PATH%([MS.文档]:动态链接库搜索顺序)

    E:WorkDevStackOverflowq048931801>set PATH=%PATH%;c:Installx64PythonPython3.5
    E:WorkDevStackOverflowq048931801>PythonService.exe
    P - Python Service Manager
    Options:
    -register - register the EXE - this should generally not be necessary.
    -debug servicename [parms] - debug the Python service.
    NOTE: You do not start the service using this program - start the
    service using Control Panel, or 'net start service_name'
    

最新更新