Kean的博客Through the interface中最近介绍了一个叫做Clipboard Manager的工具。这个小东西可以把AutoCAD里面用户复制到粘贴板里的东西一项一项的显示在一个简单的属性面板(palette)中,而且还显示出复制的时间信息,然后用户可以用palette中的右键菜单中几种粘贴方式进行粘贴。

这个软件神奇的地方是它用了钩子来钩AutoCAD中的COPYCLIP命令,然后用到了win32中的SetClipboardViewer方法来把它自己作为粘贴板浏览器(clipboard viewer)。这个功能主要是在CbPalette类中实现的。

你可以从Autodesk Labs里面下载到Clipboard Manager source code源代码。

Kean的博客中显示了他用AutoCAD2010演示了这个工具。但是我下载到的源代码只能用AutoCAD2009和之前版本编译运行。好像是.NET库版本不兼容。可能重新建一个工程然后把那些类文件加进去也能解决这个问题。我没有试。大家如果有兴趣可以自己尝试。

这个软件工具也演示了如何用VB.NET实现按需加载。Kean以前曾提供过一个用C#来实现按需加载功能的文章。

下面是原文:

Clipboard Manager: October's ADN Plugin of the Month, now live on Autodesk Labs

As Scott is leaving on a well-deserved sabbatical, he has gone ahead and posted our next Plugin of the Month a few days ahead of schedule. Here's a link to Scott's post announcing the tool.

This is a very cool little application developed by Mark Dubbelaar from Australia. Mark has been drafting/designing with AutoCAD for the last 10+ years and, during this time, has used a variety of programming languages to customize AutoCAD: LISP, VBA and now VB.NET. Mark was inspired by the "clipboard ring" functionality that used to be in Microsoft Office (at least I say "used to be" because I haven't found it in Office 2007), and decided to implement similar functionality in AutoCAD.

The implementation of the tool is quite straightforward but the functionality is really very compelling: after having NETLOADed the tool and run the CLIPBOARD command, as you use Ctrl-C to copy drawing objects from inside AutoCAD to the clipboard a custom palette gets populated with entries containing these sets of objects. Each entry contains a time-stamp and an automatically-generated name which you can then change to something more meaningful.

When you want to use these clipboard entries, you simply right-click on one and choose the appropriate paste option (which ultimately just calls through to the standard AutoCAD paste commands, PASTECLIP, PASTEBLOCK and PASTEORIG, reducing the complexity of the tool).

That's really all there is to it: a simple yet really useful application. Thanks for providing such a great little tool, Mark! :-)

Under the hood, the code is quite straightforward. The main file, Clipboard.vb, sets up the application to create demand-loading entries when first loaded into AutoCAD and defines a couple of commands – CLIPBOARD and REMOVECB, which removes the demand-loading entries to "uninstall" the application. It also contains the PaletteSet that contains our CbPalette and gets displayed by the CLIPBOARD command.

Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.Windows Imports Autodesk.AutoCAD.EditorInput Public Class ClipBoard Implements IExtensionApplication <DebuggerBrowsable(DebuggerBrowsableState.Never)> _ Private _cp As CbPalette = Nothing Public ReadOnly Property ClipboardPalette() As CbPalette Get If _cp Is Nothing Then _cp = New CbPalette End If Return _cp End Get End Property Private _ps As PaletteSet = Nothing Public ReadOnly Property PaletteSet() As PaletteSet Get If _ps Is Nothing Then _ps = New PaletteSet("Clipboard", _ New System.Guid("ED8CDB2B-3281-4177-99BE-E1A46C3841AD")) _ps.Text = "Clipboard" _ps.DockEnabled = DockSides.Left + _ DockSides.Right + DockSides.None _ps.MinimumSize = New System.Drawing.Size(200, 300) _ps.Size = New System.Drawing.Size(300, 500) _ps.Add("Clipboard", ClipboardPalette) End If Return _ps End Get End Property Private Sub Initialize() _ Implements IExtensionApplication.Initialize DemandLoading.RegistryUpdate.RegisterForDemandLoading() End Sub Private Sub Terminate() _ Implements IExtensionApplication.Terminate End Sub <CommandMethod("ADNPLUGINS", "CLIPBOARD", CommandFlags.Modal)> _ Public Sub ShowClipboard() PaletteSet.Visible = True End Sub <CommandMethod("ADNPLUGINS", "REMOVECB", CommandFlags.Modal)> _ Public Sub RemoveClipboard() DemandLoading.RegistryUpdate.UnregisterForDemandLoading() Dim ed As Editor = _ Autodesk.AutoCAD.ApplicationServices.Application _ .DocumentManager.MdiActiveDocument.Editor() ed.WriteMessage(vbCr + _ "The Clipboard Manager will not be loaded" _ + " automatically in future editing sessions.") End Sub End Class

It's the Clipboard_Palette.vb file that contains the more interesting code, implementing the behaviour of the CbPalette object. The real "magic" is how it hooks into AutoCAD's COPYCLIP by attaching itself as the default "clipboard viewer".

Imports AcApp = Autodesk.AutoCAD.ApplicationServices.Application Imports System.Windows.Forms Public Class CbPalette ' Constants for Windows API calls Private Const WM_DRAWCLIPBOARD As Integer = &H308 Private Const WM_CHANGECBCHAIN As Integer = &H30D ' Handle for next clipboard viewer Private _nxtCbVwrHWnd As IntPtr ' Boolean to control access to clipboard data Private _internalHold As Boolean = False ' Counter for our visible clipboard name Private _clipboardCounter As Integer = 0 ' Windows API declarations Declare Auto Function SetClipboardViewer Lib "user32" _ (ByVal HWnd As IntPtr) As IntPtr Declare Auto Function SendMessage Lib "User32" _ (ByVal HWnd As IntPtr, ByVal Msg As Integer, _ ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Long ' Class constructor Public Sub New() ' This call is required by the Windows Form Designer InitializeComponent() ' Register ourselves to handle clipboard modifications _nxtCbVwrHWnd = SetClipboardViewer(Handle) End Sub Private Sub AddDataToGrid() Dim currentClipboardData As DataObject = _ My.Computer.Clipboard.GetDataObject ' If the clipboard contents are AutoCAD-related If IsAutoCAD(currentClipboardData.GetFormats) Then ' Create a new row for our grid and add our clipboard ' data stored in the "tag" Dim newRow As New DataGridViewRow() newRow.Tag = currentClipboardData ' Increment our counter _clipboardCounter += 1 ' Create and add a cell for the name, using our counter Dim newNameCell As New DataGridViewTextBoxCell newNameCell.Value = "Clipboard " & _clipboardCounter newRow.Cells.Add(newNameCell) ' Get the current time and place that in another cell Dim newTimeCell As New DataGridViewTextBoxCell newTimeCell.Value = Now.ToLongTimeString newRow.Cells.Add(newTimeCell) ' Add our row to the data grid and select it clipboardDataGridView.Rows.Add(newRow) clipboardDataGridView.FirstDisplayedScrollingRowIndex = _ clipboardDataGridView.Rows.Count - 1 newRow.Selected = True End If End Sub ' Move the selected item's data into the clipboard ' Check whether the clipboard data was created by AutoCAD Private Function IsAutoCAD(ByVal Formats As String()) As Boolean For Each item As String In Formats If item.Contains("AutoCAD") Then Return True Next Return False End Function Private Sub PasteToClipboard() ' Use a variable to make sure we don't edit the ' clipboard contents at the wrong time _internalHold = True My.Computer.Clipboard.SetDataObject( _ clipboardDataGridView.SelectedRows.Item(0).Tag) _internalHold = False End Sub ' Send a command to AutoCAD Private Sub SendAutoCADCommand(ByVal cmd As String) AcApp.DocumentManager.MdiActiveDocument.SendStringToExecute( _ cmd, True, False, True) End Sub ' Our context-menu command handlers Private Sub PasteToolStripButton_Click( _ ByVal sender As Object, ByVal e As EventArgs) _ Handles PasteToolStripMenuItem.Click ' Swap the data from the selected item in the grid into the ' clipboard and use the internal AutoCAD command to paste it If clipboardDataGridView.SelectedRows.Count = 1 Then PasteToClipboard() SendAutoCADCommand("_pasteclip ") End If End Sub Private Sub PasteAsBlockToolStripMenuItem_Click( _ ByVal sender As Object, ByVal e As EventArgs) _ Handles PasteAsBlockToolStripMenuItem.Click ' Swap the data from the selected item in the grid into the ' clipboard and use the internal AutoCAD command to paste it ' as a block If clipboardDataGridView.SelectedRows.Count = 1 Then PasteToClipboard() SendAutoCADCommand("_pasteblock ") End If End Sub Private Sub PasteToOriginalCoordinatesToolStripMenuItem_Click( _ ByVal sender As Object, ByVal e As EventArgs) _ Handles PasteToOriginalCoordinatesToolStripMenuItem.Click ' Swap the data from the selected item in the grid into the ' clipboard and use the internal AutoCAD command to paste it ' at the original location If clipboardDataGridView.SelectedRows.Count = 1 Then PasteToClipboard() SendAutoCADCommand("_pasteorig ") End If End Sub Private Sub RemoveAllToolStripButton_Click( _ ByVal sender As Object, ByVal e As EventArgs) _ Handles RemoveAllToolStripButton.Click ' Remove all the items in the grid clipboardDataGridView.Rows.Clear() End Sub Private Sub RenameToolStripMenuItem_Click( _ ByVal sender As Object, ByVal e As EventArgs) _ Handles RenameToolStripMenuItem.Click ' Rename the selected row by editing the name cell If clipboardDataGridView.SelectedRows.Count = 1 Then clipboardDataGridView.BeginEdit(True) End If End Sub Private Sub RemoveToolStripMenuItem_Click( _ ByVal sender As Object, ByVal e As EventArgs) _ Handles RemoveToolStripMenuItem.Click ' Remove the selected grid item If clipboardDataGridView.SelectedRows.Count = 1 Then clipboardDataGridView.Rows.Remove( _ clipboardDataGridView.SelectedRows.Item(0)) End If End Sub ' Our grid view event handlers Private Sub ClipboardDataGridView_CellMouseDown( _ ByVal sender As Object, _ ByVal e As DataGridViewCellMouseEventArgs) _ Handles clipboardDataGridView.CellMouseDown ' Responding to this event allows us to make sure the ' correct row is properly selected on right-click If e.Button = Windows.Forms.MouseButtons.Right Then clipboardDataGridView.CurrentCell = _ clipboardDataGridView.Item(e.ColumnIndex, e.RowIndex) End If End Sub Private Sub ClipboardDataGridView_MouseDown( _ ByVal sender As System.Object, ByVal e As MouseEventArgs) _ Handles clipboardDataGridView.MouseDown ' On right-click display the row as selected and show ' the context menu at the location of the cursor If e.Button = Windows.Forms.MouseButtons.Right Then Dim hti As DataGridView.HitTestInfo = _ clipboardDataGridView.HitTest(e.X, e.Y) If hti.Type = DataGridViewHitTestType.Cell Then clipboardDataGridView.ClearSelection() clipboardDataGridView.Rows(hti.RowIndex).Selected = True ContextMenuStrip.Show(clipboardDataGridView, e.Location) End If End If End Sub ' Override WndProc to get messages Protected Overrides Sub WndProc(ByRef m As Message) Select Case m.Msg ' The clipboard has changed Case Is = WM_DRAWCLIPBOARD If Not _internalHold Then AddDataToGrid() SendMessage(_nxtCbVwrHWnd, m.Msg, m.WParam, m.LParam) ' Another clipboard viewer has removed itself Case Is = WM_CHANGECBCHAIN If m.WParam = CType(_nxtCbVwrHWnd, IntPtr) Then _nxtCbVwrHWnd = m.LParam Else SendMessage(_nxtCbVwrHWnd, m.Msg, m.WParam, m.LParam) End If End Select MyBase.WndProc(m) End Sub End Class Public Class PaletteToolStrip Inherits ToolStrip Public Sub New() MyBase.New() End Sub Public Sub New(ByVal ParamArray Items() As ToolStripItem) MyBase.New(Items) End Sub Protected Overrides Sub WndProc(ByRef m As Message) If m.Msg = &H21 AndAlso CanFocus AndAlso Not Focused Then Focus() End If MyBase.WndProc(m) End Sub End Clas

I also added a VB.NET version of the C# code that automatically registers an AutoCAD .NET application for demand-loading based on the commands it defines:

Imports System.Collections.Generic Imports System.Reflection Imports System.Resources Imports System Imports Microsoft.Win32 Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.Runtime Namespace DemandLoading Public Class RegistryUpdate Public Shared Sub RegisterForDemandLoading() ' Get the assembly, its name and location Dim assem As Assembly = Assembly.GetExecutingAssembly() Dim name As String = assem.GetName().Name Dim path As String = assem.Location ' We'll collect information on the commands ' (we could have used a map or a more complex ' container for the global and localized names ' - the assumption is we will have an equal ' number of each with possibly fewer groups) Dim globCmds As New List(Of String)() Dim locCmds As New List(Of String)() Dim groups As New List(Of String)() ' Iterate through the modules in the assembly Dim mods As [Module]() = assem.GetModules(True) For Each [mod] As [Module] In mods ' Within each module, iterate through the types Dim types As Type() = [mod].GetTypes() For Each type As Type In types ' We may need to get a type's resources Dim rm As New ResourceManager(type.FullName, assem) rm.IgnoreCase = True ' Get each method on a type Dim meths As MethodInfo() = type.GetMethods() For Each meth As MethodInfo In meths ' Get the methods custom command attribute(s) Dim attbs As Object() = _ meth.GetCustomAttributes( _ GetType(CommandMethodAttribute), True) For Each attb As Object In attbs Dim cma As CommandMethodAttribute = _ TryCast(attb, CommandMethodAttribute) If cma IsNot Nothing Then ' And we can finally harvest the information ' about each command Dim globName As String = cma.GlobalName Dim locName As String = globName Dim lid As String = cma.LocalizedNameId ' If we have a localized command ID, ' let's look it up in our resources If lid IsNot Nothing Then ' Let's put a try-catch block around this ' Failure just means we use the global ' name twice (the default) Try locName = rm.GetString(lid) Catch End Try End If ' Add the information to our data structures globCmds.Add(globName) locCmds.Add(locName) If cma.GroupName IsNot Nothing AndAlso _ Not groups.Contains(cma.GroupName) Then groups.Add(cma.GroupName) End If End If Next Next Next Next ' Let's register the application to load on demand (12) ' if it contains commands, otherwise we will have it ' load on AutoCAD startup (2) Dim flags As Integer = (If(globCmds.Count > 0, 12, 2)) ' By default let's create the commands in HKCU ' (pass false if we want to create in HKLM) CreateDemandLoadingEntries(name, path, globCmds, locCmds, _ groups, flags, True) End Sub Public Shared Sub UnregisterForDemandLoading() RemoveDemandLoadingEntries(True) End Sub ' Helper functions Private Shared Sub CreateDemandLoadingEntries( _ ByVal name As String, ByVal path As String, _ ByVal globCmds As List(Of String), _ ByVal locCmds As List(Of String), _ ByVal groups As List(Of String), _ ByVal flags As Integer, _ ByVal currentUser As Boolean) ' Choose a Registry hive based on the function input Dim hive As RegistryKey = _ If(currentUser,Registry.CurrentUser,Registry.LocalMachine) ' Open the main AutoCAD (or vertical) and "Applications" keys Dim ack As RegistryKey = _ hive.OpenSubKey( _ HostApplicationServices.Current.RegistryProductRootKey) Dim appk As RegistryKey = ack.OpenSubKey("Applications", True) ' Already registered? Just return Dim subKeys As String() = appk.GetSubKeyNames() For Each subKey As String In subKeys If subKey.Equals(name) Then appk.Close() Exit Sub End If Next ' Create the our application's root key and its values Dim rk As RegistryKey = appk.CreateSubKey(name) rk.SetValue("DESCRIPTION", name, RegistryValueKind.[String]) rk.SetValue("LOADCTRLS", flags, RegistryValueKind.DWord) rk.SetValue("LOADER", path, RegistryValueKind.[String]) rk.SetValue("MANAGED", 1, RegistryValueKind.DWord) ' Create a subkey if there are any commands... If (globCmds.Count = locCmds.Count) _ AndAlso globCmds.Count > 0 Then Dim ck As RegistryKey = rk.CreateSubKey("Commands") For i As Integer = 0 To globCmds.Count - 1 ck.SetValue(globCmds(i), locCmds(i), _ RegistryValueKind.[String]) Next End If ' And the command groups, if there are any If groups.Count > 0 Then Dim gk As RegistryKey = rk.CreateSubKey("Groups") For Each grpName As String In groups gk.SetValue(grpName, grpName, _ RegistryValueKind.[String]) Next End If appk.Close() End Sub Private Shared Sub RemoveDemandLoadingEntries( _ ByVal currentUser As Boolean) Try ' Choose a Registry hive based on the function input Dim hive As RegistryKey = _ If(currentUser,Registry.CurrentUser,Registry.LocalMachine) ' Open the main AutoCAD (or vertical) and "Applications" keys Dim ack As RegistryKey = _ hive.OpenSubKey( _ HostApplicationServices.Current.RegistryProductRootKey) Dim appk As RegistryKey = _ ack.OpenSubKey("Applications", True) ' Delete the key with the same name as this assembly appk.DeleteSubKeyTree( _ Assembly.GetExecutingAssembly().GetName().Name) appk.Close() Catch End Try End Sub End Class End Namespace

That's really all there is to it. If you have any feedback regarding the behaviour of the tool, please do send us an email.

AutoCAD的粘贴板管理器相关推荐

  1. Arduino支持STM32套件的开发板管理器安装

    自己在Arduino中安装STM32套件的时候 ,及其郁闷的是:在没有FQ的情况下,下载很慢.慢就算了我可以等,可是不支持断点续传.在下载中间容易出错,出错就完蛋了.后面根本就算下载完成都会校验出错. ...

  2. Arduino开发ESP8266时开发板管理器无法下载问题解决办法

    文章目录 一.问题描述 二.解决办法 1预先下载好对应版本的安装包 2还未解决怎么办? 总结 一.问题描述 ESP8266基于Arduino开发时需要到开发板管理器里面下载对应的ESP8266开发板, ...

  3. 如何解决Arduino IDE开发板管理器下载失败----以离线安装esp8266-2.7.2为例

    如何解决Arduino IDE开发板管理器下载失败----以离线安装esp8266-2.7.2为例 esp8266-2.7.2 离线安装步骤 注意:离线安装可以避免下载文件,但仍然会自动联网验证安装包 ...

  4. ESP8266 Arduino 解决开发板管理器下载开发包失败和速度慢的方法

    一.问题原因 在Arduino中想要下载esp8266开发包,发现下载失败,或者下载速度很慢 第一个想到的解决方法应该是找个代理网络,但是太麻烦. 第二个是不在开发板里面下载,下载外部的esp8266 ...

  5. 粘贴板管理 android,安卓剪贴板管理(Clipper Plus)

    安卓系统和window一样,只能记录一个复制片段,pc端有clipx等剪贴板增强软件,安卓手机端也是有的? Clipper Plus能够有效的增强Android系统中剪贴.复制这样的功能的应用.使用C ...

  6. 粘贴板管理工具ditto的使用

    触发ditto窗口:ctrl+~    (按住shift,再用上下键去选择对应的文本)  官网下载:https://ditto-cp.sourceforge.io/ 直接拖放至桌面可形成一个txt文档 ...

  7. Arduino使用ESP8266安装问题(包括附加开发板管理网址)

    笔者第一次接触Arduino的一些设置问题 附上Arduino(官方免费)官网下载界面-- https://www.arduino.cc/en/software (PC下载选择Windows Win ...

  8. 使用arduino开发esp8266和esp32时首选项设置方法(附国内开发板管理网址)

    摘要:本文介绍arduino在开发esp8266和esp32时的首选项设置方法.其中给出的国内开发板管理器网址速度飞起. 基于esp8266开发的板子有很多种,例如D1等. esp8266的开发板管理 ...

  9. 打开文件管理器_大批量图纸管理的贴心助手好管家,参照管理器,你值得拥有...

    原创:就说我在开发区 趣说CAD,让枯燥生动起来. 参照管理器 在开始今天的内容之前,先来看一则沃兹尼亚克对乔布斯在推出iPad过程中所起作用的评价. 沃兹尼亚克何许人也,能评价乔布斯当然也非无名之辈 ...

最新文章

  1. java ftp下载文件源码_java实现ftp文件下载的源代码
  2. asp.net mvc 重定向
  3. 把SAP Cloud for Customer嵌入到IFrame里
  4. 2014阿里巴巴校园招聘笔试题 - 中南站
  5. getsockname与getpeername
  6. 内存不足 java.lang.OutOfMemoryError: Java heap space
  7. 源码安装nginx以及平滑升级
  8. java拆分数据查相等_scikit learn:train_test_split,我可以确保在不同的数据集上进行相同的拆分...
  9. linux如何查icsci进程,linux13问
  10. Atitit.月度计划日程表 每月流程表v5
  11. 免费的社工机器人?查看是否自己被社工?
  12. 人工智能(AI)第一节课总结
  13. 126邮箱stmp服务器,免费邮箱
  14. Android 图片控件ImageView
  15. 图像互信息(MI)的计算(Python版本)
  16. 数据分析实战(六):英国电商用户行为分析
  17. DeepLearning with Pytorch Chapter04
  18. JavaScript控制div的移动跟着鼠标一起移动div
  19. 兰芝女王 的炒股心得-转载编辑
  20. Activity是什么

热门文章

  1. oracle连接超时是什么意思,oracle数据库连接超时怎么办 求指导oracle远程连接超时怎么办...
  2. 晶闸管(晶体闸流管VT)又叫可控硅,变压器同名端鉴别电路,应急灯电路
  3. 2023年度国家自然科学基金项目申请初审结果公布
  4. 史上十大免费下载应用和服务
  5. 项目成本管理__组成结构__成本的分类_固定成本和可变成本
  6. 导致服务器死机的原因集锦
  7. windows docker虚拟磁盘文件迁移ext4.vhdx
  8. SlidingMenu侧换菜单的导入
  9. python multiprocess pool模块报错pickling error
  10. 为者常成,行者常至(进步、收获、成长、自信)