近许多小伙伴都升级了Windows10最新的更新补丁KB5006670,但是这会引起打印机无法连接共享打印机甚至出错的问题。但是卸载更新又显示无法卸载,那么遇到这种情况我们要怎么解决呢?
注意,这个如果你是小白,应用时务必小心,备份好相关数据避免出现灾难性故障,如果你是专业人员请自行确认下面方法的合理性。
操作方法:
用管理员权限运行CMD, 然后在命令提示符中输入以下命令看是否可以卸载KB5006670。
wusa /uninstall /KB:5006670
如果还是无法卸载,建议在安全模式下进行卸载。
因为此补丁是针对Win10 2004以上版本推出的最新更新,如果还是无法卸载,我怀疑可能在更新的时候存在文件损坏,运行“sfc /scannow”命令尝试修复系统文件,重置 Windows 更新组件。
手动重置 Windows 更新组件
打开 Windows 命令提示符。 若要打开命令提示符,请单击 “开始” 》 “运行”。 复制并粘贴(或键入)以下命令,然后按 Enter:
cmd
停止 BITS 服务、Windows 更新服务 和 加密服务。 为此,请在命令提示符下键入以下命令。 在键入每条命令后按 Enter。
net stop bits
net stop wuauserv
net stop cryptsvc
删除 qmgr*.dat 文件。 为此,请在命令提示符下键入以下命令,然后按 Enter:
Del “%ALLUSERSPROFILE%\Application Data\Microsoft\Network\Downloader\qmgr*.dat”
如果这是使用本文中的步骤解决 Windows 更新问题的首次尝试,请转到步骤 5,而无需执行步骤 4 中的步骤。 如果在执行除步骤 4 以外的所有步骤后仍无法解决 Windows 更新问题,则只能在此时在故障排除中执行步骤 4 中的步骤。 步骤 4 中的步骤也由上述“修复解决方案”的“攻击性”模式执行。
将以下文件夹重命名为 *.BAK:
%Systemroot%\SoftwareDistribution\DataStore
%Systemroot%\SoftwareDistribution\Download
%Systemroot%\System32\catroot2
为此,请在命令提示符处键入以下命令。 在键入每条命令后按 Enter。
Ren %Systemroot%\SoftwareDistribution\DataStore DataStore.bak
Ren %Systemroot%\SoftwareDistribution\Download Download.bak
Ren %Systemroot%\System32\catroot2 catroot2.bak
将 BITS 服务 和 Windows 更新服务 重置为默认安全描述符。 为此,请在命令提示符下键入以下命令。 在键入每条命令后按 Enter。
sc.exe sdset bits D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)
sc.exe sdset wuauserv D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)
在命令提示符下键入以下命令,然后按 Enter:
cd /d %windir%\system32
重新注册 BITS 文件和 Windows 更新 文件。 为此,请在命令提示符下键入以下命令。 在键入每条命令后按 Enter。
regsvr32.exe atl.dll
regsvr32.exe urlmon.dll
regsvr32.exe mshtml.dll
regsvr32.exe shdocvw.dll
regsvr32.exe browseui.dll
regsvr32.exe jscript.dll
regsvr32.exe vbscript.dll
regsvr32.exe scrrun.dll
regsvr32.exe msxml.dll
regsvr32.exe msxml3.dll
regsvr32.exe msxml6.dll
regsvr32.exe actxprxy.dll
regsvr32.exe softpub.dll
regsvr32.exe wintrust.dll
regsvr32.exe dssenh.dll
regsvr32.exe rsaenh.dll
regsvr32.exe gpkcsp.dll
regsvr32.exe sccbase.dll
regsvr32.exe slbcsp.dll
regsvr32.exe cryptdlg.dll
regsvr32.exe oleaut32.dll
regsvr32.exe ole32.dll
regsvr32.exe shell32.dll
regsvr32.exe initpki.dll
regsvr32.exe wuapi.dll
regsvr32.exe wuaueng.dll
regsvr32.exe wuaueng1.dll
regsvr32.exe wucltui.dll
regsvr32.exe wups.dll
regsvr32.exe wups2.dll
regsvr32.exe wuweb.dll
regsvr32.exe qmgr.dll
regsvr32.exe qmgrprxy.dll
regsvr32.exe wucltux.dll
regsvr32.exe muweb.dll
regsvr32.exe wuwebv.dll
重置 Winsock。 为此,请在命令提示符下键入以下命令,然后按 Enter:
netsh winsock reset
如果运行的是 Windows XP 或 Windows Server 2003,必须设置代理设置。 为此,请在命令提示符下键入以下命令,然后按 Enter:
proxycfg.exe -d
重启 BITS 服务、Windows 更新服务 和 加密服务。 为此,请在命令提示符下键入以下命令。 在键入每条命令后按 Enter。
net start bits
net start wuauserv
net start cryptsvc
如果正在运行 Windows Vista 或 Windows Server 2008,请清除 BITS 队列。 为此,请在命令提示符下键入以下命令,然后按 Enter:
bitsadmin.exe /reset /allusers
当然我想如果有还原点,系统还原到安装KB之前的状态也可以。
其它办法:
1. 使用命令提示符卸载更新: - 打开命令提示符(以管理员身份运行)。 - 输入命令:wusa /uninstall /kb:5006670 - 按下回车键执行命令。 - 等待卸载完成,重启电脑。
2. 使用Windows更新目录卸载更新: - 打开文件资源管理器,导航到以下路径:C:\Windows\SoftwareDistribution\Download - 删除该文件夹中的所有内容。 - 打开设置应用程序,选择“更新和安全”。 - 点击“检查更新”按钮,让系统重新下载更新文件。 - 安装新的更新文件,重启电脑。
「 人们一思索,上帝就发笑 ---犹太谚语」
对于运维小伙伴来讲,Ansible并不陌生,配置简单,上手容易,只要掌握几个基本的模块就可以解决好多运维中重复的事,但是对于处理更为高级的功能和更大、更复杂的项目时,管理和维护Ansible Playbook或高效使用将变得更加困难。
下面的的playbook是一个k8s安装环境初始化的剧本,其实现方式简单,是在k8s集群中所有节点都需要做的一些处理,实现如下功能
- name: init k8s
hosts: all
tasks:
# 关闭防火墙
- shell: firewall-cmd --set-default-zone=trusted
# 关闭selinux
- shell: getenforce
register: out
- debug: msg="{{out}}"
- shell: setenforce 0
when: out.stdout !="Disabled"
- replace:
path: /etc/selinux/config
regexp: "SELINUX=enforcing"
replace: "SELINUX=disabled"
- shell: cat /etc/selinux/config
register: out
- debug: msg="{{out}}"
- copy:
src: ./hosts
dest: /etc/hosts
force: yes
# 关闭交换分区
- shell: swapoff -a
- shell: sed -i '/swap/d' /etc/fstab
- shell: cat /etc/fstab
register: out
- debug: msg="{{out}}"
# 配置yum源
- shell: tar -cvf /etc/yum.tar /etc/yum.repos.d/
- shell: rm -rf /etc/yum.repos.d/*
- shell: wget ftp://ftp.rhce.cc/k8s/* -P /etc/yum.repos.d/
# 安装docker-ce
- yum:
name: docker-ce
state: present
# 配置docker加速
- shell: mkdir /etc/docker
- copy:
src: ./daemon.json
dest: /etc/docker/daemon.json
- shell: systemctl daemon-reload
- shell: systemctl restart docker
# 配置属性,安装k8s相关包
- copy:
src: ./k8s.conf
dest: /etc/sysctl.d/k8s.conf
- shell: yum install -y kubelet-1.21.1-0 kubeadm-1.21.1-0 kubectl-1.21.1-0 --disableexcludes=kubernetes
# 缺少镜像导入
- copy:
src: ./coredns-1.21.tar
dest: /root/coredns-1.21.tar
- shell: docker load -i /root/coredns-1.21.tar
# 启动服务
- shell: systemctl restart kubelet
- shell: systemctl enable kubelet
如果搭建的集群节点很多,那么使用ansible要方便很多,但是上面的剧本没有使用角色,所有的操作都耦合在一起,所以看起来不是特别清晰,可读性差,而且一些可变的变量也没有抽离出来。复用性差,也没有考虑失败回滚的问题,大部分的操作是通过shell模块来完成的,尤其是对一些文件的操作,shell模块不满足幂等性。
高效的使用Ansible不仅仅在于功能或工具的使用,对于实践方法和项目组织更重要,对于剧本的编写规范,有以下三点:
Ansible 的一大优势是简洁性。使用playbook保持简单,我们就能更加轻松地使用、修改和理解它们。
- hosts: node1,node2
tasks:
- yum: name=httpd state=present
- copy: content="RHCE Test" dest=/var/www/html/index.html force=yes
- service: name=httpd state=restarted enabled=yes
- service: name=firewalld state=restarted enabled=yes
- firewalld: service=http state=enabled permanent=yes immediate=yes
编写Ansible项目时,应考虑和同时遵循标准的样式:遵循统一的标准有助于提高可维护性和可读性。
Ansible项目的组织和Playbook的运行方式有助于维护、故障排除和审计。
因为 Ansible 具有相对扁平的命令空间,所以变量名非常重要。应使用描述性变量且应阐明内容,如 apache_tls_port ,在角色中给最好能给角色变量添加前缀,如myapp_apache_tls_port 。
在文件系统上构建 Ansible 项目时,请使用统一的模式,推荐的示例:
在这里插入图片描述
Playbook 结构的一大优势在于,可以将较?的playbook分成较小的?件,使其更易阅读,而较小的子playbook 可能会包含可以独立运行的、适合特定用途的 play。
动态清单支持从?个真实的中央来源集中管理主机和组,并确保清单自动更新。动态清单一般与云提供商、容器和虚拟机管理系统结合使用。
如果无法使用动态清单,则其它工具可以动态构建组或其他信息。group_by 模块根据事实动态生成组成员资格,该组成员资格对 playbook 的其余部分有效。
# Create nested groups
- group_by:
key: el{{ ansible_distribution_major_version }}-{{ ansible_architecture }}
parents:
- el{{ ansible_distribution_major_version }}
TASK [group_by] ****************************************************************************************************
task path: /root/ansible/group_by.yaml:5
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
changed: [192.168.26.82]=> {
"add_group": "el7-x86_64",
"changed": true,
"parent_groups": [
"el7"
]
}
changed: [192.168.26.81]=> {
"add_group": "el7-x86_64",
"changed": true,
"parent_groups": [
"el7"
]
}
主机可以是多个组的成员,可以按以下特征将主机划分不同的种类:
使用一个专用的控制节点来控制对系统的访问和审计 Ansible 活动,让所有的 Ansible Playbook 都从上面运行。
系统管理员仍应在系统上拥有自己的账户,以及用于连接受管主机的凭据,并在需要时可以进行权限提升。当系统管理员离职时,因从受管主机的authorized_keys文件中删除其 SSH 密钥,同时撤销其 sudo 权限。也可以考虑使用红帽 Ansible Tower 作为中央控制节点。
在开发过程中、任务运行时以及Playbook投入使用后,应经常测试 Playbook 和 task
如果需要确认任务是否成功,请验证任务的结果,而不要信任模块的返回代码
- start web server
service:
name: httpd
status: started
- name: Check web site from web server
uri:
ur1: http://{{ ansible_fqdn}}
return_content: yes
register: example_webpage
failed_when: example_webpage. status !=200
block 指令可用于对任务进行分组,与 rescue 指令结合使用时,可帮助从错误和故障中恢复。
---
- name: block test
hosts: node1
tasks:
- block:
- debug: msg="vg myvg not found" #提示卷组没找到
- debug: msg="create vg myvg .. .." #做其他操作(比如创建这个卷组...)
when: ('myvg' not in ansible_lvm.vgs) #当卷组myvg不存在时
rescue:
- debug: msg="creating failed .. .." #block失败时提示创建卷组失败
always:
- shell: vgscan #列出卷组信息
register: list #保存到名为list的变量
- debug: msg={{list.stdout_lines}} #提示卷组扫描结果
即使不在?产中使用最新版本的 Ansible,也应该定期针对 Ansible 的最新版本测试 playbook。这将避免在Ansible 模块和功能不断演变时出现的问题。
如果 playbook 在运行时显示警告或弃用消息,应注意它们并做出相应的调整。通常,Ansible 中的某?功能已弃用或有变化,则该项目会在删除或更改功能之前提早四个小版本提供弃用通知。
使用 ansible-playbook --syntax-check 命令进行语法检测。
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook group_by.yaml --syntax-check
playbook: group_by.yaml
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$echo 22 >> group_by.yaml
┌──[root@vms81.liruilongs.github.io]-[~/ansible]
└─$ansible-playbook group_by.yaml --syntax-check
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: No JSON object could be decoded
Syntax Error while loading YAML.
could not find expected ':'
The error appears to be in '/root/ansible/group_by.yaml': line 11, column 1, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
22
^ here
使用 ansible-playbook --check 命令,检查模式,针对check_mode中的实际受管主机运行 Playbook(不会改变主机状态),以查看Playbook执行的更改。此项检查不能保证完全准确性,因为 playbook 可能需要实际运行?些任务,playbook 中的后续任务才能正常运行。可能有?些标记有check_mode: no指令的任务。这些任务即使在检查模式中也会运行。
tasks:
- name: This task will always make changes to the system
ansible.builtin.command: /something/to/run --even-in-check-mode
check_mode: no
- name: This task will never make changes to the system
ansible.builtin.lineinfile:
line: "important config"
dest: /path/to/myconfig.conf
state: present
check_mode: yes
register: changes_to_important_config
下面我们来看一个完整的Demo,这个Demo做的事很简单,但是剧本编写清晰,在三台机器部署一个web服务,其中一台机器用haproxy作为负载,剩下的两台机器提供web能力(安装http服务并部署APP),剧本中创建了四个角色,用于描述四种行为:
编写一个ansible.cfg 配置文件,这个不多讲,指定主机清单文件位置和ssh用户,配置sudo 提权方式。
[defaults]
inventory=inventory
remote_user=devops
[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False
inventory 主机清单文件,定义两个分组,
[lb_servers]
servera.lab.example.com
[web_servers]
server[b:c].lab.example.com
site.yml为定义的实际执行的主剧本,这里通过,这里通过import_playbook模块来引入一个外部的调用角色的模块。一般情况下,当一个playbook很长很复杂,可以通过对剧本进行拆分。通过模块化的方式将多个playbook组合为一个完整的playbook,或者把文件中的任务列表插入到play中.
嗯,简单介绍下,ansible 可以使用两种方式实现剧本的模块化:
和Java web体系中的Jsp脚本有些类似,通过include指令和include动作引入文件
我们可以看到,site.yml执行的三个剧本都是通过导入的方式。
- name: Deploy HAProxy
import_playbook: deploy_haproxy.yml
- name: Deploy Web Server
import_playbook: deploy_apache.yml
- name: Deploy Web App
import_playbook: deploy_webapp.yml
执行顺序为,创建LB、创建web Serve,部署 web app,这里把剧本行为抽象为角色,然后在deploy_*里面调用角色,实现了行为和剧本的解耦。
看一下导入的执行角色的剧本deploy_haproxy.yml
- name: Ensure HAProxy is deployed
hosts: lb_servers
force_handlers: True
roles:
# The "haproxy" role has a dependency
# on the "firewall" role. The
# "firewall" role requires a
# "firewall_rules" variable be defined.
- role: haproxy
firewall_rules:
# Allow 80/tcp connections
- port: 80/tcp
通过剧本执行LB角色,并且定义·变量firewall_rules,声明开放的端口协议,这里有一个force_handlers,我们看一下,剧本中handlers用于任务处理(布雷),可以设置一个或一块任务,但是他不会主动执行,需要通过notify通知触发(引爆),还有一些需要注意的点:
看一个Demo
---
- name: handlers test
hosts: node5
tasks:
- lvol: lv=vo001 size=100M vg=search #创建逻辑卷vo001
notify: mkfs #如果changed则通知格式化(否则无需格式化)
handlers:
- name: mkfs #定义格式化操作处理
filesystem: dev=/dev/search/vo001 fstype=xfs force=yes
...
那么这里的force_handlers即强制执行的意思,当触发他的通知对应的任务执行失败,但是handlers任然会执行,
deploy_apache.yml
- name: Ensure Apache is deployed
hosts: web_servers
force_handlers: True
roles:
# The "apache" role has a dependency
# on the "firewall" role. The
# "firewall" role requires a
# "firewall_rules" variable be defined.
- role: apache
firewall_rules:
# Allow http requests from any
# internal zone source.
- zone: internal
service: http
# Add servera, the load balancer,
# to the internal zone.
- zone: internal
source: "172.25.250.10"
deploy_webapp.yml
- name: Ensure Web App is deployed
hosts: web_servers
vars:
- webapp_version: v1.0
roles:
- role: webapp
这里需要说明下,vars定义的变量属于剧本变量,而在roles下面的变量为角色变量
我们来看一下角色,一共有四个角色,其中三个在上面的deplay_*.yaml 文件中被调用,firewall角色被apache和haproxy依赖调用
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles]
└─$ls
apache firewall haproxy webapp
关于角色这里我们简单的回顾下
ansible 中的role指的是,为了方便复杂任务(包含大批量任务操作、模板、变量等资源)的重复使用,降低playbook剧本编写难度,而预先定义好的一套目录结构。
针对每一个角色,ansible会到固定的目录去调取特定的数据,关于角色在剧本中的使用,可以看看上面 deplay_*.yaml
角色内一般不指定hosts: 清单主机列表,而是交给调用此角色的剧本来指定,当然测试除外,具体看下
haproxy 角色 在剧本中负责LB 相关行为,简单看一下目录结构
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles]
└─$cd haproxy/ #角色根目录
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$tree
.
├── defaults #定义变量的缺省值,优先级较低
│ └── main.yml
├── handlers #定义handlers处理任务
│ └── main.yml
├── meta #定义作者、版本、兼容性、依赖项等描述信息
│ └── main.yml
├── tasks #任务入口,最主要的文件
│ └── main.yml
├── templates #存放模板文件
│ └── haproxy.cfg.j2
└── tests # 用于角色测试
├── inventory
└── test.yml
6 directories, 7 files
当然,这里的角色目录并不是最全的,正常的角色中还会有vars目录用于定义变量,相对于defaults优先级更高,files目录存放一些静态文件,README.md文件用于描述自述信息,我们通过init命令生成一个角色看一下目录
┌──[root@vms81.liruilongs.github.io]-[~/ansible/roles]
└─$ansible-galaxy init demo
- Role demo was created successfully
┌──[root@vms81.liruilongs.github.io]-[~/ansible/roles]
└─$ls
demo
┌──[root@vms81.liruilongs.github.io]-[~/ansible/roles]
└─$tree
.
└── demo
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
9 directories, 8 files
┌──[root@vms81.liruilongs.github.io]-[~/ansible/roles]
└─$
嗯,回到我们的haproxy来看一下 defaults目录下的yaml文件用于定义一些缺省的变量。
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat defaults/main.yml
# Log-level for HAProxy logs
log_level: info
# Port exposed to clients
port: 80
# Name for the default backend
backend_name: app
# Port backend is exposed to
backend_port: 80
# The appservers variable is a list
# of backend servers that provide
# the web service that is proxied
# haproxy. Each server must define:
# name, address, port. Below is
# and example structure:
# appservers:
# - name: serverb.lab.example.com
# ip_address: 1.2.3.4
# port: 5000
# - name: serverc.lab.example.com
# ip_address: 1.2.3.5
# port: 5000
# The default is no defined backend servers.
appservers: []
# Socket used to communicate with haproxy service. DO NOT CHANGE
socket: /var/run/haproxy.sock
handlers这个目录用于定义需要处理被激活的任务。这里定义了两个任务,都用到了Service模块
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat handlers/main.yml
---
# handlers file for haproxy
- name: restart haproxy
service:
name: haproxy
state: restarted
- name: reload haproxy
service:
name: haproxy
state: reloaded
看下meth元数据,作者信息,版本,以及通过dependencies我们可以看到该角色依赖角色firewall
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat meta/main.yml
galaxy_info:
author: Ophelia Dunham
description: A role to ensure deployment of HAProxy
company: Example, Inc.
。。。。
license: license (GPLv2, CC-BY, etc)
min_ansible_version: 2.4
。。。。
galaxy_tags: []
。。
dependencies:
- name: firewall
这里我们简单聊聊角色依赖,角色依赖可以在使用角色时自动拉入其他角色。Ansible 执行角色依赖项,则必须使用关键字dependencies在mate文件夹下的main.yaml中声明在指定角色之前插入的角色和参数列表,我们这里的参数是定义在deploy_*.yaml
「主任务剧本」
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat tasks/main.yml
---
# tasks file for haproxy
- name: Ensure haproxy packages are present
yum:
name:
- haproxy
- socat
state: present
- name: Ensure haproxy is started and enabled
service:
name: haproxy
state: started
enabled: yes
- name: Ensure haproxy configuration is set
template:
src: haproxy.cfg.j2
dest: /etc/haproxy/haproxy.cfg
#validate: haproxy -f %s -c -q
notify: reload haproxy
模板文件编写,这里用到了jieja2模板引擎,在一般的python web项目中用的比较多一点,这里简单的理解为变量替换。
haproxy.cfg.j2模板里用到了我们之前定义的大量变量,包括default目录的下main.yaml中定义的变量,以及appservers.yaml中的变量。
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat templates/haproxy.cfg.j2
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
#Send events/messages to rsyslog server.
log 127.0.0.1:514 local0 {{ log_level }}
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
#state changes due to API calls are stored in this file.
server-state-file /usr/local/haproxy/haproxy.state
# turn on stats unix socket
# required for the ansible haproxy module.
stats socket {{ socket }} level admin
# utilize system-wide crypto-policies
ssl-default-bind-ciphers PROFILE=SYSTEM
ssl-default-server-ciphers PROFILE=SYSTEM
#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
#Loads state changes from the state file.
load-server-state-from-file global
#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend main
mode http
bind *:{{ port }}
default_backend {{ backend_name }}
#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend {{ backend_name }}
balance roundrobin
{% for server in appservers %}
server {{ server.name }} {{ server.ip }}:{{ backend_port }}
{% endfor %}
appservers 清单变量用于定义需要负载的机器域名和ip。这里为了角色的复用性,单独分离出来。
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices]
└─$cat appservers.yml
appservers:
- name: serverb.lab.example.com
ip: "172.25.250.11"
- name: serverc.lab.example.com
ip: "172.25.250.12"
剩下的就是测试相关的yaml文件,不多介绍,remote_user指定连接受管机的远程用户名
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat tests/inventory
localhost
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/haproxy]
└─$cat tests/test.yml
---
- hosts: localhost
remote_user: root
roles:
- haproxy
apache 角色用于提供http 服务,目录结构相对简单
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles]
└─$cd apache/
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/apache]
└─$tree
.
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
└── tests
├── inventory
└── test.yml
3 directories, 4 files
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/apache]
└─$
meta 文件夹我们这里不多介绍了,涉及防火墙操作,所以依赖firewall角色,看一下主任务剧本
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/apache]
└─$cat tasks/main.yml
---
# tasks file for apache
- name: Install http
yum:
name:
- httpd
- php
- git
- php-mysqlnd
state: present
- name: Configure SELinux to allow httpd to connect to remote database
seboolean:
name: httpd_can_network_connect_db
state: true
persistent: yes
- name: http service state
service:
name: httpd
state: started
enabled: yes
webapp角色用于部署web 项目到httpd服务,主要涉及缺省变量编写和主任务剧本。
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles]
└─$cd webapp/
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/webapp]
└─$tree
.
├── defaults
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
└── tests
├── inventory
└── test.yml
4 directories, 5 files
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/webapp]
└─$
defaults目录下的清单变量只有一个webapp_message,meta目录下的元数据不多介绍
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/webapp]
└─$cat defaults/main.yml
webapp_message: "This is"
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/webapp]
└─$cat meta/main.yml
主任务剧本中,用了一个dufault目录下的缺省变量和一个ansible的魔法变量,一个使用角色时定义的剧本变量。通过copy模块向http服务的引导页写入一句话。
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/webapp]
└─$cat tasks/main.yml
---
# tasks file for webapp
#- name: Copy the code from the repository
# git:
# repo: "{{ webapp_repo }}"
# version: "{{ webapp_version }}"
# dest: /var/www/html/
# accept_hostkey: yes
## key_file: deployment key??
- name: Copy a stub file.
copy:
content: "{{ webapp_message }} {{ ansible_hostname }}. (version {{ webapp_version}})\n"
dest: /var/www/html/index.html
最后来看一下firewall角色
firewall 角色并没有被显示的调用,那么它是如何被调用的?
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles]
└─$cd firewall/
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/firewall]
└─$tree
.
├── defaults
│ └── main.yml
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
└── tests
├── inventory
└── test.yml
5 directories, 6 files
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/firewall]
└─$
这里就要讲到角色依赖,我们上面的haproxy角色和apache角色都在meta/main.yaml 文件中依赖了firewall角色,所以haproxy角色和apache角色在执行的时候要先执行firewall角色.
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/firewall]
└─$cat defaults/main.yml
---
# defaults file for firewall
# This role requires that firewall_rules variable
# be defined. The variable is a list of rules, and
# each rule defines:
#
# service: (optional)
# port: (optional)
# zone: (optional)
# source: (optional)
#
# A rule should only define a service or a port.
# And example definition is:
#
# firewall_rules:
# - service: http
# zone: internal
# - port: 8443
# source: 192.168.0.2
# By default, no rules are implemented.
firewall_rules: []
一个重载firewall 配置文件的任务
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/firewall]
└─$cat handlers/main.yml
---
# handlers file for firewall
- name: reload firewalld
service:
name: firewalld
state: reloaded
主任务文件,编写防火墙配置,在配置完通知上面的handlers
┌──[root@workstation.lab.example.com]-[/home/student/DO447/labs/development-practices/roles/firewall]
└─$cat tasks/main.yml
---
# tasks file for firewall
- name: Ensure Firewall Sources Configuration
firewalld:
source: "{{ item.source if item.source is defined else omit }}"
zone: "{{ item.zone if item.zone is defined else omit }}"
permanent: yes
state: "{{ item.state | default('enabled') }}"
service: "{{ item.service if item.service is defined else omit }}"
immediate: true
port: "{{ item.port if item.port is defined else omit }}"
loop: "{{ firewall_rules }}"
notify: reload firewalld
当我们不需要这套环境了需要编写一个卸载当前环境的剧本clean.yml
- name: Clean Load Balancers
hosts: lb_servers
gather_facts: no
tasks:
- name: Remove packages
yum:
name: haproxy
state: absent
- set_fact:
firewall_rules:
- port: 80/tcp
- name: Clean Web Servers
hosts: web_servers
gather_facts: no
tasks:
- name: Remove packages
yum:
name: httpd
state: absent
- set_fact:
firewall_rules:
- zone: internal
service: http
- zone: internal
source: 172.25.250.10
- name: Clean Firewall rules
hosts: lb_servers, web_servers
tasks:
- name: Ensure Firewall Sources Configuration
firewalld:
source: "{{ item.source if item.source is defined else omit }}"
zone: "{{ item.zone if item.zone is defined else omit }}"
permanent: yes
state: 'disabled'
service: "{{ item.service if item.service is defined else omit }}"
port: "{{ item.port if item.port is defined else omit }}"
loop: "{{ firewall_rules }}"
- name: reload firewalld
service:
name: firewalld
state: reloaded
- name: Remove web application
hosts: web_servers
tasks:
- name: Remove stub file
file:
state: absent
path: "/var/www/html/index.html"
下面是Demo完整的结构
Windows标准控件:
windows标准控件由Windows 操作系统提供,在Windows 95中还提供了一些新增的控件。所有这些控件对象都是可编程的,我们可以使用Visual C++提供的对话框编辑器把它们添加到对话框中。Microsoft基础类库(MFC)提供了封装这些控件的类,它们列于表6.1。
在MFC 中,类CWnd是所有窗口类的基类,很自然的,它也是所控件类的基类。
Windows标准控件在以下环境下提供:
windows 95
Windows NT 3.51及以后版本
win32s 1.3
注意:visual C++4.2及以后版本不再支持Win32s.
VC++控件工具箱:
用来接收用户的命令,应用程序在接收到用户命令后,通常需要进行一些后台工作。按钮可以响应单击或双击动作,在按钮接收到鼠标动作后,向其父窗口发送相应的控件通知,用户可以对这些控件通知进行消息映射,从而进行相应的处理。
在一个对话框中,可以定义一个默认按钮,这只要选中按钮属性中的“Default”选项。如果在对话框活动的时候按下了Enter键,则等同于单击了默认按钮。MFC提供了CButton类支持按钮控件。
用来显示某种可能的选择,该项选择是独立的,用户可以选中或取消该选项。在选项被选中的时候核选标记出现,选项被取消时核选标记消失。MFC中由CButton类对核选框进行支持,用户可以通过SetCheck()函数和GetCheck()函数设置或获取核选框当前的状态。
BST_UNCHECKED==((CButton*)GetDlgItem(IDC_CHECK_RES1))->GetCheck()
((CButton*)GetDlgItem(IDC_CHECK_RES1))->SetCheck(true);
用来接收用户输入的字符串。通过选择编辑框的选项,编辑框可以接收字符串、数字、密码等;编辑框还可以设置成接收多行字符串的模式;可以自动进行大小写转换。编辑框可能向父窗口发送多种控件通知,如果用户需要,可以对这些控件通知进行处理。MFC提供了CEdit类支持编辑框控件。
GetDlgItem(IDC_***)->SetWindowText(Cstring);
GetDlgItem(IDC_***)->GetWindowText(Cstring);
列表框和编辑框的组合,用户除了可以在列表中对已经存在的选项进行选择外,还可以输入新的选择。MFC提供了CComboBox类对组合框控件进行支持。
CComboBox * AAA=(CComboBox*)(GetDlgItem(IDC_***));
AAA->AddString(_T("***"));
AAA->SelectString(0, _T("***"));
AAA->SetCurSel(0);
int nSel=AAA->GetCurSel();
AAA->GetLBText(nSel, Cstring);
if(strType.Trim()==_T("***"))
用来选择一系列的可能选择,用户通过滚动条可以在这些选择中浏览。在列表框中,可以进行单项选择,也可以进行多项选择,这取决于用户在控件属性对话框中的设置。MFC提供了CListBox类对列表框控件进行支持。
用来包围具有逻辑关系的一组控件,在这些控件的周围加上边界和标题。需注意的是,组成框仅仅是在视觉效果上对控件进行“成组”,真正的“成组”工作还需要另外一些工作。仅仅在视觉上展现出一组的逻辑关系,并不添加任何代码
用来选择某种可能的选择,与 (Check Box)复选框不同,该选项不是独立的。
一般是几个单选按钮组成一组,同组中的单选按钮仅有一个按钮被选中。
MFC同样使用CButton类对单选按钮控件进行支持,
SetCheck()函数和GetCheck()函数对单选按钮也是适用的。
用来在指定的位置显示特定的字符串,一般用来标识附近另一个控件的内容。显示在静态文本控件中的字符串一般不再改变,但是在需要的时候,必须为其指定一个不同食物ID号,通过调用相应的函数来进行设置。MFC提供了CStatic类支持静态控件。
显示位图(Bitmap)和图标(Icon),图形绘制与显示,主要是其方形的区域适合显示,同样方形区域也可利用(Static Text)静态文本框。
这包括水平滚动条和垂直滚动条,除了在视觉效果上的方向不同外,水平滚动条在被滚动时发生WM_HSCROLL消息,而垂直滚动条在被滚动时发送WM_VSCROLL消息。MFC提供了CScrollBar进行支持。
通常用来在程序中接受一系列离散的数值。
用户可以设置滑块控件的取值范围,并可以为控件加上刻度标记以显示特定位置的含义。
MFC提供了CSliderCtrl类进行支持。
包括一对紧靠在一起的上下箭头,使用微调按钮可以增大或者缩小某个特定的数值。
微调按钮往往都需要一个“伙伴”控件,这通常都是一个编辑框。
当微调按钮的向上箭头被单击时,编辑框中的数字就增大;反之则减小。MFC提供了CPinButtonCtrl类进行支持。
在进行一项需要较长时间的操作时来反应当前的进度。
当操作的进度不断前进时,进度条就用特色颜色填充进度条框。用户可以设定进度条的范围和当前位置。
MFC提供了CProgressCtrl类进行支持。
CProgressCtrl* progressbar=(CProgressCtrl*)GetDlgItem(IDC_PROGRESS_1);
progressbar->SetRange(0, 4);
progressbar->SetPos(0);
热键控件看起来就像一个编辑框,但是在热键控件中能够立刻反应用户刚刚按下的键组合,这在设置程序的热键时特别有用。
热键控件只是在“视觉”上显示了按键组合,设置热键的工作还需要用户添加代码完成。
MFC提供了CHotKey类进行支持。
按一定的排列顺序显示一系列带图标的字符串。
列表控件提供了四种显示模式:大图标(Icon)、小图标(Small Icon)、列表(List)和报表(Report)。
用户可以向列表控件中添加新的项,也可以控制列表控件的显示模式。
MFC提供了CListCtrl类进行支持。
初始化:
struct INFO { int id; CString time; CString type; }info; CString id; int nRow=0; id.Format(_T("%d"), info.id); m_ListCtrl.InsertItem(nRow,id); m_ListCtrl.SetItemText(nRow, 1, info.time); m_ListCtrl.SetItemText(nRow, 2, info.type); nRow ++;
添加记录:
struct INFO
{
int id;
CString time;
CString type;
}info;
CString id;
int nRow=0;
id.Format(_T("%d"), info.id);
m_ListCtrl.InsertItem(nRow,id);
m_ListCtrl.SetItemText(nRow, 1, info.time);
m_ListCtrl.SetItemText(nRow, 2, info.type);
nRow ++;
显示一系列项目的层次关系,最典型的例子是显示磁盘上的文件与文件夹。
如果有子项目的话,单击树形控件中的项目可以展开或者收缩其子项目。
MFC提供了CTreeCtrl类进行支持。
初始化
void CConfigDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码 CRect rcWnd; GetClientRect(&rcWnd); CRect rcChild=rcWnd; rcChild.top=rcWnd.top+10; rcChild.bottom -=10; rcChild.left +=160; rcChild.right -=10; CRect laneRect=rcChild; if(::IsWindow(m_1Dlg)) m_1Dlg.MoveWindow(rcChild); if(::IsWindow(m_2Dlg)) m_2Dlg.MoveWindow(rcChild); if ( ::IsWindow( pTree.GetSafeHwnd() ) ) { pTree.MoveWindow(rcWnd.left+10,rcWnd.top+30,130,350,1); } }
树节点切换
void CConfigDlg::OnTvnSelchangedTree(NMHDR *pNMHDR, LRESULT *pResult) { LPNMTREEVIEW pNMTreeView=reinterpret_cast<LPNMTREEVIEW>(pNMHDR); // TODO: 在此添加控件通知处理程序代码 HTREEITEM hSelected=pNMTreeView->itemNew.hItem; if(hSelected!=NULL) { pTree.SelectItem(hSelected); int nDat=pTree.GetItemData(hSelected); switch(nDat) { case 1: /*MessageBox(_T("YNAME"));*/ if(m_pPreWnd) m_pPreWnd->ShowWindow(SW_HIDE); m_1Dlg.ShowWindow(SW_SHOW); m_pPreWnd=&m_1Dlg; break; case 3: /*MessageBox(_T("XNAME"));*/ if(m_pPreWnd) m_pPreWnd->ShowWindow(SW_HIDE); m_2Dlg.ShowWindow(SW_SHOW); m_pPreWnd=&m_2Dlg; break; default: break; } } *pResult=0; }
改变位置
void CConfigDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码 CRect rcWnd; GetClientRect(&rcWnd); CRect rcChild=rcWnd; rcChild.top=rcWnd.top+10; rcChild.bottom -=10; rcChild.left +=160; rcChild.right -=10; CRect laneRect=rcChild; if(::IsWindow(m_1Dlg)) m_1Dlg.MoveWindow(rcChild); if(::IsWindow(m_2Dlg)) m_2Dlg.MoveWindow(rcChild); if ( ::IsWindow( pTree.GetSafeHwnd() ) ) { pTree.MoveWindow(rcWnd.left+10,rcWnd.top+30,130,350,1); } }
包含大量的控件,可以满足用户显示或者获取大量数据分类显示的要求,典型例子是Windows任务栏每个程序标签。
每个属性表又分为好几个属性页,这些属性页由各自的标签进行区分,这些属性页中都可以包容其他控件。
在显示属性表的时候,一次只能够显示一个属性页的全部内容,同时显示其他属性页的标签,用户通过单击标签打开相应的属性页。
MFC提供了CTabCtrl类进行支持。
初始化绑定变量
m_tab_Light.DeleteAllItems();
m_LightParkingDlg.DestroyWindow();
m_LightStatusDlg.DestroyWindow();
m_tab_Light.InsertItem(0, _T("0001")); // 插入第一个标签
m_tab_Light.InsertItem(1, _T("0002")); // 插入第二个标签
CRect tabRect; // 标签控件客户区的位置和大小
m_tab_Light.GetClientRect(&tabRect); // 获取标签控件客户区Rect
// 调整tabRect,使其覆盖范围适合放置标签页
tabRect.left +=2;
tabRect.right -=1;
tabRect.top +=21;
tabRect.bottom -=2;
m_LightParkingDlg.Create(IDD_DIALOG_LIGHT_PARKING, &m_tab_Light); // 创建第一个标签页
m_LightStatusDlg.Create(IDD_DIALOG_LIGHT_STATUS, &m_tab_Light); // 创建第二个标签页
//m_LightParkingDlg.InitData();
m_LightParkingDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(),SWP_SHOWWINDOW);
控件页面切换
CRect tabRect; // 标签控件客户区的位置和大小
m_tab_Light.GetClientRect(&tabRect); // 获取标签控件客户区Rect
// 调整tabRect,使其覆盖范围适合放置标签页
tabRect.left +=2;
tabRect.right -=1;
tabRect.top +=21;
tabRect.bottom -=2;
switch (m_tab_Light.GetCurSel())
{
case 0:
m_LightStatusDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_HIDEWINDOW);
m_LightParkingDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_SHOWWINDOW);
break;
case 1:
m_LightParkingDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_HIDEWINDOW);
m_LightStatusDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_SHOWWINDOW);
break;
}
用来播放一段AVI格式的视频剪辑。用户可以控制视频剪辑的播放、停止和定位,但也仅限于这些功能。动画控件设置不能播放音频剪辑,如果用户需要更高层次的视频或者音频的支持,请选用MCIWnd控件。
MFC提供了CAnimateCtrl类对动画控件进行支持。
编辑控件(Edit Control)功能的扩展。在高级编辑框中,除了简单的输入和编辑字符串外,用户还可以为字符或段落指定特定的格式,用户甚至还可以向高级编辑框中插入OLE项。
高级编辑框基本上实现了一个带格式的文本编辑器功能,而只需要用户添加少量的接口。
MFC提供了CRichEditCtrl类进行支持。
向用户提供了一种直观的选择日期和时间的方法、日期/时间选择器在外观上类似于一个组合框,但是当用户单击下拉箭头时就会展开一个日历控件供用户选择,而一旦用户做出了选择,日期/时间选择器会自动显示新的日期/时间。MFC提供了CDateTimeCtrl类进行支持。
SYSTEMTIME times_1; //开始时间日期
SYSTEMTIME timee_1; //结束时间日期
CDateTimeCtrl* dtPickctrs_1=(CDateTimeCtrl*)GetDlgItem(IDC_DATETIMEPICKER_START1); //获取时间控件句柄
CDateTimeCtrl* dtPickctre_1=(CDateTimeCtrl*)GetDlgItem(IDC_DATETIMEPICKER_END1);
memset(×_1,0,sizeof(times_1)); //时钟初始化
memset(&timee_1,0,sizeof(timee_1));
dtPickctrs_1->GetTime(×_1); //获取控件所选时间,保存至变量
dtPickctre_1->GetTime(&timee_1);
CString strTimeStart; //将时间转换为字符串
strTimeStart.Format(_T("%04d-%02d-%02d %02d:%02d:%02d"), times_1.wYear,times_1.wMonth,times_1.wDay,times_2.wHour,times_2.wMinute,times_2.wSecond);
CString strTimeEnd;
strTimeEnd.Format(_T("%04d-%02d-%02d %02d:%02d:%02d"), timee_1.wYear,timee_1.wMonth,timee_1.wDay,timee_2.wHour,timee_2.wMinute,timee_2.wSecond);
获取系统时间
SYSTEMTIME time;
::GetLocalTime(&time);
如下图,看似与真正的日历类似,操作也类似,直观的为用户提供了观察和显示当前日期的途径。MFC提供了CMonthCalCtrl类进行支持。
IP地址控件用来输入和编辑IP地址,MFC提供了CIPAddressCtrl类进行支持。
该控件外观类似于一个编辑框,但是可以自动对输入的字符按3个一组进行区分和加间隔圆点。IP地址控件为开发支持Internet技术的程序提供了方便。
在普通组合框(Combo Box)的基础上还支持图像列表。
可以在组合框中显示特定的图标表示相应的选择,而不仅仅是显示文本。
MFC提供了CComboBoxEx类进行支持。
控件使用的时候,它的Class必须有一个类的支持这个窗口类可以VC的类,例如:Button、Edit.在头文件增加一个CEdit类的成员变量:CEdit m_Text(即点击右键添加变量),然后按照Edit控件使用即可。
1.用于在 MFC 应用程序上添加超链接,就像html中的超链接一样。你点下可以链接到一个网页上去。拖控件到页面上,并绑定一个变量(m_linkCtrl),里面的内容全部是按照标准的html中href的用法写的。写错了自然链接不了的。
可以去瞧下html里面的href属性是怎/么设置的.另外要注意的是字符串中双引号的处理(记得加个转义符\),添加单击响应消息
m_linkCtrl.SetWindowTextW(_T("<a href=\"http://blog.csdn.net/miko_xiaoming\">Miko's Note</a>"));
PNMLINK pNMLink=(PNMLINK) pNMHDR;
ShellExecuteW(NULL, L"open", pNMLink->item.szUrl, NULL, NULL, SW_SHOWNORMAL); //在浏览器中打开
2.同按钮(Button)控件一样,响应相应的消息
如下图,实现多选按钮选择功能,使用时添加相应菜单
可以直接作为inet_addr(ip)的输入,分隔符为逗号(,)
不仅可以为按钮添加相应命令还可以对命令做出注解
ID:控件的资源标识。
Visiable:控件是否可见。
Disabled:控件是否被禁止、被禁止的控件无法接受键盘和鼠标输入。
Group:是否从这个空间开始一个新组。
Tab stop:在程序中是否可以用【Tab】键切换到这个控件。
Help ID:是否给控件分配一个帮助标识,帮助标识基于控件本身的资源标识。
Client edge:给控件增加一个凹陷的边界,使整个控件看起来像是凹下去的一样。
Static edge:给控件增加一个边界。
Modal frame:给控件增加一个3维的框架,与Client edge相反,它是凸出来的。
Transparent:拥有此属性的窗口是透明的,它下面的窗口不会被它遮掩住。
Accept files:拥有此属性的窗口可以接收拖放文件,如果一个文件被拖动到这个控件上面,控件会收到WM_DROPFILES消息。
No parent notify:表示控件不向其父窗口发送WM_PARENTNOTIFY消息。
Right aligned text:表示文本右对齐。
以上属性可通过控件的属性对话框进行设置,在属性对话框中按【F1】键可以查看属性的帮助信息。
每一种控件都由一个MFC控件类来管理,当通过资源编辑器在对话框上添加一个控件时,visualC++会自动为控件类生成一个实例,即对象,并调用构造函数,当对话框关闭时,其各个子控件对象被自动销毁。
也可以不使用资源编辑器,自己在程序中写代码创建、显示控件并设置控件的属性。
所有的控件类都来自CWnd,控件的某些操作和属性设置需要用到CWnd本身的成员函数,CWnd某些函数经常用来操纵控件和设置控件属性。
SetWindowText:设置控件上要显示的标题文本,即用来设置控件的caption属性
GetWindowText:得到控件的标题文本
EnableWindow:设置控件的Disabled属性,即社会自控件是否可以接收键盘和鼠标的输入
SetWindowPos:改变窗口的大小、位置和Z轴次序。
MoveWindow:改变窗口的大小和位置
GetWindowRec:得到窗口的大小、位置(信息存放在一个矩形结构中)。
GetClientRect:得到窗口客户区的大小(信息存放在一个矩形结构中 )
ShowWindow:设置窗口的可见性(即窗口是否可见)
SetWindowText/GetWindowText还可以用来设置/得到对话框的标题文本。
福利来啦~
学习从来不是一个人的事情,要有个相互监督的伙伴,对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!
*请认真填写需求信息,我们会在24小时内与您取得联系。