DllImport("User32.dll", EntryPoint = "FindWindow")\] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 这两句代码是用来"向外借"的,它在c#里有一个极其专业并且常用的名字: P/Invoke(平台调用,Platform invocation Serrvices) "跨界找人" DllImport("User32.dll",EntryPoint="FindWindow") private static extern IntPtr FindWindow(string lpClassName,String lpWindowName); 这两句必须连在一起看,它们是在向 Windows 操作系统借用底层的 C++ 函数。 **`[DllImport("User32.dll", ...)]` 的本质**: * 这是"特性(Attribute)"标签。 * 它的意思是告诉 C# 编译器:"别在咱们自己的 C# 代码里找这个函数的实现逻辑了。你去 Windows 操作系统自带的 `User32.dll`(专门管窗口界面的底层动态链接库)里面找!" * `EntryPoint = "FindWindow"`:明确指定我要找的底层函数真名叫 `FindWindow` **`private static extern IntPtr FindWindow(...)` 的本质**: * `extern` 关键字:再次强调这个方法是"外部"的,C# 只负责声明,不写具体代码。 * `IntPtr` 返回值:它本质上就是个"储物柜钥匙"或者说"海关通行证",是用来安全存放内存地址或系统句柄的专属容器。 这两句合在一起,就是赋予了 C# 程序一个超能力------**只要你输入某个软件的"窗口类名"或"窗口标题",Windows 系统就会把那个窗口的身份证号(句柄 IntPtr)返回给你。** System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false; "关闭跨线程调用的非法检查(也就是关闭安保系统)。" 底层运行逻辑(为什么要写这句) 在 C# 的 WinForms 程序里,有一个铁律:**谁创建了控件,谁才有资格修改它。** **UI 主线程(店长)** :负责创建 `pictureBox1`(显示画面)、`textBox_logAndEvent`(写日志)。只有它有资格向里面塞东西。 **视觉抓图/处理线程(送货员)**:我们在后台开的子线程,专门负责死循环抓取相机图片. 按理说,送货员(子线程)拿到了新图片 ,必须打报告(通过Invoke或者BeginInvoke)交给店长(UI线程),让店长去更新画面。 但是!打报告的代码写起来比较繁琐,****很多新手或者为了图省事的程序员,就会加上这句 `CheckForIllegalCrossThreadCalls = false;`。这相当于**关掉了店里的警报器** ,允许外面的送货员直接冲进店里,强行把图片塞进 `pictureBox1` 里。 虽然加上这句代码,程序当时跑起来不会报错了,但是在真实的工业现场(7x24小时运行,每秒检测好几次),这种"野蛮操作"是**极其危险的定时炸弹** ! 多个线程同时去抢夺界面控件的控制权,极大概率会导致内存冲突。现象就是:**跑着跑着,你的软件界面突然卡死,或者 `pictureBox1` 变成一个巨大的红叉 ❌,整个软件直接崩溃!** * **正确的做法** :删掉这句话!老老实实地用 `this.Invoke(new Action(() => { 更新界面的代码 }));` 把数据安全地交给 UI 线程。 User32.dll 主要是用c语言和c++写的 DLL的全称Dynamic Link Libray(动态链接库)。可以把它想象成为一个"已经打包好的工具箱"。操作系统或者其他程序运行的时候,随时可以从这个箱子里面拿工具用。 Window操作系统最核心的"三大基石" User32.dll Kernel32.dll Gdi32.dll User32.dll里面包含了Windows管理所有窗口,接收鼠标键盘点击的底层逻辑。 因为Window系统本身是很早以前开发的,所以这些最基础的系统DLL都是用极度底层c/c++设置汇编语言写出来的。 反编译代码 void EnableOperatorEvent1( [In][MarshalAs(UnmanagedType.BStr)] string blockName, [In][MarshalAs(UnmanagedType.BStr)] string operatorName, [In] int eventID ); 这段代码最吓人的就是那些用中括号 `[]` 包起来的东西。在 C# 里,这叫**特性(Attribute)**,你可以把它理解为给编译器和底层翻译官看的"便签"。 ##### 1. `[In]`:单向通行证 * **字面意思**:输入。 * **底层逻辑** :它告诉 C# 的内存搬运工(封送拆送器 Marshaller):"这个参数只是从我 C#(老板)这里,**单向**传给底层的 C++ 引擎(干活的工人)的。工人不需要把修改后的值传回来。" * **作用** :为了省事、省内存。如果不加,系统可能还要提防着 C++ 把数据改了再拿回来,加上 `[In]` 就是明确告诉系统:送过去就完事了,别管了。 ##### `2、[MarshalAs(UnmanagedType.BStr)]`:跨国翻译官 * **字面意思** :将数据编排/封送(Marshal)为非托管类型(UnmanagedType)的 `BStr`。 * **为什么需要它** : Windows 底层和 C# 语言的区别?C# 里的 `string` 和 C++ 里的字符串在内存里的长相**完全不一样** !如果你直接把 C# 的 `string` 丢给 C++,C++ 会看着一堆乱码直接崩溃。 * **什么是 `BStr`** :`BStr`(Basic String)是微软早年搞 COM 技术时定下的一个"国际通用字符串标准"。 * **大白话** :这句代码就像是在请一个"跨国翻译官"。它告诉 C#:"在把 `blockName` 这个字符串交给底层的 C++ 之前,请先帮我把它翻译成大家都认识的 `BStr` 格式。" 内存管理与引用传递: SciEngine sciEngine=new SciEngine(); public FrmSetVariable(SciEngine sciEngine) { InitializeComponent(); this.sciEngine = sciEngine; } 我在主窗体里面创建了一个新窗体,新窗体里执行这个一段代码,执行this.sciEngine = sciEngine;后在新窗体里的sciEngine这个对象是新的对象,只不过赋值与主窗体一致还是说是主窗体中的对象呢。如果我在这个新窗体里面用这个对象对smart3中的变量进行了改变全局变量,是否需要返回给主窗体这个对象呢? * **是同一个对象!** 绝对不是新对象,它彻彻底底就是你主窗体里的那个引擎。 * **绝对不需要返回!** 你在新窗体里改了变量,主窗体立刻就能生效。 private SciEngine sciEngine; C# 在新窗体的内存里,划出了一块极小的空间,专门用来放一个叫 `sciEngine` 的指针(引用)。因为你没写 `= new`,也没给它赋值,所以它的默认值是 **`null`(空)**。
相关推荐
yuan199972 小时前
基于 C# 实现的 Omron HostLink (FINS) 协议 PLC 通讯火星papa3 小时前
C# 任务(Task)的基础实现烛阴6 小时前
Unity资源加载进化论:从AssetBundle到Addressables,一文带你吃透手游资源管理aini_lovee9 小时前
C#与倍福PLC(通过ADS协议)通信上位机源程序实现2501_9307077810 小时前
使用C#代码压平 PDF 表单字段IT知识分享13 小时前
数字上标、下标如何打,6种常用方法详解码农学院13 小时前
itextsharp .net中如何设置两个表格的间距设为0,取网站的域名,协议、端口、当前站点目录的地址monkeyhlj14 小时前
Agent Skills简单理解星河耀银海15 小时前
Unity C#入门:变量的定义与访问权限(public/private)