跳到主要内容

SOLIDWORKS PDM Add-In 调试 - 最佳实践

在调试 SOLIDWORKS PDM add-in 时,许多开发人员发现这个过程繁琐且复杂。特别是对于之前开发桌面或 SOLIDWORKS 应用程序的开发人员来说,现在需要开发 PDM add-in。

主要的复杂性来自于 SOLIDWORKS PDM 是一个完全集成到客户端机器上的 Windows 资源管理器进程的服务器-客户端系统。这意味着 add-in(作为进程内扩展)被加载到 explorer.exe 进程中。重要的是要理解,explorer.exe 不仅是 Windows 文件资源管理器的进程,而且还管理开始菜单、任务栏、桌面等。因此,仅仅关闭 Windows 文件资源管理器是不够的,无法释放 add-in 的 dll。

SOLIDWORKS PDM 为开发人员提供了一个方便的框架,极大地简化了开发过程。您可以在 SOLIDWORKS PDM 管理 控制台的 vault 树下的 Add-ins 节点中找到 Debug Add-ins 菜单。

在管理面板中的 Debug add-in 命令{ width=320 height=297 }

您需要选择一个包含您的 add-in 的 dll,将其加载到调试器中。

add-in 的 GUID{ width=640 height=246 }

一旦选择了 add-in 的条目,它将出现在列表中,并保持在那里,直到被移除。因此,每次重新构建项目时都不需要打开此控制台。

正如我之前所指出的,SOLIDWORKS PDM 是一个客户端-服务器架构系统,所有的 add-in 都托管在服务器上,并重新分发到客户端机器。当将 add-in 添加为调试 add-in 时,不会将 add-in 的 dll 加载到服务器上。add-in 将直接从 bin 文件夹中进行本地调试。这也意味着存储库的其他用户在他们的系统中看不到该 add-in。

传统上,SOLIDWORKS PDM add-in 是通过 Notepad 进程进行调试的,方法是在项目的调试设置中选择 notepad.exe 的路径作为 Start external program 操作:

在外部 Notepad 应用程序中开始调试{ width=640 height=344 }

这样可以通过运行解决方案(F5)来启动调试过程,并打开 Notepad。为了开始实际的调试,需要执行以下操作:

  1. 在 Notepad 中选择 File->Open 菜单命令
  2. 导航到本地存储库文件夹
  3. 将过滤器更改为 All Files (.),以查看存储库中的所有文件

在 Notepad 中调试 add-in{ width=400 height=271 }

这种方法的好处是,停止 Visual Studio 的调试会话(通过单击 Visual Studio 中的停止按钮或关闭 Notepad)将释放内存中的 dll,因此不需要重新启动 explorer.exe 进程来编译 add-in 的新版本。

这种方法的局限性是:

  • 无法对文件或文件夹进行多选
  • 每次启动新的调试会话时需要执行太多步骤(例如,点击 Open 菜单,导航到存储库,更改过滤器)。每个调试会话可能需要大约 5-10 秒钟。

更好的方法是使用 PDM Vault Browser 工具。源代码可在 GitHub 上找到。源代码如下(必须在 .NET Framework 4.0 中编译,否则将无法加载调试符号):

using System;
using System.Linq;
using System.Windows.Forms;

namespace SwPdmVaultBrowser
{
class Program
{
[STAThread]
static void Main(string[] args)
{
var path = "";

if (args.Any())
{
path = args.First();
}

var dlg = new OpenFileDialog()
{
InitialDirectory = path,
Multiselect = true
};

dlg.ShowDialog();
}
}
}

这个工具是一个简单的文件浏览对话框,启用了多选选项。该工具还接受一个命令行参数,指定了 PDM 存储库中文件夹的完整路径。因此,当启动时,它将自动浏览到指定的文件夹:

使用 PDM Vault Browser 调试 add-in{ width=640 height=328 }

现在,在启动调试器时,它将自动将您带到存储库中指定的文件夹,无需指定过滤器。

视频演示:

{% youtube { id: uVcc4zvsSN0 } %}

SOLIDWORKS PDM add-in 的示例。请阅读 如何创建 SOLIDWORKS PDM Professional Add-In 文章,了解如何从头开始构建 PDM add-in。

C# 示例
using EdmLib;
using System;
using System.Linq;
using System.Runtime.InteropServices;

namespace CodeStack
{
[ComVisible(true)]
[Guid("8219E7E8-1765-4699-B509-E9DD84B337B4")]
public class PdmAddInSample : IEdmAddIn5
{
private enum Commands_e
{
GetIds
}

public void GetAddInInfo(ref EdmAddInInfo poInfo, IEdmVault5 poVault, IEdmCmdMgr5 poCmdMgr)
{
poInfo.mbsAddInName = "SW PDM Helper";
poInfo.mbsDescription = "Utility functions for SOLIDWORKS PDM";
poInfo.mlAddInVersion = 1;
poInfo.mlRequiredVersionMajor = 17; //SOLIDWORKS PDM 2017 SP0

poCmdMgr.AddCmd((int)Commands_e.GetIds, "Get ID");
}

public void OnCmd(ref EdmCmd poCmd, ref Array ppoData)
{
if (poCmd.meCmdType == EdmCmdType.EdmCmd_Menu)
{
if (poCmd.mlCmdID == (int)Commands_e.GetIds)
{
Func<EdmCmdData, int> getIdFunc = (d) =>
{
var isFile = d.mlObjectID1 != 0;

if (isFile)
{
return d.mlObjectID1;
}
else
{
return d.mlObjectID2;
}
};

var msg = string.Join(Environment.NewLine,
(ppoData as EdmCmdData[]).Select(
d => $"{d.mbsStrData1} - {getIdFunc(d)}").ToArray());

(poCmd.mpoVault as IEdmVault10).MsgBox(0, msg);
}
}
}
}
}

VB.NET 示例
Imports EdmLib
Imports System.Runtime.InteropServices
Imports System.Text

Namespace CodeStack

<ComVisible(True)>
<Guid("B761142B-BEEB-4E4D-8332-6B9E9D646B16")>
Public Class PdmAddInSample
Implements IEdmAddIn5

Private Enum Commands_e
GetIds
End Enum

Public Sub GetAddInInfo(ByRef poInfo As EdmAddInInfo, ByVal poVault As IEdmVault5, ByVal poCmdMgr As IEdmCmdMgr5) Implements IEdmAddIn5.GetAddInInfo

poInfo.mbsAddInName = "SW PDM Helper"
poInfo.mbsDescription = "Utility functions for SOLIDWORKS PDM"
poInfo.mlAddInVersion = 1
poInfo.mlRequiredVersionMajor = 17
poCmdMgr.AddCmd(CInt(Commands_e.GetIds), "Get ID1")

End Sub

Public Sub OnCmd(ByRef poCmd As EdmCmd, ByRef ppoData As Array) Implements IEdmAddIn5.OnCmd

If poCmd.meCmdType = EdmCmdType.EdmCmd_Menu Then

If poCmd.mlCmdID = Commands_e.GetIds Then

Dim msg As New StringBuilder()

For Each data As EdmCmdData In ppoData
msg.AppendLine($"{data.mbsStrData1} - {GetId(data)}")
Next

TryCast(poCmd.mpoVault, IEdmVault10).MsgBox(0, msg.ToString())

End If
End If
End Sub

Private Function GetId(data As EdmCmdData) As Integer

Dim isFile = data.mlObjectID1 <> 0

If isFile Then
Return data.mlObjectID1
Else
Return data.mlObjectID2
End If

End Function

End Class

End Namespace