当前位置:网站首页 / C#开发 / 正文

[ay]写给自己的C# win32知识-学会怎么看MSDN

时间:2017年06月01日 | 作者 : aaronyang | 分类 : C#开发 | 浏览: 2144次 | 评论 0

AY写给自己的,自己做笔记,方便以后自己查看

https://msdn.microsoft.com/library

blob.png

点 Windows API,这里我们看下Dialog Boxes

blob.png

看下Function

blob.png

我了个擦

blob.png

找到MessageBox进入  

blob.png



现在的问题,微软只给了C++的DEMO,当然Winform中写个非常easy的,当时这里我是想学到如果 举一反三会用其他的api。


下面的文档是英文的,列举了参数的说明,还有返回值的说明,很明白了。

最下面有个框

blob.png

DLL中写到,DLL 在User32.dll中


但是Win32的类型怎么和C#对应呢


blob.png

LPCTSTR 这个理解为C#的string

示例

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

 public static extern int GetShortPathName

     (

            [MarshalAs(UnmanagedType.LPTStr)] string path,

            [MarshalAs(UnmanagedType.LPTStr)] StringBuilder shortPath,

            int shortPathLength

)


[DllImport("kernel32.dll", EntryPoint="getShort")] 前面是所在的dll,后面是函数方法

使用MarshalAs(UnmanagedType.LPTStr)保证了在任何平台上都会得到LPTStr,否则默认的方式会把从C#中的字符串作为BStr传递


[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]

[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)]

[DllImport("user32.dll")]

第一个参数(DLL 名称)是定位参数并且总是第一个出现,其他参数为命名参数。在此例中,两个命名参数都默认为假,因此它们可以省略(有关默认参数值的信息,请参见各个属性的文档)。

在一个声明中可以放置多个属性,可分开放置,也可放在同一组括号中:

bool AMethod([In][Out]ref double x);

bool AMethod([Out][In]ref double x);

bool AMethod([In,Out]ref double x);

某些属性对于给定实体可以指定多次。此类可多次使用的属性的一个示例是 Conditional:

[Conditional("DEBUG"), Conditional("TEST1")] void TraceMethod() {...}



MarshalAsAttribute

指示如何在托管代码和非托管代码之间封送数据。可将该属性应用于参数、字段或返回值。

该属性为可选属性,因为每个数据类型都有默认的封送处理行为。

大多数情况下,该属性只是使用 UnmanagedType 枚举标识非托管数据的格式。

例如,默认情况下,公共语言运行库将字符串参数作为 BStr 封送到 COM 方法,但是可以通过制定MarshalAs属性,将字符串作为 LPStr、LPWStr、LPTStr 或 BStr 封送到非托管代码。某些 UnmanagedType 枚举成员需要附加信息。



新建一个C#的控制台

    class Program
    {
        [DllImport("User32.dll")]
        public static extern int MessageBox(int h, string m, string c, int type);

        static int Main(string[] args)
        {

            MessageBox(0, "Hello Win32 API", "AY测试弹窗API", 2);
            Console.ReadLine();
            return 0;
        }
    }

blob.png


讲解变形

int MessageBox(HWND hWnd,   LPCTSTR lpText,    LPCTSTR lpCaption,  UINT uType );


函数名:MessageBox将保持不变。

返回值:int 将保持不变(无论是Win32还是C#,int都是32位整数)

参数表:H开头意味着是Handle,一般情况下Handld都是指针类型,Win32平台的指针类型是用32位来存储的,所以在C#里正好对应一个int整型。不过,既然是指针,就没有什么正负之分,32位都应该用来保存数值——这样一来,用uint(无符号32位整型)来对应Win32的H类型更合理。不过提醒大家一点,int是受C#和.NET CLR双重支持的,而uint只受C#支持而不受.NET CLR支持,所以,本例还是老老实实地使用了int型。

至于LPCTSTR是Long Pointer to Constant String的缩写,说白了就是——字符串。所以,用C#里的string类型就对了。

修饰符:要求有相应的DllImport和public static extern

经过上面一番折腾,Win32的MessageBox函数就包装成C#可以调用的函数了:

    [DllImport("User32.dll")]

    public static extern int MessageBox(int h, string m, string c, int type);

第一个参数:弹出的MessageBox的父窗口是谁。本例中没有,所以是0,也就是“空指针”。

后面几个参数用过winform自己可以对照理解了。



刚刚看到一篇文章,.net framework和win32的 方法api调用匹配,让我了解了.net framework的身世了,查看



API的基本过程:

88




  动态链接库函数声明部分一般由下列两部分组成,一是函数名或索引号,二是动态链接库的文件名。  

  譬如,你想调用User32.DLL中的MessageBox函数,我们必须指明函数的名字MessageBoxA或MessageBoxW,以及库名字User32.dll,我们知道Win32 API对每一个涉及字符串和字符的函数一般都存在两个版本,单字节字符的ANSI版本和双字节字符的UNICODE版本。 


  下面是一个调用API函数的例子:  

[DllImport("KERNEL32.DLL", EntryPoint="MoveFileW", SetLastError=true,  

CharSet=CharSet.Unicode, ExactSpelling=true,  

CallingConvention=CallingConvention.StdCall)]  

public static extern bool MoveFile(String src, String dst);  


  其中入口点EntryPoint标识函数在动态链接库的入口位置,在一个受管辖的工程中,目标函数的原始名字和序号入口点不仅标识一个跨越互操作界限的函数。而且,你还可以把这个入口点映射为一个不同的名字,也就是对函数进行重命名。重命名可以给调用函数带来种种便利,通过重命名,一方面我们不用为函数的大小写伤透脑筋,同时它也可以保证与已有的命名规则保持一致,允许带有不同参数类型的函数共存,更重要的是它简化了对ANSI和Unicode版本的调用。CharSet用于标识函数调用所采用的是Unicode或是ANSI版本,ExactSpelling=false将告诉编译器,让编译器决定使用Unicode或者是Ansi版本。其它的参数请参考MSDN在线帮助. 


在C#中,你可以在EntryPoint域通过名字和序号声明一个动态链接库函数,如果在方法定义中使用的函数名与DLL入口点相同,你不需要在EntryPoint域显示声明函数。否则,你必须使用下列属性格式指示一个名字和序号。 


[DllImport("dllname", EntryPoint="Functionname")]  

[DllImport("dllname", EntryPoint="#123")]  

值得注意的是,你必须在数字序号前加“#”  

下面是一个用MsgBox替换MessageBox名字的例子:  


using System.Runtime.InteropServices;  


[DllImport("user32.dll", EntryPoint="MessageBox")]  

public static extern int MsgBox(int hWnd, String text, String caption, uint type);  



许多受管辖的动态链接库函数期望你能够传递一个复杂的参数类型给函数,譬如一个用户定义的结构类型成员或者受管辖代码定义的一个类成员,这时你必须提供额外的信息格式化这个类型,以保持参数原有的布局和对齐。 


C#提供了一个StructLayoutAttribute类,通过它你可以定义自己的格式化类型,在受管辖代码中,格式化类型是一个用StructLayoutAttribute说明的结构或类成员,通过它能够保证其内部成员预期的布局信息。布局的选项共有三种: 


布局选项  

描述  

LayoutKind.Automatic  

为了提高效率允许运行态对类型成员重新排序。  

注意:永远不要使用这个选项来调用不受管辖的动态链接库函数。  

LayoutKind.Explicit  

对每个域按照FieldOffset属性对类型成员排序  

LayoutKind.Sequential  

对出现在受管辖类型定义地方的不受管辖内存中的类型成员进行排序。  

传递结构成员  

下面的例子说明如何在受管辖代码中定义一个点和矩形类型,并作为一个参数传递给User32.dll库中的PtInRect函数,  

函数的不受管辖原型声明如下:  

BOOL PtInRect(const RECT *lprc, POINT pt);  

注意你必须通过引用传递Rect结构参数,因为函数需要一个Rect的结构指针。  

using System.Runtime.InteropServices;  


[StructLayout(LayoutKind.Sequential)]  

public struct Point {  

public int x;  

public int y;  

}  


[StructLayout(LayoutKind.Explicit]  

public struct Rect {  

[FieldOffset(0)] public int left;  

[FieldOffset(4)] public int top;  

[FieldOffset(8)] public int right;  

[FieldOffset(12)] public int bottom;  

}  


[DllImport("User32.dll")]  

public static extern Bool PtInRect(ref Rect r, Point p);  

  

类似你可以调用GetSystemInfo函数获得系统信息:  



 using System.Runtime.InteropServices;  

[StructLayout(LayoutKind.Sequential)]  

public struct SYSTEM_INFO {  

public uint dwOemId;  

public uint dwPageSize;  

public uint lpMinimumApplicationAddress;  

public uint lpMaximumApplicationAddress;  

public uint dwActiveProcessorMask;  

public uint dwNumberOfProcessors;  

public uint dwProcessorType;  

public uint dwAllocationGranularity;  

public uint dwProcessorLevel;  

public uint dwProcessorRevision;  

}  



[DllImport("kernel32")]  

static extern void GetSystemInfo(ref SYSTEM_INFO pSI);  


SYSTEM_INFO pSI = new SYSTEM_INFO();  

GetSystemInfo(ref pSI);  


类成员的传递  

同样只要类具有一个固定的类成员布局,你也可以传递一个类成员给一个不受管辖的动态链接库函数,下面的例子主要说明如何传递一个sequential顺序定义的MySystemTime类给User32.dll的GetSystemTime函数, 函数用C/C++调用规范如下: 


void GetSystemTime(SYSTEMTIME* SystemTime);  

不像传值类型,类总是通过引用传递参数.  

 


[StructLayout(LayoutKind.Sequential)]  

public class MySystemTime {  

public ushort wYear;  

public ushort wMonth;  

public ushort wDayOfWeek;  

public ushort wDay;  

public ushort wHour;  

public ushort wMinute;  

public ushort wSecond;  

public ushort wMilliseconds;  

}  


 


[DllImport("User32.dll")]  

public static extern void GetSystemTime(MySystemTime st);  


 


回调函数的传递:  

从受管辖的代码中调用大多数动态链接库函数,你只需创建一个受管辖的函数定义,然后调用它即可,这个过程非常直接。  

如果一个动态链接库函数需要一个函数指针作为参数,你还需要做以下几步:  

首先,你必须参考有关这个函数的文档,确定这个函数是否需要一个回调;第二,你必须在受管辖代码中创建一个回调函数;最后,你可以把指向这个函数的指针作为一个参数创递给DLL函数,. 


回调函数及其实现:  

回调函数经常用在任务需要重复执行的场合,譬如用于枚举函数,譬如Win32 API 中的EnumFontFamilies(字体枚举), EnumPrinters(打印机), EnumWindows (窗口枚举)函数. 下面以窗口枚举为例,谈谈如何通过调用EnumWindow 函数遍历系统中存在的所有窗口 


分下面几个步骤:  

1. 在实现调用前先参考函数的声明  



BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam)  



显然这个函数需要一个回调函数地址作为参数.  

2. 创建一个受管辖的回调函数,这个例子声明为代表类型(delegate),也就是我们所说的回调,它带有两个参数hwnd和lparam,第一个参数是一个窗口句柄,第二个参数由应用程序定义,两个参数均为整形。 


当这个回调函数返回一个非零值时,标示执行成功,零则暗示失败,这个例子总是返回True值,以便持续枚举。



3. 最后创建以代表对象(delegate),并把它作为一个参数传递给EnumWindows 函数,平台会自动地 把代表转化成函数能够识别的回调格式。 


using System;  

using System.Runtime.InteropServices;  


public delegate bool CallBack(int hwnd, int lParam);  


public class EnumReportApp {  


[DllImport("user32")]  

public static extern int EnumWindows(CallBack x, int y);  


public static void Main()  

{  

   CallBack myCallBack = new CallBack(EnumReportApp.Report);  

   EnumWindows(myCallBack, 0);  

}  


public static bool Report(int hwnd, int lParam) {  

    Console.Write("窗口句柄为");  

    Console.WriteLine(hwnd);  

    return true;  

}  

}  

指针类型参数传递:  

  在Windows API函数调用时,大部分函数采用指针传递参数,对一个结构变量指针,我们除了使用上面的类和结构方法传递参数之外,我们有时还可以采用数组传递参数。 


  下面这个函数通过调用GetUserName获得用户名  


BOOL GetUserName(  

    LPTSTR lpBuffer, // 用户名缓冲区  

    LPDWORD nSize // 存放缓冲区大小的地址指针  

);  

    

[DllImport("Advapi32.dll",  EntryPoint="GetComputerName",  ExactSpelling=false,  

SetLastError=true)]  

static extern bool GetComputerName ( [MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,  [MarshalAs(UnmanagedType.LPArray)] Int32[] nSize );  



  这个函数接受两个参数,char * 和int *,因为你必须分配一个字符串缓冲区以接受字符串指针,你可以使用String类代替这个参数类型,当然你还可以声明一个字节数组传递ANSI字符串,同样你也可以声明一个只有一个元素的长整型数组,使用数组名作为第二个参数。上面的函数可以调用如下: 


byte[] str=new byte[20];  

Int32[] len=new Int32[1];  

len[0]=20;  

GetComputerName (str,len);  

MessageBox.Show(System.Text.Encoding.ASCII.GetString(str));  


 


 


 C#与C++之间类型的对应

blob.png

blob.png

blob.png


 


另: 在进行string转换时,需要加入前缀[MarshalAs(UnmanagedType.LPStr)]


LPDWORD   对应于  ref int


blob.png

blob.png

blob.png

BOOL=System.Int32

BOOLEAN=System.Int32

BYTE=System.UInt16

CHAR=System.Int16

COLORREF=System.UInt32

DWORD=System.UInt32

DWORD32=System.UInt32

DWORD64=System.UInt64

FLOAT=System.Float

HACCEL=System.IntPtr

HANDLE=System.IntPtr

HBITMAP=System.IntPtr

HBRUSH=System.IntPtr

HCONV=System.IntPtr

HCONVLIST=System.IntPtr

HCURSOR=System.IntPtr

HDC=System.IntPtr

HDDEDATA=System.IntPtr

HDESK=System.IntPtr

HDROP=System.IntPtr

HDWP=System.IntPtr

HENHMETAFILE=System.IntPtr

HFILE=System.IntPtr

HFONT=System.IntPtr

HGDIOBJ=System.IntPtr

HGLOBAL=System.IntPtr

HHOOK=System.IntPtr

HICON=System.IntPtr

HIMAGELIST=System.IntPtr

HIMC=System.IntPtr

HINSTANCE=System.IntPtr

HKEY=System.IntPtr

HLOCAL=System.IntPtr

HMENU=System.IntPtr

HMETAFILE=System.IntPtr

HMODULE=System.IntPtr

HMONITOR=System.IntPtr

HPALETTE=System.IntPtr

HPEN=System.IntPtr

HRGN=System.IntPtr

HRSRC=System.IntPtr

HSZ=System.IntPtr

HWINSTA=System.IntPtr

HWND=System.IntPtr

INT=System.Int32

INT32=System.Int32

INT64=System.Int64

LONG=System.Int32

LONG32=System.Int32

LONG64=System.Int64

LONGLONG=System.Int64

LPARAM=System.IntPtr

LPBOOL=System.Int16[]

LPBYTE=System.UInt16[]

LPCOLORREF=System.UInt32[]

LPCSTR=System.String

LPCTSTR=System.String

LPCVOID=System.UInt32

LPCWSTR=System.String

LPDWORD=System.UInt32[]

LPHANDLE=System.UInt32

LPINT=System.Int32[]

LPLONG=System.Int32[]

LPSTR=System.String

LPTSTR=System.String

LPVOID=System.UInt32

LPWORD=System.Int32[]

LPWSTR=System.String

LRESULT=System.IntPtr

PBOOL=System.Int16[]

PBOOLEAN=System.Int16[]

PBYTE=System.UInt16[]

PCHAR=System.Char[]

PCSTR=System.String

PCTSTR=System.String

PCWCH=System.UInt32

PCWSTR=System.UInt32

PDWORD=System.Int32[]

PFLOAT=System.Float[]

PHANDLE=System.UInt32

PHKEY=System.UInt32

PINT=System.Int32[]

PLCID=System.UInt32

PLONG=System.Int32[]

PLUID=System.UInt32

PSHORT=System.Int16[]

PSTR=System.String

PTBYTE=System.Char[]

PTCHAR=System.Char[]

PTSTR=System.String

PUCHAR=System.Char[]

PUINT=System.UInt32[]

PULONG=System.UInt32[]

PUSHORT=System.UInt16[]

PVOID=System.UInt32

PWCHAR=System.Char[]

PWORD=System.Int16[]

PWSTR=System.String

REGSAM=System.UInt32

SC_HANDLE=System.IntPtr

SC_LOCK=System.IntPtr

SHORT=System.Int16

SIZE_T=System.UInt32

SSIZE_=System.UInt32

TBYTE=System.Char

TCHAR=System.Char

UCHAR=System.





原文参考地址:查看





www.ayjs.net 六安杨洋(AY)拓展

======================================== AYUI       www.ayjs.net      AY ===============























推荐您阅读更多有关于“C#,”的文章

猜你喜欢

额 本文暂时没人评论 来添加一个吧

发表评论

必填

选填

选填

必填

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

  查看权限

抖音:wpfui 工作wpf,目前主maui

招聘合肥一枚WPF工程师,跟我一个开发组,10-15K,欢迎打扰

目前在合肥市企迈科技就职

AYUI8全源码 Github地址:前往获取

杨洋(AaronYang简称AY,安徽六安人)AY唯一QQ:875556003和AY交流

高中学历,2010年开始web开发,2015年1月17日开始学习WPF

声明:AYUI7个人与商用免费,源码可购买。部分DEMO不免费

不是从我处购买的ayui7源码,我不提供任何技术服务,如果你举报从哪里买的,我可以帮你转正为我的客户,并送demo

查看捐赠

AYUI7.X MVC教程 更新如下:

第一课 第二课 程序加密教程

标签列表