# win库

win 库主要提供 Windows 系统函数、进程内窗口操作函数。

外部进程窗口操作请参考：[winex 库指南](../winex/winex.md)

## WinAPI 普通常量

请参考：[变量、常量](../../../language-reference/variables-and-constants.md)  

win 命名空间所有库用到的 API 常量，在 ~/lib/win/#DEFINE.aardio 文件中定义为智能提示模板。这些常量的标识符总是以一个下划线开始、并全部大写，在编辑器中输入下划线开始的常../builtin/raw/overload.html量名前缀就可以列出相关的常量，回车可自动输入为数值加注释的形式、并添加到代码中。

请参考：[变量、常量](../../../language-reference/variables-and-constants.md)  

## 启动与退出窗口消息循环 <a id="loopMessage" href="#loopMessage">&#x23;</a>

1. 函数原型：   

    `win.loopMessage(observer,attach=true)`

    `win.quitMessage(exitCode=0)`

  
2. 函数说明：   
  
    win.loopMessage 启动界面线程的窗口消息循环。

    > `win.quitMessage()` 则用于退出消息循环，可选在参数中用一个数值指定退出代码。当所有非模态、非 MessageOnly 的独立窗口（ 或 mainForm 窗口 ）都关闭后，将会自动调用 `win.quitMessage()` 。如果显式指定 `win.autoQuitMessage` 的值为 `false` 则禁止当前线程在关闭前述窗口时自动调用 `win.quitMessage()` 。如果窗口对象自身的 `autoQuitMessage` 的属性值为 `false` 也会禁止在关闭该窗口时自动调用 `win.quitMessage()` 。

    在一个GUI线程(窗口线程) 中只能启动一次 win.loopMessage 循环，重复调用 win.loopMessage 不会执行任何操作。 

    默认 win.loopMessage 是不需要参数的（为了获得最佳性能，也不建议指定参数）。当我们指定 observer 参数为一个回调函数时，win.loopMessage 将该函数注册成为消息观察者。每次从消息队列中取出消息时，win.loopMessage 首先会通知所有的 observer(消息观察者)。如果调用 win.loopMessage( observer,false ) ，则将消息观察者 observer 从观察队列中注销。 

    win.loopMessage 函数的返回值为消息循环退出代码，在 main.aardio 中可以用 return 语句返回退出代码。 
     
    > Windows 是以消息驱动的操作系统，Windows 消息提供了应用程序与应用程序以及应用程序与 Windows 系统之间进行通讯的手段。 对于每一个正在执行的 Windows 应用程序，操作系统为其建立一个"消息队列"，即应用程序消息队列，用来存放该程序可能创建的各种窗口的消息。当我们用鼠标、按键等在窗口上操作时，Windows 就会将相应的操作转换为消息并加入到消息队列中。当然，消息队列中除了这些消息，还有很多其他种类的消息。

    在 aardio 窗口程序中，调用 win.loopMessage 启动窗口消息循环，用来从程序的消息队列中检索窗口消息并把它们分发到相应的窗口函数中。 并且触发 aardio 中的 消息回调函数 wndproc、命令回调 oncommand、通知回调 onnotify、以及更多的各种控件事件、鼠标键盘事件、窗口绘图事件 …… 在消息循环中窗口及时刷新、正确地调整大小与自适应缩放。
     
    "消息循环"实际也就是窗口程序的主循环，win.loopMessage 退出则窗口程序终止。
    
    应用程序可以通过调用 win.quitMessage() 退出消息循环。在 aardio 中当所有非模态、非 MessageOnly 的独立窗口（ 或 mainForm 窗口 ）都关闭后，将会自动调用 win.quitMessage()  并终止 win.loopMessage 创建的消息循环（通常也就是退出界面线程）。因为关闭消息是异步处理，所以即使是在调用 win.loopMessage 前关闭窗口，仍然可能导致退出消息循环。

    每个窗口程序都必须调用 win.loopMessage，但 win.quitMessage() 很少需要手动调用。

    请参考：[窗口消息处理](ui/msg.md)

3. 调用示例： 

  
    ```aardio
    import win.ui;
    /*DSG{{*/
    var winform = win.form(text="第一个窗口程序";right=759;bottom=469)
    winform.add(
    button={cls="button";text="退出";left=354;top=325;right=532;bottom=364;z=1}
    )
    /*}}*/

    winform.button.oncommand = function(id,event){
        win.quitMessage();
    }

    winform.show();
    win.loopMessage();
    ```  

## win.delay

1. 函数原型：   

    `win.delay( msDelay,msSleep )`

2. 函数说明：   

    此函数的主要作用是延时 @msDelay 指定的毫秒数。
    但是在延时的过程中仍然会分发处理消息，以避免消息循环中断导致窗口失去响应或者其他功能异常。

    第一个参数 @msDelay 指定延时的时间，以毫秒为单位。即使不指定这个参数或参数为 0 ,此函数仍然会至少执行一次消息检查), 此参数如果为0或省略则忽略第二个参数(此时处理速度最快)。

    第二个参数 @msSleep 是可选参数，指定在循环中插入的休眠时间(毫秒)。
    在休眠过程中无法处理窗口消息，所以 @msSleep 不能设太长，最好保持默认不要修改。休眠时间越小则处理速度越快，休眠时间越大则占用 CPU 就越少。

    如果在界面的耗时循环中希望同时响应用户操作,
    建议使用速度更快的 win.peekPumpMessage 或 win.peekPumpInputMessage。

    更建议创建多线程执行耗时操作。

    在工作线程中我们应该使用 sleep 函数取代 win.delay，
    而在界面线程中不能使用 sleep ， aardio 提供了一个 thread.delay 函数，在界面线程中自动指向 win.delay，而在工作线程中自动指向 sleep 函数。 

## win.pumpMessage

1. 函数原型：   

    `win.pumpMessage()` 
  
2. 函数说明：   
  
    等待并处理下一个消息，如果没有消息就一直等待。  
    win.pumpMessage 不会象 win.delay 那样计时，也不会象 win.loopMessage 那样循环执行。  
    
    实际上除了用户操作以外，窗口也会频繁的发生各种内部消息，例如窗口重绘等消息，所以一般情况下，win.pumpMessage会较快的返回，如果在循环中希望响应用户操作，可以使用 win.pumpMessage 来替代 win.delay 。  

## 发送消息

win 库导入了库 win.api —— 用于声明部分 WinAPI 函数，其中包含以下发送窗口消息的函数：

```aardio
::PostMessage = ::User32.api("PostMessageW","addr(addr hwnd,INT msg,ADDR wParam,addr lParam)")
::PostThreadMessage = ::User32.api("PostThreadMessageW","addr(int idThread,INT msg,ADDR wParam,addr lParam)"); 
::SendMessage = ::User32.api("SendMessageW","addr(addr hwnd,INT msg,ptr wParam,ptr lParam)")
::SendMessageInt = ::User32.api("SendMessageW","addr(addr hwnd,INT msg,ADDR wParam,addr lParam)")
::SendMessageByInt = ::User32.api("SendMessageW","addr(addr hwnd,INT msg,int &wParam,int &lParam)")
::SendMessageByString = ::User32.api("SendMessageW","addr(int,INT,int,string &lParam)")
::SendMessageByStr = ::User32.api("SendMessageW","addr(int,INT,int,ustring &lParam)")
::SendMessageByStruct = ::User32.api("SendMessageW","addr(int,INT,int,struct &lParam)") 
::SendMessageTimeout = ::User32.api("SendMessageTimeoutW","addr(addr hwnd,INT msg,ptr wParam,ptr lParam,INT flags,INT timeout,int & resultult)") 
```
  
以上重载 了 SendMessageW 函数的多个版本，请参考：[重载API函数](../../builtin/raw/overload.md)  
  
除 PostThreadMessage 函数的第一个参数为线程 ID 外，其他发送消息函数第 1 个参数为窗口句柄。  
而第 2 个参数为消息 ID（数值），第 3 、第 4 个参数随消息不同而有不同的含义和用法。  
  
SendMessage 系列函数是阻塞性的，即等待消息接收、处理完毕并返回以后才返回执行后面的代码。  
Post 系列的消息发送函数是非阻塞的，发送消息以后就立即返回，不会等待消息处理完毕。  
  
## win.msgbox

1. 函数原型：   

    `win.msgbox( 消息,标题,样式,父窗口 )`

2. 函数说明：   
  
    除第一个参数以外，其他参数可选。
    样式参数请参考 WinAPI 函数 MessageBox 相关资料，一般不必指定样式。

    注意 win.msgbox 弹出的窗口默认是没有父窗口的，  
    这就导致用户可以在任务栏切换其他窗口到弹出的消息对话框前面，而消息对话框又阻塞了线程，导致前置的窗口不能响应用户的任何操作。  
    
    win.form 类创建的对象提供 msgbox 成员函数，该函数可自动指定父窗口。  
    因此在窗体中，推荐使用窗体对象的 msgbox 函数。  

3. 示例：

    
    ```aardio
    import win.ui; 

    /*DSG{{*/
    var winform = win.form(parent=...; min=1;bottom=249;max=1;text="aardio Form";right=349 )
    winform.add( 
    button={ bottom=131;text="button";left=110;right=219;top=79;z=0;cls="button" }
    )
    /*}}*/

    winform.button.oncommand = function(id,event){
        winform.msgbox("你不可能把我折腾到父窗体后面去") 
    } 

    winform.show(true) 
    win.loopMessage();
    ```  

## win.msgboxTest

1. 函数原型：   

    `ok = win.msgboxTest( 消息,标题,父窗口 )`

2. 函数说明：   
  
    除第一个参数外，其他参数可选，该函数调用 win.msgbox 弹出一个带确定按钮、取消按钮的询问对话框，如果用户按确定返回 true ，否则返回 false 。  
   
## win.find <a id="find" href="#find">&#x23;</a>


1. 函数原型：   

    `hwnd = win.find( 类名,标题 )`

    注意：本文档中 hwnd 用于表示窗口句柄的变量名。

2. 函数说明：   
  
    按类名与标题查找指定的窗口，类名与标题都是可选参数。  
    win.find使用完全匹配来查找类名、标题，如果需要模糊匹配，请使用 [winex.find](../winex/winex.md#find)  
    
## win.findEx <a id="findEx" href="#findEx">&#x23;</a>


1. 函数原型：   

    `hwnd = win.findEx( 父窗口句柄,前一个同级窗口句柄,类名,标题 )`

  
2. 函数说明：   
  
    按类名与标题在指定的父窗口查找子窗口，  
    前一个同级窗口句柄可以指定为零表示从第一个子窗口开始查找。  
    
    类名与标题都是可选参数。  
    win.findEx使用完全匹配来查找类名、标题，如果需要模糊匹配以及更强大的查找功能，请使用 [winex.findEx](../winex/winex.md#findEx)  
  

## win.isClosed

1. 函数原型：   

    `isClosed = win.isClosed()`
  
2. 函数说明：   
  
    用户是否关闭了全部窗口并终止了窗口程序，运行窗口程序时为 false，退出窗口程序时为 true。

## win.getDesktop

1. 函数原型：   

    `hwnd = win.getDesktop()`

  
2. 函数说明：   
  
    获取桌面窗口句柄，桌面窗口是桌面上所有窗口的父窗口。在很多函数中也可以用0来表示桌面窗口句柄。

## win.getActive

1. 函数原型：   

    `hwnd = win.getActive()`
  
2. 函数说明：   
  
    获取当前线程激活窗口句柄,如果要获取全局激活窗口请使用win.getForeground()

## win.setActive

1. 函数原型：   

    `win.setActive(hwnd)`
  
2. 函数说明：   
  
    设置激活窗口句柄

## win.getForeground

1. 函数原型：   

    `hwnd = win.getForeground()`

2. 函数说明：   
  
    获取前台窗口句柄，指当前用户正在使用的顶层窗口。

## win.setForeground

1. 函数原型：   

    `win.setForeground(hwnd)`
  
2. 函数说明：   
  
    激活指定窗口到前台。

## win.getFocus

1. 函数原型：   

    `hwnd = win.getFocus()`
  
2. 函数说明：   
  
    获取当前线程输入焦点所在的控件句柄，[winex.getFocus](../winex/winex.md#getFocus) 函数可以支持外部线程。

## win.setFocus

1. 函数原型：   

    `win.setFocus(hwnd)`
  
2. 函数说明：   
  
    将第一个参数 @hwnd 指定句柄的窗口设为输入焦点，此函数只能用于当前线程。外部线程请 winex.attach() 以后调用 win.setFocus(hwnd)。

## win.setTop

1. 函数原型：   

    `win.setTop(hwnd)`

  
2. 函数说明：   
  
    将窗口设置到当然线程Z序顶部（即最前面），注意win.setForeground是设置到全局Z序的顶部。

## win.setTopmost

1. 函数原型：   

    `win.setTopmost( hwnd,topmost=true )`

  
2. 函数说明：   
  
    将第一个参数 @hwnd 指定句柄的窗口设置为始终在最前，即保持不被其他窗口覆盖。

## win.getThreadProcessId

1. 函数原型：   

  
    ```aardio
    var tid,pid = win.getThreadProcessId( hwnd )
    ```  
  
2. 函数说明：   
  
    返回指定窗口的线程ID,进程ID

## win.getClass

1. 函数原型：   

    `classname = win.getClass(hwnd)`

  
2. 函数说明：   
  
    返回窗口类名

## win.getId

1. 函数原型：   

    `ctrlId = win.getId(hwnd)`

  
2. 函数说明：   
  
    返回控件ID

## win.getText

1. 函数原型：   

    `text = win.getText( hwnd,缓冲区长度=文本长度 )`

  
2. 函数说明：   
  
    返回控件文本内容，可以指定缓冲区长度，也可以不指定长度，由win.getText自动获取文本长度并分配合适的缓冲区。

## win.setText

1. 函数原型：   

    `win.setText(hwnd,文本)`

  
2. 函数说明：   
  
    设置控件文本

## win.getTextById

1. 函数原型：   

    `text = win.getTextById(hwnd,id,len=256)`

  
2. 函数说明：   
  
    在第一个参数 @hwnd 指定句柄上查找指定id的控件，并返回文本，len指定缓冲区长度，默认为256个字节。

## win.setTextById

1. 函数原型：   

    `win.setTextById( hwnd,id,文本)`
  
2. 函数说明：   
  
    在第一个参数 @hwnd 指定句柄的窗口上为指定 id 的控件设置文本。

## win.getParent

1. 函数原型：   

    `parent = win.getParent( hwnd )`
  
2. 函数说明：   
  
    获取父窗口句柄。

## win.setParent

1. 函数原型：   

    `win.setParent( 子窗口句柄,父窗口句柄 )`

  
2. 函数说明：   
  
    为第一个参数指定的窗口设定父窗口。

## win.getRoot

1. 函数原型：   

    `hwndroot = win.getRoot( hwnd )`
  
2. 函数说明：   
  
    此函数递归调用 win.getParent 获取一个窗口的最顶层父窗口。

## win.enable

1. 函数原型：   


    ```aardio
    win.enable( hwnd , 1 ) //启用窗口
    win.enable( hwnd , 0 ) //禁用窗口
    ```  

2. 函数说明：   
  
    启用或禁用指定窗口