在 Solidworks PDM 库上 运行 makepy 之后缺少 class 方法

Missing class methods after running makepy on Solidworks PDM library

我正在尝试使用 Solidworks PDM API 来自动执行一些文件任务。我做的第一件事是在 PDMWorks Enterprise 库上 运行 makepy.py,详见 here。这创建了一个 python 文件,其中包含一堆 class 定义,然后我将其重命名为 "pdm_lib.py"。 API 定义了一堆 "interfaces" 和 "members,",在 python 中似乎转化为 "classes" 和 "methods"。例如,这是 IEdmVault5 class 定义的样子:

class IEdmVault5(DispatchBaseClass):
    'IEdmVault5 Interface'
    CLSID = IID('{11AD8C69-12EA-4C9D-B20B-8C6D43B735AC}')
    coclass_clsid = IID('{AE784C6C-0155-11D3-B24B-0000F879F93B}')

    # Result is of type IEdmStrLst5
    def BrowseForFile(self, hParentWnd=defaultNamedNotOptArg, lEdmBrowseFlags=8, bsFilter='', bsDefaultExtension=''
            , bsDefaultFileName='', bsDefaultFolder='', bsCaption=''):
        'method BrowseForFile'
        return self._ApplyTypes_(22, 1, (9, 32), ((3, 1), (3, 49), (8, 49), (8, 49), (8, 49), (8, 49), (8, 49)), 'BrowseForFile', '{AAFEA179-C9AE-4032-81C4-2AFCAA67B81A}',hParentWnd
            , lEdmBrowseFlags, bsFilter, bsDefaultExtension, bsDefaultFileName, bsDefaultFolder
            , bsCaption)

    # Result is of type IEdmFolder5
    def BrowseForFolder(self, hParentWnd=defaultNamedNotOptArg, bsMessage=defaultNamedNotOptArg):
        'method BrowseForFolder'
        ret = self._oleobj_.InvokeTypes(15, LCID, 1, (9, 0), ((3, 1), (8, 1)),hParentWnd
            , bsMessage)
        if ret is not None:
            ret = Dispatch(ret, 'BrowseForFolder', '{050E7719-E0B4-4824-824F-6055B41B52FD}')
        return ret

    # Result is of type IEdmMenu5
    def CreatePluginMenu(self, hMenu=defaultNamedNotOptArg, lInsertPosition=defaultNamedNotOptArg, plStartID=defaultNamedNotOptArg, lSelFileCount=defaultNamedNotOptArg
            , lSelFolderCount=defaultNamedNotOptArg, lCreateMenuFlags=defaultNamedNotOptArg, plItemCount=pythoncom.Missing):
        'method CreatePluginMenu'
        return self._ApplyTypes_(14, 1, (9, 0), ((3, 1), (3, 1), (16387, 3), (3, 1), (3, 1), (3, 1), (16387, 2)), 'CreatePluginMenu', '{65C885D1-C105-45D7-89D2-86B59F5DDCBC}',hMenu
            , lInsertPosition, plStartID, lSelFileCount, lSelFolderCount, lCreateMenuFlags
            , plItemCount)

    # Result is of type IEdmSearch5
    def CreateSearch(self):
        'method CreateSearch'
        ret = self._oleobj_.InvokeTypes(20, LCID, 1, (9, 0), (),)
        if ret is not None:
            ret = Dispatch(ret, 'CreateSearch', '{DF0EDC59-1AD6-401A-B332-70C10318E263}')
        return ret

    # Result is of type IEdmDictionary5
    def GetDictionary(self, bsName=defaultNamedNotOptArg, bCreateIfNew=defaultNamedNotOptArg):
        'method GetDictionary'
        ret = self._oleobj_.InvokeTypes(13, LCID, 1, (9, 0), ((8, 1), (11, 1)),bsName
            , bCreateIfNew)
        if ret is not None:
            ret = Dispatch(ret, 'GetDictionary', '{656C1CE8-E21B-4FDB-A493-484ABBBA5197}')
        return ret

    def GetErrorString(self, lError=defaultNamedNotOptArg, pbsErrorName='0', pbsDescription='0'):
        'method GetErrorString'
        return self._ApplyTypes_(5, 1, (24, 32), ((3, 1), (16392, 50), (16392, 50)), 'GetErrorString', None,lError
            , pbsErrorName, pbsDescription)

    # Result is of type IEdmFile5
    def GetFileFromPath(self, bsFilePath=defaultNamedNotOptArg, ppoRetParentFolder=0):
        'method GetFileFromPath'
        return self._ApplyTypes_(16, 1, (9, 0), ((8, 1), (16393, 50)), 'GetFileFromPath', '{BDFB0459-491E-4E29-9E86-A726B508766F}',bsFilePath
            , ppoRetParentFolder)

    # Result is of type IEdmFolder5
    def GetFolderFromPath(self, bsFolderPath=defaultNamedNotOptArg):
        'method GetFolderFromPath'
        ret = self._oleobj_.InvokeTypes(17, LCID, 1, (9, 0), ((8, 1),),bsFolderPath
            )
        if ret is not None:
            ret = Dispatch(ret, 'GetFolderFromPath', '{050E7719-E0B4-4824-824F-6055B41B52FD}')
        return ret

    # Result is of type IEdmObject5
    def GetObject(self, eType=defaultNamedNotOptArg, lObjectID=defaultNamedNotOptArg):
        'method GetObject'
        ret = self._oleobj_.InvokeTypes(12, LCID, 1, (9, 0), ((3, 1), (3, 1)),eType
            , lObjectID)
        if ret is not None:
            ret = Dispatch(ret, 'GetObject', '{2EE10E23-4B8A-4BC2-9043-E40FB5603169}')
        return ret

    def GetVaultNameFromPath(self, bsPath=defaultNamedNotOptArg):
        'method GetVaultNameFromPath'
        # Result is a Unicode object
        return self._oleobj_.InvokeTypes(4, LCID, 1, (8, 0), ((8, 1),),bsPath
            )

    def GetVersion(self, plMajor=defaultNamedNotOptArg, plMinor=defaultNamedNotOptArg):
        'method GetVersion'
        return self._ApplyTypes_(10, 1, (24, 0), ((16387, 3), (16387, 3)), 'GetVersion', None,plMajor
            , plMinor)

    def Login(self, bsUserName=defaultNamedNotOptArg, bsPasswd=defaultNamedNotOptArg, bsVaultName=defaultNamedNotOptArg):
        'method Login'
        return self._oleobj_.InvokeTypes(1, LCID, 1, (24, 0), ((8, 1), (8, 1), (8, 1)),bsUserName
            , bsPasswd, bsVaultName)

    def LoginAuto(self, bsVaultName=defaultNamedNotOptArg, hParentWnd=defaultNamedNotOptArg):
        'method LoginAuto'
        return self._oleobj_.InvokeTypes(2, LCID, 1, (24, 0), ((8, 1), (3, 1)),bsVaultName
            , hParentWnd)

    def MsgBox(self, lParentWnd=defaultNamedNotOptArg, bsMsg=defaultNamedNotOptArg, eType=0, bsCaption=''):
        'method MsgBox'
        return self._ApplyTypes_(21, 1, (3, 32), ((3, 1), (8, 1), (3, 49), (8, 49)), 'MsgBox', None,lParentWnd
            , bsMsg, eType, bsCaption)

    def RefreshFolder(self, bsFolderPath=defaultNamedNotOptArg):
        'method RefreshFolder'
        return self._oleobj_.InvokeTypes(23, LCID, 1, (24, 0), ((8, 1),),bsFolderPath
            )

    def SetAddInWnd(self, lAddInWnd=defaultNamedNotOptArg, lParentWnd=defaultNamedNotOptArg):
        'method SetAddinWnd'
        return self._oleobj_.InvokeTypes(25, LCID, 1, (24, 0), ((3, 1), (3, 1)),lAddInWnd
            , lParentWnd)

    def VerifyVersion(self, lMajor=defaultNamedNotOptArg, lMinor=defaultNamedNotOptArg):
        'method VerifyVersion'
        return self._oleobj_.InvokeTypes(11, LCID, 1, (24, 0), ((3, 1), (3, 1)),lMajor
            , lMinor)

    _prop_map_get_ = {
        "CommandID": (8, 2, (3, 0), (), "CommandID", None),
        "IsLoggedIn": (3, 2, (11, 0), (), "IsLoggedIn", None),
        "Language": (9, 2, (3, 0), (), "Language", None),
        "Name": (6, 2, (8, 0), (), "Name", None),
        # Method 'RootFolder' returns object of type 'IEdmFolder5'
        "RootFolder": (7, 2, (9, 0), (), "RootFolder", '{050E7719-E0B4-4824-824F-6055B41B52FD}'),
        "RootFolderID": (18, 2, (3, 0), (), "RootFolderID", None),
        "RootFolderPath": (24, 2, (8, 0), (), "RootFolderPath", None),
        "SilentMode": (19, 2, (11, 0), (), "SilentMode", None),
    }
    _prop_map_put_ = {
    }
    def __iter__(self):
        "Return a Python iterator for this object"
        try:
            ob = self._oleobj_.InvokeTypes(-4,LCID,3,(13, 10),())
        except pythoncom.error:
            raise TypeError("This object does not support enumeration")
        return win32com.client.util.Iterator(ob, None)

这是我编写的一段简单的代码,用于连接到文件库并签出文件:

from pdm_lib import *
folder_path = 'C:\Logos_Production\vendors\mcmaster-carr\test\'
file_path = 'C:\Logos_Production\vendors\mcmaster-carr\test\1019A12.SLDPRT'

username = 'username'
pw = 'pw'
vault_name = 'vault_name'

vault = EdmVault5()
vault.Login(username, pw, vault_name)
folder = vault.GetFolderFromPath(folder_path)
file = vault.GetFileFromPath(file_path, folder)[0]
file.LockFile(folder.ID, file.CurrentVersion)

我 运行 遇到的问题是当我尝试使用 "IEdmBatchUnlock" 界面时。根据API documention,下面的"members"(方法)应该可用:

这是我创建 IEdmBatchUnlock 并调用 "AddSelection" 方法的尝试:

from pdm_lib import *

username = 'username'
pw = 'pw'
vault_name = 'vault'

vault = EdmVault5()
vault.Login(username, pw, vault_name)

unlocker = logos_prod.CreateUtility(6)
unlocker.AddSelection(vault, selected_items)

我也尝试过使用后期绑定来做同样的事情:

from win32com.client import Dispatch

username = 'username'
pw = 'pw'
vault_name = 'vault'

vault = win32com.client.Dispatch('ConisioLib.EdmVault.1')
vault.Login(username, pw, vault_name)

unlocker = logos_prod.CreateUtility(6)
unlocker.AddSelection(vault, selected_items)

两组代码似乎都成功创建了一个 "IEdmBatchUnlock" 对象,但在这两种情况下,当我尝试 运行 "AddSelection" 时,我都遇到了相同的 "object has no attribute 'AddSelection'" 错误方法。当我查看 pdm_lib.py 中的 class 定义时,这就是我所看到的:

class IEdmBatchUnlock(DispatchBaseClass):
    'IEdmBatchUnlock Interface'
    CLSID = IID('{748E6759-E16D-4F69-A0BC-98A9A9F2E650}')
    coclass_clsid = None

    _prop_map_get_ = {
        "Comment": (5, 2, (3, 0), ((16392, 10),), "Comment", None),
    }
    _prop_map_put_ = {
        "Comment": ((5, LCID, 4, 0),()),
    }
    def __iter__(self):
        "Return a Python iterator for this object"
        try:
            ob = self._oleobj_.InvokeTypes(-4,LCID,3,(13, 10),())
        except pythoncom.error:
            raise TypeError("This object does not support enumeration")
        return win32com.client.util.Iterator(ob, None)

我在这里或 pdm_lib.py 中的任何其他地方都没有看到对上述任何方法的任何引用,因此当我在 IEdmBatchUnlock 对象上调用 AddSelection 方法时,我并不感到惊讶,python 抛出错误。关于如何在 Python 中实现此功能的任何建议?

*编辑:为清楚起见更新了我的 post

如果你去 IEdmBatchUnlock interface page under accessors you will see IEdmVault7::CreateUtility.

您不应该直接创建它。

我最终通过两种不同的方式解决了这个问题:

  1. win32com 中的动态调度
  2. 正在使用 comptypes 生成模块

选项 1 代码:

import comtypes.client as cc
cc.GetModule('C:\Program Files (x86)\SOLIDWORKS PDM\EdmInterface.dll')
import comtypes.gen._5FA2C692_8393_4F31_9BDB_05E6F807D0D3_0_5_22 as pdm_lib2

vault = cc.CreateObject('ConisioLib.EdmVault.1')
vault.LoginAuto(vault_name, 0)

选项 2 代码:

import win32com.client
vault = cc.CreateObject('ConisioLib.EdmVault.1')
vault.LoginAuto(vault_name, 0)

当我以这种方式创建保险库对象(然后调用实用程序方法来创建解锁器)时,我能够访问 "AddSelection" 方法。不知道为什么这行得通而 makepy.py 没有,但我不是 COM 对象方面的专家