c#中的“跨界找人”

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++ 会看着一堆乱码直接崩溃。

  • 什么是 BStrBStr(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(空)

相关推荐
Chris _data2 小时前
并发单词频率统计器 - 从零到完整实现(C# 实战)
开发语言·c#
iCxhust11 小时前
C# 命令行指令 查看二进制文件
开发语言·单片机·嵌入式硬件·c#·proteus·微机原理·8088单板机
雪豹阿伟14 小时前
15.C# —— 多接口,泛型方法,泛型效率、泛型类和泛型接口、泛型约束
c#·上位机
影寂ldy14 小时前
C# const 常量 / readonly 只读 / static readonly
java·开发语言·c#
iCxhust14 小时前
c#多串口重量采集上位机程序
开发语言·汇编·c#·微机原理·8088单板机
雪豹阿伟15 小时前
14.C# —— 虚方法,new/override,密封类,索引器,接口
c#·上位机
糖果店的幽灵15 小时前
LangChain 1.3 完全教程:从入门到精通-Part 11: Tools(工具系统)
开发语言·langchain·c#
专注VB编程开发20年19 小时前
python翻译网页HTML的难题
python·c#·html
z落落20 小时前
C# 抽象类(abstract)
java·开发语言·c#