从.NET调用Python COM服务器

6
我想使用win32com扩展实现python com服务器,然后从.NET中调用服务器。我使用以下示例实现了com服务器,它可以正常运行,但是当我尝试使用C#消费它时,出现了FileNotFoundException,并显示以下消息:“检索具有CLSID {676E38A6-7FA7-4BFF-9179-AE959734DEBB} 的组件的COM类工厂失败,原因是以下错误:8007007e。” 我也发布了C#代码。我想知道是否有遗漏,感谢您的帮助。
谢谢, Sarah
#PythonCOMServer.py

import pythoncom
class PythonUtilities:
    _public_methods_ = [ 'SplitString' ]
    _reg_progid_ = "PythonDemos.Utilities"
    # NEVER copy the following ID

    # Use"print pythoncom.CreateGuid()" to make a new one.
    _reg_clsid_ = pythoncom.CreateGuid()
    print _reg_clsid_
    def SplitString(self, val, item=None):
        import string
        if item != None: item = str(item)
        return string.split(str(val), item)

# Add code so that when this script is run by
# Python.exe,.it self-registers.

if __name__=='__main__':        
    print 'Registering Com Server'
    import win32com.server.register
    win32com.server.register.UseCommandLine(PythonUtilities)


// the C# code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {

              Type pythonServer;
              object pythonObject;
              pythonServer = Type.GetTypeFromProgID("PythonDemos.Utilities");
              pythonObject = Activator.CreateInstance(pythonServer);

        }
    }
}

2
请注意Python代码中的警告,不要在每次调用时使用新的GUID。只需创建一个GUID即可。 - gimel
你发布的代码是用于注册COM服务器的;你是否也已经实现并运行了实际的服务器? - rob
我以为注册服务器就意味着它在运行。你能给我更多的指南吗?谢谢。 - Sarah
2个回答

12

COM服务器只是一种软件(DLL或可执行文件),它通过定义的协议接受远程过程调用(RPC)。协议的一部分规定服务器必须具有唯一ID,存储在Windows注册表中。

在我们的情况下,这意味着您已经“注册”了一个不存在的服务器。因此出现错误(组件未找到)。

因此,代码应该像这样(通常情况下,这是未经测试的代码!):

import pythoncom

class HelloWorld:
    _reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER
    _reg_clsid_ = "{B83DD222-7750-413D-A9AD-01B37021B24B}"
    _reg_desc_ = "Python Test COM Server"
    _reg_progid_ = "Python.TestServer"
    _public_methods_ = ['Hello']
    _public_attrs_ = ['softspace', 'noCalls']
    _readonly_attrs_ = ['noCalls']
    # for Python 3.7+
    _reg_verprogid_ = "Python.TestServer.1"
    _reg_class_spec_ = "HelloWorldCOM.HelloWorld"

    def __init__(self):
        self.softspace = 1
        self.noCalls = 0

    def Hello(self, who):
        self.noCalls = self.noCalls + 1
        # insert "softspace" number of spaces
        return "Hello" + " " * self.softspace + str(who)

if __name__ == '__main__':
    if '--register' in sys.argv[1:] or '--unregister' in sys.argv[1:]:
        import win32com.server.register
        win32com.server.register.UseCommandLine(HelloWorld)
    else:
        # start the server.
        from win32com.server import localserver
        localserver.serve(['{B83DD222-7750-413D-A9AD-01B37021B24B}'])

那么你应该从命令行运行(假设脚本名为HelloWorldCOM.py):

HelloWorldCOM.py --register
HelloWorldCOM.py

HelloWorld类是服务器的实际实现。它公开了一个方法(Hello)和一些属性,其中两个属性中的一个是只读的。 使用第一条命令,您可以注册服务器;使用第二条命令,您可以运行服务器,然后其他应用程序就可以使用它了。


为了让之前的代码能够无误运行,我调用了方法localserver.main()而不是localserver.serve('B83DD222-7750-413D-A9AD-01B37021B24B')。我启动了服务器并将_reg_progid_作为参数传递给它。 - Sarah
2
localserver.serve('B83DD222-7750-413D-A9AD-01B37021B24B') 几乎正确。您需要包括括号并将其放入列表中:localserver.serve(['{B83DD222-7750-413D-A9AD-01B37021B24B}'])。否则完美无缺。 - Endre Both
有没有可能在不注册的情况下运行它?请看我的问题:https://dev59.com/5VgQ5IYBdhLWcg3w9Ygb - Rahul
对于使用Python 3.7(和最新版本的PyWin32)的用户,您需要添加变量:*_reg_verprogid__reg_class_spec_*。第一个变量与_reg_progid_大致相同(例如Python.TestServer.2),第二个变量应该是_<filename>.<classname>_(就像Python模块一样)。 - KilZone
如何优雅地停止正在运行的服务器 localserver.serve([{clsid}])?@EndreBoth - Krishna Kumar
另外,我如何使用RTD函数直接在Excel中访问Hello方法?例如,我无法在Excel中获取任何值=RTD("Python.TestServer","","Hello","Jack")。@EndreBoth - Krishna Kumar

0

你需要在你的C#可执行文件上运行进程监视器来追踪未找到的文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接