2.如何调用WindowsApi

在上一篇章节中 很不专业的介绍了一下WindowsApi 如果你本身就知道 那你也压根不用看 如果你本身就不知道 就算我说的再多 估计你也觉得是多余 所以还是来点实际的
首先 在C#中想要调用那些Api(当然不一定是Windows提供的Api) 你得添加一个引用:

using System.Runtime.InteropServices;
有了这个命名空间 你才能做下一步 这用以MoveWindow函数为例来讲解:


如上截图 如果拉到底部可以看到 这个函数在user32.dll里面 而上一篇说过这个dll在Window的系统路径下 而MoveWindow函数就在这个dll里面 在调用这个函数前先来看看这个函数的说明 我把chm里面的东西复制出来看:

函数功能:
    该函数改变指定窗口的位置和尺寸。对于顶层窗口 位置和尺寸是相对于屏幕的左上角的:
    对于子窗口 位置和尺寸是相对于父窗口客户区的左上角坐标的。
函数原型:
    BOOL MoveWindow(HWND hWnd.int x.int y,int nWidth,int nHeight,BOOL BRePaint);
参数:
    hWnd:窗口句柄。//看不懂不用知道
    x:指定窗口的新位置的左边界。
    Y:指定窗口的新位置的顶部边界。
    nWidth:指定窗口的新的宽度。
    nHaight:指定窗口的新的高度。//下面这个看不懂不用看
    bRepaint:确定窗口是否被刷新。如果该参数为TRUE 窗口接收一个WM_PAINT消息;
        如果参数为FALSE 不发生任何刷新动作。它适用于客户区 非客户区(包括标题栏和滚动条) 
        及由于移动子窗口而露出的父窗口的区域。如果参数为FALSE 
        应用程序就必须明确地使窗口无效或重画该窗口和需要刷新的父窗口。
返回值:
    如果函数成功 返回值为非零;如果函数失败 返回值为零。若想获得更多错误信息 
        请调用GetLastError函数。//GetLastError()也是一个函数 在kernel32中
备注://看不懂不用看
    如果bRepaint为TRUE 系统在窗口移动后立即给窗口过程发送WM_PAINT消息
        (即由MoveWindow函数调用UPdateWindow函数)
    如果bRepaint 为FALSE 系统将WM_PAINT消息放在该窗口的消息队列中
        消息循环只有在派遣完消息队列中的其他消息时才派遣WM_PAINT消息
        MoveWindow给窗口发送WM_WfNOWPOSCHANGING WM_WINDOWPOSCHANGED 
        WM_MOVE WM_SIZE和WM_NCCALCSIZE消息 
速查:
    Windows NT:3.1 以上版本:Windows:95 以上版本;Windows CE:1.0 以上版本:
        头文件:winuser.h;库文件:user32.lib。
函数签名形式是C++的 淡定习惯就好如果你会C++无所谓 如果不会 没事如果你用多了之后自然什么意思 第一个参数可能有点看不懂HWND 这个是啥?其实全写是HandleWindow 一个窗口的句柄 如果你不知道什么是句柄 那就暂时先别管 暂时先理解为是一个窗体的编号 下一篇来说 中间四个不用说 最后一个应该知道把bool 以后像这样全大写的东西多了去了C++的习惯 这个值简单来说 就是当窗体被移动后 是否刷新窗体

现在知道这个函数要怎么用了 那么 现在怎么才能调用到他?平时写代码的时候 调用函数基本都是自己写的 所以知道怎么调用 但是这个函数在一个系统的dll里面 如果平时用C#写过dll的话你可能会很自然的这样想 难道要引用dll?然后new一个对象 或者类名打点 那个方法就被点出来了?、、这里恐怕有那么一点不一样 平时你用C#写的dll是托管代码是运行在.Net平台受.Net平台管理的 而系统的dll是非托管代码 是不受.Net平台约束的 不然一个没有装.Net平台的电脑 那么那些系统的dll岂不是就运行不了了?、

那到这里估计就郁闷了 又不能添加引用 那要怎么调用到这个函数?话不多说 直接上全部代码上来一看究竟再说

首先随便你搞一个啥工程 然后添加一个button 就像下面这样:

然后cs文件全部代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Runtime.InteropServices;

namespace WindowsApplication1
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll")]//表示下面这个函数在user32.dll中
        public static extern bool 
            MoveWindow(IntPtr hWnd, int x, int y, int width, int height, bool bRePaint);
        //extern表示外部的 也就是说这个函数在其他地方 而上面的[DllImport(Path)]指定函数的位置
        //还有 记得static关键字这个是必须的 没有他 编译都不通过 然后后面该怎么就怎么
        public Form1() {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e) {
            MoveWindow(this.Handle/*当前窗体的句柄*/, 100, 100, 200, 200, true);
        }
    }
}
按照上面方式 就可以调用了 当程序运行的时候点击button然后 窗体就会移动到你指定的坐标和大小 如果你最后一个参数是false的话可能会出现如下效果:

因为窗体在桌面上的位置信息发生了变化 但是有没有刷新窗体的后果 当然他不可能一直都是黑的 你把窗体拖出屏幕外 再拖回来 被挡住的地方就会正常 或者最小化再最大化

看到上面的效果可能你不觉的有啥估计你觉得就这个东西 自己也能写一个一样功能的函数啊 干嘛搞的那么麻烦比如:

private void MyMoveWindow(
    Form frm, int x, int y, int width, int height, bool bRePaint)
{
    frm.Left = x;
    frm.Top = y;
    frm.Width = width;
    frm.Height = height;
    if (bRePaint) frm.Refresh();//在这里 其实多余了
}
或许你觉和上面的MoveWindow函数没有区别 在本例中你确实看不到啥明显的区别 不过我告诉你 区别大了你自己写的这个 你能移动你自己程序的窗口 外界程序的 比如IE浏览器 QQ窗体 计算器窗体什么的 你能移动么?、、假设现在桌面上 有一个QQ聊天窗口 叫你写个程序点击button把那个QQ聊天窗体 给我移动到左上角去 这里的MyMoveWindow根本就办不到 所以在MoveWindow中的第一个参数接收的是一个窗口的句柄 而不是.Net中的Form 如果你要移动那个窗体 只需要将指定窗体的句柄传进去就是了 至于什么是句柄下一篇来说 这一篇只是演示一下 怎么调用

要注意的是上面[DllImport(...)]这里面不是只能一个string类型的参数 还有很多可选参数:

见得比较多的就是CharSet EntryPoint SetLastError

[DllImport("user32.dll", EntryPoint = "MoveWindow", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool AAA(IntPtr hWnd, int x, int y, int width, int height, bool bRePaint);

比如上面的 如果用了EntryPoint去指定入口的话 那么下面你声明的函数 就可以用其他你喜欢的名字

CharSet表示这个函数中出现的字符串的编码用什么形式 注意很多接收字符串参数的函数是有两个版本的 比如MessageBox有MessageBoxA和messageBoxW两个版本 MessageBox并不是函数的真正名字在C++中是一个宏定义 根据情况决定使用那个版本的函数 如果着这里你把CharSet设置成Unicode那么在调用函数的时候会自己在你定义的函数名上加上W如果是Ansi则会加上A 好像默认不写是Auto来着 根据[Windows核心编程]一书说 Windows NT开始由Unicode构建的 所以就算你调用的是A版本的函数 那么系统内部也会将其转换为Unicode然后进行处理

最后的SetLastError 表示是否设置错误号码 意思就是 并不是每个函数你都能调用成功的有些函数可能因为你参数原因或者其他原因导致函数调用失败 而失败了会产生一个错误号码 这个错误号码可以通过GetLastError()函数获取到 然后根据这个错误号 你可以得到一个错误信息比如:

[DllImport("kernel32.dll",SetLastError = true)]//MoveWindow略
public static extern int GetLastError();

private void button1_Click(object sender, EventArgs e) {
    if (!MoveWindow((IntPtr)10086, 100, 100, 10086, 200, false)) {
        //MessageBox.Show(new Win32Exception(Marshal.GetLastWin32Error()).Message);
        MessageBox.Show(new Win32Exception(GetLastError()).Message);
    }
}
句柄乱输入一个参数(句柄其实就是一个数字) GetLastError被定义在kernel32中 .Net其实对它有封装在Marshal类中可以直接调用这个函数Win32Exception也是.Net中自己封装的 然后运行程序 你会看到如下效果:

一个无效的窗体句柄

有时候想知道为什么调用的函数失败 就可以通过GetLastError来获取 别天真的以为 try catch 来捕获 - -!、、try catch 只会捕获throw抛出来的异常 而MoveWindow这些函数都啥时候写的了、、里面没有throw这种前卫的东西的只有设置错误号的代码、、

好吧这一篇就暂时先到这里、、、因为要怎么调用也确实没啥要讲的 引用命名空间 [DllImport(...)] 然后申明函数 然后调用 没啥可以多说的、、、


添加时间:2014-05-10 03:11:03 编辑时间:2016-11-09 23:50:56 阅读:842 
C#Windows编程 C#Win32
还没有人留言 要不你来抢一个沙发?
  • 编写评论

      我觉得区分大小写是一个码农的基本素质
[访问统计] 今天:44 总数:147817 提示:未成年人 请在大人陪同下浏览本站内容 还有:世界上最帅的码农 -> 石头 RSS:http://st233.com/rss Powered by -> Crystal_lz