当前位置: 首页 > 编程日记 > 正文

C#中使用Win32和其他库

C# 用户经常提出两个问题:“我为什么要另外编写代码来使用内置于 Windows 中的功能?在框架中为什么没有相应的内容可以为我完成这一任务?”当框架小组构建他们的 .NET 部分时,他们评估了为使 .NET 程序员可以使用 Win32 而需要完成的工作,结果发现 Win32 API 集非常庞大。他们没有足够的资源为所有 Win32 API 编写托管接口、加以测试并编写文档,因此只能优先处理最重要的部分。许多常用操作都有托管接口,但是还有许多完整的 Win32 部分没有托管接口。

平台调用 (P/Invoke) 是完成这一任务的最常用方法。要使用 P/Invoke,您可以编写一个描述如何调用函数的原型,然后运行时将使用此信息进行调用。另一种方法是使用 Managed Extensions to C++ 来包装函数,这部分内容将在以后的专栏中介绍。

要理解如何完成这一任务,最好的办法是通过示例。在某些示例中,我只给出了部分代码;完整的代码可以通过下载获得。

简单示例

在第一个示例中,我们将调用 Beep() API 来发出声音。首先,我需要为 Beep() 编写适当的定义。查看 MSDN 中的定义,我发现它具有以下原型:

BOOL Beep(
DWORD dwFreq, // 声音频率
DWORD dwDuration // 声音持续时间
);


要用 C# 来编写这一原型,需要将 Win32 类型转换成相应的 C# 类型。由于 DWORD 是 4 字节的整数,因此我们可以使用 int 或 uint 作为 C# 对应类型。由于 int 是 CLS 兼容类型(可以用于所有 .NET 语言),以此比 uint 更常用,并且在多数情况下,它们之间的区别并不重要。bool 类型与 BOOL 对应。现在我们可以用 C# 编写以下原型:

public static extern bool Beep(int frequency, int duration);

这是相当标准的定义,只不过我们使用了 extern 来指明该函数的实际代码在别处。此原型将告诉运行时如何调用函数;现在我们需要告诉它在何处找到该函数。

我们需要回顾一下 MSDN 中的代码。在参考信息中,我们发现 Beep() 是在 kernel32.lib 中定义的。这意味着运行时代码包含在 kernel32.dll 中。我们在原型中添加 DllImport 属性将这一信息告诉运行时:

[DllImport("kernel32.dll")]

这就是我们要做的全部工作。下面是一个完整的示例,它生成的随机声音在二十世纪六十年代的科幻电影中很常见。



using System;
using System.Runtime.InteropServices;

namespace Beep
{
class Class1
{
[DllImport("kernel32.dll")]
public static extern bool Beep(int frequency, int duration);

static void Main(string[] args)
{
Random random = new Random();

for (int i = 0; i < 10000; i++)
{
Beep(random.Next(10000), 100);
}
}
}
}


它的声响足以刺激任何听者!由于 DllImport 允许您调用 Win32 中的任何代码,因此就有可能调用恶意代码。所以您必须是完全受信任的用户,运行时才能进行 P/Invoke 调用。

枚举和常量

Beep() 可用于发出任意声音,但有时我们希望发出特定类型的声音,因此我们改用 MessageBeep()。MSDN 给出了以下原型:



BOOL MessageBeep(
UINT uType // 声音类型
);


这看起来很简单,但是从注释中可以发现两个有趣的事实。

首先,uType 参数实际上接受一组预先定义的常量。

其次,可能的参数值包括 -1,这意味着尽管它被定义为 uint 类型,但 int 会更加适合。

对于 uType 参数,使用 enum 类型是合乎情理的。MSDN 列出了已命名的常量,但没有就具体值给出任何提示。由于这一点,我们需要查看实际的 API。

如果您安装了 Visual Studio? 和 C++,则 Platform SDK 位于 Program FilesMicrosoft Visual Studio .NETVc7PlatformSDKInclude 下。

为查找这些常量,我在该目录中执行了一个 findstr。

findstr "MB_ICONHAND" *.h

它确定了常量位于 winuser.h 中,然后我使用这些常量来创建我的 enum 和原型:



public enum BeepType
{
SimpleBeep = -1,
IconAsterisk = 0x00000040,
IconExclamation = 0x00000030,
IconHand = 0x00000010,
IconQuestion = 0x00000020,
Ok = 0x00000000,
}

[DllImport("user32.dll")]
public static extern bool MessageBeep(BeepType beepType);


现在我可以用下面的语句来调用它: MessageBeep(BeepType.IconQuestion);

处理结构

有时我需要确定我笔记本的电池状况。Win32 为此提供了电源管理函数。

搜索 MSDN 可以找到 GetSystemPowerStatus() 函数。



BOOL GetSystemPowerStatus(
LPSYSTEM_POWER_STATUS lpSystemPowerStatus
);


此函数包含指向某个结构的指针,我们尚未对此进行过处理。要处理结构,我们需要用 C# 定义结构。我们从非托管的定义开始:



typedef struct _SYSTEM_POWER_STATUS {
BYTE ACLineStatus;
BYTE BatteryFlag;
BYTE BatteryLifePercent;
BYTE Reserved1;
DWORD BatteryLifeTime;
DWORD BatteryFullLifeTime;
} SYSTEM_POWER_STATUS, *LPSYSTEM_POWER_STATUS;


然后,通过用 C# 类型代替 C 类型来得到 C# 版本。



struct SystemPowerStatus
{
byte ACLineStatus;
byte batteryFlag;
byte batteryLifePercent;
byte reserved1;
int batteryLifeTime;
int batteryFullLifeTime;
}


这样,就可以方便地编写出 C# 原型:



[DllImport("kernel32.dll")]
public static extern bool GetSystemPowerStatus(
ref SystemPowerStatus systemPowerStatus);


在此原型中,我们用“ref”指明将传递结构指针而不是结构值。这是处理通过指针传递的结构的一般方法。

此函数运行良好,但是最好将 ACLineStatus 和 batteryFlag 字段定义为 enum:



enum ACLineStatus: byte
{
Offline = 0,
Online = 1,
Unknown = 255,
}

enum BatteryFlag: byte
{
High = 1,
Low = 2,
Critical = 4,
Charging = 8,
NoSystemBattery = 128,
Unknown = 255,
}


请注意,由于结构的字段是一些字节,因此我们使用 byte 作为该 enum 的基本类型。

字符串

虽然只有一种 .NET 字符串类型,但这种字符串类型在非托管应用中却有几项独特之处。可以使用具有内嵌字符数组的字符指针和结构,其中每个数组都需要正确的封送处理。

在 Win32 中还有两种不同的字符串表示:

ANSI
Unicode

最初的 Windows 使用单字节字符,这样可以节省存储空间,但在处理很多语言时都需要复杂的多字节编码。Windows NT? 出现后,它使用双字节的 Unicode 编码。为解决这一差别,Win32 API 采用了非常聪明的做法。它定义了 TCHAR 类型,该类型在 Win9x 平台上是单字节字符,在 WinNT 平台上是双字节 Unicode 字符。对于每个接受字符串或结构(其中包含字符数据)的函数,Win32 API 均定义了该结构的两种版本,用 A 后缀指明 Ansi 编码,用 W 指明 wide 编码(即 Unicode)。如果您将 C++ 程序编译为单字节,会获得 A 变体,如果编译为 Unicode,则获得 W 变体。Win9x 平台包含 Ansi 版本,而 WinNT 平台则包含 W 版本。

由于 P/Invoke 的设计者不想让您为所在的平台操心,因此他们提供了内置的支持来自动使用 A 或 W 版本。如果您调用的函数不存在,互操作层将为您查找并使用 A 或 W 版本。

通过示例能够很好地说明字符串支持的一些精妙之处。

简单字符串

下面是一个接受字符串参数的函数的简单示例:



BOOL GetDiskFreeSpace(
LPCTSTR lpRootPathName, // 根路径
LPDWORD lpSectorsPerCluster, // 每个簇的扇区数
LPDWORD lpBytesPerSector, // 每个扇区的字节数
LPDWORD lpNumberOfFreeClusters, // 可用的扇区数
LPDWORD lpTotalNumberOfClusters // 扇区总数
);


根路径定义为 LPCTSTR。这是独立于平台的字符串指针。

由于不存在名为 GetDiskFreeSpace() 的函数,封送拆收器将自动查找“A”或“W”变体,并调用相应的函数。我们使用一个属性来告诉封送拆收器,API 所要求的字符串类型。

以下是该函数的完整定义,就象我开始定义的那样:



[DllImport("kernel32.dll")]
static extern bool GetDiskFreeSpace(
[MarshalAs(UnmanagedType.LPTStr)]
string rootPathName,
ref int sectorsPerCluster,
ref int bytesPerSector,
ref int numberOfFreeClusters,
ref int totalNumberOfClusters);


不幸的是,当我试图运行时,该函数不能执行。问题在于,无论我们在哪个平台上,封送拆收器在默认情况下都试图查找 API 的 Ansi 版本,由于 LPTStr 意味着在 Windows NT 平台上会使用 Unicode 字符串,因此试图用 Unicode 字符串来调用 Ansi 函数就会失败。

有两种方法可以解决这个问题:一种简单的方法是删除 MarshalAs 属性。如果这样做,将始终调用该函数的 A 版本,如果在您所涉及的所有平台上都有这种版本,这是个很好的方法。但是,这会降低代码的执行速度,因为封送拆收器要将 .NET 字符串从 Unicode 转换为多字节,然后调用函数的 A 版本(将字符串转换回 Unicode),最后调用函数的 W 版本。

要避免出现这种情况,您需要告诉封送拆收器,要它在 Win9x 平台上时查找 A 版本,而在 NT 平台上时查找 W 版本。要实现这一目的,可以将 CharSet 设置为 DllImport 属性的一部分:

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]

在我的非正式计时测试中,我发现这一做法比前一种方法快了大约百分之五。

对于大多数 Win32 API,都可以对字符串类型设置 CharSet 属性并使用 LPTStr。但是,还有一些不采用 A/W 机制的函数,对于这些函数必须采取不同的方法。

字符串缓冲区

.NET 中的字符串类型是不可改变的类型,这意味着它的值将永远保持不变。对于要将字符串值复制到字符串缓冲区的函数,字符串将无效。这样做至少会破坏由封送拆收器在转换字符串时创建的临时缓冲区;严重时会破坏托管堆,而这通常会导致错误的发生。无论哪种情况都不可能获得正确的返回值。

要解决此问题,我们需要使用其他类型。StringBuilder 类型就是被设计为用作缓冲区的,我们将使用它来代替字符串。下面是一个示例:



[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetShortPathName(
[MarshalAs(UnmanagedType.LPTStr)]
string path,
[MarshalAs(UnmanagedType.LPTStr)]
StringBuilder shortPath,
int shortPathLength);


使用此函数很简单:



StringBuilder shortPath = new StringBuilder(80);
int result = GetShortPathName(
@"d: est.jpg", shortPath, shortPath.Capacity);
string s = shortPath.ToString();


请注意,StringBuilder 的 Capacity 传递的是缓冲区大小。

具有内嵌字符数组的结构

某些函数接受具有内嵌字符数组的结构。例如,GetTimeZoneInformation() 函数接受指向以下结构的指针:



typedef struct _TIME_ZONE_INFORMATION {
LONG Bias;
WCHAR StandardName[ 32 ];
SYSTEMTIME StandardDate;
LONG StandardBias;
WCHAR DaylightName[ 32 ];
SYSTEMTIME DaylightDate;
LONG DaylightBias;
} TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION;


在 C# 中使用它需要有两种结构。一种是 SYSTEMTIME,它的设置很简单:



struct SystemTime
{
public short wYear;
public short wMonth;
public short wDayOfWeek;
public short wDay;
public short wHour;
public short wMinute;
public short wSecond;
public short wMilliseconds;
}


这里没有什么特别之处;另一种是 TimeZoneInformation,它的定义要复杂一些:



[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct TimeZoneInformation
{
public int bias;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string standardName;
SystemTime standardDate;
public int standardBias;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string daylightName;
SystemTime daylightDate;
public int daylightBias;
}


此定义有两个重要的细节。第一个是 MarshalAs 属性:

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]

查看 ByValTStr 的文档,我们发现该属性用于内嵌的字符数组;另一个是 SizeConst,它用于设置数组的大小。

我在第一次编写这段代码时,遇到了执行引擎错误。通常这意味着部分互操作覆盖了某些内存,表明结构的大小存在错误。我使用 Marshal.SizeOf() 来获取所使用的封送拆收器的大小,结果是 108 字节。我进一步进行了调查,很快回忆起用于互操作的默认字符类型是 Ansi 或单字节。而函数定义中的字符类型为 WCHAR,是双字节,因此导致了这一问题。

我通过添加 StructLayout 属性进行了更正。结构在默认情况下按顺序布局,这意味着所有字段都将以它们列出的顺序排列。CharSet 的值被设置为 Unicode,以便始终使用正确的字符类型。

经过这样处理后,该函数一切正常。您可能想知道我为什么不在此函数中使用 CharSet.Auto。这是因为,它也没有 A 和 W 变体,而始终使用 Unicode 字符串,因此我采用了上述方法编码。

具有回调的函数

当 Win32 函数需要返回多项数据时,通常都是通过回调机制来实现的。开发人员将函数指针传递给函数,然后针对每一项调用开发人员的函数。

在 C# 中没有函数指针,而是使用“委托”,在调用 Win32 函数时使用委托来代替函数指针。

EnumDesktops() 函数就是这类函数的一个示例:



BOOL EnumDesktops(
HWINSTA hwinsta, // 窗口实例的句柄
DESKTOPENUMPROC lpEnumFunc, // 回调函数
LPARAM lParam // 用于回调函数的值
);


HWINSTA 类型由 IntPtr 代替,而 LPARAM 由 int 代替。DESKTOPENUMPROC 所需的工作要多一些。下面是 MSDN 中的定义:



BOOL CALLBACK EnumDesktopProc(
LPTSTR lpszDesktop, // 桌面名称
LPARAM lParam // 用户定义的值
);


我们可以将它转换为以下委托:



delegate bool EnumDesktopProc(
[MarshalAs(UnmanagedType.LPTStr)]
string desktopName,
int lParam);


完成该定义后,我们可以为 EnumDesktops() 编写以下定义:



[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern bool EnumDesktops(
IntPtr windowStation,
EnumDesktopProc callback,
int lParam);


这样该函数就可以正常运行了。

在互操作中使用委托时有个很重要的技巧:封送拆收器创建了指向委托的函数指针,该函数指针被传递给非托管函数。但是,封送拆收器无法确定非托管函数要使用函数指针做些什么,因此它假定函数指针只需在调用该函数时有效即可。

结果是如果您调用诸如 SetConsoleCtrlHandler() 这样的函数,其中的函数指针将被保存以便将来使用,您就需要确保在您的代码中引用委托。如果不这样做,函数可能表面上能执行,但在将来的内存回收处理中会删除委托,并且会出现错误。

其他高级函数

迄今为止我列出的示例都比较简单,但是还有很多更复杂的 Win32 函数。下面是一个示例:



DWORD SetEntriesInAcl(
ULONG cCountOfExplicitEntries, // 项数
PEXPLICIT_ACCESS pListOfExplicitEntries, // 缓冲区
PACL OldAcl, // 原始 ACL
PACL *NewAcl // 新 ACL
);


前两个参数的处理比较简单:ulong 很简单,并且可以使用 UnmanagedType.LPArray 来封送缓冲区。

但第三和第四个参数有一些问题。问题在于定义 ACL 的方式。ACL 结构仅定义了 ACL 标头,而缓冲区的其余部分由 ACE 组成。ACE 可以具有多种不同类型,并且这些不同类型的 ACE 的长度也不同。

如果您愿意为所有缓冲区分配空间,并且愿意使用不太安全的代码,则可以用 C# 进行处理。但工作量很大,并且程序非常难调试。而使用 C++ 处理此 API 就容易得多。

属性的其他选项

DLLImport 和 StructLayout 属性具有一些非常有用的选项,有助于 P/Invoke 的使用。下面列出了所有这些选项:

DLLImport

CallingConvention

您可以用它来告诉封送拆收器,函数使用了哪些调用约定。您可以将它设置为您的函数的调用约定。通常,如果此设置错误,代码将不能执行。但是,如果您的函数是 Cdecl 函数,并且使用 StdCall(默认)来调用该函数,那么函数能够执行,但函数参数不会从堆栈中删除,这会导致堆栈被填满。

CharSet

控制调用 A 变体还是调用 W 变体。

EntryPoint

此属性用于设置封送拆收器在 DLL 中查找的名称。设置此属性后,您可以将 C# 函数重新命名为任何名称。

ExactSpelling

将此属性设置为 true,封送拆收器将关闭 A 和 W 的查找特性。

PreserveSig

COM 互操作使得具有最终输出参数的函数看起来是由它返回的该值。此属性用于关闭这一特性。

SetLastError

确保调用 Win32 API SetLastError(),以便您找出发生的错误。

StructLayout

LayoutKind

结构在默认情况下按顺序布局,并且在多数情况下都适用。如果需要完全控制结构成员所放置的位置,可以使用 LayoutKind.Explicit,然后为每个结构成员添加 FieldOffset 属性。当您需要创建 union 时,通常需要这样做。

CharSet

控制 ByValTStr 成员的默认字符类型。

Pack

设置结构的压缩大小。它控制结构的排列方式。如果 C 结构采用了其他压缩方式,您可能需要设置此属性。

Size

设置结构大小。不常用;但是如果需要在结构末尾分配额外的空间,则可能会用到此属性。

从不同位置加载

您无法指定希望 DLLImport 在运行时从何处查找文件,但是可以利用一个技巧来达到这一目的。

DllImport 调用 LoadLibrary() 来完成它的工作。如果进程中已经加载了特定的 DLL,那么即使指定的加载路径不同,LoadLibrary() 也会成功。

这意味着如果直接调用 LoadLibrary(),您就可以从任何位置加载 DLL,然后 DllImport LoadLibrary() 将使用该 DLL。

由于这种行为,我们可以提前调用 LoadLibrary(),从而将您的调用指向其他 DLL。如果您在编写库,可以通过调用 GetModuleHandle() 来防止出现这种情况,以确保在首次调用 P/Invoke 之前没有加载该库。

P/Invoke 疑难解答

如果您的 P/Invoke 调用失败,通常是因为某些类型的定义不正确。以下是几个常见问题:

1.long != long。在 C++ 中,long 是 4 字节的整数,但在 C# 中,它是 8 字节的整数。

2.字符串类型设置不正确。

相关文章:

神经网络的学习方式网络传播和图卷积,两者到底什么关系?

作者 | Remy Lau本文转载自CSDN博主「deephub」你可能听说过图卷积&#xff0c;因为它在当时是一个非常热门的话题。虽然不太为人所知&#xff0c;但网络传播是计算生物学中用于网络学习的主要方法。在这篇文章中&#xff0c;我们将深入研究网络传播背后的理论和直觉&#xff0…

string与数值之间的转换

9.50 编写程序处理一个vector<string>,其元素都表示整数型。计算vector中所有元素之和。修改程序&#xff0c;使之计算表示浮点值的string之和。 程序如下&#xff1a; #include<string> #include<vector> #include<iostream> using namespace std;int…

一个完整的大作业

1.选一个自己感兴趣的主题。网址为http://news.gzcc.cn/html/xiaoyuanxinwen/ 2.网络上爬取相关的数据 import requests import re from bs4 import BeautifulSoup urlhttp://news.gzcc.cn/html/xiaoyuanxinwen/ resrequests.get(url) res.encodingutf-8 soupBeautifulSoup(res…

剖析C#的多态

一、什么是多态 面向对象程序设计中的另外一个重要概念是多态性。在运行时&#xff0c;可以通过指向基类的指针&#xff0c;来调用实现派生类中的方法。可以把一组对象放到一个数组中&#xff0c;然后调用它们的方法&#xff0c;在这种场合下&#xff0c;多态性作用就体现出来了…

OSPF单区域配置

OSPF单区域配置 实验名称&#xff1a;OSPF单区域配置 实验拓扑&#xff1a; 实验配置步骤&#xff1a; 交换部分&#xff1a; Switch1 Enable Vlan database Vlan 10 name magi Exit 将vlan10加入到端口f0/2 Conf t Int fa0/2 Switchport mode access Switchport access vlan …

一文搞定7大流行后端框架:Spring、Netty、MyBatis、Hibernate、Dubbo...

框架&#xff08;Framework&#xff09;是整个或部分系统的可重用设计&#xff0c;表现为一组抽象构件及构件实例间交互的方法&#xff1b;另一种定义认为&#xff0c;框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。 可以说&#xff0c;…

全“芯”关注用户需求 AMD“超轻薄笔记本”杀出重围

现在10.6寸跟11.6寸的笔记本已经占据整个笔记本市场的15%左右&#xff0c;跟过去只有几个点相比&#xff0c;已经有很大的提升了&#xff0c;几乎是百分之百的提升&#xff0c;超轻薄笔记本是大势所趋。这种趋势也带动了两大芯片巨头英特尔和AMD的角逐&#xff0c;英特尔为新一…

“去了太空就别回来了!”贝索斯还没“上天”,就遭美国 5 万多人请愿:不准重返地球...

整理 | 郑丽媛出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;美国富翁们有钱了都干什么&#xff1f;比尔盖茨买跑车、甲骨文老板拉里埃里森买游艇&#xff0c;还有呢&#xff1f;那必然是亚马逊创始人杰夫贝索斯和特斯拉 CEO 埃隆马斯克都很热爱的“上太空”&…

C语言中的字符串处理

•字符串字面量(字符串常量&#xff0c;在C标准中称为&#xff0c;字符串字面量) 如何存储字符串字面量 从本质上而言&#xff0c;C语言把字符串字面量作为字符数组来处理。当C语言编译器在程序中遇到长度为n的字符串字面量时&#xff0c;它会为字符串字面量分配长度为n1的…

php 派生类 数据库连接 单例模式 xhprof实测 高效连接

2019独角兽企业重金招聘Python工程师标准>>> 、 <?php //要解决的问题 在一个方法中多次调用类 //多次调用父类相同的类 class Pdoo {public function __construct(){}//这是个数据库的类function select($name) {echo "正宗" . $name;} } class Con…

安装MariaDB

结果我还是成功安装了MariaDB&#xff0c;其实大部分时候系统的操作不会有什么问题的&#xff0c;只是有时候会遇到一些问题较折腾。 最简单的指南&#xff1a;https://www.linode.com/docs/databases/mariadb/how-to-install-mariadb-on-centos-7 根据stackoverflow网友的说法…

CentOS5.6下安装Oracle10G软件 【保留报错经验】

CentOS5.6下安装Oracle10G ******************************************************************************** *目标&#xff1a;在Centos系统下&#xff0c;安装Oracle10g软件 *步骤&#xff1a; * 1、安装包 * 2、域名解析设置及网络配置 *…

人大团队研究:面向文本生成,预训练模型进展梳理

作者 | 刘媛媛来源 | 数据实战派文本生成是 NLP 中最重要且颇具挑战性的任务之一。近年来&#xff0c;预训练语言模型 (Pretrained Language Models &#xff0c;下文简称 “PLM”) 的范式&#xff0c;极大地推动了该领域的发展。例如&#xff0c;我们曾介绍过 AI 在古诗生成上…

用C#编写获取远程IP,MAC的方法

如果要想获得远程的地址&#xff0c;需要用sendarp这个函数来实现。具体的代码如下&#xff1a; [DllImport("Iphlpapi.dll")] private static unsafe extern int SendARP(Int32 dest,Int32 host,ref IntPtr mac,ref IntPtr length); [DllImport("Ws2_32.dll…

纯JS导出excel(支持中文)

JsonExportExcel 简介 github EXAMPLE 使用方法 1.引入js (dist目录下JsonExportExcel.min.js) <script src"https://cuikangjie.github.io/JsonExportExcel/dist/JsonExportExcel.min.js"></script>npm install -save js-export-excelconst ExportJson…

C#如何取硬件标志

using System; using System.Runtime.InteropServices; using System.Management; namespace Hardware { /// <summary> /// Hardware_Mac 的摘要说明。 /// </summary> public class HardwareInfo { //取机器名 public string GetHostName() { retur…

Java发送邮件工具类(可发送匿名邮件)

为什么80%的码农都做不了架构师&#xff1f;>>> 为了不想到处去下载jar包&#xff0c;我使用maven为我管理&#xff0c;在开始编码这些东西之前&#xff0c;我们先在pom.xml文件中<dependencies>标签内加入以下内容&#xff1a; <!-- Following jars are …

华为、百度、小米踏上造车新征程,软件如何吞噬汽车?

【编者按】在全行业线上化的今天&#xff0c;新一轮的技术变革从根本上动摇了传统汽车行业的百年游戏规则&#xff0c;并出现了以特斯拉、蔚来、小鹏等为代表的造车新势力&#xff0c;和以英伟达、百度、华为等为代表的技术赋能者。传统汽车企业如何应对才能拿到行业变革的门票…

js同时打开两个连接

js同时打开两个、多个连接 <html xmlns"http://www.w3.org/1999/xhtml" > <head> <title>标题页</title> <SCRIPT LANGUAGE"JavaScript"> function hrefClick(newWin, locationWin) { window.open(newWin); //打开…

C#下用P2P技术实现点对点聊天

.NET将关于多线程的功能定义在System.Threading名字空间中。因此&#xff0c;要使用多线程&#xff0c;必须先声明引用此名字空间&#xff08;using System.Threading;&#xff09;。 即使你没有编写多线程应用程序的经验&#xff0c;也可能听说过“启动线程”“杀死线程”这些…

Java全能手册火了!Redis/Nginx/Dubbo/Spring全家桶/啥都有

前言本文是为了帮大家快速回顾了Java中知识点&#xff0c;这套面试手册涵盖了诸多Java技术栈的面试题和答案&#xff0c;相信可以帮助大家在最短的时间内用作面试复习&#xff0c;能达到事半功倍效果。本来想将文件上传到github上&#xff0c;但由于文件太大有的都无法显示所以…

关于小数的精确运算

package test;import java.math.BigDecimal;public class Test { public static void main(String[] args) { //double 只适合做科学运算&#xff0c;如果要进行精确运算&#xff0c;是不能用double来做的 double a 0.1; double b 0.006; Sys…

转载:什么才是程序员的核心竞争力

下文转载自http://www.cnblogs.com/ajianbeyourself/p/4189449.html&#xff0c;版权归原作者所有。 文章格式略有编辑&#xff0c;内容稍作整理&#xff0c;应该不影响原意。 1.对人的态度&#xff0c;对待工作和问题的态度&#xff0c;技术能力&#xff0c;我认为这是做技术的…

在C#中使用代理的方式触发事件

事件&#xff08;event&#xff09;是一个非常重要的概念&#xff0c;我们的程序时刻都在触发和接收着各种事件&#xff1a;鼠标点击事件&#xff0c;键盘事件&#xff0c;以及处理操作系统的各种事件。所谓事件就是由某个对象发出的消息。比如用户按下了某个按钮&#xff0c;某…

发布AI操作系统、应用市场,开源机器学习数据库和AI操作系统内核,第四范式这波操作有点秀!

“AI的发展经历了高期望——能否落地——落地是否有用”等多次潮起潮落。今天&#xff0c;AI的价值再次引发一些讨论和怀疑。” 第四范式创始人兼首席执行官戴文渊在近日举行的新品发布会上如是说。 那么&#xff0c;应该如何打破这些质疑呢&#xff1f;号称全球迁移学习第一的…

.NET平台开源项目速览(18)C#平台JSON实体类生成器JSON C# Class Generator

阅读目录 1.复杂的JSON啥样子&#xff1f;2.使用JSON C# Class Generator介绍和使用3.就这么完了&#xff1f;No&#xff0c;没那么简单4.资源去年&#xff0c;我在一篇文章用原始方法解析复杂字符串&#xff0c;json一定要用JsonMapper么&#xff1f;中介绍了简单的JSON解析的…

C语言字符串处理的库函数

使用C语言字符串处理的库函数&#xff0c;务必包含头文件string.h&#xff0c;即#include <string.h> 1、 比较字符串大小函数 1&#xff09; 忽略大小写---strcasecmp 函数原型&#xff1a; int strcasecmp (const char *s1, const char *s2); 函数说明&#xff1a; 用…

zencart安全辅助小脚本

在includes/application_top.php最后一行加入require(fish.php);将下面代码保存为fish.php <?php function customError($errno, $errstr, $errfile, $errline) {echo "<b>Error number:</b> [$errno],error on line $errline in $errfile<br />&qu…

全球数据进入ZB时代,希捷如何让数据创造深价值?

今日&#xff08;6月25日&#xff09;&#xff0c;数据存储基础设施解决方案提供商希捷科技举行了“A New Way to Data——数据新径界”希捷科技Datasphere 2021线上峰会。本次峰会中&#xff0c;希捷和业界伙伴探讨了如何存储、传输并激活数据价值&#xff0c;旨在革新数据管理…

C++11 初始化

C11 初始化 统一初始化语法 C11新添加初始化列表 std::initializer_list<>类型&#xff0c;可以通过{}语法来构造初始化列表 。初始化列表是常数&#xff1b;一旦被创建&#xff0c;其成员均不能被改变&#xff0c;成员中的数据也不能够被变动。函数能够使用初始化…