Paradoxon:Python上的无声崩溃';s ctypes.CDLL导入时,而不是直接运行时-这怎么可能?

Paradoxon:Python上的无声崩溃';s ctypes.CDLL导入时,而不是直接运行时-这怎么可能?,python,python-import,ctypes,Python,Python Import,Ctypes,所以,作为一个Linux爱好者,我在Windows上偶然发现了一些我无法解释的令人困惑的事情 我对这个例子有一个项目结构分析: D:\PROJECT | | tolkien.py | __init__.py | \---MiddleEarth | gondor.py | isengrad.c | __init__.py | \---lib isengrad.so 问题:我将isengrad.c编译到

所以,作为一个Linux爱好者,我在Windows上偶然发现了一些我无法解释的令人困惑的事情

我对这个例子有一个项目结构分析:

D:\PROJECT
|
|   tolkien.py
|   __init__.py
|   
\---MiddleEarth
    |   gondor.py
    |   isengrad.c
    |   __init__.py
    |   
    \---lib
            isengrad.so
问题:我将
isengrad.c
编译到共享库
isengrad.so
,然后将其加载到
gondor.py
中。我的目标是将
gondor.py
导入
tolkien.py

直接运行时,
gondor.py
运行正常,而导入时,代码会在通过
ctypes.CDLL
加载共享库时退出,不会出现任何错误消息

复制: 文件的内容(添加了一些“状态消息”以跟踪问题发生的位置):

isengrad.c:

然后,使用

然后在gondor.py中访问共享库:

现在,检查直接使用
gondor.py
与在
tolkien.py
中导入时发生的情况:

D:\project>python MiddleEarth/gondor.py
started gondor
gondor loads isengrad
gondor loaded isengrad
2
gondor ran
gondor finished

D:\project>python tolkien.py
started tolkien
started gondor
gondor loads isengrad

D:\project>
直接运行它不会产生任何问题。但是导入它会导致整个程序崩溃,而在加载共享库时不会出现任何单词和回溯。这是怎么发生的?我甚至硬编码了共享库的路径,所以不同的工作目录应该不会有问题。。。我对Kubuntu上的同一个项目没有任何问题,所以这可能是一些与Windows相关的东西

环境:

  • Python:
    Python 3.7.3(默认值,2019年3月27日,17:13:21)[MSC v.1915 64位(AMD64)]::win32上的Anaconda,Inc.
  • 操作系统:
    Windows 10.0.17134内部版本17134
    (安装在C:)
  • GCC:通过Cygwin安装,版本7.4.0
  • 请询问是否需要其他详细信息

    • 从我看到这个问题的那一刻起,我想说这是一种未定义的行为(UB)。Python自带了C运行时(UCRTLib),而Cygwin.dll自带了自己的。在进程中混合使用编译器和C运行时通常会导致灾难
      我找到了一份官方声明(重点是我的):

      不,您必须使用其中一个,它们是互斥的

      有关MSVCRT*.DLL的更多详细信息,请查看

      现在,UB的美妙之处在于它描述了一种看似随机的行为

      我准备了一个全面的示例(稍微修改一下代码)

      isengrad.c:

      #如果已定义(_WIN32)
      #定义ISENGRAD\u导出\u API\u declspec(dllexport)
      #否则
      #定义ISENGRAD\u导出\u API
      #恩迪夫
      ISENGRAD_导出API int ISENGRAD(int霍比特人){
      返回霍比特/2;
      }
      
      script0.py:

      #/usr/bin/env蟒蛇3
      导入系统
      导入ctypes
      dll_name=“./lib/isengrad_{0:s}{1:03d}.dll”。格式(sys.argv[1][:3]if sys.argv else sys.platform[:3]。lower(),ctypes.sizeof(ctypes.c_void_p)*8)
      打印(“正在尝试加载:{0:s}”。格式(dll_名称))
      isengrad_dll=ctypes.CDLL(dll_名称)
      打印(“加载DLL”)
      def main():
      isengrad_func=isengrad_dll.isengrad
      isengrad_func.argtypes=[ctypes.c_int]
      isengrad_func.restype=ctypes.c_int
      res=isengrad_func(46)
      打印(“{0:s}返回{1:}”。格式(isengrad_func.\uuu name_uu,res))
      如果名称=“\uuuuu main\uuuuuuuu”:
      打印(“Python{0:s}{1:d}位在{2:s}\n.format(“.join(sys.version.split(“\n”)中的项的item.strip()),如果sys.maxsize>0x100000000,则为64,否则为32,sys.platform))
      main()
      打印(“\n完成”)
      
      脚本1.py:

      #/usr/bin/env蟒蛇3
      导入系统
      导入脚本0
      def main():
      通过
      如果名称=“\uuuuu main\uuuuuuuu”:
      打印(“Python{0:s}{1:d}位在{2:s}\n.format(“.join(sys.version.split(“\n”)中的项的item.strip()),如果sys.maxsize>0x100000000,则为64,否则为32,sys.platform))
      main()
      打印(“\n完成”)
      
      输出

      • 我将使用3个窗口:
        • cmd-Win(32位和64位)
        • Cygwin的Mintty:
          • 64位
          • 32位
      • 请注意,即使我将每个内容粘贴到一个块中(以避免分散它们),我也会在运行命令时在它们之间切换
      • Cygwin 32位:

        [cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q056855348]>~/sopr.sh
        ***设置较短的提示,以便粘贴到StackOverflow(或其他)页面时更适合***
        [032bit prompt]>gcc-shared-fPIC-o lib/isengrad_cyg_032.dll isengrad.c
        [032位提示符]>ls lib/*.dll
        lib/isengrad_cyg_032.dll lib/isengrad_cyg_064.dll lib/isengrad_win_032.dll lib/isengrad_win_064.dll
        [032位提示]>
        [032bit prompt]>python3 script0.py cyg
        正在尝试加载:./lib/isengrad\u cyg\u 032.dll
        加载的DLL
        Python 3.6.4(默认值,2018年1月7日,17:45:56)[GCC 6.4.0]cygwin上32位
        伊森格勒23日返回
        完成。
        [032位提示]>
        [032bit prompt]>python3 script1.py cyg
        正在尝试加载:./lib/isengrad\u cyg\u 032.dll
        加载的DLL
        Python 3.6.4(默认值,2018年1月7日,17:45:56)[GCC 6.4.0]cygwin上32位
        完成。
        [032位提示]>
        [032bit prompt]>python3 script0.py win
        正在尝试加载:./lib/isengrad\u win\u 032.dll
        加载的DLL
        Python 3.6.4(默认值,2018年1月7日,17:45:56)[GCC 6.4.0]cygwin上32位
        伊森格勒23日返回
        完成。
        [032位提示]>
        [032位提示]>python3 script1.py win
        正在尝试加载:./lib/isengrad\u win\u 032.dll
        加载的DLL
        Python 3.6.4(默认值,2018年1月7日,17:45:56)[GCC 6.4.0]cygwin上32位
        完成。
        
      • Cygwin 64位:

        [cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q056855348]>~/sopr.sh
        ***设置较短的提示,以便粘贴到StackOverflow(或其他)页面时更适合***
        [064位提示符]>gcc-shared-fPIC-o lib/isengrad_cyg_064.dll isengrad.c
        [064位提示符]>ls lib/*.dll
        lib/isengrad_cyg_032.dll lib/isengrad_cyg_064.dll lib/isengrad_win_032.dll lib/isengrad_win_064.dll
        [064位提示]>
        [064位提示符]>python3 script0.py cyg
        正在尝试加载:./lib/isengrad\u cyg\u 064.dll
        加载的DLL
        Python 3.6.8(默认为
        
        D:\project>chdir MiddleEarth
        D:\project\MiddleEarth>gcc -fPIC -shared -o lib/isengrad.so isengrad.c
        
        print("started gondor")
        
        import os, ctypes
        path_to_isengrad = "D:/project/MiddleEarth/lib/isengrad.so"  
        
        print("gondor loads isengrad")
        gondor = ctypes.CDLL(path_to_isengrad)     # <--- crashes here when imported, not when ran directly
        print("gondor loaded isengrad")
        
        
        gondor.isengrad.argtypes = (ctypes.c_int,)
        
        def faramir(hobbit):
            catched_hobbits = gondor.isengrad(hobbit)
            return catched_hobbits
        
        if __name__ == '__main__':
            print(faramir(5))
            print("gondor ran")
        
        print("gondor finished")
        
        print("started tolkien")
        from MiddleEarth import gondor
        print("tolkien imported gondor")
        
        got = gondor.faramir(4)
        print(got)
        
        print("tolkien worked")
        
        D:\project>python MiddleEarth/gondor.py
        started gondor
        gondor loads isengrad
        gondor loaded isengrad
        2
        gondor ran
        gondor finished
        
        D:\project>python tolkien.py
        started tolkien
        started gondor
        gondor loads isengrad
        
        D:\project>