cad c# 二次开发 ——动态加载dll 文件制作(loada netloadx)

原理:制作一个dll工具,此dll工具可动态加载调试代码所生成的dll。

using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using Autodesk.AutoCAD.ApplicationServices.Core;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using LoadaDotNet;
namespace load
{public class Class1{private bool ev = false;[CommandMethod("netloadx")]public void Netloadx()//输入netloadx可动态加载程序的二进制文件{//IL_0017: Unknown result type (might be due to invalid IL or missing references)//IL_001d: Expected O, but got Unknown//IL_0036: Unknown result type (might be due to invalid IL or missing references)//IL_003c: Invalid comparison between Unknown and I4Editor editor = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;string text = "山水qq443440204";OpenFileDialog val = new OpenFileDialog();((FileDialog)val).Filter = "dll文件(*.dll)|*.dll";((FileDialog)val).Title = "打开dll文件";if ((int)((CommonDialog)val).ShowDialog() != 1){return;}text = ((FileDialog)val).FileName;AssemblyDependent assemblyDependent = new AssemblyDependent(text);bool flag = true;AssemblyDependent.LoadDllMessage[] array = assemblyDependent.Load().ToArray();foreach (AssemblyDependent.LoadDllMessage loadDllMessage in array){if (!loadDllMessage.LoadYes){editor.WriteMessage("\n" + loadDllMessage.Path + "失败!");flag = false;}}if (flag){editor.WriteMessage("\n加载成功!\n");}}[CommandMethod("ww")]public void ww()//输入ww可自动加载目录下的ifoxdemo{var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;var db = doc.Database;var ed = doc.Editor;var ad = new AssemblyDependent(@"G:\Csharp\Ifox\IfoxDemo\IfoxDemo\bin\Debug\net48\IfoxDemo.dll");  //写上你dll的路径var msg = ad.Load();bool allyes = true;foreach (var item in msg){if (!item.LoadYes){ed.WriteMessage("\n**" + item.Path + "**重复版本号,无需再次加载!" + System.Environment.NewLine);allyes = false;}}if (allyes){ed.WriteMessage("\n加载成功!\n");}if (!ev) { System.AppDomain.CurrentDomain.AssemblyResolve += RunTimeCurrentDomain.DefaultAssemblyResolve; ev = true; }}[CommandMethod("www")]public void www()//输入www自动加载路径下的demo.dll{var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;var db = doc.Database;var ed = doc.Editor;var ad = new AssemblyDependent(@"G:\Csharp\Demo\Demo\bin\Debug\Demo.dll");  //写上你dll的路径var msg= ad.Load();bool allyes = true;foreach (var item in msg){if (!item.LoadYes){ed.WriteMessage("\n**" + item.Path +"**重复版本号,无需再次加载!" + System.Environment.NewLine);allyes = false;}}if (allyes){ed.WriteMessage( "\n加载成功!\n");}if (!ev) { System.AppDomain.CurrentDomain.AssemblyResolve += RunTimeCurrentDomain.DefaultAssemblyResolve; ev = true; }}[CommandMethod("sxcx")]public void 属性查询()//输入sxcs可查询实体属性{List<Entity> ents = SelectEntities<Entity>();if (ents is null || ents.Count == 0){Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("未选择!\n");return;}object obj = ents[0];string str = "";str += "对象全部属性:   \n";str += "类型:    " + obj.GetType() + "\n";PropertyInfo[] pis = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);foreach (var pi in pis){try { str += pi.Name + " :    " + pi.GetValue(obj, null).ToString() + "\n"; }catch { str += pi.Name + "     " + "Null" + "\n"; }}str += "\n";//MessageBox.Show(str);TextForm f = new TextForm();f.richTextBox1.Text = str;Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(f);}public  List<T> SelectEntities<T>() where T : Entity{List<T> result = new List<T>();Editor editor = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;var pso = new PromptSelectionOptions();pso.MessageForAdding = "\n请选择:";PromptSelectionResult psr = editor.GetSelection(pso);if (psr.Status == PromptStatus.OK){ObjectId[] objectids = psr.Value.GetObjectIds();Database database = HostApplicationServices.WorkingDatabase;using (Transaction tran = database.TransactionManager.StartTransaction()){foreach (var item in objectids){Entity entity = item.GetObject(OpenMode.ForRead) as Entity;if (entity is T){result.Add(entity as T);}}}}return result;}}
}

第二个:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;namespace load
{[Serializable]public class AssemblyDependent : IDisposable{string _dllFile;/// <summary>/// cad程序域依赖_内存区(不可以卸载)/// </summary>private Assembly[] _cadAs;/// <summary>/// cad程序域依赖_映射区(不可以卸载)/// </summary>private Assembly[] _cadAsRef;/// <summary>/// 加载DLL成功后获取到的程序集/// </summary>public List<Assembly> MyLoadAssemblys { get; private set; }/// <summary>/// 当前域加载事件,运行时出错的话,就靠这个事件来解决/// </summary>public event ResolveEventHandler CurrentDomainAssemblyResolveEvent{add{AppDomain.CurrentDomain.AssemblyResolve += value;}remove{AppDomain.CurrentDomain.AssemblyResolve -= value;}}/// <summary>/// 链式加载dll依赖/// </summary>/// <param name="dllFile"></param>public AssemblyDependent(string dllFile){_dllFile = Path.GetFullPath(dllFile);//相对路径要先转换 Path.GetFullPath(dllFile);//cad程序集的依赖_cadAs = AppDomain.CurrentDomain.GetAssemblies();//映射区_cadAsRef = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies();//被加载的都存放在这里MyLoadAssemblys = new List<Assembly>();}/// <summary>/// 返回的类型,描述加载的错误/// </summary>public class LoadDllMessage{public string Path;public bool LoadYes;public LoadDllMessage(string path, bool loadYes){Path = path;LoadYes = loadYes;}public override string ToString(){if (LoadYes){return "加载成功:" + Path;}return "加载失败:" + Path;}}/// <summary>/// 加载信息集合/// </summary>List<LoadDllMessage> LoadYesList;bool _byteLoad;/// <summary>/// 加载程序集/// </summary>/// <param name="byteLoad">true字节加载,false文件加载</param>/// <returns>返回加载链的</returns>public List<LoadDllMessage> Load(bool byteLoad = true){_byteLoad = byteLoad;if (!File.Exists(_dllFile)){throw new ArgumentNullException("路径不存在");}LoadYesList = new List<LoadDllMessage>();//查询加载链之后再逆向加载,确保前面不丢失var allRefs = GetAllRefPaths(_dllFile);allRefs.Reverse();foreach (var path in allRefs){try{//路径转程序集名string assName = AssemblyName.GetAssemblyName(path).FullName;//路径转程序集名var assembly = _cadAs.FirstOrDefault(a => a.FullName == assName);if (assembly != null){LoadYesList.Add(new LoadDllMessage(path, false));//版本号没变不加载continue;}byte[] buffer = null;bool flag = true;//实现字节加载if (path == _dllFile){LoadOK = true;}
#if DEBUG//为了实现Debug时候出现断点,见链接,加依赖// https://www.cnblogs.com/DasonKwok/p/10510218.html// https://www.cnblogs.com/DasonKwok/p/10523279.htmlvar dir = Path.GetDirectoryName(path);var pdbName = Path.GetFileNameWithoutExtension(path) + ".pdb";var pdbFullName = Path.Combine(dir, pdbName);if (File.Exists(pdbFullName) && _byteLoad){var pdbbuffer = File.ReadAllBytes(pdbFullName);buffer = File.ReadAllBytes(path);var ass = Assembly.Load(buffer, pdbbuffer);MyLoadAssemblys.Add(ass);flag = false;}
#endifif (flag){Assembly ass = null;if (_byteLoad){buffer = File.ReadAllBytes(path);ass = Assembly.Load(buffer);}else{ass = Assembly.LoadFile(path);}MyLoadAssemblys.Add(ass);}LoadYesList.Add(new LoadDllMessage(path, true));//加载成功}catch{LoadYesList.Add(new LoadDllMessage(path, false));//错误造成}}MyLoadAssemblys.Reverse();return LoadYesList;}//链条后面的不再理会,因为相同的dll引用辨识无意义/// <summary>/// 第一个dll加载是否成功/// </summary>public bool LoadOK { get; private set; }/// <summary>/// 加载出错信息/// </summary>public string LoadErrorMessage{get{var sb = new StringBuilder();bool allyes = true;foreach (var item in LoadYesList){if (!item.LoadYes){sb.Append(Environment.NewLine + "** 此文件已加载过,重复名称,重复版本号,本次不加载!");sb.Append(Environment.NewLine + item.ToString());sb.Append(Environment.NewLine);allyes = false;}}if (allyes){sb.Append(Environment.NewLine + "** 链式加载成功!");sb.Append(Environment.NewLine);}return sb.ToString();}}/// <summary>/// 获取加载链/// </summary>/// <param name="dll"></param>/// <param name="dlls"></param>/// <returns></returns>List<string> GetAllRefPaths(string dll, List<string> dlls = null){if (dlls == null){dlls = new List<string>();}if (dlls.Contains(dll) || !File.Exists(dll)){return dlls;}dlls.Add(dll);//路径转程序集名string assName = AssemblyName.GetAssemblyName(dll).FullName;//在当前程序域的assemblyAs内存区和assemblyAsRef映射区找这个程序集名Assembly assemblyAs = _cadAs.FirstOrDefault(a => a.FullName == assName);Assembly assemblyAsRef;//内存区有表示加载过//映射区有表示查找过但没有加载(一般来说不存在.只是debug会注释掉Assembly.Load的时候用来测试)if (assemblyAs != null){assemblyAsRef = assemblyAs;}else{assemblyAsRef = _cadAsRef.FirstOrDefault(a => a.FullName == assName);//内存区和映射区都没有的话就把dll加载到映射区,用来找依赖表if (assemblyAsRef == null){// assemblyAsRef = Assembly.ReflectionOnlyLoad(dll); 没有依赖会直接报错var byteRef = File.ReadAllBytes(dll);assemblyAsRef = Assembly.ReflectionOnlyLoad(byteRef);}}//遍历依赖,如果存在dll拖拉加载目录就加入dlls集合foreach (var assemblyName in assemblyAsRef.GetReferencedAssemblies()){//dll拖拉加载路径-搜索路径(可以增加到这个dll下面的所有文件夹?)string directoryName = Path.GetDirectoryName(dll);var path = directoryName + "\\" + assemblyName.Name;var paths = new string[]{path + ".dll",path + ".exe"};foreach (var patha in paths){GetAllRefPaths(patha, dlls);}}return dlls;}/// <summary>/// 递归删除文件夹目录及文件/// </summary>/// <param name="dir"></param>/// <returns></returns>static void DeleteFolder(string dir){if (Directory.Exists(dir)) //如果存在这个文件夹删除之{foreach (string d in Directory.GetFileSystemEntries(dir)){if (File.Exists(d))File.Delete(d); //直接删除其中的文件elseDeleteFolder(d); //递归删除子文件夹}Directory.Delete(dir, true); //删除已空文件夹}}/// <summary>/// Debug的时候删除obj目录,防止占用/// </summary>public void DebugDelObjFiles(){try{var filename = Path.GetFileNameWithoutExtension(_dllFile);var path = Path.GetDirectoryName(_dllFile);var pdb = path + "\\" + filename + ".pdb";if (File.Exists(pdb)){File.Delete(pdb);}var list = path.Split('\\');if (list[list.Length - 1] == "Debug" && list[list.Length - 2] == "bin"){var bin = path.LastIndexOf("bin");var proj = path.Substring(0, bin);var obj = proj + "obj";DeleteFolder(obj);}}catch{ }}#region Disposepublic bool Disposed = false;/// <summary>/// 显式调用Dispose方法,继承IDisposable/// </summary>public void Dispose(){//由手动释放Dispose(true);//通知垃圾回收机制不再调用终结器(析构器)_跑了这里就不会跑析构函数了GC.SuppressFinalize(this);}/// <summary>/// 析构函数,以备忘记了显式调用Dispose方法/// </summary>~AssemblyDependent(){//由系统释放Dispose(false);}/// <summary>/// 释放/// </summary>/// <param name="ing"></param>protected virtual void Dispose(bool ing){if (Disposed){//不重复释放return;}//让类型知道自己已经被释放Disposed = true;GC.Collect();}#endregion}
}

第三个:

using System;
using System.Linq;
using System.Reflection;namespace load
{public static class RunTimeCurrentDomain{#region  程序域运行事件 // 动态编译要注意所有的引用外的dll的加载顺序// cad2008若没有这个事件,会使动态命令执行时候无法引用当前的程序集函数// 跨程序集反射// 动态加载时,dll的地址会在系统的动态目录里,而它所处的程序集(运行域)是在动态目录里.// netload会把所处的运行域给改到cad自己的,而动态编译不通过netload,所以要自己去改.// 这相当于是dll注入的意思,只是动态编译的这个"dll"不存在实体,只是一段内存./// <summary>/// 程序域运行事件/// </summary>   public static Assembly DefaultAssemblyResolve(object sender, ResolveEventArgs args){var cad = AppDomain.CurrentDomain.GetAssemblies();/*获取名称和版本号都一致的,调用它*/Assembly load = null;load = cad.FirstOrDefault(a => a.GetName().FullName == args.Name);if (load == null){/*获取名称一致,但是版本号不同的,调用最后的可用版本*/var ag = args.Name.Split(',')[0];//获取 最后一个符合条件的,//否则a.dll引用b.dll函数的时候,b.dll修改重生成之后,加载进去会调用第一个版本的b.dll            foreach (var item in cad){if (item.GetName().FullName.Split(',')[0] == ag){//为什么加载的程序版本号最后要是*//因为vs会帮你迭代这个版本号,所以最后的可用就是循环到最后的.load = item;}}}return load;}#endregion}
}

第四个sxcx:

namespace LoadaDotNet
{partial class TextForm{/// <summary>/// Required designer variable./// </summary>private System.ComponentModel.IContainer components = null;/// <summary>/// Clean up any resources being used./// </summary>/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>protected override void Dispose(bool disposing){if (disposing && (components != null)){components.Dispose();}base.Dispose(disposing);}#region Windows Form Designer generated code/// <summary>/// Required method for Designer support - do not modify/// the contents of this method with the code editor./// </summary>private void InitializeComponent(){this.richTextBox1 = new System.Windows.Forms.RichTextBox();this.SuspendLayout();// // richTextBox1// this.richTextBox1.Location = new System.Drawing.Point(11, 11);this.richTextBox1.Margin = new System.Windows.Forms.Padding(2);this.richTextBox1.Name = "richTextBox1";this.richTextBox1.Size = new System.Drawing.Size(455, 947);this.richTextBox1.TabIndex = 0;this.richTextBox1.Text = "";// // TextForm// this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;this.ClientSize = new System.Drawing.Size(477, 969);this.Controls.Add(this.richTextBox1);this.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.Margin = new System.Windows.Forms.Padding(2);this.Name = "TextForm";this.Text = "属性查询 (山水443440204)";this.TransparencyKey = System.Drawing.Color.White;this.ResumeLayout(false);}#endregionpublic System.Windows.Forms.RichTextBox richTextBox1;}
}

(咨询收费)联系↓↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/493948.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于AT89C52单片机的6位电子密码锁设计

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/90166684?spm1001.2014.3001.5503 14 部分参考设计如下&#xff1a; 目 录 摘要 1 abstract 2 1 绪论 3 1.1 课题背景 3 1.2 课题的目的和意义 3 1.3 电子密码…

文件解析漏洞中间件(iis和Apache)

IIS解析漏洞 IIS6.X #环境 Windows Server 2003 在iis6.x中&#xff0c;.asp文件夹中的任意文件都会被当做asp文件去执行 在默认网站里创建一个a.asp文件夹并创建一个1.jpg写进我们的asp代码 <%now()%> #asp一句话 <%eval request("h")%> 单独创建一…

ASP.NET|日常开发中数据集合详解

ASP.NET&#xff5c;日常开发中数据集合详解 前言一、数组&#xff08;Array&#xff09;1.1 定义和基本概念1.2 数组的操作 二、列表&#xff08;List<T>&#xff09;2.1 特点和优势2.2 常用操作 三、字典&#xff08;Dictionary<K, V>&#xff09;3.1 概念和用途…

OpenCV putText增加中文支持

OpenCV 默认并不支持中文字符显示&#xff0c;需要增加 freetype 支持&#xff0c;也需正确设置中文字体才能正常显示中文。 OpenCV 2.x 版本没有该模块&#xff0c;而 OpenCV 3.x 及以上版本才正式引入了 freetype 模块 &#xff0c;可检查并更新到较新且包含该模块的版本。 O…

设计模式期末复习

一、设计模式的概念以及分类 二、设计模式的主题和意图 设计模式的主题是关于软件设计中反复出现的问题以及相应的解决方案。这些主题是基于长期实践经验的总结&#xff0c;旨在提供一套可复用的设计思路和框架&#xff0c;以应对软件开发中的复杂性和变化性。 三、面向对象程…

Windows脚本清理C盘缓存

方法一&#xff1a;使用power文件.ps1的文件 脚本功能 清理临时文件夹&#xff1a; 当前用户的临时文件夹&#xff08;%Temp%&#xff09;。系统临时文件夹&#xff08;C:\Windows\Temp&#xff09;。 清理 Windows 更新缓存&#xff1a; 删除 Windows 更新下载缓存&#xff0…

随手记:小程序兼容后台的wangEditor富文本配置链接

场景&#xff1a; 在后台配置wangEditor富文本&#xff0c;可以文字配置链接&#xff0c;图片配置链接&#xff0c;产生的json格式为&#xff1a; 例子&#xff1a; <h1><a href"https://uniapp.dcloud.net.cn/" target"_blank"><span sty…

OpenHarmony-6.IPC/RPC组件

IPC/RPC组件机制 1.基本概念 IPC&#xff1a;设备内的进程间通信&#xff08;Inter-Process Communication&#xff09;。 RPC&#xff1a;设备间的进程间通信&#xff08;Remote Procedure Call&#xff09;。 IPC/RPC用于实现跨进程通信&#xff0c;不同的是前者使用Binder驱…

米思齐图形化编程之ESP32开发指导

在当今充满创意与探索的科技领域&#xff0c;米思齐图形化编程为广大爱好者开启了一扇通往智能硬件控制的便捷之门&#xff0c;尤其是当它与强大的 ESP32相结合时&#xff0c;更是碰撞出无限可能的火花。ESP32作为一款高性能、多功能的微控制器&#xff0c;拥有丰富的外设接口与…

tslib(触摸屏输入设备的轻量级库)的学习、编译及测试记录

目录 tslib的简介tslib的源码和make及make install后得到的文件下载tslib的主要功能tslib的工作原理tslib的核心组成部分tslib的框架和核心函数分析tslib的框架tslib的核心函数ts_setup()的分析(对如何获取设备名和数据处理流程的分析)函数ts_setup()自身的主要代码ts_setup()对…

使用 AI 辅助开发一个开源 IP 信息查询工具:一

本文将分享如何借助当下流行的 AI 工具,一步步完成一个开源项目的开发。 写在前面 在写代码时&#xff0c;总是会遇到一些有趣的机缘巧合。前几天&#xff0c;我在翻看自己之前的开源项目时&#xff0c;又看到了 DDNS 相关的讨论。虽然在 2021 年我写过两篇相对详细的教程&am…

Oracle:数据库的顶尖认证

在信息技术的飞速发展中&#xff0c;Oracle Corporation&#xff08;甲骨文公司&#xff09;以其在数据库领域的卓越成就而闻名遐迩。自1977年成立以来&#xff0c;Oracle已经从一个小型软件公司成长为全球最大的企业级软件公司之一&#xff0c;其产品和技术广泛应用于金融、电…

「配置应用的可见性」功能使用教程

引言 对于「应用可见性」这一概念&#xff0c;可能很多开发者小伙伴还不是很熟悉。简单举一个很典型的场景例子&#xff0c;当你开发的应用需要调起第三方应用时&#xff0c;这里就涉及到应用可见性的问题了&#xff0c;如果不配置相关的应用可见性&#xff0c;则你的应用是无…

flask flask-socketio创建一个网页聊天应用

应用所需环境&#xff1a; python 3.11.11 其他 只需要通过这个命令即可 pip install flask3.1.0 Flask-SocketIO5.4.1 -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple 最好是用conda创建一个新的虚拟环境来验证 完整的pip list如下 Package Version ----…

[计算机网络]唐僧的”通关文牒“NAT地址转换

1.NAT&#xff1a;唐僧的通关文牒 在古老的西游记中&#xff0c;唐僧师徒四人历经九九八十一难&#xff0c;终于取得了真经。然而&#xff0c;他们并不是一开始就获得了通关文牒&#xff0c;而是经过了重重考验&#xff0c;最终得到了国王的认可&#xff0c;才顺利通过了各个关…

数据结构经典算法总复习(下卷)

第五章:树和二叉树 先序遍历二叉树的非递归算法。 void PreOrderTraverse(BiTree T, void (*Visit)(TElemType)) {//表示用于查找的函数的指针Stack S; BiTree p T;InitStack(S);//S模拟工作栈while (p || !StackEmpty(S)) {//S为空且下一个结点为空&#xff0c;意味着结束遍…

【Windows版】opencv 和opencv_contrib配置

一、参考资料 &#xff08;四十一&#xff09;CMakeVSopencv/opencv_contrib 环境配置 从源码安装&#xff2f;penCV&#xff0c;使用python windowsvscodeopencv源码安装配置 二、关键步骤 1. opencv与opencv_contrib版本对齐 下载 opencv 下载 opencv_contrib opencv…

2014年IMO第4题

△ A B C \triangle ABC △ABC 中, B C BC BC 上有一点 P P P 满足 ∠ B A P = ∠ A C B \angle BAP=\angle ACB ∠BAP=∠ACB, 还有一点 Q Q Q 满足 ∠ A = Q A C = ∠ A B C \angle A=QAC=\angle ABC ∠A=QAC=∠ABC. 分别延长 A P AP AP, A Q AQ AQ 一倍至 M M M, N …

基于微信小程序的乡村旅游系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了多年的设计程序开发&#xff0c;开发过上千套设计程序&#xff0c;没有什么华丽的语言&#xff0c;只有实…

拆解一个微型气泵了解工作原理

原因 在焊接电路板时&#xff0c;测试打印后想要拆卸错误的板子上的元件&#xff0c;但每次拆卸过程中吸锡器手动按压到手疼。忽然看见一种小型气泵&#xff0c;不知道能不能做一个电动的吸锡器。 拆卸过程 第一步先把前盖板拆掉&#xff0c;这一步很好办到&#xff0c;就三…