跳到主要内容

通过剪贴板将参数传递给SOLIDWORKS VBA宏

系统剪贴板可以存储不同类型的数据(包括但不限于文本、图像、HTML等)。最简单的方法是将自定义参数写入文本缓冲区,但这将清除缓冲区中已有的所有数据(如果有的话)。这可能会引起混淆,并导致用户体验不佳,因为运行宏可能会覆盖已经复制到剪贴板的文本。

另一种方法是将数据写入具有唯一名称的自定义缓冲区,以便它不会明确地暴露给用户,只能通过代码访问。

让我们从将从不同的“主”宏调用的“目标”宏开始。

Dim swApp As SldWorks.SldWorks

Sub main()

Set swApp = Application.SldWorks

swApp.SendMsgToUser "指定的参数: " & ArgumentHelper.GetArgument()

End Sub

上面的示例中,从“主”宏传递的参数值将在“目标”宏的消息框中提取并显示给用户:

宏中显示传递的参数值的消息框{ width=400 height=132 }

帮助类从SwMacroArgs格式中读取缓冲区的值。这是一个已知的自定义名称,主宏(将写入参数值)和目标宏(将读取值)都知道。如果需要,可以将其重命名为任何其他自定义名称。

Const ARG_FORMAT = "__SwMacroArgs__"

Private Declare PtrSafe Function RegisterClipboardFormat Lib "User32" Alias "RegisterClipboardFormatA" (ByVal lpString As String) As LongPtr
Private Declare PtrSafe Function OpenClipboard Lib "User32" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function GetClipboardData Lib "User32" (ByVal wFormat As LongPtr) As LongPtr
Private Declare PtrSafe Function GlobalSize Lib "kernel32" (ByVal hClipMemory As LongPtr) As Long
Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hClipMemory As LongPtr) As LongPtr
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpvDest As Any, lpvSource As Any, ByVal cbCopy As LongPtr)
Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hClipMemory As LongPtr) As Long
Private Declare PtrSafe Function CloseClipboard Lib "User32" () As Long

Public Function GetArgument() As String

On Error GoTo ErrorHandler

Dim hClipMemory As LongPtr
Dim lSize As Long
Dim lpClipMemory As LongPtr
Dim wFormat As LongPtr

wFormat = RegisterClipboardFormat(ARG_FORMAT)

If OpenClipboard(0&) = 0 Then
RaiseError "无法打开剪贴板"
End If

hClipMemory = GetClipboardData(wFormat)

If hClipMemory > 0 Then

lSize = GlobalSize(hClipMemory)

If lSize > 0 Then

lpClipMemory = GlobalLock(hClipMemory)

If lpClipMemory > 0 Then

Dim bData() As Byte
ReDim bData(lSize - 1) As Byte

CopyMemory bData(0), ByVal lpClipMemory, lSize

GlobalUnlock hClipMemory

GetArgument = Trim(StrConv(bData, vbUnicode))

End If

End If

Else
RaiseError "未指定参数"
End If

GoTo Finally

ErrorHandler:
MsgBox "关键错误: " & Err.Description

Finally:
CloseClipboard '必须关闭剪贴板,否则会内存泄漏

End Function

Sub RaiseError(desc As String)

Const SYS_ERR_OFFSET As Integer = 513

Err.Raise Number:=vbObjectError + SYS_ERR_OFFSET, _
Description:=desc
End Sub

为了调用宏并传递参数,需要将SwMacroArgs格式的缓冲区值设置为Unicode字符串。以下是不同编程语言中如何实现此操作的示例。

VBA宏

Argument Helper 模块

Const ARG_FORMAT = "__SwMacroArgs__"

Const GHND As Integer = &H42

Private Declare PtrSafe Function RegisterClipboardFormat Lib "User32" Alias "RegisterClipboardFormatA" (ByVal lpString As String) As LongPtr
Private Declare PtrSafe Function GlobalAlloc Lib "kernel32" (ByVal wFlags As LongPtr, ByVal dwBytes As LongPtr) As Long
Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As Long
Private Declare PtrSafe Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, ByVal lpString2 As Any) As Long
Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As Long
Private Declare PtrSafe Function OpenClipboard Lib "User32" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function CloseClipboard Lib "User32" () As Long
Private Declare PtrSafe Function SetClipboardData Lib "User32" (ByVal wFormat As LongPtr, ByVal hMem As LongPtr) As Long

Public Sub SetArgument(arg As String)

On Error GoTo ErrorHandler

Dim wFormat As LongPtr

wFormat = RegisterClipboardFormat(ARG_FORMAT)

Dim hGlobalMemory As Long
Dim lpGlobalMemory As Long

hGlobalMemory = GlobalAlloc(GHND, Len(arg))
lpGlobalMemory = GlobalLock(hGlobalMemory)
lpGlobalMemory = lstrcpy(lpGlobalMemory, arg)

If GlobalUnlock(hGlobalMemory) <> 0 Then
RaiseError "无法解锁内存"
End If

If OpenClipboard(0&) = 0 Then
RaiseError "无法打开剪贴板"
End If

SetClipboardData wFormat, hGlobalMemory

GoTo Finally

ErrorHandler:
MsgBox "关键错误: " & err.Description

Finally:
CloseClipboard

End Sub

Sub RaiseError(desc As String)

Const SYS_ERR_OFFSET As Integer = 513

err.Raise Number:=vbObjectError + SYS_ERR_OFFSET, _
Description:=desc
End Sub

Dim swApp As SldWorks.SldWorks

Sub main()

Set swApp = Application.SldWorks

ArgumentHelper.SetArgument "来自VBA宏的参数"

Dim err As Long

If False = swApp.RunMacro2("D:\Macros\GetArgumentMacro.swp", _
"Macro1", "main", swRunMacroOption_e.swRunMacroUnloadAfterRun, err) Then

swApp.SendMsgToUser "无法运行宏。错误代码: " & err

End If

End Sub
C#
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
using System.Runtime.InteropServices;
using System;
using System.Windows.Forms;
using System.Text;
using System.IO;

namespace CodeStack
{
public partial class SolidWorksMacro
{
const string ARG_NAME = "__SwMacroArgs__";

public void Main()
{
SetArgument("来自C#宏的参数");

int err;
if (!swApp.RunMacro2(@"D:\Macros\GetArgumentMacro.swp",
"Macro1", "main", (int)swRunMacroOption_e.swRunMacroUnloadAfterRun, out err))
{
swApp.SendMsgToUser(string.Format("无法运行宏。错误代码: {0}", err));
}
}

private static void SetArgument(string arg)
{
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(arg)))
{
Clipboard.SetData(ARG_NAME, stream);
}
}

public SldWorks swApp;
}
}
VB.NET
Imports SolidWorks.Interop.sldworks
Imports SolidWorks.Interop.swconst
Imports System.Runtime.InteropServices
Imports System.IO
Imports System.Windows.Forms
Imports System.Text
Imports System

Partial Class CodeStack

Const ARG_NAME As String = "__SwMacroArgs__"

Public Sub Main()
SetArgument("来自VB.NET宏的参数")
Dim err As Integer
If Not swApp.RunMacro2("D:\Macros\GetArgumentMacro.swp", "Macro1", "main", CInt(swRunMacroOption_e.swRunMacroUnloadAfterRun), err) Then
swApp.SendMsgToUser(String.Format("无法运行宏。错误代码: {0}", err))
End If
End Sub

Private Shared Sub SetArgument(ByVal arg As String)
Using stream As MemoryStream = New MemoryStream(Encoding.UTF8.GetBytes(arg))
Clipboard.SetData(ARG_NAME, stream)
End Using
End Sub

Public swApp As SldWorks

End Class

注意:上述示例不处理“竞争条件”(当多个具有不同参数的宏可能并行运行时)。使用Mutex或Semaphore对象来同步对共享资源的访问。