我需要扩展这个项目的功能,以包含更多的命令(您可能注意到,在GetSelectedFilePaths()中,我有一个目前没有使用的案例,这是为了支持我计划在下一个sprint中添加的其他命令)。所以,在进一步研究这个项目之前,我想我应该把它张贴在这里进行审查。
Solidworks EPDM是一种数据管理工具。它本质上是一个shell扩展,它使用数据库存储文件数据/元数据,并充当文件库。这是API文档。
EPDM外接程序主要是通过添加额外的菜单命令、工具栏按钮、数据卡上的按钮等来扩展功能,它们可以从外接程序中运行过程。
到目前为止,我的外接程序只包含了几个菜单命令。我首先选择了这些命令,因为它们是相似的和相关的,但是我计划在下一个开发sprint中使用更多的命令、菜单按钮等来扩展这个外接程序。

在继续扩展这个外接程序之前,我特别感兴趣的是如何/在哪里重构它,以提高可读性和/或性能。我主要关心的是未来维护的可读性(我不是程序员,所以一旦完成,我可能要一年才能重新投入到它中,甚至做任何编码),但我不想为了可读性而牺牲主要的性能收益。
我自己已经做了大约3次重构,而且我认为它正在进行得很好,但是我相信它会更好,因为我不是编程专家。
特别是,我认为在以下方面仍有改善的余地:
RunCommand (目前是OnCmd外部Try块中的局部变量)成为Private Property,然后使VerifiedPaths的Get过程使用RunCommand.ValidatorIndex调用GetValidSelectionPaths过程,但我不确定这样做对可读性和效率是否更好还是更糟。ResetMyProperties过程--我使用这个类的属性来保存给定命令使用的数据,然后重新设置它,以便每个属性都为下一个接收的命令做好准备。我不确定这是否是一个好的实践,或者我是否更好地将它分解成另一个类来充当一个变量容器。Imports System.Windows.Forms
Imports EPDM.Interop.EPDMResultCode
Imports EdmLib
Imports System.Reflection
Imports System.Runtime.CompilerServices
Module MyExtensions
<Extension()>
Public Function IsAllocated(ByVal input As System.Array) As Boolean
Return Not ((input Is Nothing) OrElse (input.Length = 0) OrElse (input.GetValue(0) Is Nothing))
End Function
End Module
Public Class EngChangeProcess
Implements IEdmAddIn5
Private Structure MenuCommand
Dim Index As Long
Dim CommandName As String
Dim MenuFlags As Integer
Dim ExecutingTemplate As String
Dim ValidatorIndex As Long
Dim CreateReferences As Boolean
End Structure
Private Enum ValidatorIndex
NewDRR
NewNPR
NewECN
End Enum
Private Const pathDRR As String = "\_DRR\"
Private Const pathNPR As String = "\_NPR\"
Private Const pathECRN As String = "\_ECRN\"
Private Const extDRR As String = ".LokDRR"
Private Const extNPR As String = ".LokNPR"
Private Const extECRN As String = ".LokECRN"
Private NewDRR As MenuCommand
Private NewNPR As MenuCommand
Private NewECN As MenuCommand
Private Commands As Dictionary(Of Long, MenuCommand)
Private mVault As EdmVault5
Private mCommand As EdmCmd
Private mSelected As System.Array
Private mTemplate As System.String
Private mPaths As List(Of String)
Private Property SourceVault As EdmVault5
Get
Return mVault
End Get
Set(value As EdmVault5)
mVault = value
End Set
End Property
Private Property SourceCommand As EdmCmd
Get
Return mCommand
End Get
Set(value As EdmCmd)
mCommand = value
End Set
End Property
Private Property SourceSelection As System.Array
Get
Return mSelected
End Get
Set(value As System.Array)
mSelected = value
End Set
End Property
Private Property TargetTemplate As System.String
Get
Return mTemplate
End Get
Set(value As System.String)
mTemplate = value
End Set
End Property
Private Property VerifiedPaths As List(Of String)
Get
Return mPaths
End Get
Set(value As List(Of String))
mPaths = value
End Set
End Property
Private Sub ResetMyProperties()
SourceVault = Nothing
SourceCommand = Nothing
SourceSelection = Nothing
TargetTemplate = String.Empty
VerifiedPaths = Nothing
End Sub
Private Sub DefineMenuCommands()
If Commands Is Nothing Then Commands = New Dictionary(Of Long, MenuCommand)
If Commands.ToArray.IsAllocated Then Exit Sub
NewDRR = New MenuCommand
With NewDRR
.Index = 0
.CommandName = "Create new Document Revision Request"
.ExecutingTemplate = "New Document Revision Request (DRR)"
.ValidatorIndex = ValidatorIndex.NewDRR
.MenuFlags = EdmMenuFlags.EdmMenu_MustHaveSelection + EdmMenuFlags.EdmMenu_OnlyFiles + EdmMenuFlags.EdmMenu_OnlyInContextMenu
.CreateReferences = True
Commands.Add(.Index, NewDRR)
End With
NewNPR = New MenuCommand
With NewNPR
.Index = 1
.CommandName = "Create new New Product Request"
.ExecutingTemplate = "New New Product Request (NPR)"
.ValidatorIndex = ValidatorIndex.NewNPR
.MenuFlags = EdmMenuFlags.EdmMenu_MustHaveSelection + EdmMenuFlags.EdmMenu_OnlyFiles + EdmMenuFlags.EdmMenu_OnlyInContextMenu
.CreateReferences = True
Commands.Add(.Index, NewNPR)
End With
NewECN = New MenuCommand
With NewECN
.Index = 2
.CommandName = "Issue new Engineering Change Notice"
.ExecutingTemplate = "New Engineering Change Notice (ECN)"
.ValidatorIndex = ValidatorIndex.NewECN
.MenuFlags = EdmMenuFlags.EdmMenu_MustHaveSelection + EdmMenuFlags.EdmMenu_OnlyFiles + EdmMenuFlags.EdmMenu_OnlyInContextMenu
.CreateReferences = True
Commands.Add(.Index, NewECN)
End With
End Sub
Public Sub GetAddInInfo(ByRef poInfo As EdmAddInInfo,
ByVal poVault As IEdmVault5,
ByVal poCmdMgr As IEdmCmdMgr5) Implements IEdmAddIn5.GetAddInInfo
Try
'Make sure menu command structures are ready to use:
DefineMenuCommands()
'Specify add-in information for EPDM to display
With poInfo
.mbsAddInName = "Engineering Document Change Management Add-in by Ssoeder"
.mbsCompany = "Lokring Technology LLC"
.mbsDescription = "This add-in provides the functionality for the Engineering document change process."
.mlAddInVersion = GetVersionAsInt()
'Specify minimum version of SolidWorks Enterprise PDM
.mlRequiredVersionMajor = 15
.mlRequiredVersionMinor = 1
End With
'Register menu commands with the EPDM shell extension.
'The command ID (lCmdID) is provided to OnCmd when the command is executed
For Each kvp In Commands
With kvp.Value
poCmdMgr.AddCmd(lCmdID:=.Index,
bsMenuString:=.CommandName,
lEdmMenuFlags:=.MenuFlags)
End With
Next
Catch ex As Runtime.InteropServices.COMException
Select Case ex.ErrorCode
Case EPDM.Interop.EPDMResultCode.EdmResultSuccessCodes_e.S_EDM_32BIT_ADDIN
MessageBox.Show("Succesfully registered 32bit addin.")
Case EPDM.Interop.EPDMResultCode.EdmResultSuccessCodes_e.S_EDM_64BIT_ADDIN
MessageBox.Show("Successfully registered 64bit addin")
Case Else
MessageBox.Show("An unhandled COMException has ocurred. Details: " & vbNewLine & _
"HRESULT = 0x" & ex.ErrorCode.ToString("X") & ": " & ex.Message)
End Select
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
Public Sub OnCmd(ByRef poCmd As EdmCmd, ByRef ppoData As System.Array) Implements IEdmAddIn5.OnCmd
Try
'Set private properties to hold the data passed with the command
SourceVault = poCmd.mpoVault
SourceCommand = poCmd
SourceSelection = ppoData
'Get the command structure for the command ID
Dim RunCommand As MenuCommand = Commands(poCmd.mlCmdID)
'Verify the selection meets requirements of the command
VerifiedPaths = GetValidSelectionPaths(RunCommand.ValidatorIndex)
If Not VerifiedPaths.ToArray.IsAllocated Then Throw New Exception("No valid files selected. Cannot execute command.")
'Set the template to execute
TargetTemplate = RunCommand.ExecutingTemplate
'Execute the template
TryRunTemplate(CreateReferences:=RunCommand.CreateReferences)
Catch ex As Exception
MessageBox.Show(ex.ToString)
Finally
ResetMyProperties()
End Try
MessageBox.Show("Complete")
End Sub
Private Function GetValidSelectionPaths(ByRef Index As ValidatorIndex) As List(Of String)
Try
Dim Exceptions As New List(Of Exception)
Dim RawPaths As List(Of String) = GetSelectedFilePaths(Exceptions)
For Each ex As Exception In Exceptions
MessageBox.Show(ex.ToString)
Next
Select Case Index
Case ValidatorIndex.NewDRR, ValidatorIndex.NewNPR
'Just make sure we have at least one file
If RawPaths.Count > 0 Then Return RawPaths
Case ValidatorIndex.NewECN
'We want to verify we have at least one NPR or DRR file to be the target of the ECN
Try
Dim ValidPaths As New List(Of String)
For Each Path As String In RawPaths
If (Path.Contains(pathNPR) AndAlso Path.Contains(extNPR)) _
OrElse (Path.Contains(pathDRR) AndAlso Path.Contains(extDRR)) _
Then ValidPaths.Add(Path)
Next
If ValidPaths.Count > 0 Then Return ValidPaths
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Select
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
Return Nothing
End Function
Private Function GetVersionAsInt() As Integer
Dim v As Version = Assembly.GetExecutingAssembly().GetName().Version()
Debug.Assert(v.Major >= 0 And v.Major < 100)
Debug.Assert(v.Minor >= 0 And v.Minor < 100)
Debug.Assert(v.Build >= 0 And v.Build < 10000)
Debug.Assert(v.Revision >= 0 And v.Revision < 100)
Dim longVersion As Long = v.Major * 100000000L + _
v.Minor * 1000000L + _
v.Build * 100L + _
v.Revision
Return CInt(longVersion)
End Function
Private Sub TryRunTemplate(Optional ByVal CreateReferences As Boolean = True)
'Must be logged into the vault to run the template
Try
If Not SourceVault.IsLoggedIn Then
SourceVault.LoginAuto(SourceVault.Name, SourceCommand.mlParentWnd)
End If
Catch ex As Exception
MessageBox.Show(ex.ToString, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End Try
'Run the template and get the files created
Dim NewFiles As List(Of String) = GetFilesFromTemplate()
Try
If Not NewFiles.ToArray.IsAllocated Then Throw New Exception("No files returned by" & "" & TargetTemplate & """." & vbNewLine & "Please contact the EPDM administrator.")
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
'Create the references if required
If CreateReferences Then
Try
Dim Exceptions As New List(Of Exception)
CreateTheReferences(NewFiles:=NewFiles,
ReferenceFiles:=VerifiedPaths,
ExceptionBuffer:=Exceptions)
If Exceptions.ToArray.IsAllocated Then
For Each ex As Exception In Exceptions
MessageBox.Show(ex.ToString)
Next
End If
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End If
End Sub
Private Function GetFilesFromTemplate() As List(Of String)
Try
Dim TheTemplate As IEdmTemplate53 = GetTemplate()
Dim DataReturn As System.Array
'Execute the template
Dim RefreshFlag As Long = TheTemplate.RunEx(hParentWnd:=SourceCommand.mlParentWnd,
lCurrentFolderID:=SourceVault.RootFolderID,
ppoRetData:=DataReturn)
'Refresh the folder view if required
If RefreshFlag = EdmRefreshFlag.EdmRefresh_FileList Then SourceVault.RefreshFolder(SourceVault.RootFolderPath)
'Check that the execution returned some data
If Not DataReturn.IsAllocated Then Throw New Exception("No data was returned from template " &
"" & TargetTemplate & "" & "." & vbNewLine &
"Contact the EPDM administrator if you continue to have problems.")
'Return the path(s) of the newly created file(s)
Dim NewFilePaths As New List(Of String)
Dim path As String = String.Empty
For Each data As EdmData In DataReturn
Select Case data.Type
Case EdmDataType.EdmData_File
path = data.Get(EdmDataPropertyType.EdmProp_Path)
If path.Length > 0 Then NewFilePaths.Add(path)
path = String.Empty
End Select
Next
Return NewFilePaths
Catch ex As Exception
MessageBox.Show(ex.ToString)
Return Nothing
End Try
End Function
Private Function GetTemplate() As IEdmTemplate53
Try
Dim TheTemplate As IEdmTemplate53 = Nothing
Dim TemplateMgr As IEdmTemplateMgr5 = SourceVault.CreateUtility(EdmUtility.EdmUtil_TemplateMgr)
Dim Position As IEdmPos5 = TemplateMgr.GetFirstTemplatePosition
Dim Match As Boolean = False
While (Not Position.IsNull) And (Not Match)
TheTemplate = TemplateMgr.GetNextTemplate(Position)
If TheTemplate.GetMenuString.ToLower = TargetTemplate.ToLower Then Match = True
End While
If Match Then Return TheTemplate
Throw New Exception("Template " & "" & TargetTemplate & "" & " was not found on the vault " &
"" & SourceVault.Name & "" & ". Please contact the EPDM administrator.")
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
Return Nothing
End Function
Private Function GetSelectedFilePaths(ByRef ExceptionBuffer As List(Of Exception)) As List(Of String)
Dim FilePaths As New List(Of String)
For Each SelectedItem As EdmCmdData In SourceSelection
'Reference: http://help.solidworks.com/2015/english/api/epdmapi/epdm.interop.epdm~epdm.interop.epdm.edmcmddata.html
Try
Select Case SourceCommand.meCmdType
Case EdmCmdType.EdmCmd_Menu
If SelectedItem.mlObjectID1 > 0 Then
Dim SelectedFile As IEdmFile6 = SourceVault.GetObject(EdmObjectType.EdmObject_File, SelectedItem.mlObjectID1)
FilePaths.Add(SelectedFile.GetLocalPath(SelectedItem.mlObjectID3))
SelectedFile = Nothing
End If
Case EdmCmdType.EdmCmd_CardButton
If SelectedItem.mlObjectID1 > 0 Then
FilePaths.Add(SelectedItem.mbsStrData2)
End If
End Select
Catch ex As Exception
ExceptionBuffer.Add(ex)
End Try
Next
Return FilePaths
End Function
Private Sub CreateTheReferences(ByVal NewFiles As List(Of String),
ByVal ReferenceFiles As List(Of String),
ByRef ExceptionBuffer As List(Of Exception))
Try
Dim ReferenceBuilder As IEdmAddCustomRefs = SourceVault.CreateUtility(EdmUtility.EdmUtil_AddCustomRefs)
For Each NewPath As String In NewFiles
Try
Dim ParentFile As IEdmFile6 = SourceVault.GetFileFromPath(NewPath)
Dim ParentFolder As IEdmFolder6 = SourceVault.GetFolderFromPath(Left(NewPath, InStrRev(NewPath, "\")))
Dim UnlockLater As Boolean = False
With ParentFile
If Not .IsLocked Then
UnlockLater = True
.LockFile(ParentFolder.ID, SourceCommand.mlParentWnd, CInt(EdmLockFlag.EdmLock_Simple))
End If
End With
'Add the file references - they are not committed to the database yet at this step
ReferenceBuilder.AddReferencesPath(ParentFile.ID, ReferenceFiles.ToArray)
'Compute the reference tree. Returns True or False to indicate if the method executed successfully
Dim Success As Boolean = ReferenceBuilder.CreateTree(0)
#If CONFIG = "Debug" Then
'When debugging, it can be helpful to show the reference dialog before comitting the references:
Success = ReferenceBuilder.ShowDlg(SourceCommand.mlParentWnd)
#End If
'Commit the references to the database. Returns True or False to indicate if the method executed successfully
If Success Then Success = ReferenceBuilder.CreateReferences()
'Throw a new exception explaining that references couldn't be added to the file.
If Not Success Then Throw New Exception("ERROR: Could not create the file reference(s) on " &
"" & ParentFile.Name & "" & "." & vbNewLine &
"Check the template to ensure it is NOT set to check-in newly created files." &
vbNewLine & "If still having problems after ensuring files are not being checked-in " &
"then contact the EPDM administrator.")
'Refreshes the shell folder view.
SourceVault.RefreshFolder(ParentFolder.LocalPath)
If UnlockLater Then ParentFile.UnlockFile(lParentWnd:=SourceCommand.mlParentWnd,
bsComment:="System: Added reference(s) for " & String.Join(";", ReferenceFiles.ToArray) & " created by " & TargetTemplate,
lEdmUnlockFlags:=0)
Catch ex As Exception
ExceptionBuffer.Add(ex)
End Try
Next
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
End Class发布于 2015-08-18 21:23:39
你不应该像这样写你的if块
If (Path.Contains(pathNPR) AndAlso Path.Contains(extNPR)) \_ OrElse (Path.Contains(pathDRR) AndAlso Path.Contains(extDRR)) \_ Then ValidPaths.Add(Path)
这是欺骗,使它成为一个一边倒的声明,以避免不得不放置一个End If之后。
它应该是这样的
If (Path.Contains(pathNPR) AndAlso Path.Contains(extNPR)) _
OrElse (Path.Contains(pathDRR) AndAlso Path.Contains(extDRR)) Then
ValidPaths.Add(Path)
End If如果你有一个明显不对的负长度,那么长度应该大于零。
不幸的是,使用扩展方法,不传递任何参数(或无参数)是合法的,因此您仍然需要检查是否将"nothing“传递到函数中。
但是,我确实建议您形成如下的返回条件
Return (Not(input Is Nothing) AndAlso (input.Length > 0) AndAlso Not(input.GetValue(0) Is Nothing))通过这种方式,您可以清楚地看到,一个片段可以抛出一个false并将其返回给函数调用。要使它更容易读懂,就应该这样做。
Dim inputIsNothing As boolean : Set inputIsNothing = input Is Nothing
Dim inputIsArray As boolean : Set inputIsMoreThanNothing = input.Length > 0
Dim inputArrayIsEmpty As boolean : Set inputArrayIsEmpty = input.GetValue(0) Is Nothing
Return Not inputIsNothing AndAlso inputIsArray AndAlso Not inputArrayIsEmpty它比你的版本稍长一些,但在我看来更容易读懂。
发布于 2015-07-29 10:18:13
我有很多私人财产,也许作为田地会更好?
这是你的私人财产之一。(不过,他们都是这样的。)
私有属性SourceCommand作为EdmCmd返回mCommand End Get ( value As EdmCmd) mCommand =value End Set End属性
你并没有从他们那里得到任何好处。如果只是使用私有字段,就会简单得多。如果您在每次访问mCommand时都必须调用某个附加到字段的逻辑,那么这是值得的。例如:
Private Property SourceCommand As EdmCmd
Get
Return If(mCommand Is Nothing, new EdmCmd, mCommand)
End Get
Set(value As EdmCmd)
mCommand = value
End Set
End Property但是,始终使用属性来访问实例变量的Public。总之,使用私有财产并不会有什么害处。它确实会让你远离他们内心的改变。这真的是一个判断力的决定。
我不完全确定在大多数情况下会出现什么异常,所以我基本上只是在调试时找到它们,然后在找到它们时添加消除或处理它们的方法。因此,我的异常捕获和处理可能设置得更好或者更全面。
这是我在.Net中正确处理异常的方法,到目前为止,它对我很有帮助。有时候,您只是知道您可能得到一个NullReferenceException或一个IndexOutOfBounds,但一般来说,您真的不知道什么异常会发生,直到它们发生。
你不该做的就是这个。
Catch ex As Exception ExceptionBuffer.Add(ex) End Try Next Catch ex As Exception MessageBox.Show(ex.ToString) End Try
首先,如果你能帮上忙的话,你不应该真的抓到Exception。这实际上是吞咽每一个可能的异常,并向最终用户显示异常文本。此外,您已经重复了"Catch,“代码。
我理解欲望(需要吗?)在代码中有一个位置来处理任何可能发生在您的外接程序中的异常,这样就不会使主机崩溃。我自己也做过。理想情况下,您的外接程序将创建一个"app“或"plugin”class.This类的实例,它将有一个Main例程,而在该例程中,"Catch,Show“应该是唯一的位置。它下面的所有其他代码要么应该处理特定的异常,要么根本不处理。然后,如果发生意外异常,它就会一直冒泡,直到到达Main中的捕获点为止。
我没有机会深入研究你的代码。我肯定还有很多要说的,但我得把它留给另一位评论员。
发布于 2015-08-19 11:02:34
Enum ValidatorIndex NewDRR NewNPR NewECN Enum Enum Private Const pathDRR As String = "\_DRR\“Private Const pathNPR As String = "\_NPR\”Private Const pathECRN As String = "\_ECRN\“Private Const extDRR As String = ".LokDRR”Private Const extNPR As String = ".LokNPR“Private Const extECRN As String = ".LokECRN”Private NewDRR As String 19# Private As‘.NewDRR = NewDRR .Index =0 .CommandName =“创建新文档修订请求”.ExecutingTemplate =“新文档修订请求”.ValidatorIndex = ValidatorIndex.NewDRR .MenuFlags = EdmMenuFlags.EdmMenu_MustHaveSelection + EdmMenuFlags.EdmMenu_OnlyFiles + EdmMenuFlags.EdmMenu_OnlyInContextMenu .CreateReferences = True Commands.Add(.Index,( NewDRR)以NewNPR =新建MenuCommand和NewNPR .Index =1 .CommandName =“创建新产品请求”.ExecutingTemplate =“新产品请求”.ValidatorIndex = ValidatorIndex.NewNPR .MenuFlags = EdmMenuFlags.EdmMenu_MustHaveSelection + EdmMenuFlags.EdmMenu_OnlyFiles + EdmMenuFlags.EdmMenu_OnlyInContextMenu .CreateReferences = True结束
这里的枚举并不是一个糟糕的方法,但是如果您引入一些OOP,它可能会更好。创建从MenuCommand继承的基类。在这个基类中,您将在ctor中设置共享属性(如MenuFlags)。然后,为每个菜单命令创建一个新的子类。最后,您应该得到一些类似于此的代码。
NewDDR = New DDRCommand
Commands.Add(NewDDR.Index, NewDDR)
NewNPR = New NPRCommand
Commands.Add(NewNPR.Index, NewNPR)
' ...https://codereview.stackexchange.com/questions/98376
复制相似问题