我正在使用 C# 开发一个独立的 WinForm 程序,该程序使用 Solidworks EPDM api。该程序采用顶级程序集并查找程序集中的所有引用和引用文件。例如所有子装配体、零件文件和图纸。然后,程序从 EPDM 中检出所有文件,更新数据卡,并将所有文件检回 EPDM。
我已经成功实现了查找所有引用和引用文件并使用后台工作人员更新数据卡信息的代码部分。这部分代码不需要访问UI线程。我希望能够添加检查文件并将其重新签入后台工作人员中的代码。问题是用于执行签出和签入的方法将 this.Handle 作为参数。我知道从后台工作程序中访问 UI 线程会引发跨线程异常。该代码不访问任何 UI 控件。它只需要访问this.Handle。是否可以以线程安全的方式将 this.Handle 传递给后台工作人员,而不会引发跨线程异常?
这是我第一次使用后台工作人员,所以我的知识有限。下面是我想在后台工作程序中运行的代码。
private void BatchCheckout(Dictionary<string, string> SelectedFiles)
{
try
{
IEdmBatchGet batchGetter = (IEdmBatchGet)vault.CreateUtility(EdmUtility.EdmUtil_BatchGet);
EdmSelItem[] ppoSelection = new EdmSelItem[SelectedFiles.Count];
IEdmFile5 aFile;
IEdmFolder5 aFolder;
IEdmFolder5 ppoRetParentFolder;
IEdmPos5 aPos;
int i = 0;
foreach (KeyValuePair<string, string> kvp in SelectedFiles)
{
aFile = vault1.GetFileFromPath(kvp.Key, out ppoRetParentFolder);
aPos = aFile.GetFirstFolderPosition();
aFolder = aFile.GetNextFolder(aPos);
ppoSelection[i] = new EdmSelItem();
ppoSelection[i].mlDocID = aFile.ID;
ppoSelection[i].mlProjID = aFolder.ID;
i = i + 1;
}
batchGetter.AddSelection((EdmVault5)vault1, ref ppoSelection);
batchGetter.CreateTree(this.Handle.ToInt32(), (int)EdmGetCmdFlags.Egcf_Lock);
batchGetter.GetFiles(this.Handle.ToInt32(), null);
}
catch (System.Runtime.InteropServices.COMException ex)
{
MessageBox.Show("HRESULT = 0x" + ex.ErrorCode.ToString("X") + " " + ex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "\n" + GetStackTrace(ex));
}
}
我多年来一直是 StackOverflow 的读者,几乎找到了我遇到的每个问题的答案。这是我在 StackOverflow 上遇到的第一个问题。我真的希望有人能回答这个问题。
编辑:
我已经成功测试了 AndrewK 的建议,并很高兴地报告它确实适用于我的批量结帐方法。当我在后台工作程序中运行批量签入方法时,我收到以下 COM 异常:
无法将类型“System.__ComObject”的 COM 对象转换为接口类型“EPDM.Interop.epdm.IEdmBatchUnlock2”。此操作失败,因为对 IID 为“{F0970446-4CBB-4F0F-BAF5-F9CD2E09A5B3}”的接口的 COM 组件上的 QueryInterface 调用因以下错误而失败:不支持此类接口(HRESULT 异常:0x80004002 (E_NOINTERFACE)) .
只有当我从后台工作人员运行代码时,才会出现此异常。
这是我的 BatchCheckin 方法的代码:
private void BatchCheckin(Dictionary<string, string> SelectedFiles)
{
try
{
int i = 0;
IEdmFolder5 ppoRetParentFolder;
IEdmFile5 aFile;
IEdmFolder5 aFolder;
IEdmPos5 aPos;
EdmSelItem[] ppoSelection = new EdmSelItem[SelectedFiles.Count];
IEdmBatchUnlock2 batchUnlock;
foreach (KeyValuePair<string, string> kvp in SelectedFiles)
{
aFile = vault5.GetFileFromPath(kvp.Key, out ppoRetParentFolder);
aPos = aFile.GetFirstFolderPosition();
aFolder = aFile.GetNextFolder(aPos);
ppoSelection[i] = new EdmSelItem();
ppoSelection[i].mlDocID = aFile.ID;
ppoSelection[i].mlProjID = aFolder.ID;
i = i + 1;
}
batchUnlock = (IEdmBatchUnlock2)vault7.CreateUtility(EdmUtility.EdmUtil_BatchUnlock);
batchUnlock.AddSelection((EdmVault5)vault5, ref ppoSelection);
batchUnlock.CreateTree(0, (int)EdmUnlockBuildTreeFlags.Eubtf_ShowCloseAfterCheckinOption + (int)EdmUnlockBuildTreeFlags.Eubtf_MayUnlock);
batchUnlock.Comment = "Updates";
batchUnlock.UnlockFiles(0, null);
}
catch (System.Runtime.InteropServices.COMException ex)
{
MessageBox.Show("HRESULT = 0x" + ex.ErrorCode.ToString("X") + " " + ex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "\n" + GetStackTrace(ex));
}
}
当我调用Vault7.CreateUtility 时,出现异常。 BatchCheckin 代码与 BatchCheckout 几乎相同。我在这两种方法中对Vault7.CreateUtility 进行相同的调用。唯一的区别是 BatchCheckin 方法中的 EdmUtility 标志设置为 EdmUtil_BatchUnlock。关于这个 AndrewK 有什么线索吗?
更新:
我能够通过将batchUpdate从IEdmBatchUnlock2接口更改为IEdmBatchUnlock接口来解决COM异常。这是代码更改:
private void BatchCheckin(Dictionary<string, string> SelectedFiles)
{
int i = 0;
IEdmFolder5 ppoRetParentFolder;
IEdmFile5 aFile;
IEdmFolder5 aFolder;
IEdmPos5 aPos;
EdmSelItem[] ppoSelection = new EdmSelItem[SelectedFiles.Count];
IEdmBatchUnlock batchUnlock = (IEdmBatchUnlock)vault7.CreateUtility(EdmUtility.EdmUtil_BatchUnlock);
try
{
foreach (KeyValuePair<string, string> kvp in SelectedFiles)
{
aFile = vault5.GetFileFromPath(kvp.Key, out ppoRetParentFolder);
aPos = aFile.GetFirstFolderPosition();
aFolder = aFile.GetNextFolder(aPos);
ppoSelection[i] = new EdmSelItem();
ppoSelection[i].mlDocID = aFile.ID;
ppoSelection[i].mlProjID = aFolder.ID;
i = i + 1;
}
batchUnlock.AddSelection((EdmVault5)vault5, ref ppoSelection);
batchUnlock.CreateTree(0, (int)EdmUnlockBuildTreeFlags.Eubtf_ShowCloseAfterCheckinOption + (int)EdmUnlockBuildTreeFlags.Eubtf_MayUnlock);
batchUnlock.Comment = "Release to Production ECO";
batchUnlock.UnlockFiles(0, null);
}
catch (System.Runtime.InteropServices.COMException ex)
{
MessageBox.Show("HRESULT = 0x" + ex.ErrorCode.ToString("X") + " " + ex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "\n" + GetStackTrace(ex));
}
}
我猜测这是 IEdmBatchUnlock2 接口中的一个错误。如果从后台工作程序调用,IEdmBatchUnlock2 将导致 COM 异常,但如果从 UI 线程调用,则不会导致 COM 异常。当从后台工作人员调用时,IEdmBatchUnlock 接口不会导致 COM 异常。
只需在句柄处输入 0 即可。只要您的代码不需要用户输入,它就可以工作。我经常这样做。
batchGetter.AddSelection((EdmVault5)vault1, ref ppoSelection);
batchGetter.CreateTree(0, (int)EdmGetCmdFlags.Egcf_Lock);
batchGetter.GetFiles(0, null);
注意批量签出逻辑流程如何为用户提供显示获取签出对话框的选项,因此这整个事情不是线程安全的。这也可以在 API 的其他区域找到。
您必须从加载项/程序的主线程进行调用。 GetFiles 调用从来不应该从工作线程中调用。
投机警报!
API 搭载了 UI 的代码隐藏功能。就设计而言,这有点事后的想法。