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(空)。