如何从 Python 文件句柄打开 .NET FileStream 对象?

Posted

技术标签:

【中文标题】如何从 Python 文件句柄打开 .NET FileStream 对象?【英文标题】:How can I open a .NET FileStream object from a Python file handle? 【发布时间】:2015-07-21 03:52:49 【问题描述】:

我需要在 Python 中打开一个可写文件句柄,然后将文件描述符交给 .NET 程序集中的函数(通过 pythonnet's clr module 访问。

从 Python 文件对象获取到 win32 HANDLE* 相当简单,如this question 所示:

import clr
from Microsoft.Win32.SafeHandles import SafeFileHandle
from System.IO import FileStream, FileAccess

pyf=open("c:/temp/testing123.txt","w")
fileno=pyf.fileno()
print fileno               # 6
handle = msvcrt.get_osfhandle(fileno)
print handle               # 1832L

According to MSDN,现在应该可以从直接的 IntPtr(句柄)或 SafeFileHandle 包装器构造标准的 FileStream 对象。

FileStream(IntPtr, FileAccess)
FileStream(SafeFileHandle, FileAccess)

问题是...如何说服clr 模块将handle 转换为IntPtr

我尝试了以下各种版本,但它们都给我错误:

FileStream(IntPtr(handle), True)
FileStream(IntPtr(Int64(handle), True)
FileStream(IntPtr(Int32(handle), True)
SafeFileHandle(IntPtr(handle), True)
...

=> TypeError ("value cannot be converted to System.IntPtr")

关于如何将这个该死的文件句柄转换为 C# 的任何建议?

【问题讨论】:

这个怎么样? ***.com/a/14334609/2230844 否则为什么不使用 COM,如果这是 Win32? @denfromufa,由于各种原因,CLR API 更适合这个项目。您链接到的答案解释了如何将托管句柄从 C++ 转换为 C#,但不幸的是,它对我的​​ Python 案例没有帮助。 【参考方案1】:

这个答案可能有点太晚了,但是像this 这样的东西会起作用吗?

from contextlib import contextmanager
import ctypes
import io
import os, sys
import tempfile

libc = ctypes.CDLL(None)
c_stdout = ctypes.c_void_p.in_dll(libc, 'stdout')

@contextmanager
def stdout_redirector(stream):
    # The original fd stdout points to. Usually 1 on POSIX systems.
    original_stdout_fd = sys.stdout.fileno()

    def _redirect_stdout(to_fd):
        """Redirect stdout to the given file descriptor."""
        # Flush the C-level buffer stdout
        libc.fflush(c_stdout)
        # Flush and close sys.stdout - also closes the file descriptor (fd)
        sys.stdout.close()
        # Make original_stdout_fd point to the same file as to_fd
        os.dup2(to_fd, original_stdout_fd)
        # Create a new sys.stdout that points to the redirected fd
        sys.stdout = io.TextIOWrapper(os.fdopen(original_stdout_fd, 'wb'))

    # Save a copy of the original stdout fd in saved_stdout_fd
    saved_stdout_fd = os.dup(original_stdout_fd)
    try:
        # Create a temporary file and redirect stdout to it
        tfile = tempfile.TemporaryFile(mode='w+b')
        _redirect_stdout(tfile.fileno())
        # Yield to caller, then redirect stdout back to the saved fd
        yield
        _redirect_stdout(saved_stdout_fd)
        # Copy contents of temporary file to the given stream
        tfile.flush()
        tfile.seek(0, io.SEEK_SET)
        stream.write(tfile.read())
    finally:
        tfile.close()
        os.close(saved_stdout_fd)

【讨论】:

是的,这也是一个合理的选择,尽管在 Windows 下尝试修改 stdin/stdout 文件句柄可能非常繁琐。我认为将 Windows C API 文件句柄转换为 C# 文件句柄仍然更直接。无论如何,您现在是负责实际使用此代码的人:-)。它会引起问题吗? 我认为最好的答案是,还没有......最新版本的pythonnet 存在兼容性问题,并试图找到一个标准库替代品。【参考方案2】:

感谢pythonnet mailing list 上的好心人的解答。

关键是使用Overloads构造函数将win32的HANDLE强制转换为IntPtr类型。

这是一个完整的工作示例:

import tempfile, msvcrt
import clr, msvcrt
from System.IO import FileStream, FileAccess
from System import IntPtr

with tempfile.NamedTemporaryFile(suffix='.txt', delete=False) as pyf:
    fileno=pyf.fileno()
    print "fileno", fileno
    handle = msvcrt.get_osfhandle(fileno)
    print "HANDLE", handle

    pyf.write("Python\n")
    pyf.flush()

    cs_handle = IntPtr.Overloads[long](handle)
    cs_fs = FileStream(cs_handle, FileAccess.Write)
    cs_fs.Write("CLR\n", 0, 4)
    cs_fs.Flush()

print "file should contain a line from Python and a line from CLR: ", pyf.name

【讨论】:

以上是关于如何从 Python 文件句柄打开 .NET FileStream 对象?的主要内容,如果未能解决你的问题,请参考以下文章

Python 2.7:仅从文件句柄(而不是路径)获取文件的大小

python操作文本文件

linux如何获取磁盘的繁忙程度和打开的文件句柄数?

Python修炼之路-文件操作

Python--文件操作和集合

python 文件读写操作