zoukankan      html  css  js  c++  java
  • struts2学习笔记

    .struts2(2.1)知识基础部分

    Ø  1 .struts2概述

    Action、基本数据验证、Result、常用的配置文件、OGNL和ValueStack、Struts2 Tags

    类型转换、上传下载、防止重复提交、Interceptor(拦截器)、MVC设计思想、Struts2基于webWork

    Ø  2 .Action相关内容

    <1>action入门    <2>namespace

    <3>自定义Action  <4>路径设置

    <5>调用action中定义的方法

    <6>通配符  <7>接受用户输入的3中方式

    <8>乱码问题解决 <9>简单的数据校验 <10>aciton中访问web元素

    <11>包含模块的文件 <12>默认的action

    Ø  3 .struts2入门

    ü  构建项目

    <1>整合jar

    需要的核心jar包freemarker-2.3.13 、ognl-2.6.11、 struts2-core-2.1.6、xwork-2.1.2

           <2>增加struts2的核心过滤器

    Web.xml中增加如下内容:

        <filter>

          <filter-name>struts2</filter-name>

       <filter-class>org.apache.struts2.dispatcher.

    ng.filter.StrutsPrepareAndExecuteFilter</filter-class>

        </filter>

        <filter-mapping>

        <filter-name>struts2</filter-name>

        <url-pattern>/*</url-pattern>

    </filter-mapping>

    其中StrutsPrepareAndExecuteFilter包含两个过滤器

    <3>增加struts2的配置文件

    Struts.xml放到根目录下内容如下:

    <?xml version="1.0" encoding="UTF-8" ?>

    <!DOCTYPE struts PUBLIC

        "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"

        "http://struts.apache.org/dtds/struts-2.1.dtd">

    <struts>

         <constant name="struts.devMode" value="true"></constant>

          <package name="front" namespace="/struts2test" extends="struts-default" >

          <action name="hello">

                <result>/page/hello.jsp</result>

           </action>

        </package>

    </struts>

    以上例子用没有实际的action类,而是中间转发过程

    访问此程序地址为:http://localhost:9080/struts2_001/struts2test/hello

    访问规则为地址+命名包空间名称+action的名称

    ü  常用小技巧

    <1>struts2.xml配置开发模式,使的修改配置文件之后不用重新启动,访问直接生效

    配置strtus2常量

    <constant name="struts.devMode" value="true"></constant>

    <2>查看jar对应的源码

    在eclipse中切换到package查看模式,选中查看的包,右击属性

    可以绑定到特定的项目空间(workspace)、特定的文件、特定的文件夹;

    绑定到源代码文件夹,之后遍可以直接访问源码;

    <3>绑定java doc可以查看api文档

    查看方式,选种类或方法之后F1后,可以查看api文档

     

     

     

     

    Ø  4 .struts2工作机制

    Struts2.xml中的package元素的namespace元素和访问路径一一对应的

    访问路径中的后缀名action可以进行省略

    Ø  5 .namespace元素

    ü  Namesapce的作用

    ü  详解说明

    <package name="firstTest" namespace="/" extends="struts-default">

        <action name="helloAction">

            <result>/page/hello.jsp</result>

        </action>

    </package>

    1. A.   package包含三个属性namenamespaceextendspackage作用是为了防止重复(类似于java中的包的作用);
    2. B.        action:必须增加name属性,可以增加具体实现class属性
    3. C.       result可以添加name属性以及对应值,不增加此属性,默认为success(具体值可以在action中进行转发定义)
    4. D.       namespace默认为空(namespace=””),可以访问任何一个action(匹配的),任何对应的路径(namespace 下)的action都可以访问

    copy项目的时候,由于webContext还是原来定义的名字,进行如下修改

    项目右击属性web配置,对项目的访问上下文进行修改

     

    Ø  6 .action

    ü  核心内容

    action控制视图的转向,可以自己定义action,action可以是一个普通的类,只要类中有方法execute即可

    统一修改jsp中代码的编码方式

    <package name="firstTest" namespace="" extends="struts-default">

        <action name="hello" class="com.action.HelloAction">

            <result>/page/hello.jsp</result>

        </action>

     </package>

    不配置action的class属性值的时候,默认使用了ActionSupport类

    编写action的三种方式

    <1>Action可以实现action接口

    <2>继承ActionSupport(用此种)

    <3>可以是普通的java类,必须包含execute方法

    Action实现视图转发的类都是返回字符串数据,和具体result中的属性name对应

    Ø  7 .路径(path)问题

    在页面中链接(<a href=”index.jsp”>index.jsp<a>)以及提交到某一地址,由于struts2中运用了namespace概念,访问任意地址,都先便利struts2.xml中的namespace信息,如果找不到对应的命名空间信息,会链接到非预期的地址,为了访问具体地址,一般用绝对路径解决此问题

    通常struts2访问的原理,首先请求信息寻找(web.xml),之后通过过滤器,进行请求转发

    ,过滤器读取配置问题struts2.xml,根据namespace和action具体具体转发的action类,如果找寻不到

    具体的namepace和action,则请求转发给web容器进行处理

    具体访问路径可以根据服务器信息进行绝对定位,struts2的访问路径是根据action的请求地址,而不是jsp的地址进行

    请求转发,对于jsp的路径尽量使用如下方式

    String path = request.getContextPath();

    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

    url:basePath+path+jspname;

    String path = request.getContextPath();

    String basePath

     = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

    <a href="<%=basePath %>page/index.jsp">index.jsp</a>

     

    Ø  8 . 动态方法调用

    action执行的时候,默认执行execute方法,但可以自己定义方法

    <1>方式1:

    配置文件中增加method属性,可以提供给请求转发进行调用,可以配置参数method属性

    在配置文件struts2.xml中配置信息如下

    <package name="user" namespace="user" extends="struts-default">

           <action name="userAction" class="actionClassPath" method="methodName">

                  <result name="adduser">/page/useradd.jsp</action>

           </action>

    </package>

    缺点:如果存在多个方法(add,delete,update,query),每个方法都需要单独配置一个action块

    ,不推荐使用

    <2>方式2(建议使用):动态方法调用(dmi dynamic method invoke)

    在配置文件struts2.xml中,不用配置属性method,只需要配置一个action,请求调用时候直接动态调用

    (不同的方法,各自需要返回自己的视图,需要配置多个result)

    <package name="user" namespace="user" extends="struts-default">

           <action name="userAction" class="actionClassPath">

                  <result name="adduser">/page/useradd.jsp</action>

                  <result name="deleteuser">/page/userdelete.jsp</action>

                  <result name="updateuser">/page/userupdate.jsp</action>

           </action>

    </package>

    请求地址格式为:basePath+path+namespace/+action!methodName;

    Ø  9.请求action的地址通配符形式

    利用*通配符号,并且结合参数占位符{1}{2}.....

    约定优于配置(提前约定好jsp页面以及action中方法的命名规则),用通用的配置,匹配所有的路径调用信息

    <package name="user" namespace="/user" extends="struts-default">

                  <action name="user*" class="com.action.UserAction" method="{1}">

                         <result name="{1}_page">/page/user.jsp</result>

                         <result name="{1}_user">/page/user.jsp</result>

                         <result name="{1}_user">/page/user.jsp</result>

                  </action>

           </package>

    {1}代表第一个*

    {2}代表第二个*

    Ø  10.action接受请求参数

    注意请求地址中的namespace前不用增加"/"(action="namespace/actionname!methodname")

    action中有三种接受参数值的方式

    <1>属性接受(普通属性,基本类型的参数和复合类型的参数)

       数组和基本属性相同增加setter和getter方法

       注意:基本类型的数据必须提供setter和getter方法

       对Map类型的数据,只要提供getter方法就可以(struts2内部调用的是Map的put方法)

       struts2的action接受属性值是对应的getName中的Name并不是属性的值

       在action中增加属性定义字段,并且增加对应的setter和getter方法

       如下:

       1)action中增加属性

      public class UserAction extends ActionSupport{

                  private String userName;

                  private String password;

                  public String add(){

                  System.out.println("add_page");

                  return "add_page";

                  }

                  public String getUserName() {

                  return userName;

                  }

                  public void setUserName(String userName) {

                  this.userName = userName;

                  }

                  public String getPassword() {

                  return password;

                  }

                  public void setPassword(String password) {

                  this.password = password;

           }

           2)form表单地址

           <form name="userForm" method="post" action="user/user!add">

           username:<input name="userName" type="text"/>

           password:<input name="password" type="text"/>

        <input type="submit"  name="sb"  value="submit"/>

    </form>

     <3>调试属性信息,通过调试信息可以很对定位读取属性的信息

     <%@taglib uri="/struts-tags" prefix="s" %>

     <s:property value="userName"/>

     <s:debug></s:debug> 调试信息

    <2>domainModel域模型接受参数

    将属性封装到vo(pojo)中,之后在action进行引用

     不用new vo,struts2已经对vo进行了对象初始化

     并在vo提供对应属性的setter和getter方法

     在表单属性的名称需要引用vo的名称

     user.userName

     user.password

     如下:

     1)定义action并引用User

     public class UserAction extends ActionSupport{

           private User user;  

           public User getUser() {

                  return user;

           }

           public void setUser(User user) {

                  this.user = user;

           }

           public String add(){

                  System.out.println("add_page");

                  return "add_page";

           }

     2)表单元素名称定义

     <form name="userForm" method="post" action="user/user!add">---注意地址访问信息中命名空间

           username:<input name="user.userName" type="text"/>

           password:<input name="user.password" type="text"/>

        <input type="submit"  name="sb"  value="submit"/>

    </form>

     3)获得属性的值

          <s:property value="user.userName"/>

        <s:property value="user.password"/>

       <s:debug></s:debug>

    <3>ModelDriven(模型驱动)接受属性参数(这种方式不常用)

       action实现接口ModelDriven<T>,且实现getModel方法

       此时引用vo必须进行new

       如下:

       1)action内容

       public class UserAction extends ActionSupport implements ModelDriven<User>{

           private User user = new User();   

           public String add(){

                  System.out.println("add_page");

                  return "add_page";

           }

           public User getModel() {

                  return user;

           }

    }    

      2)请求页面参数名称

      <form name="userForm" method="post" action="user/user!add">

           username:<input name="userName" type="text"/>

           password:<input name="password" type="text"/>

        <input type="submit"  name="sb"  value="submit"/>

           </form>

           3)读取参数的值进行显示

           <s:property value="userName"/>

        <s:property value="password"/>

         <s:debug></s:debug>

    Ø  11.中文乱码

    Struts2.xml文件中新增配置属性

    <constant name=”struts.i18n.encoding” encode=”GBK”/>

     在struts2中如果在版本2.1中利用新的filter即

    org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter

    即使增加编码配置方式,对于汉字也是乱码

    可以换回原来的过滤器

    org.apache.struts2.dispatcher.FilterDispatcher

    汉字的乱码问题就解决了

    Ø  12.简单的数据校验

    <1>简单介绍

    ActionSupport中的方法addFieldError和标签<s:fielderror fieldName=””/>进行简单的数据校验

    实际上errors的校验的信息在Map中

    如下:

    public class UserAction extends ActionSupport{

        private String userName;

        public String add(){

               System.out.println("userName:"+userName);

           if(!this.userName.equals("wuoguoqing")){

               this.addFieldError("userName", "输入信息错误");

           }

           return "add_user";

        }

        public String getUserName() {

           return userName;

        }

        public void setUserName(String userName) {

           this.userName = userName;

        }

    }

    页面

    <s:fielderror fieldName="userName" theme="simple"/>

      <s:property value="errors.userName"/>

     <s:debug></s:debug>

    有关错误调试以及ValueStack信息

    <s:debug></s:debug>用于显示页面中的值栈中的信息

     

    <2>可以对同一个字段指定多个错误信息

    if(!this.userName.equals("wuoguoqing")){

               this.addFieldError("userName", "输入信息错误1");

               this.addFieldError("userName", "输入信息错误2");

               this.addFieldError("userName", "输入信息错误3");

               this.addFieldError("userName", "输入信息错误4");

               return  "add_user";

           }

    Ø  13.action访问web页面中的元素

    action中如何web元素对象request,session,application中的内容

    通常有四种方式ActionContext(Map),(RequestAware、SessionAware、ApplicationAware)、ServletActionContext、ServletRequestAware

    <1>ActionContext方式

    通过ActionContext对象获得web元素对象的内容,

    ActionContext封转了web元素存储访问的功能

    通过Map进行访问,并且struts2可以将Map中的内容映射成HttpServletRequest中的内容,可以在页面中

    直接通过jsp方式进行访问(request.getAttribute()),这种方式依赖于容器中的ActionContext,ActionContext并不是

    单例形式,内部通过ThreadLocal实现

    如下:

    public class UserAction extends ActionSupport {

           private Map request;

           private Map session;

           private Map application;

           public UserAction(){

                  request= (Map)ActionContext.getContext().get("request");

                  session= (Map)ActionContext.getContext().getSession();

                  application= (Map)ActionContext.getContext().getApplication();

           }

          

           public String add(){

               request.put("rusername","username");

               session.put("susername","susername");

               application.put("ausername","ausername");

                  System.out.println("add_page");

                  return "add_page";

           }

          

    }

    将web元素的内容通过jsp或ognl方式进行访问显示

    <%=request.getAttribute("rusername")%>

    <s:property value="#request.rusername"/>

    <2>实现对应接口(RequestAware、SessionAware、ApplicationAware由struts2提供)

    在调用Action类的时候,先检查此action是否实现了上述接口,如果实现了调用对应的

    setter方法,实例化web元素,由struts2实现

    Aware:得知、知道

    实现对应接口就知道对应web元素中的内容了

    如下:

    对初始化工作request/session/application由容器实现(DI dependcy injection)

    public class UserAction extends ActionSupport implements RequestAware,

                  SessionAware, ApplicationAware {

           private Map<String, Object> request;

           private Map<String, Object> session;

           private Map<String, Object> application;

           public String add() {

                  request.put("rusername", "username");

                  session.put("susername", "susername");

                  application.put("ausername", "ausername");

                  System.out.println("add_page");

                  return "add_page";

           }

           public void setRequest(Map<String, Object> arg0) {

                  this.request = arg0;

           }

           public void setSession(Map<String, Object> arg0) {

                  this.session = arg0;

           }

           public void setApplication(Map<String, Object> arg0) {

                  this.application = arg0;

           }

    }

    <3>通过ServletActionContext获得对应的web元素的内容

    import javax.servlet.ServletContext;

    import javax.servlet.http.HttpServletRequest;

    import javax.servlet.http.HttpSession;

    import org.apache.struts2.ServletActionContext;

    import com.opensymphony.xwork2.ActionSupport;

    public class UserAction extends ActionSupport {

           private HttpServletRequest request;

           private HttpSession session;

           private ServletContext application;

           public UserAction(){

                  request = ServletActionContext.getRequest();

                  session = request.getSession();

                  application = session.getServletContext();

           }

           public String add() {

                  request.setAttribute("rusername", "rusername");

                  System.out.println("add_page");

                  return "add_page";

           }

    }

    <4>实现ServletRequestAware接口

       如下:

       public class UserAction extends ActionSupport implements ServletRequestAware {

           private HttpServletRequest request;

           private HttpSession session;

           private ServletContext application;

           public String add() {

                  System.out.println("add_page");

                  return "add_page";

           }

           public void setServletRequest(HttpServletRequest arg0) {

           this.request = arg0;

           this.session=this.request.getSession();

           this.application = this.session.getServletContext();

                 

           }

    }

    Ø  14.配置文件中包含模块配置文件(struts2.xml)

    此中方式可以根据模块划分,将配置文件进行分块规划

    <struts>

           <constant name="struts.devMode" value="true"></constant>

           <include file="user.xml"></include>

           <package name="user" namespace="/user" extends="struts-default">

                  <action name="user" class="com.action.UserAction">

                         <result name="add_page">/page/user.jsp</result>

                  </action>

           </package>

    </struts>

    Ø  15.默认action(默认定位访问那个页面,访问不存在的就调转到指定页面)

    <package name="default" namespace="/" extends="struts-default">

                  <default-action-ref name="index">

                         <result>/page/user.jsp</result>

                  </default-action-ref>

    </package>

    Action总结

    Ø  16.Result的配置

    result的类型:

    如果result如果不指定类型(type),默认为dispather(只能跳转到页面)

    常用的包含:

    dispatcher、redirect、chain、rediectAction

    freemarker、stream、velocity......

    <1>dispatcher:默认result使用此种方式(地址显示action的地址信息)

    <2>redirect:客户端跳转(地址显示jsp)

    <3>redirectAction:直接跳转到另一个action

    <4>chain:从可以action直接跳转到另一个action

    注意chain跳转到其他package下的action中

    配置如下:

    <action  name="r3">

           <result  type="chain">

           <param name="actionName">actionname</param><!--指定action的名字-->

           <param name="namespace">namespace</param><!--指定namespace的名字-->

           </result>

    </action>

    <package name="default" namespace="/" extends="struts-default">

                  <default-action-ref name="index" >

                  </default-action-ref>

                  <action name="index">

                         <result>/index.jsp</result>

                  </action>

                  <global-results>

                         <result name="mainpage">/index.jsp</result>

                  </global-results>

           </package>

          

           <package name="result" namespace="/result"  extends="struts-default">

                  <action name="r1">

                         <result type="dispatcher">/page/r1.jsp</result>

                  </action>

                  <action  name="r2">

                         <result  type="redirect">/page/r2.jsp</result>

                  </action>

                  <action  name="r3">

                         <result type="chain">r1</result>

                  </action>

                  <action  name="r4">

                         <result  type="redirectAction">r2</result>

                  </action>

           </package>

    Ø  17.全局结果集(Globals_Result)

    一般用于维护公共的结果集合,其他包中若使用此结果集可以通过extends集成实现(如错误页面、主页面)

    在同一个包中(struts2.xml中package)中,如果多个action公用相同的结果集,可以将公用的result写入到

    <global-results>元素中如:

    <global-results>

           <result name="mainpage">/index.jsp</result>

    </global-results>

    同一包级下的action可以使用定义的globalresult

    如果不在同一个包中,可以在包元素中使用extends属性使用其他包中的全局结果集

    Ø  18.动态结果集

    action中根据传递的参数,动态确定放回结果集的信息

    如下:

    public class DynamicAction extends ActionSupport implements ServletRequestAware  {

           private HttpServletRequest request;

           private String type;

           private String url;

           public String getUrl() {

                  return url;

           }

           public void setUrl(String url) {

                  this.url = url;

           }

           public String getType() {

                  return type;

           }

           public void setType(String type) {

                  this.type = type;

           }

           public void setServletRequest(HttpServletRequest arg0) {

                  this.request=arg0;

           }

           public String execute(){

                  System.out.println("type:"+type);

                  if("1".equals(type)){

                         url="/page/r1.jsp";

                  }else{

                         url="/page/r2.jsp";

                  }

                 

                  return "sucess";

           }

    配置文件如下:

    <package name="dynamic" namespace="/dynamic" extends="default">

    <action name="dynamic" class="com.action.DynamicAction">

    <result name="sucess">${url}</result>

    </action>

    </package>

    Ø  19.L(Object Graph navigation language)

    Action中调用ServletActionContext获得Request

    每一个特定的Action都有一个对应的ValueStack,struts2把相关action的属性信息存入其中,

    以供页面或其他程序中调用

    ü  访问对象的属性(普通属性

    注意:

    如果要访问action中定义的属性以及对象并在jsp运用ognl表达式进行显示,

    在action中的属性以及对象必须设置setter/getter,这样struts2才会把相应的属性以及对象存放在action的栈中,页面才能进行数据访问

    public class UserAction extends ActionSupport {

        private String name;

        private String password;

        private User user = new User();

        private List<User> list  = new ArrayList<User>();

    。。。。。。setter/getter

    }

    userName:<s:property value="name"/><br/>

      password:<s:property value="password"/><br/>

      user:<s:property value="user.userName"/><br/>

      user static method:<s:property value="@com.vo.User@say()"/><br/>

      <s:property value="'<hr/><hr/>'" escape="false"/>

      List:<br>

      <s:property value="list.get(0).userName"/><br/>

      <s:property value="list.size()"/>

     

     

    <1>访问valueStack中的普通属性

    public class UserAction extends ActionSupport {

        private String userName;

        private String password;

    。。。。。。

    }

    页面直接访问属性的值

    <s:property value="userName"/><br/>

    <s:property value="password"/><br/>

    <2>访问valueStack中的vo

    一般情况下action中vo可以不进行new(地址中传递vo.attribute,struts2会自动创建vo对象,否则的需要自己创建,如地址信息中传递user.name=nm&&user.pwd=p,自动构建user对象),也可以直接new一个vo对象

    public class UserAction extends ActionSupport {

        private User user;

        private User user = new User();

    。。。。。。

    }

    页面中访问对象的属性值

    <s:property value="user.name"/><br/>

        <s:property value="user.pwd"/><br/>

    <3>ognl对象图导航语言

    传值cat.friend.name=nm

    通过此种语言可以对聚合对象进行导航,以便访问对象中的属性

    <s:property  value="cat.friend.name"/>

    即访问对象的属性

    vo.vo1….attibute

    ü  访问对象的方法(普通方法)

    可以访问action的普通方法

    或访问action中属性(对象)的基本方法

    <s:property value="user.pwd.length()"/><br/>

    <s:property value="cat.say()”/>//对象中的方法

    <s:property value="method()”/> //action中普通方法

    ü  访问对象静态方法

    <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>

    格式@静态类@静态方法()

    格式@静态类@静态属性

    ü  访问普通对象的构造方法

    <s:property value="new com.vo.User()"/><br/>

    ü  访问集合属性的内容

    Ø  20.struts-tags

    通用标签、控制标签、UI标签

    ü  <1>通用标签

    l  property标签

    有关property的说明:

    <s:property value=”objectName”/>访问valuestack中的objectName对象的值(通过ognl表达式)

    <s:property value=”‘stringconstant’e”/>直接显示字符串的值

    如果增加default属性,相当于如果栈中存在对应的对象,就显示默认值

    l  set标签

    相关属性var/name scope,name属性高级版本已经去掉

    <1><s:set var=”varname”  value=”object”/>

    设置一个变量var,并给予赋值object(此处为对象类型,可以通过ognl表达式进行访问)

     如果不指定存贮范围,默认存贮在request中

     可以通过#varname对变量的内容(从ActionContext)进行访问

    <s:set var="param"  value="user"></s:set>

    <s:property  value="#param"/>

    <%=request.getAttribute("param") %>

    <2>设定范围

    <s:set var="param"  value="user" scope=”page”></s:set>

    设定范围后,通过#paraname无法访问,指定在指定范围内进行范围才可以

    <s:set var="pageparam"  value="'reeerwwerwererwr'" scope="session"></s:set><br>

    <s:debug></s:debug>

    <s:property value="#session.pageparam"/>

    注意:<s:debug/>标签可以嵌套在其他标签中(其他标签之下,查看当前标签的在栈中的内容),指定valueStack的调试范围

    l  bean标签

    引入对象,存放到ActionContext中,以供调用

    <s:property value="%{setvalue}"/>

    %{setvalue} 表示强制将setvalue作为ognl表达式访问,而不是作为字符串处理

    一般标签只要设置了var相当于在valuestack中建立了对应的对象以及内容

    在整个页面的任何位置都可以进行访问(ActionContext)

    <s:bean name="com.vo.User" var ="users">

        <s:param name="userName" value="'ppppppuser'"></s:param>

    //给User对象设置属性值

    </s:bean>

    <s:property value="#users.userName"/>

    <s:debug></s:debug>

    可以利用#objectName访问valuestack中的对象

    l  include标签

    <s:include value=”/config/sss.jsp”/>

    l  fielderror标签

    <s:fielderror fieldName="struts.errorname" theme="simple"></s:fielderror>

    l  if标签

    <s:set var="count"  value="'12'"></s:set>

    <s:if test="count>1">

        very big

    </s:if>

    <s:elseif test="count>13">

    veryvery big

    </s:elseif>

    <s:else>

    very

    </s:else>

    注意:test中的对象的类型,如果是数值的话如何比较

  • 相关阅读:
    CEF解决加载慢问题
    CEF之CefSettings设置locale
    Win32程序支持命令行参数的做法(转载)
    VC++实现程序重启的方法(转载)
    CEF之CefSettings设置日志等级
    libcurl开源库在Win32程序中使用下载文件显示进度条实例
    libcurl开源库在Win7 + VS2012环境下编译、配置详解 以及下载文件并显示下载进度 demo(转载)
    使用ShellExecute打开目标文件所在文件夹并选中目标文件
    linux下gimp的使用
    linux下的chm阅读器?
  • 原文地址:https://www.cnblogs.com/huboking/p/4330278.html
Copyright © 2011-2022 走看看