Search code examples
pythoncomsolidworks

Missing class methods after running makepy on Solidworks PDM library


I'm taking a crack at using the Solidworks PDM API to automate some file tasks. The first thing I did was run makepy.py on the PDMWorks Enterprise library as detailed here. This created a python file with a bunch of class definitions that I then renamed to "pdm_lib.py". The API defines a bunch of "interfaces" and "members," which in python seems to translate into "classes" and "methods". For example, this is what the IEdmVault5 class definition looks like:

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)

Here's a simple bit of code I wrote that connects to a file vault and checks out a file:

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)

The problem I'm running into is that when I try to use the "IEdmBatchUnlock" interface. According to the API documention, the following "members" (methods) should be available for it:

  • AddSelection
  • CreateFileTree
  • GetFilelist
  • ShowDlg
  • UnlockFiles

Here's my attempt to create an IEdmBatchUnlock and call the "AddSelection" method:

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)

I've also tried to do the same using late binding:

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)

Both sets of code appear to successfully create a "IEdmBatchUnlock" object, but In both cases I get the same "object has no attribute 'AddSelection'" error when I attempt to run the "AddSelection" method. When I look at the class definition in pdm_lib.py, this is all I see:

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)

I don't see any reference to any of the aforementioned methods here, or anywhere else in pdm_lib.py, so I'm not surprised that when I call the AddSelection method on an IEdmBatchUnlock object, python throws an error. Any suggestions on how I can get this working in Python?

*EDIT: Updated my post for clarity


Solution

  • I ultimately solved this problem in two separate ways:

    1. Dynamic Dispatch in win32com
    2. Generating module using comptypes

    Option 1 Code:

    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)
    

    Option 2 Code:

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

    When I created the vault objects this way (and then called the utility method to create the unlocker), I was able to access the "AddSelection" method. Not sure why this worked and makepy.py didn't, but I'm not an expert on COM objects