精华内容
下载资源
问答
  • 简单 PHP + MySQL 数据库动态网站制作

    千次阅读 多人点赞 2016-05-16 15:31:10
    在这篇文章中,我尽量用最浅显易懂的语言来说明使用 PHP, MySQL 制作一个动态网站的基本技术。阅读本文需要简单的 HTML 基础知识和(任一编程语言的)编程基础知识(例如变量、值、循环、语句的概念等)。 PHP ...

    在这篇文章中,我尽量用最浅显易懂的语言来说明使用 PHP, MySQL 制作一个动态网站的基本技术。阅读本文需要简单的 HTML 基础知识和(任一编程语言的)编程基础知识(例如变量、值、循环、语句块的概念等)。

    PHP 基础

    概述

    PHP 是一种解释性语言,可用于对网页进行预处理。PHP 脚本在服务器端运行,其运行结果是一个可用来显示的网页。尽管可以完成许多类似工作,但是 JavaScript 和 PHP 的一大区别就是,JavaScript 是在浏览器端运行的。事实上,浏览器会接收 JavaScript 代码并运行它,所以用户是可以查看 JavaScript 代码的。而 PHP 不会将原始代码交给浏览器, 只会将其运行的结果交给浏览器,所以用 PHP 处理用户登陆、用户权限等问题是安全可靠的。

    PHP 与 HTML

    实际编写的时候,通常采用的方式是建立扩展名为 php 的文件(网页文件本质上是文本文件)。编写 php 代码和编写 html 代码并没有多少区别,而最方便的地方在于,在一个 php 文件中,两种代码是可以混编的。

    规则:php 代码需要包含在 <?php ... ?> 标签中,就像这样:

    <?php
    
        // code goes here
    
    ?>

    提示:这是一个 php 和 html 混编的较为生动的例子。

    <?php
    
        if ($var == true):
    
    ?>
    
    <html id="ie6">
    
    <?php
    
        else:
    
    ?>
    
    <html id="ie8">
    
    <?php
    
        endif;
    
    ?>

    这里的意思是,如果 php 中的变量 $var 的值为 true,则放置一个标签,否则放置另一个标签。PHP 的 if 语句可以像上面那样写,也可以写成C/C++风格的:

    <?php
    
        if ($var == true) {
            // do something
        } else {
            // do other things
        }
    
    ?>

    关于 PHP 中的操作符

    PHP 采用的操作符和 C/C++ 是类似的,例如用 = 表示赋值,== 表示相等性比较,以及 < 和 > (小于、大于)比较符、! 取反、&&逻辑与、||逻辑或等。当然,也支持 +-*/ 等数学表达式的运算。

    关于 PHP 中的变量

    PHP 中变量的命名一律以符号 $ 开头,可以使用下划线,例如 $is_logged_in 就是一个表意清晰的变量名。和大多数编程语言不同,PHP 中变量没有类型的概念,而且不用声明就可以直接使用。虽然很爽,但是变量多了的时候也容易混乱,这一点需要特别注意。

    关于 PHP 中的语句

    这一点 PHP 和许多其他常见的编程语言很类似,也可以用 if...else 选择语句(之前已经见过了),PHP 还包括 while 循环、foreach 循环等,以后遇到了会详细介绍。

    MySQL 基础

    使用 MySQL 数据库是存储数据的一种方法,MySQL 需要和 PHP 配合来完成对数据库的查询(这里术语“查询”包括写入、更新、读取等)操作。利用 MySQL,你可以创建许多数据库(database),每个数据库可以包含多个表(table),而每个表包含若干字段。为了高效,一般会采取分类维护多个表的方式,而不是把所有数据都储存在同一个表中。

    MySQL 需要服务器支持。使用的第一步是建立一个数据库,可以用相应的图形化工具(例如 phpMyAdmin)来建立数据库,也可以在终端直接使用下列 SQL 语句来创建一个名为 database_name 的数据库:

    CREATE DATABASE database_name;

    创建好数据库之后,需要创建表。可以通过下列 SQL 语句来创建一个名为 table_name 的表:

    USE database_name;
    
    CREATE TABLE table_name (
      first_name varchar(30),
      last_name varchar(30),
    );

    第一句说明在哪个数据库中添加表,第二个说明添加的表的详情。这里我们在表中添加了两个字段,分别叫做 first_name 和 last_name,它们的类型是 varchar(30)。其中 varchar 是一种可变长字符类型,而 30 代表了最大长度。

    其他常见的数据类型如下:

    VARCHAR(100) --可变字符 
    CHARACTER(1) --定长
    INTEGER --整数
    DECIMAL(10, 2) --小数(小数点前后的位数)
    TIMESTAMP --日期和时间
    DATE --日期

    你可能已经看出来了,MySQL 的注释符为 --。你可能觉得没太大用,但是它却是一种稍后要提到的攻击的关键之处。此外,一般字符串都应该使用变长的VARCHAR类型,而非定长的CHARACTER类型,因为后者会占据更多的空间,而这是不必要的。

    使 PHP 和 MySQL 协作

    第一种方式

    现在你已经创建好了 SQL 数据表,并对 PHP 语言有了一个概览。下面我们直奔主题,学习如何对数据表进行查询。

    为了使 PHP 和 MySQL 进行交互,需要为 PHP 提供你的数据库用户名、密码、数据库名和数据表名。当然,最重要的,查询操作的 SQL 语句。我们一一来观察是如何实现的。

    <?php
    
        define('DB_HOST', 'localhost');
        define('DB_USER', 'renfei');
        define('DB_PASS', 'root');
        define('DB_NAME', 'database_name');
    
        $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
        $query = "INSERT INTO table_name (column1, column2) VALUES ('value1', 'value2')";
    
        mysqli_query($dbc, $query);
    
    ?>

    下面来解释一下这一坨代码的工作原理。

    • 首先 3~6 行为 PHP 中的 define 语句,作用很明显,把 DB_HOST 定义为 localhost,下面的代码中就可以使用 DB_HOST 来代替 localhost。这样做的好处在于,如果mysqli_connect函数在代码中出现多次,修改参数的时候则只需修改 define语句,非常方便。
    • 然后是一个叫做 mysqli_connect() 的函数,它需要四个变量,分别是主机名、用户名、密码、数据库名。这个函数执行后的返回值传递给变量 $dbc$dbc 包含了一次数据库连接。注意,这个变量名是任意的,并不强制要求叫做$dbc
    • 然后,我们把要对数据库执行的操作对应的 SQL 语句以字符串的形式赋给变量 $query。这个变量名也是任意的。应该注意到,这里的 SQL 语句是不以分号结尾的。
    • 最后,我们执行 mysqli_query(); 函数,该函数有两个参数,分别是一个数据库连接,和一个 SQL 查询操作。执行该函数后,相应的查询操作被执行。

    如果把这些代码保存成一个网页,当用户打开网页的时候,如果各项参数正确,它就会完整地运行下去。

    这里的 SQL 语句的含义是向叫做 table_name 的表中插入一行,其中把 colume# 字段的值相应地设置为 value#。这里只设定了两个字段的数值(表中还可以有其他字段;没有显式说明的字段则留空或者使用数据表指定的默认值)。该语句的通用形式为:

    INSERT INTO table_name (column1, column2, ...) VALUES ('value1', 'value2', ...)

    如果你要做的仅仅是执行一个 SQL 语句,那么使用这种模式就可以完成。提醒一下,$dbc 变量往往是重复使用的。

    另一个常用的 SQL 语句就是修改某一行。它的形式为:

    UPDATE table_name SET
    column1 = 'preferred_value1',
    column2 = 'preferred_value2',
    ...,
    WHERE id = '$id'

    当然,这个语句应该是写到一行的,不过为了清晰我分开来写。它的含义是,修改名为 table_name 的表中字段 id 的值是变量 $id 的值的所有行,把 column1 字段的值设为 preferred_value1,把 column2 字段的值设为 preferred_value2,依此类推。这里我们还看到,值既可以用常量表示,也可以用变量表示。

    注意:会修改所有符合 WHERE 子句限定的条件的行(如果省略 WHERE 子句,就会修改所有行)。WHERE 子句可以设定多个条件,也可以使用比较运算符。例如:

    WHERE age > 20 AND gender = 'male'
    WHERE is_admin = 'true' OR id = '$id'

    (如果你想问 AND 和 OR 为什么不是符号 && 和 || 的话,我想提示你,不要把 PHP 语言和 SQL 语言搞混了。这是 SQL 语言,而我只说过 PHP 语言和 C/C++ 有些类似)。

    下面介绍其他 SQL 语句。

    -删除table_name表中的所有行
    DELETE FROM table_name
    
    --删除table_name表中email字段为david@example.com的所有行
    DELETE FROM table_name WHERE email = 'david@example.com'
    
    --删除table_name
    DROP TABLE table_name
    
    --删除table_name表中的score字段
    ALTER TABLE table_name DROP COLUMN score
    
    --table_name表添加一个叫age的字段,类型为 INTEGER
    ALTER TABLE table_name ADD COLUMN age INTEGER

    可见,第一种方式的本质就是编写一条 SQL 语句,然后通过 PHP 来执行它。下面,我们来看第二种方式。

    第二种方式

    有时,我们不满足于让服务器去执行一条 SQL 语句。我们会需要从数据库中查询信息,然后把得到的信息储存起来(其实就是储存在变量中)。这样,我们需要一些额外的工作。先看一坨代码:

    <?php
    
        $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
        $query = "SELECT * FROM table_name WHERE problem_id='$id'";
        $result = mysqli_query($dbc, $query);
        $row = mysqli_fetch_array($result);
        $problem_title = $row['problem_title'];
    
    ?>

    这里我们省略了 define 语句。

    这一坨代码和上一坨的主要区别是,我们使用了 mysqli_query() 函数的返回值,把它保存到 $result 变量中。这个 $result 变量里边保存的即为执行 SELECT 语句的返回结果。

    解释一下 SELECT 语句,它的作用是选取 table_name 表中符合 WHERE 子句条件的所有行。上面的语句会选定每一行的所有字段(通配符说明了这一点),并且把这些信息全部储存到变量 $result中。

    然后,用变量 $row 储存 mysqli_fetch_array() 函数的返回值。$row 这个变量非常神奇,$row['column_name'] 这个事儿包含的内容正是刚才选定的行的 column_name 字段的值(事实上,$row 正是一个数组)。这里,我们把它赋给了 $problem_title 变量。

    到这里你应该问一个问题:如果满足 WHERE 子句条件的有不止一行的话怎么办?要解答这个问题,需要稍微细致的讲解一下 $row 这个事儿。如果满足条件的只有一行,那么使用 $row = mysqli_fetch_array($result) 自然会把这唯一的一行信息储存到 $row 中。如果有很多行,那么第一次使用 $row = mysqli_fetch_array($result) 会把第一行的信息储存到 $row 中,而第二次使用 $row = mysqli_fetch_array($result) 会把第二行的信息储存到 $row 中。如果这时没有下一行了,再次调用的话 $row 会储存逻辑假(false 或 0)。类似,如果符合 WHERE 子句条件的一行都没有,那么执行后 $row 直接存储逻辑假。

    最后补充一点刚才没有提到的。如果不需要所有字段的数据,可以只选择需要的字段。方法是把原来 SQL 语句中的通配符换成字段名称。例如:

    SELECT problem_name, problem_type FROM table_name WHERE problem_id='$id'

    while 循环在 PHP 中的应用举例

    如果我们要把一个数据库的许多行信息都展示在网页中,那么需要用到 while 循环和上面的第二种方式。代码如下:

    <?php
    
        $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
        $query = "SELECT user_id FROM database_name ORDER BY user_id ASC";
        $result = mysqli_query($dbc, $query);
    
        while ($row = mysqli_fetch_array($result)) {
            $user_id = $row['user_id'];
            echo "<tr>";
            echo "<td>".$user_id."</td>";
            echo "</tr>";
        }
    
    ?>

    如果有一定编程基础的话上面的代码很容易看懂。上面新出现了三种用法,说明如下:

    • SELECT 语句可以附加一个 ORDER BY 子句,用来控制顺序。例如这里是按照字段 user_id 升序排列。下面的例子会先按照 user_rank 降序排列,user_rank 相同时按照 user_id 升序排列:
    SELECT user_id, user_rank FROM table_name ORDER BY user_rank DESC, user_id ASC
    • 关于 PHP 中的 echo 语句,它可以用来生成文本,类似于 C 中的 printf() 函数。这里利用它直接生成 HTML 代码。它的用法参考例子就可以了。
    • 关于符号 . 的用法,它的作用是连接字符串(和变量),往往和 echo 配合使用,用法参考示例。

    从表单获取信息

    概述

    这一部分我们演示如何构建一个表单,使用户填写这个表单并把内容储存到数据库。这一技术是用户注册系统和用户互动的基础。

    要实现这个功能,需要 HTML 和 PHP 配合完成。HTML 负责表单,而 PHP 负责获取信息并使用 SQL 查询储存信息。首先来看 HTML 部分(就是普通的表单):

    <form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" /><br/>
        <label for="info">信息:</label>
        <input type="text" id="info" name="info" /><br/>
    
        <input type="submit" value="Submit" name="submit"/>
    </form>

    属于 HTML 部分的不再解释了,说一说新鲜的。这里的 action 属性后面的 $_SERVER['PHP_SELF'](严格地说,$_SERVER),是 PHP 的一个超级全局变量,内容是当前页面的相对路径,例如 signup.php。这个 action 属性的含义是指定用户填写的信息在哪里被处理,这里是在当前页面处理。一般的做法都是将负责处理这部分信息的 PHP 代码和 HTML 代码放在同一页面内。

    下面来看一下相应的 PHP 处理部分的代码:

    <?php
    
        $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    
        if (isset($_POST['submit'])) {
            $user = $_POST['username'];
            $info = $_POST['info'];
            $query = "INSERT INTO table_name (tb_user, tb_info) VALUES ('$user', '$info')";
            mysqli_query($dbc, $query);	
            echo "<p>提交成功</p>";
        }
    
        mysqli_close($dbc);
    
    ?>

    首先仍然是建立数据库连接。当用户点击 sumbit 按钮后,表单的内容会被储存在 PHP 中 $_POST 超级全局变量内,这个超级全局变量仍然是一个数组。isset() 函数用来检查变量是否被设置,只有用户点击 submit 后 isset($_POST['submit']) 才返回真,所以不用担心,首次加载表单(那时用户还没有填写任何内容)是不会执行这部分 PHP 代码的,只有用户提交之后才会执行。用户填写的具体内容可以用 $_POST['name'] 来获取。这里的 name 对应的是 HTML 中 name 属性的内容。这一段程序把用户填写的内容赋给变量,然后执行插入到数据库的操作。

    这里新出现了一个内容,就是 mysqli_close() 函数,它的作用是关闭数据库连接。当我们不再需要这个连接的时候,及时关闭是一个好主意。

    需要注意的是,这仅仅是最简单的代码,而且实际上是不完善的。如果要真正投入使用,我们需要使它更健壮一些。下面逐一讨论这些内容。

    检查用户输入是否合法

    如果用户根本没有填写表单,就直接点击提交按钮,会发生什么?在上面的实例中,PHP 依然会乖乖地把空内容插入,而这显然是垃圾信息,不是我们需要的。所以,需要在插入前检查被插入的变量是否为空。例如:

    <?php
    
        if (!empty($user) && !empty($info)) {
            // 插入操作
        }
    
    ?>

    这里出现了 empty() 函数,用于检查内容是否为空。注意这里使用 isset() 是无效的,因为 isset() 检查的是是否“被设置”,而被设置为空也属于被设置。

    错误提示

    用户输入有误时,上面的改进除了不执行SQL查询,并没有多少直观上的变化。用户不会收到任何信息表明他们的填写是不合适的。所以我们要在这时产生一些提示,引导用户正确填写表单。

    <?php
    
        if (!empty($user) && !empty($info)) {
            // 插入操作
        } else {
            echo "请填写全部内容后再提交";
        }
    
    ?>

    防范 SQL 注入攻击

    我们执行的 SQL语句中包含变量,执行的时候会直接把变量内容替换进去。而如果攻击者在输入框中输入一些危险的字符(通常包含 SQL 注释符 --,以及其他预先精心设置的内容),就可能导致该次 SQL 查询完全被改写成攻击者需要的意思。为了防范这种攻击,我们需要对可能存在的危险字符进行过滤和转义,较为便捷的方法是使用两个函数。改进后的部分如下:

    <?php
    
        $user = mysqli_real_escape_string($dbc, trim($_POST['username']));
        $info = mysqli_real_escape_string($dbc, trim($_POST['info']));
    
    ?>

    粘性表单

    如果用户第一次填写失败,他们希望能保留已经填写好的内容,只做些修改就好了。这需要使用粘性表单技术。要实现,只需要稍稍改动 HTML 表单部分的代码:

    <label for="username">用户名:</label>
    <input type="text" id="username" name="user" value="<?php if (!empty($user)) echo $user; ?>" >

    显而易见,如果用户填写后因为某些原因没有提交而是回到了这个表单,并且之前填写了 user 字段的内容,那么此时 $user 变量已经被赋值了。那么就会在 HTML 表单显示这些内容,避免用户再次输入。

    构造一个注册页面

    虽然上面说了很多,但是仅仅满足了我们最基本的输入要求。许多时候我们需要更为复杂的功能。举例来说,要写一个注册页面,必须检查用户名是否重复,还要对密码采取某种技术加密以保证安全。

    检查用户是否重复

    基本原理就是,根据需要判重的字段(例如用户名)去数据库搜索。如果发现结果则用户名重复,如果没有找到则允许注册。需要一个新函数 mysqli_num_rows(),返回 SELECT 语句得到的行数,根据其是否等于 0 进行判断。

    <?php  
    
        $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);    
        $query = "SELECT * FROM table_name WHERE user_name = '$user'";
        $data = mysqli_query($dbc, $query);
        if (mysqli_num_rows($data) == 0) {
            // 把内容插入数据库
            echo "注册成功";
            mysqli_close($dbc);
            exit();
        } else {
            echo "用户名已被占用,请重新选择用户名";
            $user = "";
        }
    
    ?>

    把 $user 清空是为了配合粘性表单。

    需要说明的是 exit(); 函数,它会立刻终止 PHP 的运行。因为用户已经注册成功,没有必要执行后面的任何代码,所以使用这个函数。写自己的程序的时候可以亲自试验是否需要这一行、PHP 和 HTML 在 php 中的顺序不同有何影响。我通常的做法是把 PHP 代码放在前面,HTML 代码放在后面。

    对密码进行加密存储

    明文存储密码是对用户很不负责的,不仅数据库管理员可以看到密码,一旦数据库泄漏,密码就会被公开。所以,我们应该加密存储用户密码。在 PHP 中,可以使用 sha1() 函数进行加密(sha 即 secure hash algorithm 的首字母缩写),它是一种不可逆的加密,加密后会生成定长的一段字符串,并且是无法由这段字符串还原原密码的。

    加密的原理是,用户输入密码后,利用 PHP 把 hash 过的密码储存在数据库中。用户登陆的时候,把用户输入的密码进行 hash 运算,之后和数据库中的进行比对。

    使用方法如下:

    sha1($password)

    只注册没有用,必须添加登录功能。登录功能可以使用 Cookie 来实现。这里假定你已经了解 Cookie 的基础知识,只说如何实现。

    <?php
        setcookie('user_type', $user_type, time() + (60 * 60 * 24 * 30));
    ?>

    上面的代码用来设置 Cookie,其中函数的第一个参数为 Cookie 名称,第二个参数为数值(这里用一个变量传递),第三个参数为过期时间,单位秒。示例为一个月。

    可以用设置多个 Cookie 来存储许多内容,例如用户 ID、用户组(管理员还是普通用户)等。

    用户登陆后,我们可以设置一个 Cookie 来存储登录信息(即哪个用户登陆的),然后通过检查这个 Cookie 来设定相应功能。

    <?php
        if (isset($_COOKIE['user_type'])) {
            $user_type = $_COOKIE['user_type'];
        }
    ?>

    要删除 Cookie,只需要把过期时间设定在过去。

    <?php
    
        setcookie('user_type', '', time() - 3600);
    
    ?>

    不要问我为什么设定在过去一个小时,设定几个小时都没问题。

    设置 Cookie 有其潜在的危险。由于 Cookie 是保存在用户本地的,所以用户完全可以通过篡改 Cookie 来达到他们的目的。所以,把 Cookie 的值设置得“通俗易懂”不是一个好主意。例如,我们要用 Cookie 来保存登陆的用户名,如果单纯把这个用户名存入 Cookie,那么攻击者会很容易通过修改成他人的用户名来伪造 Cookie 登陆。所以,我们需要其他的手段来防止这一点。

    我的做法是,用户注册的时候,把用户名按一定手段进行变换,然后使用 SHA() 函数加密生成一个用户密钥,然后把这个密钥储存进数据库。登陆时,再把这个密钥存储到 Cookie 中,通过检查 Cookie 中的密钥和数据库中用户密钥的匹配情况判定是哪位用户登录。这样,只要你的用户名变换方法不泄露,攻击者就很难按他们的想法伪造 Cookie。

    使用 GET 方法

    在网页间传递信息除了刚才介绍的 POST 方法外,还有 GET 方法。GET 方法是通过 URL 来完成信息传递的。例如,构造下列网址:

    http://www.renfei.org/index.php?id=2
    

    网址最后有 ?id=2 标记。这个信息会储存在$_GET['id'] 这个超级全局变量中,并且可以在 PHP 中使用:

    <?php
    
        if (isset($_GET['id'])) {
            $id = $_GET['id'];
            // code goes there
        }
    
    ?>

    这个例子中我们把 2 赋给了变量 $id。当然,也可以构造这样的网址:

    http://www.renfei.org/index.php?id=2&message=10
    

    除了多一个可以使用的 $_GET['message'] 以外没有任何不同。

    这个特性的用处之一就是可以根据网址的不同,配合数据库查询,返回不同的网页内容。例如我做的在线问答系统,就是根据 problem_id 来返回不同题目的。

    注意,由于 GET 方法的数值是不可靠的(用户可以手动构造 URL 来传递他们想要的参数),所以应该仅仅用它来做一些无关痛痒的事情(例如显示不同的页面内容)。这里我并没有强调 GET 方法的数值是“透明”的:虽然 POST 方法的数值不会显示在 URL 中,但是它还是会通过 HTTP Header 发送到服务器,用许多插件和小工具都可以查看 HTTP Header 信息。

    另外,如果你的表单是用来上传文件的,那么估计你会更喜欢 POST 方法:因为  GET 方法得到的 URL 可能会很长,甚至超过浏览器的限制!

    使用模板

    最后一部分,来讲一下使用模板构造一个网站。

    事实上,网站的每个页面中,有许多部分是完全相同的,例如数据库连接常量(就是那些define语句)以及每一页的 header 和 footer 部分等。这样,我们没必要在每一页内写相同的代码。除了麻烦和浪费空间以外,还有一点很重要的原因,就是修改的时候工作量很大。

    PHP 中 require_once 语句作用就是把其他文件的内容插入此处。例如,我们可以创建一个define.php,把define语句全部写到里面,并在每个页面顶部添加如下语句:

    <?php
    
        require_once "define.php";
    
    ?>

    这样一来,会把 define.php 中的内容插入当前位置。同理,我们可以建立一个 header.php 和 footer.php,写好页面的头部、底部之后在每个其他页面导入就可以了。

    PHP的错误处理

    分级的错误信息

    最后来讲一下 PHP 的错误处理机制。如果你写了有错误的 PHP 代码,那么运行的时候系统会自动生成一些错误提示信息并且打印到屏幕上,以提醒用户修复。通常,这些错误信息是分级的。首先,是 notice。如果屏幕出现了 notice: (...) 的提示说明你有需要修复的小问题(你没有完全按照规则进行),不过问题不大,代码还是会继续执行完毕。而 warning 则更严重一些,如果出现 warning,你可能需要思考一下你是否真的知道自己在做什么,并作出修改。但是,程序仍然会运行。如果出现了 error,那么 PHP 是在跟你说:你是个白痴;这种代码无法执行,程序的运行会中止。

    在写 PHP 程序的时候,我们需要这些错误提示来帮助我们改正错误,但是当产品发布的时候,开发人员往往倾向于隐藏错误提示:用户收到这些信息是很让人恼火的,而且,让他人知道你的代码有什么漏洞总归不是一个好主意,因为这可能被某些图谋不轨的攻击者加以利用。

    Suppression Operator

    有时,为了代码的简洁性考虑我们可能会故意犯一些无关痛痒的小错误。例如,如果 $_GET 中的某一个元素不一定总会被提交到 PHP,那么理论上应该使用 isset() 函数来进行检测。但是,如果你觉得到处使用这个函数太麻烦了,可以省略 isset() 函数而直接使用这个元素,只不过如果它没有被设置的话会返回一个 notice 错误信息(类似于 C/C++ 中的变量未声明)。这时,为了忽略这一条信息,可以使用错误抑制操作符 @。例如:

    <?php
    
        if (@$_GET['opt']) {
            // code goes here...
        }
    
    ?>

    其他提示

    • 尽管没有特别强调,但是有几个函数是需要灵活掌握和使用的,例如 exit()。它可以立刻结束 PHP 程序的运行。例如,有的页面需要一定用户权限才能访问,则可以把验证权限的代码放在页面顶端,如果验证失败则显示错误信息并调用 exit() 函数。
    • 当一个 SQL 连接的使命完成后,不要忘了用 mysqli_close() 关闭它。
    • 设计 SQL 数据库的结构是一件非常重要的事情,设计的原则是高效且便于查询。一旦你的数据库充满各种信息,再想更改它的结构就会变得有些困难。
    • SQL 的知识这里介绍得不多。它有许多特性,比如默认值、主键等。默认值的意思是如果不设定,那么该字段采用默认值;主键则规定该字段每行是不能重复的。默认值除了固定字符以外,还可以设定为时间,甚至自增。例如,要建立一个用户数据库,为每个用户分配一个唯一 ID,则可以把数据库中的 ID 字段设为 AUTO INCREMENT,这样每次不用手工维护这个字段,只要新增一行,这个字段的数值就增 1(默认从 1 开始),很方便。 一般会把这种 ID 字段设为主键。
    • 本来打算简要介绍一下 PHP 和 MySQL 的,但是一写就是 7000 字。即使如此,本文介绍的所有特性也仅仅是构建一个动态网站最基本的知识,而且许多非重要的知识并没有介绍。你应该通过书籍更深入地学习。
    展开全文
  • 动平衡仪制作方法总结

    千次阅读 2017-01-27 20:34:54
    动平衡做不好, 再好的涡喷也是废铁。转几下就废了。 下面总结了一下我自己最近在网上找到的方案。 省去大家再找解决方法了。 第一种方案: 配重多次换方位试错法,手摸也可以。 最简单. 适合DIY如果有测试...

    由于最近自己迷上了涡喷。 总想自己做一台。但是做涡喷容易,把涡喷调整好可不容易。 其中最难弄的是动平衡。 动平衡做不好, 再好的涡喷也是块废铁。转几下就废了。
    下面总结了一下我自己最近在网上找到的方案。 省去大家再找解决方法了。

    第一种方案: 配重多次换方位试错法,手摸也可以。
    最简单. 适合DIY如果有测试震动量(数字化的最好,指针的也行)手机上的振动测试仪也可以.

    转速测试仪, (如果没有转速测试仪可以固定油门定量转速.)。
    具体的平衡方法,
    第一步: 根据不加配重时的振动量添加合适的配重, 开动电机, 看看振动量是增大还是减小.
    第二步: 如果振动增大,说明配重块的位置跟不平衡的相位角度相差不大. 记下此点的相位和振动量. 如果振动变小. 说明……此处省略很多废话.
    第三步: 改变一下配重的相位. 重复第二步. 直至振动最小. 此时的相位就是不平衡量的对角.
    第四步:消掉部分多余量。改变配重重新开始。
    总结: 此种方法平衡的好坏跟振动量测试的灵敏度有关.和最高转速有关.当然还有耐心和重复次数有关。
    具体操作视频可以看视频。
    http://v.youku.com/v_show/id_XNTUwNDE1NjI4.html

    第二种方案来自网友:
    这里写图片描述

    这里写图片描述

    总的来说就是测量出最大振动的地方在那个位置。 尤其是高速的时候。 微小的不平衡。也是灾难性的后果。
    振动传感器的选用。
    电涡流, 价格较贵。精度较高 , 无接触。
    电阻应变片,价格便宜。精度可以。有接触。 只要能达到较高的测量精度都可以。
    转速传感器的选用。
    激光漫反射传感器,无接触
    光电编码传感器,无接触。
    激光对射传感器, 无接触。
    霍尔传感器,无接触。 需要在轴上加个磁片。

    当然下面还有我自己的方案。 只需要一个传感器,就能测试出转速和震动相位。缺点就是对传感器的采样频率要求特别高。机器的转速越高,对传感器的灵敏度要求就越高。
    如果设备转速高达3万转每分钟的时候。
    =500转/秒
    =500hz
    那么单片机的采样频率一定要高于此值方可。或者有软件算法可以重复插补相位振动数据也行,就是算法较复杂。

    方法如下。
    这里写图片描述

    这两天又看到了一个分析软件叫
    ADS(advanced vibration analysis)是安装于电脑端的一款强大的数据后处理分析软件,可以用于简历数据库、路径管理,并提供高级诊断工具(时域波形分析、FFT频谱分析、波德图、瀑布图分析、轴心轨迹等)。软件共有3个级别,完全满足了用户的所以分析需求。

    有空研究一下。

    展开全文
  • 各种DLL制作方法

    千次阅读 2007-05-22 15:54:00
    创建MFC 的常规DLL(设工程名为MyDLL1)(详工程F:/VcSample/DLL示例/DLL动态联接库之构共享内存)1. 新建工程MFC AppWizard(dll),选第二项- Regular DLL using shared MFC DLL(选第一项:Regular DLL with MFC ...
    一.创建MFC 的常规DLL(设工程名为MyDLL1)(详工程F:/VcSample/DLL示例/DLL动态联接库之构共享内存)
    1.      新建工程 MFC AppWizard(dll),选第二项- Regular DLL using shared MFC DLL
    (选第一项:Regular DLL with MFC statically linked,同第二项的区别是静态联接MFC)
    2.      则系统自动建立文件 :MyDLL1.h、MyDLL1.cpp、MyDLL1.def等文件.
     文件MyDLL1.h内容: (略写)
    MyDLL1.cpp内容(略写)
    class CMyDLL1 App : public CWinApp
    {public:
    CMyDLL1App();
        DECLARE_MESSAGE_MAP()
    };
    //+1
    //+3
    BEGIN_MESSAGE_MAP(CMyDLL1App, CWinApp)
    END_MESSAGE_MAP()
    CMyDLL1App:: CMyDLL1App()
    {     }
    //+2
    CMyDLL1App theApp;
    文件 MyDLL1.def 内容
     
    LIBRARY       " MyDLL1"
    DESCRIPTION 'MyDLL1 Windows
                    Dynamic Link Library'
    EXPORTS
        ; 外部函数出口写在这里
    在 .def文件中,每行前加上分号表示
    注释内容
    3.      +1的位置上加入自定义的函数
    如 :#define DLLEXPORT extern "C" _declspec(dllexport)
             DLLEXPORT int WINAPI GetCount();       //DLL中定义的函数GetCount()
             DLLEXPORT void WINAPI AddCount();     // DLL中定义的函数AddCount()
    4.      +2的地方加入在头文件定义的函数的函数体代码内容
    int WINAPI GetCount()
    {return iCount; }     //返回计数值
    void WINAPI AddCount()
    { iCount++;}       //增加计数值
    5.      若要求支持 DLL中某些变量为全局共享变量(任何进程改动该变量都会影响其它进程调用的值),则应在+3的位置加入代码段
    #pragma data_seg("PANTO")       //字符参数”PANTO“详文件DLLDemo.def中的定义,
    //表示这#... #段之间的变量为共享内存变量
         int iCount=0;            //初始化共享类成员变量为0
    #pragma data_seg()             //即变量iCount不管哪个进程何时调用并更改后,其以后调//用均采用最后一次更改的值
    6.      更改 MyDLL1.def的内容如下:
    ; DLLDemo.def :在这个文件中为这个DLL声明和定义模块参数.
    LIBRARY       "DLLDemo"
    DESCRIPTION 'DLLDemo Windows 动态联接库'
    EXPORTS
        ; 在这里写输出定义的函数及分配序号
        ;给函数指定一个顺序号以便可以用函数GetProcAddress()调用这个数字
          GetCount @1   
         AddCount @2
    ;声明PANTO段为共享段,详执行文件DLLDemo.cpp中的变量定义及初始化
    SECTIONS
    PANTO SHARED    ;PANTO仅表示一标签不是关键字,表示在PANTO块中为共享变量
    7.      在 EXE工程中要调用这个DLL库中的两个函数应:
    1>在要调用的函数相关文件中 #include " MyDLL1.h"
    2>在调用时,不必寻找DLL中函数入口地址,如:
    txt.Format("从DLL中调用共享内存变量iCount当前值=%d", GetCount()); (由于在EXE工程//中已包含了DLL工程中的主头文件,这里就不必写判断函数入口地址的代码了)
    pDC->DrawText(txt,&ClientRect,DT_BOTTOM);              //往视图上写字符
    AddCount();   //调用DLL中的函数,如使公用变量iCount值+1。
     
    二.创建MFC扩展DLL工程(设工程名为MyDLL2) (详工程 F:/VcSample/DLL示例/VC动态联接库示例/DYNLINK3)
    1.      MFC AppWizard(dll),选第三项-MFC Extension DLL(using shared MFC DLL)
    2.      则系统自动建立文件 :MyDLL2.cpp、MyDLL2.def等文件.
     文件MyDLL2.cpp内容: (略写)
    static AFX_EXTENSION_MODULE MyDLL2DLL = { NULL, NULL };
    extern "C"  int  APIENTRY
    DllMain(HINSTANCE hInstance, DWORD dwReason,
    LPVOID lpReserved)
    {//删除这些若你使用lpReserved
    UNREFERENCED_PARAMETER(lpReserved);
    if (dwReason == DLL_PROCESS_ATTACH)
    {    TRACE0("MYDLL2.DLL 正初始化!/n");
         if (!AfxInitExtensionModule(MyDLL2DLL, hInstance))
             return 0;
         new CDynLinkLibrary(MyDLL2DLL);
    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {    TRACE0("MYDLL2.DLL终止!/n");
         AfxTermExtensionModule(MyDLL2DLL);
    }
    return 1;    // 成功
    }
     
    文件 MyDLL2.def 内容
    LIBRARY      " MyDLL2"
    DESCRIPTION 'MyDLL2 Windows Dynamic Link Library'
    EXPORTS
    ; 外部函数出口写在这里
    3.      在 DLL工程中任意加入MFC类或继承类(可为对话框类等等),若增加的类要求在
    EXE工程中可以象在本工程一样的引用方式,则定义的类名前应加上 AFX_EXT_CLASS关键字:如class AFX_EXT_CLASS CDllDialog : public Cdialog {…};
    4.      将生成的 MyDLL2.dll及MyDLL2.lib文件拷贝到EXE工程目录下,并将MyDLL2.lib文件包含到EXE工程的资源中。
    5.      若在 EXE文件中要用到DLL中的D11Dialog对话框类,则还应在相应头文件include
    #include “D11Dialog.h”     //可将此文件从DLL工程目录复制到EXE工程目录
    6.      在调用时 DLL中函数或引用DLL中类时,无需再查找DLL入口地址,可象本工程中的
    其它函数或类一样直接使用 .
    如 : CDllDialog dll; //CDllDialog类在动态联接库文件DYNLINK3.DLL中定义的
       dll.DoModal();
     
    三.寻址方式调用DLL(DLL工程中的任何文件不包含在EXE工程中)
    1. 新建一Win32 Dynamic-Link Library类型工程,选项二-A mpty DLL project
    2.则生成的DLL工程中不含有任何代码文件,这时可自行加入以下代码,若文件名为 MyDLL3.c (把文件包含工程中)
    #include <windows.h>
    #include <string.h>
    #include <stdio.h>
    #define DllExport __declspec(dllexport)
    ///
    //定义一个可输出的返回值为long类型的函数Multiply2Longs
    DllExport long Multiply2Longs(LONG lNum1, LONG lNum2)
    {    return lNum1 * lNum2;
    }
    //定义一个可输出的返回值为char*的函数GetStr
    DllExport char* GetStr(char* str1)
    {static char mychar[81];
     char* CHARlink;
     strcpy(mychar,"static char mychar[81]=DLL中定义的的字符串");
     CHARlink=mychar;
     strcat(CHARlink,"(加EXE传入DLL中的char *str1=)");
     strcat(CHARlink,str1);
     return CHARlink;
    }
    3.若要使用DLL中的GetStr函数,则先应把DLL文件复制到EXE工程目录下
    void CMainWnd::OnGetStr()
    { CHAR *CResult;
     HINSTANCE hModule;
    typedef CHAR *(MyType)(CHAR *);
     MyType* pfunMyType =NULL; //*
    VERIFY(hModule = ::LoadLibrary("MyDLL3.dll")); //导入文件MyDLL3.DLL
    try{  //得到DLL中函数GetStr()的地址
       VERIFY(
          pfunMyType =
         (MyType*)::GetProcAddress(
                (HMODULE) hModule, "GetStr")
       );
        CHAR *ch;
        ch="EXE传过去的字符串";
       CResult = (*pfunMyType)(ch);
       CString sMsg;    //显示测试结果
       sMsg.Format("调用DLL文件MyDLL3.dll中函数:/n DllExport CHAR *GetStr(CHAR * str1)/n{char *mychar;/nmychar=/'/'......./'/';/n........../n return mychar;(以下为DLL中返回字符串内容:)/n}/n :/n/n/n %s",
                    CResult);
     ::MessageBeep(MB_OK);
       MessageBox(sMsg, "GetStr()", MB_OK | MB_ICONINFORMATION);
     }
     catch(...)// 释放DLL
     {MessageBox("调用DLL中函数失败","错误",MB_OK|MB_ICONINFORMATION);}
       FreeLibrary(hModule);
     }
    注:所有的同EXE无任何关联的DLL都可以用上述方法调用,但必须保证函数名和参数准确)
    四.静态调用 (设工程名为MyDLL4)
    1.同第一种新建工程 MFC AppWizard(dll),选第二项- Regular DLL using shared MFC DLL
    2.可删除MyDLL4.def文件不用,将系统生成的MyDLL4.h 及MyDLL4.cpp文件更改如下:
    MyDLL4.h内容
    MyDLL4.cpp内容
    define DllExport __declspec(dllexport)
    //定义DLL中可导出的全局函数
    extern "C" DllExport FLOAT MyDLLSub(
    FLOAT f1, FLOAT f2);
    class CMyDLL4App : public CWinApp
    {
    public:
        CMyDLL4App();
    };
     
     
    #include "stdafx.h"
    #include "resource.h"   // main symbols
    #include "MyDLL4.h"
    ///
    CMyDLL4App:: MyDLLSub ()
    { }
    extern "C" DllExport FLOAT MyDLLSub (FLOAT f1, FLOAT f2)
    {   return f1 * f2;
    }
    ///
    CMyDLL4App MyDll;
    3.将编绎的MyDLL4.lib文件复制到EXE工程目录下并将它包含含到EXE工程资源中。
     1>在EXE主头文件中加入行:
    #define DllImport __declspec(dllimport)
    extern "C" DllImport FLOAT MyDLLSub(FLOAT f1, FLOAT f2);
     2>在要调用DLL中的函数MyDLLSub的模块文件中
    void CMainWnd::OnFileCallRegularDll()
    { FLOAT f1 = 25.3f;
       FLOAT f2 = 13.5f;
       FLOAT fResult = MyDLLSub(f1, f2);   //不用寻找DLL入口地址
       CString sMsg;
     sMsg.Format("调用DLL文件dynlink2.dll中函数:/n extern /'C/'/' DllExport FLOAT MyDLLSub(FLOAT f1, FLOAT f2)/n {return f1 * f2;}/n %0.2f * %0.2f = %0.2f",
                   f1, f2, fResult);       //显示测试结果
       ::MessageBeep(MB_OK); 
       MessageBox(sMsg, "从MyDLL4.dll中调用函数MyDLLSub()", MB_OK |
    MB_ICONINFORMATION);
    }
     
    五.纯资源DLL的编制
    1.新建工程: 创建一个WIN32 DLL(Win32 Dynamic-Link Library)工程,不是MFC的DLL
    2. 资源的DLL就是只包含资源的DLL,例如:图标,位图,字符串,声音,视频,对话框等。使用纯资源DLL可以节约可执行文件的大小,可以被所有的应用程序所共享,从而提高系统性能。纯资源DLL的编写比普通的DLL要简单的多,工程工作空间并没有资源TAB栏,需在向工程中插入资源后系统会提示产生.RC文件,或创建一个资源文件 *.RC,添加到资源DLL的工程中去。然后添加一个初始化DLL的原文件。
    如系统默认提供了以下几种:
    Accelerator :加速键             Dialog:      对话框
    Bitmap :     位图               HTML:        超文本文件
    Cursor :     光标               Icon:        图标
    Menu :       菜单               String Table:字符串表
    Toolbar :    工具条             Version:     版本号
    还可以自行导入任何类型的资源(资源名称自己定)如:
    AVI :动画                    WAV:声音文件             DATA:二进制文件 等等
    注意:加入这些自定义资源后,在工作空间资源TAB上显示的资源名有双引号括起
    3.示例代码
    #include <windows.h>
    extern "C"
    BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID )
    {    return 1;}
    这是纯资源DLL所必须需的代码,保存这个文件为*.CPP。编译这个资源DLL。
    在应用程序显示的调用这个DLL,使用LoadLibrary函数装入资源DLL,FindResource和LoadResource来装入各种资源,或者使用下列的特定的资源装入函数:
    FormatMessage            LoadAccelerators            LoadBitmap
    LoadCursor               LoadIcon                    LoadMenu
    LoadString
     
    4. 在EXE工程中调用纯资源DLL的方法:
    当资源使用结束,你的应用程序须调用FreeLibrary函数来释放资源。
    下面就讲一下如何调用编写好的资源DLL
    首先在应用程序中声明一个DLL的句柄,HINSTANCE m_hLibrary;在OnCreate( )
    函数中调用LoadLirbrary( ),在OnDestory( )中调用FreeLibrary()。
     
     
     
    六.自定义LIB类库的DLL制作过程:
    1.在StdAfx.h文件中加入定义
        #ifndef _CJX_EXT_CLASS
         #ifdef _AFXDLL
              #define _CJX_EXT_CLASS     AFX_CLASS_EXPORT //总时输出本类库文件...
         #else
              #define _CJX_EXT_CLASS     //不使用静态联编辑.
         #endif
         #endif // _CJX_EXT_CLASS
    2.在DLL主头文件中加入(主头文件要自己加入)
    //define中的字符串可根据实际进行更改,以符合字意
    #ifndef _CJXLIB_INLINE
    #define _CJXLIB_INLINE inline
    #endif // _AFXCMN_INLINE
    #ifndef _CJX_EXT_CLASS
    #ifdef _AFXDLL
    #define _CJX_EXT_CLASS     AFX_CLASS_EXPORT //总是输出本DLL文件...
    #else
    #define _CJX_EXT_CLASS     // 静态联编DLL文件.
    #endif
    #endif // _CJX_EXT_CLASS
    #ifndef __AFXTEMPL_H__
    #include <afxtempl.h>
    #endif
    #ifndef __AFXPRIV_H__
    #include <afxpriv.h>
    #endif
    //#include <../src/afximpl.h> //屏蔽这一系统产生的这一行
    #define _delete(p){if(p){delete p;p=NULL;}}
    #define _deleteMeta(p){if(p){::DeleteEnhMetaFile(p);p=NULL;}}
     
    #ifndef __MYCOMBOBOXEX_H__ //示例:DLL中有一自定义类CMyComboBox 中的定义#define __MYCOMBOBOXEX_H__
    #include "MyComboBox.h"
    #endif
    //..............
    //以下定义在EXE工程中编绎加载LIB库时出现的编绎信息
    #ifdef _AFXDLL
     #ifdef _DEBUG
       #define _CJX_COMMENT     "MyLibd.lib"
       #define _CJX_MESSAGE     "MyLibd类库将自动联接使用动态联接调试版 MyLibd.dll"
     #else
       #define _CJX_COMMENT     "MyLib.lib"
       #define _CJX_MESSAGE     "MyLib类库将自动联接使用动态联接发行版MyLib.dll"
     #endif // _DEBUG
    #else
     #ifdef _DEBUG
       #define _CJX_COMMENT     "MyLibdStaticd.lib"
       #define _CJX_MESSAGE     "MyLib类库将自动联接使用静态联接调试版MyLibdStaticd.dll"
     #else
       #define _CJX_COMMENT     "MyLibStatic.lib"
       #define _CJX_MESSAGE     "MyLib类库将自动联接使用静态联接发行版MyLibStaticd.dll"
     #endif // _DEBUG
    #endif // _AFXDLL
     
    #pragma comment(lib, _CJX_COMMENT)
    #pragma message(_CJX_MESSAGE)
     
    #endif // __CJLIBRARY_H__
    ///
    3.在DLL主模块文件中加入
    #include "DLL主头文件"
    在DllMain()函数尾加入处理当前打开的是否是正确的DLL版本[可选]
         _GetComCtlVersion(); //函数为自定义函数,用于得到系统COMCTL32.DLL的版本号
         if (_ComCtlVersion < VERSION_IE4)
         {
              CString str = "你调用的COMCTL32.DLL文件版本应在 4.2或更高才可以保证程序正常运行.../n注意!/n/nCopyright ?1998-99 罗伟";
              ::MessageBox(NULL, str, "加载 COMCTL32.DLL失败", MB_ICONSTOP);
             return 0;
         }
         return 1;   // ok
    //
    4.向DLL工程中加入类的定义:如重载CComboBox为CMyComboBox
    1>用向导向工程中加入类 CMyComboBox 生成对应的两文件MyComboBox.h    MyComboBox.CPP
    2>修改头文件的define定义为( 也可不修改,但向导产生的太长)
        #ifndef __MYCOMBOBOXEX_H__
        #define __MYCOMBOBOXEX_H__
    3>修改类名定义为
    class _CJX_EXT_CLASS CMyComboBoxEx : public CComboBox
    {     DECLARE_DYNAMIC(CMyComboBoxEx)
    ...........................
    };
    //可在类体外加入内联函数的函数定义.如:
    _CJXLIB_INLINE CMyComboBoxEx::CMyComboBoxEx()
         { }
    _CJXLIB_INLINE DWORD CMyComboBoxEx::GetExtendedStyle() const
         { ASSERT(::IsWindow(m_hWnd)); return (BOOL) ::SendMessage(m_hWnd, CBEM_GETEXTENDEDSTYLE, 0, 0); }
    _CJXLIB_INLINE DWORD CMyComboBoxEx::SetExtendedStyle(DWORD dwExMask, DWORD dwExStyles)
         { ASSERT(::IsWindow(m_hWnd)); return (DWORD) ::SendMessage(m_hWnd, CBEM_SETEXTENDEDSTYLE, (DWORD) dwExMask, (LPARAM) dwExStyles); }
    4>在类中加入重载或自定义的函数体如:
    int CMyComboBoxEx::InsertItem(int iItem, LPCTSTR lpszItem, int iIndent, int iImage, int iSelectedImage, UINT mask)
    {
         COMBOBOXEXITEM cbItem;
     
         cbItem.mask                   = mask;
         cbItem.iItem             = iItem;
         cbItem.pszText           = (LPSTR)lpszItem;
         cbItem.iImage            = iImage;
         cbItem.iSelectedImage    = iSelectedImage;
         cbItem.iIndent           = iIndent;
         return InsertItem(&cbItem);
    }
    BOOL CMyComboBoxEx::SetItem(const COMBOBOXEXITEM* pCBItem)
    {     ASSERT(::IsWindow(m_hWnd));
         ASSERT(pCBItem != NULL);
         ASSERT(AfxIsValidAddress(pCBItem, sizeof(COMBOBOXEXITEM), FALSE));
         return (int) ::SendMessage(m_hWnd, CBEM_SETITEM, 0, (LPARAM) pCBItem);
    }
    5.在EXE中使用LIB类库的方法
    1>在EXE 工程中的StdAfx.h文件中包含DLL工程主头文件
    #include "..//include//CJLibrary.h" //包含这个文件,可用DLL工程CJLibary动态联接库中的所有类
    2>将.DLL 和.LIB文件复制到EXE 工程目录下或在EXE工程中设置LIB库文件所在目录即可
    3>在要使用DLL 中的类定义的类中定义成员变量
       CMyComboBoxEx * cCombo;
    在函数体中象一般变量一样使用DLL中的类函数
    3>也可以重载DLL中的类如下例
    class CCombo :public CmyComboBoxEx{…..}
     
    展开全文
  • 如何制作GIF表情包,动态GIF怎么做

    千次阅读 2019-05-31 18:23:33
    就想把那一单独截取出来作一个GIF动态图分享给别人,虽然有的播放器有截取的功能,但是有的时间和效果往往是不好的,还有在保存视频之后才能进行操作,其实有更简单的方法,那如何制作GIF表情包,动态GIF怎么做呢...

    在碎片化的阅读时代,我们更愿意选择看视频而不是长篇的文字了解新的知识。有时候看到一段视频中有一段很有意义或者是比较精彩的地方,就想把那一块单独截取出来作一个GIF动态图分享给别人,虽然有的播放器有截取的功能,但是有的时间和效果往往是不好的,还有在保存视频之后才能进行操作,其实有更简单的方法,那如何制作GIF表情包,动态GIF怎么做呢?

    1、网上虽然有很多的GIF制作工具,今天要说的操作过程就十分的简单了,制作过程傻瓜化,几分钟就能整出一个GIF动画来;

    2、打开迅捷GIF制作工具,页面上有启动屏幕录制的功能,即会跳出一个GIF的录制框;

    3、打开要制作GIF的素材,录制框的大小可以自行调整,通过“开始”“停止”控制开关即可;

    4、录制完成后,会自动跳转到编辑的页面,上方一栏可以进行简单的一些操作,比如添加文字和水印;

    5、编辑完成之后,点击导出gif,对制作好的GIF进行保存,同时可以自定义保存位置以及保存名称。

    以上是关于如何制作GIF表情包,动态GIF怎么做的方法,希望可以帮助到你!

    展开全文
  • 简易计算器实验说明硬件设计动态数码管介绍矩阵按键介绍行列扫描线翻转法硬件实物连接软件设计`AC代码:`实验现象 实验说明 利用51单片机和keil uVision4共同完成通过按键来控制8位共阴数码管显示数据,并且通过按键...
  • 该文档提供了一种wincc里面组态流体流动动画的一种方法,利用C脚本实现
  • 最近的项目要求动态光晕的效果。 何谓动态光晕?之前不知道别人怎么称呼这个效果, ...新项目中我是负责游戏特效这一的,操作过程中我觉得这东西蛮不错的,很有感觉。 之前的项目中我是负责整个游戏场景的编码
  • ………………………………………………………… →进入正题← ………………………………………………………… 第一步: 先用手机端给银行卡转一笔钱,一钱以下就行! 下载二维码生成器软件(暂时只支持电脑版本)...
  • 这两天需要将某个磁盘分区用win7自带的bitlocker进行加密,结果因为磁盘类型是‘动态磁盘’类型,所以无法加密,需要转换为基本类型。在网络上找了好多相关工具和方法,都无法实现,最后总算找到了无损转换的方法及...
  • 网站制作开发的步骤和方法

    千次阅读 2010-12-17 19:42:00
     另外一个工具就是代码编辑工具,例如写字本、EditPlus等,这些工具主要编辑asp等动态网页。  此外还有一些网络编程工具,javascript、java编辑器等。  网页制作也是一个比较吃香的行业,要真正做一个好的网站...
  • 制作最小linux内核(1)

    千次阅读 2017-02-16 20:23:02
    深入理解 Linux 2.6 的 initramfs 機制 (上) 一文提到了制作简易initramfs的过程;而另一篇文章使用udevadm(modinfo)查找linux下设备对应的驱动 则提到了匹配设备驱动的方法,本文对上面两篇文章做个总结----定制一...
  • 静态的意思是要注册的所有项和键,他们的值在制作安装包的时候就知道的,我们可以直接在is的Registry面板里设置 我假设我要建一个项:HKEY_LOCAL_MACHINE//SOFTWARE//NNLLTEST,在NNLLTEST下有两个string value的...
  • 讲述了动态HTML的有关知识以及目前浒的网页设计方法,学会如何建立动态HTML语句,并逐步掌握动态HTML应用技巧
  • 详解制作根文件系统

    千次阅读 2016-07-12 11:06:04
    由于动态链接的应用程序本身并不含有它所调用的C库函数的代码,因此执行时需要动态链接库加载器来为它加载相应的C库文件,所以第6类文件是需要拷贝的。 除此之外,第5类文件当然要拷贝。但第5类文件的大小也...
  • python钢琴自动脚本

    千次阅读 2019-08-20 21:05:41
    人在美国,刚下飞机,在等行李的时候写下这篇博客,纪念一下我长达两天的踩坑史,以及一种新的思维,面向百度编程,本文介绍的是如何用python做钢琴脚本,用到模块有 PIL模块 numpy模块 ctypes模块(使用动态链接库)...
  • Python——制作表白神图

    千次阅读 2020-04-05 21:09:10
    Python——制作表白神图PIL制作表白图片前言解析总代码 PIL制作表白图片 前言 在网上看到了很多由文字构成的图片,感觉挺厉害的,so...我们要做的就是先构造一画布,将原图片取出每一像素的颜色,然后用相同颜色的...
  • 如何制作完美消音伴奏

    万次阅读 2011-09-09 15:31:00
    如何制作完美消音伴奏  爱唱歌的我们在找不到原版伴奏的时候,常常会用一些软件自己来制作消音伴奏,用来制作的软件无非是cooledit或者Dart Karaoke然而用它们制作出来的伴奏有...下边的制作方法结合了几个伴奏制作软
  • 赢在电子商务网站制作与营销:B2C版  陈益材,王楗楠编著 ISBN978-7-121-15386-0 2012年2月出版 定价:49.00元 16开 384页 内 容 简 介 本书以介绍企业如何创建B2C电子商务网站并进行经营推广为主线,分析...
  • /*注:本教程针对HBuilder5.0.0,制作日期2014-12-31*/创建HTML结构: h 8 (敲h激活代码列表,按8选择第8个项目,即HTML代码,或者敲h t Enter)中途换行: 'Ctrl+Enter'设置charset: m e 6 Enter引用外部js:...
  • UE4无限地形制作

    万次阅读 2018-08-28 21:55:02
     好吧,这次总结的还是UE4,在这里要向CE3的朋友们道一声歉,因为本人水品有限,对于CE3的探索学习陷入了一个非常大的瓶颈,暂时抽不出一儿大的单独的时间去解决,另外,自己也有一点小小的私念,就是期盼Cryteck...
  • Android ROM制作教程,让你拥有自己喜欢的ROM

    万次阅读 多人点赞 2018-09-20 20:40:18
    制作工具 update-编写美化 集成 编译反编译 一. 制作目的: 1. 培养个人兴趣,感受动手动脑的快乐。 2. 学习手机基本知识,最终动手制作自己的ROM。 3. 帮助那些需要帮助的朋友(说不定有人喜欢你制作的ROM) 二. ...
  • 讲述了动态HTML的有关知识以及目前浒的网页设计方法,学会如何建立动态HTML语句,并逐步掌握动态HTML应用技巧。
  • 协议:事先约定或定义的一组精确地...标记型网页制作工具:是指创建的网页文件在编辑过程中只能看到页面的源代码,而需要转到浏览器中才能够看到页面的效果。要求用户对HTML的使用方法非常熟悉才能够做出希望要的...
  • Unity游戏运行资源的制作与下载

    千次阅读 2013-08-30 15:38:05
    我们的游戏制作完发布出去提供给玩家,为了给...所以我们应该采取动态加载的方式,让玩家在玩游戏的过程中来一点一点从服务器加载游戏资源。要实现这样的效果,首先就必须要制作用于一点点加载的游戏资源。 (注:本文
  • dll文件的c++制作dll文件的c++制作

    千次阅读 2009-02-08 14:03:00
    dll文件的c++制作1、首先用vs2005建立一个c++的dll动态链接库文件,这时,// DllTest.cpp : 定义 DLL 应用程序的入口点。//#include "stdafx.h"//#include "DllTest.h"#ifdef _MANAGED#pragma managed(push, off)#...
  • 制作自己的AMI

    千次阅读 2015-03-12 11:42:51
    亚马逊AMI market上提供的AMI有的含有Market Code(不允许将跟...制作方法和步骤如下: (1)自己在合适的位置安装一个精简版的CentOS,尽量最简化安装,因为一会要把系统文件内容全部同步到亚马逊上去。还可以直接将虚
  • java制作视频播放器

    万次阅读 多人点赞 2015-10-02 12:12:02
    前言国庆前几天,无意中看到关于VLCJ包的文章,可以导入java工程制作自己的java视频播放器,感觉有意思,就自己捣鼓几天,做出了个功能较为齐全的跨平台的视频播放器,高级功能之后有时间再弄,我把项目的源码放在...
  • CSS制作水平垂直居中对齐

    千次阅读 2012-06-29 14:06:02
    这段时间,我收集了几种不同的方式制作垂直居中方法,但每种方法各有千秋呀,要正确的选择也是一件不容易的事情。我会将这几种方法一一介绍给大家,以供大家参考。或许对于我这样的初学者有一定的帮助。 用CSS来...
  • 网页制作HTML代码全攻略

    万次阅读 多人点赞 2017-06-08 09:06:44
    网页制作HTML代码全攻略

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 41,762
精华内容 16,704
关键字:

动态块制作方法