C# 与 Windows API 交互的“秘密武器”:结构体和联合体

一、引言

在 C# 的编程世界里,当我们想要深入挖掘 Windows 系统的底层功能,与 Windows API 打交道时,结构体和联合体就像是两把神奇的钥匙🔑 它们能够帮助我们精准地操控数据,实现一些高级且强大的功能。就好比搭建一座精密的机器,每个零件都必须严丝合缝,结构体和联合体就是这些关键零件,确保我们与 Windows API 的交互顺畅无阻。今天,咱们就一起揭开它们神秘的面纱,看看在 C# 调用 Windows API 的过程中,它们究竟有着怎样的魔力。

二、结构体和联合体是什么?

(一)结构体的定义与特性

在 C# 中,结构体(Struct)是一种值类型,这意味着它在内存中有着独特的存储方式,与引用类型不同,值类型变量直接存储其数据,而不是存储对数据的引用。结构体就像是一个精致的收纳盒,能够将多个不同类型或者相同类型的数据成员有序地封装在一起。比如说,当我们在处理图形相关的编程任务时,常常需要表示一个点的坐标,这时就可以定义一个结构体:

// 定义一个结构体,用于存储点的坐标
public struct Point
{public int X;public int Y;public Point(int x, int y){X = x;Y = y;}
}

在上述代码中,我们清晰地定义了 Point 结构体,它包含了两个整型数据成员 X 和 Y,分别用于表示点在二维平面中的横坐标与纵坐标。结构体中的构造函数则为我们提供了便捷的初始化方式,就像给收纳盒里的物品设定初始摆放位置一样。在 Main 方法中,我们可以轻松地创建一个 Point 结构体的实例:

class Program
{static void Main(){// 创建一个点结构体实例Point p = new Point(10, 20);Console.WriteLine($"点P的坐标是: ({p.X}, {p.Y})");}
}

通过这段代码,我们将坐标点 (10, 20) 封装在 p 实例中,并且能够准确无误地将其坐标信息打印输出,是不是非常直观且方便呢?结构体这种能够将相关数据紧密捆绑在一起的特性,使得我们在处理复杂数据组合时,代码逻辑更加清晰,数据管理更加高效。

(二)联合体的独特之处

联合体(Union),从名字上看就感觉它有着特殊的魔力,它其实是一种特殊的结构体。想象一下,在同一个小小的内存空间里,它可以像一位神奇的换装大师,随时变换存储的数据类型。在 C 或 C++ 中,联合体有直接的语法支持,而在 C# 中,虽然没有原生的联合体关键字,但我们可以巧妙地借助结构体和显式布局来模拟出它的行为。比如说:

using System;
using System.Runtime.InteropServices;
// 定义一个模拟联合体的结构体
[StructLayout(LayoutKind.Explicit)]
public struct UnionExample
{[FieldOffset(0)]public int IntegerValue;[FieldOffset(0)]public double DoubleValue;
}

在这段代码中,我们通过 StructLayout(LayoutKind.Explicit) 属性精心规划了结构体的内存布局,让它按照我们期望的方式排列。再利用 FieldOffset(0) 属性,指明 IntegerValue 和 DoubleValue 这两个成员都从内存的起始位置(偏移量为 0)开始存储,这就意味着它们共享同一块内存区域。

接着,在 Main 方法中,我们来看看它的神奇表现:

class Program
{static void Main(){// 创建一个联合体实例,并设置整数值UnionExample u = new UnionExample();u.IntegerValue = 123;// 打印整数值Console.WriteLine($"整数值: {u.IntegerValue}");// 设置双精度浮点数值u.DoubleValue = 123.456;// 打印双精度浮点数值Console.WriteLine($"双精度浮点数值: {u.DoubleValue}");}
}

当我们先给 IntegerValue 赋值为 123 并打印时,一切都很正常。但当我们紧接着给 DoubleValue 赋值为 123.456 后,再去打印 IntegerValue,就会发现它的值已经发生了变化,不再是原来的 123。这是因为它们共享同一块内存,新的数据覆盖了旧的数据,就如同换装大师穿上了新衣服,旧衣服自然就看不到了。这种特性使得联合体在某些特定场景下,能够极大地节省内存空间,当我们明确知道在不同时刻只需要使用其中一种数据类型时,联合体就能发挥它的超能力。与结构体相比,结构体的每个成员都有自己独立的内存空间,数据相互独立,互不干扰;而联合体则是以牺牲数据的同时存在性为代价,换来内存利用的高效性,在不同的编程需求下,它们各自有着不可替代的优势。

三、结构体的魔法

(一)在 C# 中的定义和使用方式

在 C# 中定义结构体,就如同打造一个专属的工具盒。我们使用 struct 关键字开启结构体的定义之旅,在大括号内精心排列各种数据成员,这些成员可以是基本数据类型,如 int、double、string 等,也能是其他已定义的结构体类型,就像在收纳盒里放置不同种类的小物件。例如:

// 定义一个表示书籍信息的结构体
public struct Book
{public string Title;public string Author;public int PageCount;public Book(string title, string author, int pageCount){Title = title;Author = author;PageCount = pageCount;}
}

在这段代码里,Book 结构体如同一个精致的书籍档案夹,它收纳了书籍的标题 Title、作者 Author 以及页数 PageCount 这些关键信息,构造函数则为快速整理书籍档案提供了便捷方式。当我们在 Main 方法中使用它时:

class Program
{static void Main(){// 创建一个Book结构体实例Book myBook = new Book("《C#编程探秘》", "神秘博主", 300);Console.WriteLine($"书名:{myBook.Title},作者:{myBook.Author},页数:{myBook.PageCount}");}
}

通过实例化 Book 结构体,我们轻松地将一本虚拟书籍的信息封装其中,并准确地将这些信息展示出来,是不是感觉结构体就像是我们手中灵活的数据整理小助手呢?而且,结构体作为值类型,在赋值操作时会进行数据的完整拷贝,这意味着改变一个结构体实例的成员值,不会影响到其他副本,就像复制了多个一模一样的收纳盒,修改其中一个盒子里的物品,不会影响到其他盒子。

(二)实际应用场景举例

结构体在实际编程中的应用场景十分广泛,尤其是在与 Windows API 交互时,它常常扮演着数据传递使者的重要角色。在图形处理领域,当我们需要绘制图形、处理图像坐标或者判断图形之间的位置关系时,结构体就大显身手了。就拿前面提到的 Point 结构体来说,它可以精准地表示二维平面上的一个点的坐标,无论是绘制简单的线条、矩形,还是复杂的多边形,都离不开它对坐标点的准确描述。在绘制一个矩形时,我们可能会定义如下结构体:

[StructLayout(LayoutKind.Sequential)]
public struct Rectangle
{public Point TopLeft;public Point BottomRight;
}

这里的 Rectangle 结构体巧妙地利用了 Point 结构体,将矩形的左上角和右下角坐标封装起来,使得在图形绘制函数中传递矩形信息变得简洁明了。当调用绘制矩形的 Windows API 函数时,只需将 Rectangle 结构体实例作为参数传递,函数就能精准地获取矩形的位置和大小信息,在屏幕上绘制出我们期望的图形。

在系统信息获取方面,结构体同样不可或缺。比如,当我们想要获取系统的内存状态、CPU 信息或者窗口相关属性时,Windows API 提供了相应的函数,而这些函数往往要求以特定结构体作为参数来接收返回的数据。以获取系统内存状态为例,可能会有类似这样的结构体:

[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_INFO
{public uint dwLength;public uint dwMemoryLoad;public uint dwTotalPhys;public uint dwAvailPhys;public uint dwTotalPageFile;public uint dwAvailPageFile;public uint dwTotalVirtual;public uint dwAvailVirtual;
}

通过调用相关的 Windows API 函数,并传入 MEMORY_INFO 结构体实例,函数就能将系统内存的各种详细信息填充到结构体的各个成员中,我们后续只需在 C# 代码中读取这些成员值,就能清晰地了解系统内存的使用情况,就像是拥有了一个系统内存的信息仪表盘,为优化系统性能提供精准的数据支持。

四、联合体的超能力

(一)C# 中模拟联合体的方法

在 C# 中,虽然没有像 C 或 C++ 那样直接使用 union 关键字来定义联合体,但我们拥有强大的.NET 框架特性,能够巧妙地模拟出联合体的神奇功能。关键就在于 StructLayout 和 FieldOffset 这两个属性。StructLayout 属性如同一位精密的建筑师,它精心规划结构体在内存中的布局方式。当我们将其设置为 LayoutKind.Explicit 时,就相当于告诉编译器:“嘿,我要按照自己的精确规划来安排结构体成员的内存位置。” 而 FieldOffset 属性则像是一个个精准的坐标标记,它指明了每个结构体成员在内存中的具体偏移量。

就拿之前提到的代码为例:

using System;
using System.Runtime.InteropServices;
// 定义一个模拟联合体的结构体
[StructLayout(LayoutKind.Explicit)]
public struct UnionExample
{[FieldOffset(0)]public int IntegerValue;[FieldOffset(0)]public double DoubleValue;
}

在这段代码里,[StructLayout(LayoutKind.Explicit)] 为 UnionExample 结构体设定了独特的内存布局规则,使其成员的存储打破常规。[FieldOffset(0)] 这个标记对于 IntegerValue 和 DoubleValue 来说,就如同将它们都精确地放置在内存的起始起跑线,意味着它们共享同一块初始内存区域。这就模拟出了联合体那种在同一内存位置存储不同数据类型的特性。当我们在 Main 方法中操作这个模拟联合体时:

class Program
{static void Main(){// 创建一个联合体实例,并设置整数值UnionExample u = new UnionExample();u.IntegerValue = 123;// 打印整数值Console.WriteLine($"整数值: {u.IntegerValue}");// 设置双精度浮点数值u.DoubleValue = 123.456;// 打印双精度浮点数值Console.WriteLine($"双精度浮点数值: {u.DoubleValue}");}
}

可以清晰地看到,先给 IntegerValue 赋值为 123 并打印,一切顺利。但当紧接着给 DoubleValue 赋值为 123.456 后,再次打印 IntegerValue,它的值已经被新的数据无情覆盖,不再是最初的 123。这正是联合体内存共享特性的生动体现,在 C# 中通过这样巧妙的模拟,我们同样能够驾驭这种强大的数据存储方式。

(二)应用优势与注意事项

联合体在实际应用中有着诸多独特的优势。最显著的一点就是它能够极大地节省内存空间。想象一下,在某些特定的场景下,比如处理一些传感器数据,我们可能在不同时刻只需要用到整数类型或者浮点类型来表示数据,这时使用联合体,让它们共享同一块内存,就避免了像结构体那样为每个成员开辟独立内存空间,从而有效减少内存的占用。就如同在一个狭小的储物箱里,通过合理安排物品的存放方式,让不同的物品在不同时间共用同一个空间,达到空间利用的最大化。

在与一些底层硬件交互或者解析特定格式的二进制数据时,联合体也能发挥关键作用。例如,在网络编程中接收数据包,数据包的头部可能根据不同的协议版本,某个字段既可以是整数表示的简单标识,也可以是复杂结构体表示的详细信息。这时,联合体就能让我们轻松应对这种数据格式的变化,根据实际情况灵活地将同一块内存区域解读为不同的数据类型,高效地解析数据包,确保数据传输的准确与流畅。

不过,使用联合体也需要格外小心,如同在钢丝上行走,稍有不慎就可能引发问题。由于成员共享同一块内存,数据覆盖的风险时刻存在。就像前面的例子,给一个成员赋值后,再给另一个共享内存的成员赋值,之前的值就会被覆盖丢失。所以在代码编写过程中,必须时刻牢记当前联合体中存储的数据类型,精准地操作对应的成员,避免出现数据混乱的情况。而且,这种内存共享的特性也使得联合体的代码可读性相对较差,对于后续维护代码的开发者来说,可能需要花费更多的精力去理解其中的数据流向和逻辑关系。因此,在使用联合体时,权衡好内存优化与代码可维护性之间的平衡至关重要,确保它在正确的场景下发挥最大的效能,而不是成为引发程序错误的 “定时炸弹”。

五、结构体和联合体在 Windows API 中的应用

(一)重要性体现

在与 Windows API 交互的过程中,结构体和联合体的重要性怎么强调都不为过。Windows API 作为 Windows 系统的底层接口,函数繁多且功能各异,它们就像一个个精密的工具,等待着我们用合适的数据去驱动。而结构体和联合体,恰恰就是构建这些精准数据的基石。许多 Windows API 函数需要特定格式的数据才能正常工作,结构体和联合体能够完美地将数据按照要求进行封装,以正确的格式传递给 API 函数,确保程序与 Windows API 之间的通信顺畅无阻,就如同为不同形状的拼图找到了恰好匹配的空位,让整个画面得以完整呈现。

比如说,当我们想要获取系统中某个窗口的详细信息时,Windows API 提供了 GetWindowInfo 这样的函数,但它要求传入一个特定结构体来接收返回的数据。这个结构体必须精确地定义各个成员的类型和顺序,以匹配 API 底层的期望。如果没有结构体来整齐划一地整理这些数据,我们就像是将一堆杂乱无章的零件丢给一个精密仪器,仪器根本无法正常运转,程序自然也就无法获取到准确的窗口信息,更别提后续的处理与操作了。

(二)具体实例剖析

让我们深入剖析一个获取窗口信息的实例,看看结构体是如何在其中发挥关键作用的。首先,我们定义了如下结构体:

using System;
using System.Runtime.InteropServices;
// 定义一个结构体,用于存储窗口信息
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWINFO
{public uint cbSize;public RECT rcWindow;public RECT rcClient;public uint dwStyle;public uint dwExStyle;public uint dwWindowStatus;public uint cxWindowBorders;public uint cyWindowBorders;public ushort atomWindowType;public ushort wCreatorVersion;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{public int left;public int top;public int right;public int bottom;
}

在这段代码中,WINDOWINFO 结构体宛如一个精心设计的窗口信息收纳盒,它的每个成员都各司其职。cbSize 成员就像是一个标识,告诉 API 函数这个结构体的大小,确保数据传输的准确性;rcWindow 和 rcClient 成员则通过嵌套的 RECT 结构体,精确地描述了窗口的整体矩形区域以及客户区矩形区域,从窗口的边界位置到内部可用区域的范围都能精准定位;其他成员如 dwStyle、dwExStyle 等,分别对应着窗口的样式、扩展样式以及各种状态信息,涵盖了窗口外观、行为等多方面的特征。

当我们在 Main 方法中调用 GetWindowInfo 函数时:

class Program
{[DllImport("user32.dll")]public static extern bool GetWindowInfo(IntPtr hWnd, ref WINDOWINFO pwi);static void Main(){// 假设我们已经获取到了目标窗口的句柄,这里用hwnd表示IntPtr hwnd = /* 获取窗口句柄的代码 */; WINDOWINFO windowInfo = new WINDOWINFO();windowInfo.cbSize = (uint)Marshal.SizeOf(windowInfo);bool result = GetWindowInfo(hwnd, ref windowInfo);if (result){Console.WriteLine($"窗口位置:({windowInfo.rcWindow.left}, {windowInfo.rcWindow.top}) - ({windowInfo.rcWindow.right}, {windowInfo.rcWindow.bottom})");Console.WriteLine($"客户区位置:({windowInfo.rcClient.left}, {windowInfo.rcClient.top}) - ({windowInfo.rcClient.right}, {windowInfo.rcClient.bottom})");Console.WriteLine($"窗口样式:{windowInfo.dwStyle}");// 可以继续输出其他成员信息}}
}

在上述代码中,首先我们通过 DllImport 引入 GetWindowInfo 函数,确保能够在 C# 代码中调用这个 Windows API 函数。接着,在 Main 方法里,我们假设有了目标窗口的句柄(实际应用中需要通过其他方式获取,如 FindWindow 函数等),创建了 WINDOWINFO 结构体实例并初始化 cbSize 成员,这一步至关重要,它让 API 函数知道要接收的数据结构大小。当调用 GetWindowInfo 函数后,如果返回值为 true,表示成功获取到窗口信息,此时我们就能像打开装满宝藏的箱子一样,从结构体的各个成员中取出窗口的详细信息,无论是窗口在屏幕上的坐标位置、客户区的范围,还是窗口的样式特征,都能一目了然,为后续对窗口的进一步操作,如移动、缩放、修改样式等提供了坚实的数据基础,让我们的程序能够与 Windows 系统的窗口进行深度交互,实现更加丰富多样的功能。

六、常见问题与解决策略

(一)定义和使用中的错误类型

在使用结构体和联合体与 Windows API 交互的过程中,我们就像在布满礁石的航道上航行,一不小心就可能触礁。常见的错误类型还真不少,比如说结构体成员类型不匹配,就像拿着错误尺寸的拼图碎片,怎么也塞不进对应的空位。当我们定义的结构体成员类型与 Windows API 要求的不一致时,可能导致数据传输错误,进而引发程序崩溃或者得到错误的结果。比如在调用一个获取网络数据包信息的 Windows API 函数时,结构体中某个表示数据包长度的成员,在 C# 中错误地定义为 int 类型,而 API 要求的是 uint 类型,这就可能导致读取数据包长度时出现负值或者溢出的情况,使得后续对数据包的解析完全错乱。

联合体内存布局错误也是一个容易让人掉进的 “陷阱”。由于联合体成员共享同一块内存,如果在定义联合体时,没有正确设置 FieldOffset 等属性,或者不小心改变了成员的顺序、大小,就会像打乱了房间里家具的摆放,导致数据存储和读取混乱。例如,在一个模拟硬件设备状态的联合体中,一个成员用于表示设备的开关状态(bool 类型),另一个成员用于表示设备的温度值(float 类型),如果错误地设置了偏移量,可能会出现读取开关状态时得到的却是温度值的一部分二进制数据,将其错误解读为开关状态,这显然会引发严重的逻辑错误。

还有结构体或联合体在跨平台使用时的兼容性问题,就如同在不同规格的轨道上行驶的火车,容易脱轨。不同的操作系统或者硬件平台,对数据的存储方式、字节对齐等可能存在差异。在 32 位系统上正常运行的结构体代码,移植到 64 位系统时,可能因为指针类型的长度变化、结构体的填充字节数不同等原因,导致数据错位,程序出现莫名其妙的错误。这就要求我们在编写代码时,要有前瞻性,充分考虑到可能的平台差异,使用合适的属性(如 StructLayout 的不同布局选项)来确保结构体和联合体在不同平台上都能稳定运行。

(二)调试技巧与工具推荐

当遇到这些棘手的问题时,别慌,我们有一些调试的 “秘密武器”。首先,善用 Visual Studio 的调试功能,它就像是我们的程序侦探。在调试模式下,可以逐步执行代码,观察结构体和联合体变量在每一步的变化,查看内存中的数据存储情况。通过设置断点,在关键代码行暂停执行,仔细检查变量的值是否符合预期。比如在调用 Windows API 函数前后,分别查看作为参数传递的结构体成员值,看是否在函数调用过程中被正确赋值或者修改。还可以利用监视窗口,实时跟踪结构体和联合体中各个成员的数值变化,一旦发现异常,就能迅速定位到问题代码所在。

另外,有一个非常实用的工具 ——P/Invoke Interop Assistant,它像是一位贴心的导航员,能帮助我们在复杂的 P/Invoke 调用中找到正确的方向。当我们对结构体或函数的声明不确定时,只需将 Native 函数或者结构的声明拷贝到工具的相应文本框,点击生成,就能获取对应的.NET 声明,避免了手动声明时容易出现的错误。而且它还能查找 Win32 API 在.NET 中的声明,验证我们编写的.NET 函数或结构在 C 中的声明是否正确,为跨语言调用保驾护航,大大提高了我们编写代码的效率和准确性,让我们在与 Windows API 交互的编程之旅中更加顺畅。

七、总结

通过这一趟深入的探索之旅,我们清晰地认识到结构体和联合体在 C# 与 Windows API 交互中无可替代的关键作用。结构体宛如一位严谨的数据管家,有条不紊地将各类相关数据整理打包,以精准无误的格式递交给 Windows API,确保信息传递的准确性,让我们的程序能够顺利获取系统资源、操控窗口等诸多强大功能。联合体则像一位神奇的空间魔法师,在特定场景下,通过巧妙共享内存,为我们节省宝贵的内存资源,特别是在处理一些对内存占用敏感、数据类型需灵活切换的任务时,展现出独特的优势。

然而,这一路上我们也看到了不少隐藏在暗处的 “礁石”,从结构体成员类型的匹配失误,到联合体内存布局的错乱,再到跨平台时兼容性的挑战,稍有不慎就可能让程序 “触礁”。但别怕,我们手握 Visual Studio 调试利器以及 P/Invoke Interop Assistant 导航仪,只要善加利用,就能在遇到问题时迅速找准方向,化险为夷。

希望大家在今后的编程实践中,大胆地运用结构体和联合体这两把神奇钥匙,开启 Windows API 的强大宝库,去探索更多未知的编程天地,让你的程序绽放出别样的光彩,轻松应对各种复杂的开发需求,向着编程高手之路大步迈进!

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

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

相关文章

C++实现设计模式---状态模式 (State)

状态模式 (State) 状态模式 是一种行为型设计模式,它允许对象在运行时根据内部状态的改变来动态改变其行为。通过将状态相关的行为封装到独立的类中,状态模式使得状态的切换更加清晰和灵活。 意图 将对象的行为和状态分离,随着状态的改变动…

202312 青少年软件编程等级考试C/C++ 一级真题答案及解析(电子学会)

第 1 题 数的输入和输出 输入一个整数和双精度浮点数,先将浮点数保留2位小数输出,然后输出整数。 时间限制:1000 内存限制:65536 输入 一行两个数,分别为整数N(不超过整型范围),双精度浮点数F,以一个空格分开。 输出 一行两个数,分别为保留2位小数输出的F,以…

信息系统项目管理-采购管理-采购清单示例

序号类别产品/服务名称规格/功能描述数量备注1硬件服务器高性能处理器,大容量存储10HP、DELL2网络设备高速路由器和交换机10华为3工作站多核处理器,高分辨率显示器25国产设备4移动检查设备手持式移动检查仪,可连接云平台30国产设备5打印机和扫…

继续以“实用”指导Pythonic编码(re通配表达式)(2024年终总结②)

弃现成工具手剥任务🧐,我哈哈滴就像笨笨的傻大个儿😋。 (笔记模板由python脚本于2025年01月12日 23:29:33创建,本篇笔记适合熟悉正则表达式的coder翻阅) 【学习的细节是欢悦的历程】 Python官网:https://www.python.or…

Mysql学习笔记之函数

1.简介 SQL提供了很多函数,便于在查询时能够快速的进行计算或者计数等操作,下文介绍一些实际场景中常用的函数。 2.字符串函数 常用的字符串函数如下表所示: 函数名描述LENGTH(str)返回字符串的长度,以字节为单位。例如LENGTH…

pytest 参数介绍

命令行参数描述常见使用案例-v / --verbose显示每个测试用例的详细信息,包括测试名称和状态pytest -v-s / --captureno禁用输出捕获,允许 print() 输出显示pytest -s-q / --quiet安静模式,减少输出,仅显示每个测试的通过/失败结果…

10步打造完美ASP.NET、Web API和控制台应用程序文件夹结构

一、前言 在大型项目中,合理的文件夹结构是项目成功的关键之一。一个好的文件夹结构就像是一座井然有序的图书馆,每一本书(代码文件)都有其固定的位置,让人能迅速找到所需。它可以让团队成员更容易理解和维护代码&…

conntrack iptables 安全组

centos 安装yum install conntrack-tools 1. conntrack状态 NEW: 新建连接(第一次包)。 ESTABLISHED: 已建立连接,正在传输数据。 RELATED: 与已有连接相关的连接,如 FTP 数据连接。 INVALID: 无效连接,无法识别或不…

相机和激光雷达的外参标定 - 无标定板版本

1. 实现的效果 通过本软件实现求解相机和LiDAR的外参,即2个传感器之间的三维平移[x, y, z]和三维旋转[roll, pitch, yaw]。完成标定后,可将点云投影到图像,效果图如下: 本软件的优势:(1)无需特…

WPF系列九:图形控件EllipseGeometry

简介 EllipseGeometry用于绘制一个椭圆的形状。它通常与其他图形元素结合使用,比如 Path 或者作为剪切区域来定义其他元素的外形。 定义椭圆:EllipseGeometry 用来定义一个椭圆或者圆的几何形状。参与绘制:可以被用作 Path 元素的数据&…

EFCore HasDefaultValueSql (续2 HasComputedColumnSql)

前情:EFCore HasDefaultValueSql EFCore HasDefaultValueSql (续1 ValueGeneratedOnAdd)-CSDN博客 小伙伴在使用 HasDefaultValueSql 时,对相关的 ValueGeneratedOnAdd, HasComputedColumnSql 也有了疑问: HasComputedColumnSql 对于计算…

qt设置qwidget背景色无效

最近在做一个界面,有三个子窗体,于是就把各个子窗体分别做成了三个UI,再将3个UI,放到1个UI,再将那一个UI在其他窗体上进行提升。 最后就发现怎么设置qwidget的背景都没有效果。 在Qt中,如果是给Qwidget的…

【Rust学习笔记】Rust 的所有权介绍

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 博客内容主要围绕: 5G/6G协议讲解 高级C语言讲解 Rust语言讲解 文章目录 Rust中的所有权介绍1.1 一个简单的例子1.2 一个稍微复杂的例…

Centos9-SSH免密登录配置-修改22端口-关闭密码登录-提高安全性

Centos9-SSH免密登录配置-修改22端口-关闭密码登录 生成秘钥对将公钥信息存进authorized_keys测试登录查询访问记录、比对指纹更换22访问端口关闭账号密码登录生成秘钥对 生成密钥对,指定 备注 和 文件目录命令执行后,默认两次回车,不设置秘钥使用密码ssh-keygen -t rsa -b …

CentOS7下Hadoop集群分布式安装详细图文教程

1、集群规划 主机 角色 DSS20 NameNode DataNode ResourceManager NodeManager DSS21 SecondaryNameNode NameNode NodeManager DSS22 DataNode NodeManager 1.1、环境准备 1.1.1 关闭防火墙 #查看防火墙状态 firewall-cmd --state #停止…

Github 2025-01-11 Rust开源项目日报 Top10

根据Github Trendings的统计,今日(2025-01-11统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10C项目1Swift项目1Yazi - 快速终端文件管理器 创建周期:210 天开发语言:Rust协议类型:MIT LicenseStar数量:5668 个Fork数量:122…

在 Vue 项目中使用地区级联选

在 Vue 项目中使用地区级联选择的完整流程: 1.安装依赖包,这个包提供了中国省市区的完整数据。 npm install element-china-area-data --save 2.导入数据 import { regionData } from element-china-area-data 这个包提供了几种不同的数据格式&#…

深入学习 Python 爬虫:从基础到实战

深入学习 Python 爬虫:从基础到实战 前言 Python 爬虫是一个强大的工具,可以帮助你从互联网上抓取各种数据。无论你是数据分析师、机器学习工程师,还是对网络数据感兴趣的开发者,爬虫都是一个非常实用的技能。在本文中&#xff…

npm i 报错

nodejs中 使用npm install命令时报错 npm err! file C: \user\admin\package.json_package.json 里缺少 description 和 repository 两个n字段。-CSDN博客

基于改进粒子群优化的无人机最优能耗路径规划

目录 1. Introduction2. Preliminaries2.1. Particle Swarm Optimization Algorithm2.2. Deep Deterministic Policy Gradient2.3. Calculation of the Total Output Power of the Quadcopter Battery 3.OptimalEnergyConsumptionPathPlanningBasedonPSO-DDPG3.1.ProblemModell…