cad.net 句柄遍历数据库
说明
青蛙拿到了一种遍历数据库的新方式,然后发现这个东西遍历数据库奇慢.
这是一种利用了句柄来作为遍历的方式.
而句柄是递增模式的,直到递增到 db.Handseed.Value ,这个值非常大,有long长度的14位那么大,仅是天正导致?
为了停止它,我使用了一个变量,免得出现"生产消费模式"引起循环错误.
青蛙的信息来源
ObjectIds上缓慢的迭代
同时因为id = db.GetObjectId(false, handle, 0);
在acad08的表现非常慢,所以我被迫调用了Arx函数.
但是当我和e大做完了之后,我发现上面链接的第二页居然有....
遍历组码
同时发现这是遍历dwg所有组码的方式,因为entget并不能获取动态块组码信息,所以才需要它.
貌似还可以ent.DxfOut
有了组码,就可以获取动态块可见性名称了,其他的或许也可以...
分析动态块组码见动态块专篇
Arx代码见e大博文遍历动态块块定义的可见性名称方法
代码
#if !HC2020
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.Runtime;
using GrxCAD.EditorInput;
using Acap = GrxCAD.ApplicationServices.Application;
#endif
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace JoinBox.试验库
{
public class CmdTest_FastIterationClass
{
[CommandMethod("CmdTest_FastIteration")]
public void CmdTest_FastIteration()
{
var dm = Acap.DocumentManager;
var doc = dm.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
ed.WriteMessage($"{Environment.NewLine}利用句柄遍历数据库");
var dic = db.HandleToObjectId();
ed.WriteMessage($"{Environment.NewLine}利用句柄找到动态块可见性的名称");
int entNum = 0;
db.Action(tr => {
foreach (var dicItem in dic)
{
var id = dicItem.Value;
if (!id.IsOk())
continue;
try
{
string strDxfName = id.GetDxfName(tr);
//if (string.IsNullOrEmpty(strDxfName))
// continue;
ed.WriteMessage($"{Environment.NewLine}图元{++entNum}:DxfName={strDxfName};句柄={dicItem.Key};Id={id}");
if (strDxfName == "BLOCKVISIBILITYPARAMETER")
{
AcdbAdsHelper.AcdbGetAdsName(out AdsName eName, id);
var rb = new ResultBuffer();
var elist = AcdbAdsHelper.AcdbEntGet(ref eName);
if (elist == IntPtr.Zero)
elist = AcdbAdsHelper.AcdbEntGetX(ref eName, rb.UnmanagedObject);
if (elist == IntPtr.Zero)
continue;
rb = ResultBuffer.Create(elist, true);
foreach (var tv in rb)
{
if (tv.TypeCode == 303)//动态块的可见性参数被改的名称记录位置
PrintDxf(tv, ed);
}
}
}
catch (System.Exception e)
{
Debugger.Break();
}
}
});
ed.WriteMessage($"{Environment.NewLine}结束了!");
}
public void PrintDxf(TypedValue ty, Editor ed)
{
ed.WriteMessage($"{Environment.NewLine}TypeCode:{ty.TypeCode};;;Value:{ty.Value}");
}
}
public static class DatabaseHelper
{
public static string GetDxfName(this ObjectId id, Transaction tr)
{
string strDxfName;
#if !NET35
strDxfName = id.ObjectClass.DxfName;
#else
DBObject obj = tr.GetObject(id, OpenMode.ForRead);
//strDxfName = RXClass.GetClass(obj.GetType()).DxfName;//这里并不能准确获取到dxfName
strDxfName = obj.GetRXClass().DxfName;
#endif
return strDxfName;
}
/// <summary>
/// 遍历句柄获取数据库的ids
/// 可能是已经删除的,不一定满足id.IsOk()
/// </summary>
/// <param name="db"></param>
/// <param name="ed">控制是否打印时间</param>
public static Dictionary<Handle, ObjectId> HandleToObjectId(this Database db, Editor ed = null)
{
Dictionary<Handle, ObjectId> dicResult = new();
DateTime start = DateTime.Now;//时间计数
long time = 0; //时间计数
db.TraverseHandle(h => {
var handle = new Handle(h);
var id = db.TryGetObjectId(handle);
if (id != ObjectId.Null)
dicResult.Add(handle, id);
if (ed != null)
{
//计时输出
var len = DateTime.Now - start;
if (len.Seconds != time && len.Seconds % 5 == 0)//5秒输出一次
{
time = len.Seconds;
ed.WriteMessage($"{Environment.NewLine}数量: {h},已经过了{len.Days}天,{len.Hours}小时,{len.Minutes}分,{len.Seconds}秒");
ed.UpdateScreenEx();//此函数写在 https://www.cnblogs.com/JJBox/p/11354224.html
}
}
});
return dicResult;
}
/// <summary>
/// 遍历句柄
/// </summary>
/// <param name="db">数据库</param>
/// <param name="action">扔出去个迭代句柄数字</param>
static void TraverseHandle(this Database db, Action<long> action)
{
#if !NET35
var entlast = Autodesk.AutoCAD.Internal.Utils.EntLast().OldIdPtr;
var btids = db.BlockTableId.OldIdPtr;
if (IntPtr.Size == 4)
{
var endHandle = entlast.ToInt32();
var h2 = btids.ToInt32();
for (var h = h2; h < endHandle; h++)
action(h);
}
else if (IntPtr.Size == 8)
{
var endHandle = entlast.ToInt64();
var h2 = btids.ToInt64();
for (var h = h2; h < endHandle; h++)
action(h);
}
#else
var endHandle = db.Handseed.Value;//写到这里防止句柄出现递增
var h2 = db.BlockTableId.Handle.Value;
for (var h = h2; h < endHandle; h++)
action(h);
#endif
}
}
public static class TryGetObjectIdHelper
{
/*
* id = db.GetObjectId(false, handle, 0);
* 参数意义: db.GetObjectId(如果没有找到就创建,句柄号,标记..将来备用)
* 在vs的输出会一直抛出:
* 引发的异常:“Autodesk.AutoCAD.Runtime.Exception”(位于 AcdbMgd.dll 中)
* "eUnknownHandle"
* 这就是为什么慢的原因,所以直接运行就好了!而Debug还是需要用arx的API替代.
*/
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/,
EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")]
extern static int getAcDbObjectId17x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved);
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acdb17.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/,
EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")]
extern static int getAcDbObjectId17x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved);
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/,
EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QAE?AW4ErrorStatus@Acad@@AAVAcDbObjectId@@_NABVAcDbHandle@@K@Z")]
extern static int getAcDbObjectId18x32(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved);
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("acdb18.dll", CallingConvention = CallingConvention.ThisCall/*08的调用约定 高版本是__cdecl*/,
EntryPoint = "?getAcDbObjectId@AcDbDatabase@@QEAA?AW4ErrorStatus@Acad@@AEAVAcDbObjectId@@_NAEBVAcDbHandle@@K@Z")]
extern static int getAcDbObjectId18x64(IntPtr db, out ObjectId id, [MarshalAs(UnmanagedType.U1)] bool createnew, ref Handle h, uint reserved);
/// <summary>
/// 句柄转id,NET35(08~12)专用的
/// </summary>
/// <param name="db">数据库</param>
/// <param name="handle">句柄</param>
/// <param name="id">返回的id</param>
/// <param name="createIfNotFound">不存在则创建</param>
/// <param name="reserved">保留,用于未来</param>
/// <returns>成功0,其他值都是错误.可以强转ErrorStatus</returns>
static int GetAcDbObjectId(IntPtr db, Handle handle, out ObjectId id, bool createIfNotFound = false, uint reserved = 0)
{
id = ObjectId.Null;
switch (Acap.Version.Major)
{
case 17:
{
if (IntPtr.Size == 4)
return getAcDbObjectId17x32(db, out id, createIfNotFound, ref handle, reserved);
else
return getAcDbObjectId17x64(db, out id, createIfNotFound, ref handle, reserved);
}
case 18:
{
if (IntPtr.Size == 4)
return getAcDbObjectId18x32(db, out id, createIfNotFound, ref handle, reserved);
else
return getAcDbObjectId18x64(db, out id, createIfNotFound, ref handle, reserved);
}
}
return -1;
}
/// <summary>
/// 句柄转id
/// </summary>
/// <param name="db">数据库</param>
/// <param name="handle">句柄</param>
/// <returns>id</returns>
public static ObjectId TryGetObjectId(this Database db, Handle handle)
{
#if !NET35
//高版本直接利用
var es = db.TryGetObjectId(handle, out ObjectId id);
//if (!es)
#else
var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id);
//if (ErrorStatus.OK != (ErrorStatus)es)
#endif
return id;
}
/// <summary>
/// 此命令用来测试:内部调用NET35(08~12)专用的 GetAcDbObjectId,
/// 外部调用封装的 TryGetObjectId.
/// </summary>
[CommandMethod("CmdTest_handloop")]
public static void CmdTest_handloop()
{
var dm = Acap.DocumentManager;
var doc = dm.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
for (long i = 1; i < db.Handseed.Value; i++)
{
Handle handle = new(i);
var es = GetAcDbObjectId(db.UnmanagedObject, handle, out ObjectId id);
if (ErrorStatus.OK == (ErrorStatus)es)
ed.WriteMessage("\n执行成功");
if (id.IsNull)
continue;
ed.WriteMessage("\nid不是空的");
if (id.IsValid)
ed.WriteMessage($"\n有效ID:{id},句柄是{handle}");
if (id.IsErased)
{
ed.WriteMessage("是已经删除的ID");
continue;
}
using (var tr = db.TransactionManager.StartTransaction())
{
DBObject obj = tr.GetObject(id, OpenMode.ForRead);
if (obj == null)
continue;
if (obj != null)
ed.WriteMessage($"\n句柄={handle};DxfName={id.GetDxfName(tr)}");
//九个符号表
if (obj is BlockTable)
ed.WriteMessage("\nID是table");
}
}
}
}
}
缺省函数
操作备注
edata
而且我发现一个问题,这个句柄,如果你保存了,可能会写入很多的附加对象,句柄就会浪费很多..
比如打开文件,句柄是2c5,继续画直线,2c6.... 2c7...2c8 如果保存了.
就从 313 开始..中间因保存,重新打开后句柄浪费了75个左右...
ctrl+c应该是先复制一个对象到 剪贴板,然后粘贴.
所以要多生成几次.co 就不会乱增..
(完)