分类

类型:
不限 游戏开发 计算机程序开发 Android开发 网站开发 笔记总结 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
原创:
不限
年份:
不限 2018 2019 2020 2021

技术文章列表

  • 【翻译】基于python-docx实现的解析docx文件,顺序保存文字、表格、图片

    前些天由于程序的需要,要从docx文档中,按文档的顺序获取文字、图片、表格信息,按自己想要的排版方式,转换成markdown格式。但是,卡在了按顺序获取文字、图片、表格上。查找了很多资料,很多都是介绍怎么使用python-docx添加文字、图片、表格的。后来经过多方查找,终于找到一份国外的源码(源码地址附在文后),已经实现了按顺序获取文字、图片、表格的功能。为了节省大家搜索时间,所以把他的英文文档翻译成中文,方便大家查阅。原文翻译如下:
    python-docx是用于创建和处理Microsoft Word(.docx)文件的Python库。Python-docx包无法按文档顺序完全读取段落,表格和图像。按照文档顺序,它只能一次获取所有段落,或者一次获取所有表格,或者一次获取所有图像。在这里,我提供了一种方法,可以将docx文件中的段落、表格和图像按文档顺序读取到python的数据框中。
    该代码位于“Para_table_image_extraction.py”的文件内。在以任何docx文件作为输入运行此代码时,此代码将生成3个数据帧,即“combined_df”、“table_list”和“image_df”:

    docx文件的所有内容(包括段落,表和图像)都按顺序存储在名为“combined_df”的python数据帧中
    如果在文档的段落之后出现图像,则对该图像的引用将存储在“combined_df”数据框中,而不是实际图像。您将必须从“combined_df”引用图像索引,并从名为“image_df”的单独数据帧中检索图像数据,该数据帧存储image_index和每个图像的相应base64编码图像数据
    类似地,如果在文档中遇到表,则将填充“combined_df”数据框中的“table_id”列。而且,您将不得不从“table_list”中检索与“ table_id”相关的对应表

    下图是示例 word 文档(docx):

    下图是根据上述docx文件生成的“combined_df”数据帧:

    在上述数据帧中:

    para_text:表示文档的实际段落内容(每一行代表文档中的每个段落)
    table_id:表示表格的表ID(如果在该文档的该位置存在表)。如果没有表格,则其值表示为“ Novalue”
    style:代表相应段落的段落样式

    注意,图像和表格不会这样存储在combined_df数据框中。对这些对象的引用仅存储在combined_df中。这意味着对于在Combined_df中存在的每个图像ID或表格ID,您将必须从combined_df中检索图像ID或表格ID,并从image_df和table_list中根据这些ID来获取图像数据或表格数据。
    图像文件以以下符号表示:
    combined_df中的 Document_Imagefile/image1.png/rId7/0这表示该引用实际上是一个图像文件,其中“image1.png”作为图像名称,“rId7”作为图像的唯一ID或标识符,“0”图片索引将在image_df中引用。

    表格对象表示为“<docx.table.0x1020f1160处的表对象>”,它表示在该位置存在一个带有相应table_id的表。您可以在“table_list”索引中根据table_id来获取获取表格数据。table_list包含所有表格的信息。

    我还没有包含使用来自combined_df的唯一标识符从image_df和table_list提取图像和表格的代码。我之所以没有这样做,是因为用例可能因情况而异。您可以用自己的方式提取它们,然后执行您希望执行的任何后续操作。
    原文链接:Python-docx-Reading-paragraphs-tables-and-images-in-document-order
    0 留言 2021-02-13 10:13:48 奖励38点积分
  • 使用AlphaBlend函数实现位图半透明绘制

    背景自己使用VC和VS写过很多小游戏,而且不是用现成的游戏引擎,纯粹是使用GDI函数来进行绘图。所以,积累了一些绘图的经验。
    那么,对于位图半透明的绘制,在小游戏中使用也比较多。例如烟雾、光等之类的绘制。在没有了解 AlphaBlend 函数之前,绘制半透明位图都是获取两张图片的RGB数据,然后按指定透明度计算出混合后的RGB的值,再显示出来。这样,需要自己计算的过程,算是麻烦。而现在,AlphaBlend 函数直接封装了这步操作,提供了方便使用的接口。
    现在,我们就来介绍使用 AlphaBlend 函数绘制半透明位图,写成文档,分享给大家。
    函数介绍AlphaBlend 函数
    该函数用来显示具有指定透明度的图像。
    函数声明
    AlphaBlend( HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int hHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction );
    参数

    hdcDest:指向目标设备环境的句柄。
    nXOriginDest:指定目标矩形区域左上角的X轴坐标,按逻辑单位。
    nYOriginDest:指定目标矩形区域左上角的Y轴坐标,按逻辑单位。
    nWidthDest:指定目标矩形区域的宽度,按逻辑单位。
    hHeightdest:指向目标矩形区域的高度,按逻辑单位。
    hdcSrc:指向源设备环境的句柄。
    nXOriginSrc:指定源矩形区域左上角的X轴坐标,按逻辑单位。
    nYOriginSrc:指定源矩形区域左上角的Y轴坐标,按逻辑单位。
    nWidthSrc:指定源矩形区域的宽度,按逻辑单位。
    nHeightSrc:指定源矩形区域的高度,按逻辑单位。
    blendFunction:指定用于源位图和目标位图使用的alpha混合功能,用于整个源位图的全局alpha值和格式信息。最后一个参数blendFunction是一个BLENDFUNCTION结构。
    BLENDFUNCTION结构介绍如下:
    typedef struct _BLENDFUNCTION {​ BYTE BlendOp;​ BYTE BlendFlags;​ BYTE SourceConstantAlpha;​ BYTE AlphaFormat;}BLENDFUNCTION, PBLENDFUNCTION, LPBLENDFUNCTION;
    BlendOp: 这个参数必须也只能为AC_SRC_OVER(0x00),意思就是把源图片覆盖到目标之上。
    BlendFlags: 必须为0 。
    SourceConstantAlpha: 简写为SCA,指定源图片的透明度,这个值是会和源图片的Alpha通道值合并计算的;即设置透明度,0为完全透明,255为完全不透明 。
    AlphaFormat: 可以填两种,一种是0x00,一种是AC_SRC_ALPHA (0x01);0表示常量alpha值,AC_SRC_ALPHA表示每个像素有各自的alpha通道。

    返回值

    如果函数执行成功,那么返回值为TRUE;如果函数执行失败,那么返回值为FALSE。

    透明度绘制介绍下面,我们举例说明,什么是透明度绘制。现在,有两张位图,一张是背景图:

    另一张是其它图片:

    那么,半透明绘制,就是要将两张图片绘制在一起,实现下面的效果:

    实现原理半透明位图绘制的功能实现,主要是靠 AlphaBlend 函数完成的。AlphaBlend 函数的最后一个参数 BLENDFUNCTION 结构指定用于源位图和目标位图使用的alpha混合功能。
    其中,BLENDFUNCTION 结构的 SourceConstantAlpha 变量,指定了源图片的透明度,这个值是会和源图片的 Alpha 通道值合并计算的;即设置透明度,0为完全透明,255为完全不透明 。也就是说,只要我们控制这个变量的值,就控制了位图绘制的透明度。
    编码实现导入库文件#include <WinGDI.h>#pragma comment(lib, "Msimg32.lib")
    绘制背景位图BOOL PaintBmp(HWND hWnd){ // 获取窗口的客户区域的显示设备上下文环境的句柄 HDC hDC = ::GetDC(hWnd); // 创建一个与hDC兼容的内存设备上下文环境 HDC hBuf = ::CreateCompatibleDC(hDC); // 加载位图, 获取位图句柄 HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, "image\\bg.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); // 选择位图句柄到hBuf中, 并获取返回的原来位图句柄 HBITMAP hOldBmp = (HBITMAP)::SelectObject(hBuf, hBmp); // 绘图 ::BitBlt(hDC, 0, 0, 764, 397, hBuf, 0, 0, SRCCOPY); // 还原位图对象 ::SelectObject(hBuf, hOldBmp); // 释放位图 ::DeleteObject(hBmp); // 释放兼容的内存设备上下文环境 ::DeleteDC(hBuf); // 释放设备上下文环境 ::ReleaseDC(hWnd, hDC); return TRUE;}
    指定透明度绘制BOOL PaintAlphaBlendBmp(HWND hWnd){ // 获取窗口的客户区域的显示设备上下文环境的句柄 HDC hDC = ::GetDC(hWnd); // 创建一个与hDC兼容的内存设备上下文环境 HDC hBuf = ::CreateCompatibleDC(hDC); // 加载位图, 获取位图句柄 HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, "image\\1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); // 选择位图句柄到hBuf中, 并获取返回的原来位图句柄 HBITMAP hOldBmp = (HBITMAP)::SelectObject(hBuf, hBmp); // 设置透明度参数 BLENDFUNCTION ftn = { 0 }; ftn.BlendOp = 0; ftn.BlendFlags = 0; ftn.SourceConstantAlpha = 200; // 透明度指定 ftn.AlphaFormat = 0; // 指定透明度绘制 ::AlphaBlend(hDC, 0, 0, 650, 350, hBuf, 0, 0, 650, 350, ftn); // 还原位图对象 ::SelectObject(hBuf, hOldBmp); // 释放位图 ::DeleteObject(hBmp); // 释放兼容的内存设备上下文环境 ::DeleteDC(hBuf); // 释放设备上下文环境 ::ReleaseDC(hWnd, hDC); return TRUE;}
    程序测试调用上述封装好的函数进行测试,两幅位图成功按照指定的透明度混合显示:

    总结这个小程序重点是要理解 AlphaBlend 函数的使用方式,可以自己更改透明度的值尝试几次,加深理解。这个函数同样支持对位图伸缩绘制,使用起来比较灵活。
    4 留言 2018-12-20 12:36:17 奖励5点积分
  • 编程学习笔记之vector学习心得

    一、vector介绍在c++中,vector是一个可以存储各种类型对象的集合容器,可以把它看成一种能够动态【吃胖】或【减肥】的数组。vector是一个类模板,使用【vector<T>】格式可以生成一个模板类,考虑到vector容器所应具备的一些特征,vector应该设计成不管传递给vector的模板参数是什么数据类型或类类型,一个vector生成的模板类,其内存大小应该是不变的。为验证我的想法,我在我的机器上测验了一下,使用【sizeof(A)】获得的内存大小为16,其中A为我的一个自定义类类型,不管我把A制造成多大,得到的结果总是 =16,然后我用高大上的开发工具vs13智能显示了一下vector模板类里面的数据成员,发现一共有4个,它们分别是:_Myfirst、_Myend、_Mylast和忘了怎么拼写的“myblabla。。。”,如果我没猜错的话,以上四种类型应该是三个指针和一个整型,它们所代表的意思分别是起始位置、终点位置、当前位置以及当前vector类所占据的内存大小。
    值得一提的是,vector是一种线性顺序式集合容器,虽然它可以动态分配存储空间,但它所占用的内存,是一块连续存储的内存。看到这里也许您有点迷惑,既然是动态分配了,为何还会像静态变量那样使用连续存储内存呢?让我先从设计初衷解释一下,首先前辈们在制造vector时,希望它具有数组一样的高效率,当我们通过下标访问vector中的某个元素时,可以直接用指针偏移量的方式寻址访问,而不要像链表那样弄个循环找到那个位置。其次再从内存分配机制这个角度阐述一下,为求容易理解,我使用例子吧:比如我们用vector生成一个int类容器A,那么机器首先为这个A在堆中开辟了一个大小为100的连续内存区域【其中100是我随便举得例子,A本身的内存是16,A相当于一个标签,贴在一个容器上面,这个容器现在的大小是100】,并把这块区域当成是A的对应容器,然后程序运行到某个位置时,需要往容器里面添加一个元素,但程序发现A容器已经饱和,一旦添加了这个元素,A现在对应的容器会容纳不下,怎么办呢?程序会重新向堆中申请一块更大的连续内存,比如200的大小。如果有,则把原先的100连续内存中的元素逐个拷贝到新内存区域中去,最后在拷贝完成之后,销毁原先的100内存。这个过程是比较麻烦点,但好歹达到了目的——访问方便。
    vector支持很多操作,向尾部添加元素可以用【push_back(T);】,其中T为模板参数的引用;在尾部销毁元素可以用【pop_back()】;在某个位置插入元素可以用【insert(n,T)】,其中n代表位置,T是模板参数的引用;判断容器是否为空可以用【empty()】,如果容器为空,此表达式会返回true,否则返回false;清空容器用clear()等。除此之外,vector还定义了很多操作符重载,比如【!=】、【==】等。
    二、vector的使用要在程序中使用vector,首先我们要包含它所在的库【#include <vector>】,并且还要做好相应的using说明【using std::vector;】,但为了写代码方便,我的说明一向都写成这样【using namespace std;】。做好以上工作后,我们就可以在自己的代码中使用vector生成模板类了。
    接下来说说vector生成模板类最常见的四种初始化情况,比如下面4个重载构造函数:
    // 创建一个int类型的vector容器,但这个容器是空的vector<int> vi; // 创建一个int类型的vector容器,该容器暂有10个int元素,每个元素被赋初始值0vector<int> vi2(10); // 创建一个int类型的vector容器,该容器暂有5个int元素,每个元素被赋值99vector<int> vi3(5,99); // 创建一个int类型vector容器,该容器拷贝了vi2,既该容器具有10个值为0的元素vector<int> vi4(vi2);
    说了这么多,下面让我们写一个简单小程序来巩固一下,该程序使用vector生成一个int型容器,然后由我们自己随便给容器添加若干个元素,接着程序负责求出这些元素的平均值以及找出最大值,最后程序把求出的平均值插入到容器中间,再对容器内所有的元素按照递增顺序排列,代码如下:
    #include <iostream>#include <vector>#include <algorithm> //调用sort排序函数必须包含的头文件using namespace std;int main(){ int value = 0, average = 0, max = 0, num = 0; vector<int>vi; while (cin >> value) vi.push_back(value); //利用循环向容器插入数值元素 cout << "现在容器内所有元素是:"; for (vector<int>::size_type vinum = 0; vinum != vi.size(); vinum++) //使用size_type声明一个变量,并用这个变量当作vi的下标 cout << vi[vinum]<<" "; value = 0; for (vector<int>::const_iterator i = vi.begin(); i != vi.end(); i++) //这次换个方式遍历,i是vector容器的指针 { value += *i; num++; *i > max ? (max = *i) : 0; } cout << value << endl; average = value / num; cout << "容器内平均值是" << average << "\n最大值是" << max << endl; for (num = 0; num != vi.size(); num++); num /= 2; vi.insert(vi.begin() + num, 1, average); //在这里我们把平均值插入到容器中间 sort(vi.begin(), vi.end()); //在这里我们对容器内所有元素进行排序 cout << "经过处理,容器内元素被处理成如下排列:"; for (vector<int>::const_iterator i = vi.begin(); i != vi.end(); i++) //最后我们输出经过处理的容器内所有元素 cout << " " << *i; cout << endl; return 0;}
    0 留言 2021-01-20 09:20:55 奖励37点积分
  • 基于java swing和MySql实现的药品销存管理系统

    此项目可以说是半原创,因为是根据一个已有项目进行变造而来,拥有的功能也比原项目更多
    一.药品销存管理管理系统的设计与实现打开系统后为登录界面,用户输入账号密码后进入系统,系统分为管理员、药店销售员、药品采购员三类用户,不同类型用户显示界面不同。
    管理员功能:

    库存管理:实现药品的增删改查,包括药品名称、规格、进货单价、销售单价、药品数量、厂商
    销售管理:销售记录的增删改查,包括药品名称、销售价、销售时间、销售数量,如果是会员,自动积分
    员工信息管理:员工信息增删改查,包括员工账号、账号类型、密码等
    个人账号设置:包括修改密码等

    销售员功能:

    销售管理:销售记录的增删改查,包括药品名称、销售价、销售时间、销售数量,如果是会员,自动积分
    销售记录管理:销售记录的增删查改
    个人账号设置:包括修改密码等

    药品采购员功能:

    进货管理:药品增加,包括新进药品的名称、规格、进货单价、进货数量、厂商,如果新进药品信息与库存药品信息不同,则需分开保存
    个人账号设置:修改密码等

    二、MySQL表2.1 药品信息表结构设计药品信息主要方便存储每个药品的参数,比如药品名,库存,进价,售价,生产厂商。表的具体结果如下:

    2.2 药品销售记录表该表的设计主要是方便对药品销售记录的查看,表的具体结构设计如下:

    2.3 员工级别表结构设计该表主要用于管理员对员工分配职位,使员工得到相应的权限

    2.4 员工信息表结构设计该表主要用于储存员工信息,包括员工的用户名、密码、姓名、员工级别。

    三、本系统的结构图如下:
    四、各模块功能4.1 系统管理员模块功能该模块主要包括员工记录设置、库存记录设置和销售记录设置、账号设置等。

    员工记录设置:该功能选项用于系统管理员对员工用户名、密码、姓名、所属角色查看操作,方便管理员更加直观的了解员工信息
    库存记录设置:该子模块主要是系统管理员对药品库存信息的查看
    销售记录设置:该子模块主要是系统管理员对药品销售情况的查看
    账号设置设置:该子模块主要是用于管理员对员工信息的增、删、改的操作,还有员工职位的分配

    4.2 系统售货员模块功能该模块主要包括销售记录设置、销售管理设置和个人账号管理设置、添加销售设置。

    销售记录设置:该子模块主要是系统销售员对药品销售记录信息的查看
    销售管理设置:该子模块是系统销售员对销售记录信息进行修改和删除操作
    个人账号管理设置:该子模块是系统销售员对个人账户密码进行更改操作
    添加销售设置:该子模块是系统销售员添加销售信息操作

    4.3 系统进货员模块功能该模块主要包括进货管理设置、库存查看设置和个人账号管理设置、库存管理设置。

    库存查看设置:该子模块主要是系统进货员对药品库存记录信息的查看
    进货管理设置:该子模块是系统进货员对药品信息进行添加操作
    个人账号管理设置:该子模块是系统销售员对个人账户密码进行更改操作
    库存管理设置:该子模块是系统进货员对药品库存信息进行修改、删除操作

    五、用户登录注册模块程序设计本模块主要是用户通过药品销存系统的首页登录进入该系统。用户输入正确的用户名和密码,系统会根据用户的身份进行相应权限划分;如果登录信息有错误,则系统提示登入错误的信息,并且禁止系统用户进行任何操作。
    药品销存管理系统的登录主页面如图所示。

    六、系统功能模块的实现6.1 管理员模块本模块中最主要的是对员工记录、库存记录、销售记录的查看操作,还有对员工信息进行添加,修改,删除等操作。
    其中系统管理员模块功能的操作页面如下图所示:

    6.2 售货员模块本模块中最主要的是销售记录、销售管理、账号管理、添加销售操作,对数据库record表进行添加,修改,删除、查找等操作。
    其中系统售货员模块功能的操作页面如下图所示:

    6.3 进货员模块此模块主要用来进货管理、库存查看、库存管理、账号管理操作,对数据库medic表进行添加,修改,删除、查找等操作。
    其中系统管理员对订单进行管理的操作页面如下图所示:
    1 留言 2021-01-18 09:00:49 奖励56点积分
  • 基于Android实现的小型在线订餐APP饿了么

    1 绪论1.1 研究背景进入到移动互联时代,人们会愈加频繁地使用手机,安卓系统占据了智能手机界很大市场份额,安卓开发,在今后的一段时间之内,依旧会是热门之一。为实现人们网上在线点外卖的需求,需要开发一个安卓外卖app,实现包括商品的查看,下单,支付,用户信息修改等功能。
    网上在线订餐作为手机购物的一种表现形式,采用将图片和文字内容相结合的方式将商品信息展现给用户,这个方式使用户可以查看不同来源的商品信息以及不同层次的信息,以最大程度上了解商品的具体信息,同时可以搜索距离较近的商品信息。
    在线订餐客户端市场已经成为移动媒体竞争的焦点,互联网各大门户网站纷纷使出浑身解数希望占领尽可能多的移动在线订餐客户端市场份额。根据数据显示,在中国,在线订餐客户端市场排名前三的客户端分别为美团,饿了么和口碑,其中,这些在线订餐客户端在功能上过于繁重,对Android的设备系统版本有一定的要求,没有很好地做到向下兼容低版本,在较低版本的手机无法成功安装这些在线订餐客户端。
    1.2 研究现状目前市场上我们所见的个性化在线订餐APP 就是以“饿了么”为代表的,这些个性化在线订餐 APP 大多通过大数据进行用户行为分析,了解到受众的需求,然后利用算法技术,将受众感兴趣的美食商品信息推送给用户的 APP.在线订餐 APP 拥有强大的技术基础,满足了大部分受众的“使用与满足”需求,因而能在传统新闻资讯诸APP 中脱颖而出并长期占据领先地位。
    饿了么通过用户行为分析和精准定位给用户个性化进行个性化资讯推荐,这一切都依赖于饿了么的算法技术,依靠算法技术对大数据进行分析,而这算法技术的核心就是网络爬虫和矩阵筛选。
    在信息大爆炸的今天,用户身上贴满了各式各样的标签,性别、年龄、职业等都是受众的标签,根据这些标签将用户进行简单的分类。但是饿了么利用算法数据对受众的个性化特征进行更加精确的分析,通过受众登录客户端的第三方账户的社交情况或者直接注册登录客户端的浏览信息情况进行算法分析,从而得出受众的使用需求。
    随着互联网和移动终端的发展,用户对信息获取的要求越来越高,从以前单一的接受信息到现在想要获取自己感兴趣的资讯。饿了么涉及各种外卖信息,可以快速搜素到商品品信息,也可以推荐用户关心的内容。
    除了首页的个性化推荐商品之外,饿了么还有药品、便利卖场等多个频道,用户可以选择设置自己感兴趣的频道进行内容浏览,在用户浏览这些内容时产生的数据也会被今日头条记录下来形成更加精确的图谱,随着使用次数的不断深入,用户的使用图谱也会更加精确,越来越符合用户的使用需求。
    1.3 研究目的与意义本文分析了小型餐饮店管理与发展的现状和面临的问题,发现传统的服务模式已经不能适应市场发展的需求。随着网络技术的发展和普及,方便、快捷、个性化的网.上订餐服务正在进入人们的生活。针对这类问题,结合当代科技发展的最新成果,我们开发的这款饿了么app,目的是为了便于使用者方便地在线订餐,并且,支持个人信息的个性化设置,以便于其获取喜欢的商品信息。
    此外,Android 平台在线订餐客户端软件的开发可以进一步扩大各个商家的商品信息的覆盖面, 让广大公众能够随时随地方便快捷地获取商品信息,了解喜好的商家和商品。
    2 需求分析本系统的需求分析部分,主要是通过对系统用例图、系统整体的业务需求、系统功能需求、系统性能需求以及对系统的可行性分析等的分析与总结,对系统的需求有一个整体的了解,再根据这些需求确定系统的功能,同时确保系统的可扩展性。
    2.1 系统用例图
    2.2 业务需求分析伴随着我国市场经济的高度发展,企业越来越依赖于市场和客户,这种依赖关系已经开始逐步提升到关乎企业生存的高度。如何获得更多客流量,如何通过小的代价而使商家获得更大的知名度,这是使企业得以生存、发展、壮大的关键。年销售过亿的众多电子商务案例让人们看到了更多的商机。与应用场景相当有限的PC (个人电脑)相比,生而具备便携属性的手机给了电商们更大的想象空间。移动电子商务由此产生了。移动电子商务就是利用手机、PDA及掌上电脑等无线终端进行的B2B、B2C或C2C的电子商务。它将因特网、移动通信技术、短距离通信技术及其它信息处理技术完美的结合,使人们可以在任何时间、任何地点进行各种商贸活动,实现随时随地、线上线下的购物与交易、在线电子支付以及各种交易活动、商务活动、金融活动和相关的综合服务活动等。
    而Android操作系统凭借着自己开放的平台允许任何移动终端厂商加入到Android联盟中,从而使得越来越多的用户倾向于选择操作系统为Android的手机品牌。选择设计基于Android平台的软件,毫无疑问能覆盖更多的受众。
    随着移动互联网的快速发展,在线订餐APP也成为了大多用户的一部分,只要有智能手机,就能随时随地点餐,而且能在指定时间内送达指定地点,完全给了用户足不出门的理由,当之无愧的偷懒神器!本课题研究的是制作一个小型的在线订餐APP——“饿了吗?”,主要是为人们提供一个在线平台,了解附近餐厅的具体情况,在线购买,方便快捷!
    2.3 功能需求分析根据上文中对业务需求的调研,本系统的具体功能需求如下:

    登录:点击登录按钮,根据注册的手机号和密码进行登录
    注册:用户根据系统提示,填写信息,完成注册
    修改信息:已登录APP的用户点击头像,可以进入修改信息界面,按照提示修改信息
    进入首页:已登录的用户在“饿了吗”首页可以选择店家,也可以在搜索栏搜索店家和菜品
    管理订单:已登录的用户可以点击订单,可以查看历史订单,已配送和未配送订单,对于交易成功的订单可以对该店家进行评价
    进入店家:选择心仪菜品加入购物车,并在购物车下单



    2.4 性能需求分析
    在线支持用户并发业务数据的综合处理时间不超过2秒
    系统的最大并发用户数为100
    系统运行稳定,故障率较低,并且可维护性高
    系统具有可扩展性,添加功能模块不需要对系统进行重构,只需将新开发的功能接口接入即可

    2.5 系统可行性分析本章节主要对本课题的系统可行性进行分析,系统可行性分析主要包括技术可行性分析和经济可行性分析两方面的内容。技术可行性分析是对开发本系统所采用的技术进行简单的介绍,并且对其进行分析;经济可行性分析则是从系统的成本到用户使用的经济效应进行分析。
    实现“饿了吗?”在线订餐系统需要使用Windows系统和Android Studio工具进行开发,后端云服务器BMOB为整个应用提供数据支持,Android端采用传统的MVC三层架构进行开发。
    3 系统总体设计本系统的总体设计部分主要是对本系统的设计方案进行论证,并对系统的总体进行设计,最后对系统功能的设计思路以及系统的业务流程进行说明,其中包括数据库的选择,开发框架的选择等。
    3.1 系统总体设计
    3.2 功能模块设计3.2.1 首页模块功能设计首页模块的功能有店家显示和搜索界面。

    3.2.2 个人信息模块功能设计用户登录后,可以处理登录信息和修改个人信息。可修改的内容包括性别,昵称,生日和地区。

    3.2.3 登录模块功能设计登录功能是保障系统安全的屏障。本系统采用手机号密码登录这一种登录方式。

    3.2.4 注册模块功能设计用户可以通过输入手机号和密码来进行注册,并确认密码保证正确。

    3.2.5 店家详情模块功能设计用户可以点击进入店家详情查看店家菜品,加入购物车以及下单。

    3.2.6 订单详情模块功能设计用户点击订单后,订单详情展示,用户可以评论该订单。

    3.3 数据库设计本节将对本系统的数据库设计进行叙述,包括数据库的E-R图设计,以及数据库表的设计。表设计部分以每张表为单位对表的字段、字段类型、字段作用等进行叙述并以表格的形式进行详细展示。
    3.3.1 数据库E-R图用户表

    商家表

    商品表

    订单表

    评价表

    3.3.2 系统数据库表设计本系统数据库表共有5张,下面将展示每张表的设计以及详细说明。
    (1)用户表(User)用户表主要用于保存用户的登录信息,包括唯一标识number,密码,性别,头像,生日,昵称,地址。这张表尤为重要,是系统能否进入的关键表。



    Number
    Password
    Sex
    Image
    Birth
    Name
    Address




    17600006117
    123456

    Tx1jpg
    1.24
    赵先生
    南京市秦淮区


    15600007996
    123456

    Tx2.jpg
    4.30
    何先生
    盐城市阜宁县


    15600003302
    123456

    Tx3.jpg
    12.13
    陈先生
    南京市江宁区


    ……
    ……
    ……
    ……
    ……
    ……
    ……



    (2)商家表(Store)商家表主要用于保存商家的信息,包括唯一标识id,商家名,商家图片,评分,类型,销售量,起送费。其中type类型为0表示美食,为1表示水果,为3表示饮品。



    Id
    Name
    Image
    Score
    Type
    Sales
    Qisong




    1
    香儿麦
    Tx1.jpg
    4.4
    0
    223
    12


    2
    何记花甲
    Tx2.jpg
    4.6
    0
    301
    13


    3
    桥头排骨
    Tx3.jpg
    4.8
    0
    449
    15


    4
    水果先知
    Sgtx1.jpg
    4.7
    1
    324
    13


    5
    百果园
    Sgtx2.jpg
    4.2
    1
    199
    14


    6
    果小露
    Sgtx3.jpg
    4.1
    1
    179
    15


    7
    CoCo
    Yptx1.jpg
    4.3
    2
    219
    17


    8
    一点点
    Yptx2.jpg
    4.5
    2
    327
    16


    9
    五十岚
    Yptx3.jpg
    4.7
    2
    408
    12


    ……
    ……
    ……
    ……
    ……
    ……



    (3)商品表商品表主要用于保存商品信息,包括唯一标识id,商家id,iamge,价格,内容,销售量,好评率。其中通过商家id,可以定位到该商品是哪个商家的。



    Id
    Sid
    Image
    Price
    Content
    Sales
    Like




    1
    1
    Hb.jpg
    10
    香辣鸡腿堡
    198
    93%


    2
    1
    Jk.jpg
    7
    黑椒鸡块
    7
    80%


    3
    1
    Kb.jpg
    8
    奥尔良烤堡
    46
    100%


    4
    2
    Jzghj.jpg
    11
    金针菇花甲
    214
    98%


    5
    2
    Kchj.jpg
    13
    烤肠花甲
    152
    95%


    6
    2
    Xghj.jpg
    12
    香菇花甲
    124
    98%


    7
    5
    Hlg.jpg
    3.5
    海南火龙果
    101
    100%


    8
    5
    Jxmj.jpg
    7.8
    江西蜜桔
    176
    97%


    9
    5
    Fjbl.jpg
    5.8
    福建白梨
    89
    95%


    ……
    ……

    ……
    ……
    ……
    ……



    (4)订单表(Order)订单表主要用于保存订单的信息,包括唯一标识id,商品id,用户id,地址。



    Id
    Goods id
    Uid
    Address




    1
    1
    17600006117
    南京市秦淮区


    2
    9
    17600006117
    南京市秦淮区


    3
    2
    15600007996
    盐城市阜宁县


    4
    7
    15600003302
    南京市江宁区


    ……
    ……
    ……
    ……



    (5)评论表(Evaluate)评论表主要用于保存评论的信息,包括唯一标识id,商家id,用户id,时间,点赞,内容。



    Id
    Sid
    Uid
    Time
    Like
    Contents




    1
    1
    17600006117
    2019-12-08
    1
    好吃


    2
    7
    17600006117
    2019-12-08
    3
    还不错


    3
    13
    15600007996
    2019-12-08
    5
    口味nice


    4
    9
    15600003302
    2019-12-08
    6
    Very good!


    ……
    ……
    ……
    ……
    ……
    ……



    4 系统详细设计系统详细设计部分将对本系统的每个模块的详细设计进行叙述,为本系统的开发提供更为全面的指导。
    4.1 登录模块详细设计用户进入个人中心页面,点击“用户名”栏,跳出登录页面,若已注册用户,则输入账号和密码进行登录。

    4.2 注册模块详细设计用户进入个人中心页面,点击“立即注册”,根据提示信息完成注册操作,然后返回登录界面,通过账号密码登录APP。

    4.3 点餐模块详细设计用户进入APP首页,可以查看店家,点击某店家可以进入该店家浏览菜品,勾选菜品点击加入购物车,进入购物车,勾选其中菜品点击下单以购买美食。

    4.4 个人信息模块详细设计成功登录APP的用户进入个人页面,点击用户名一栏,跳转至修改信息页面,用户可以根据自己的需求修改自己的个人信息,然后保存返回;若用户不想做任何修改,则直接返回退出。

    4.5 搜索模块详细设计用户进入“饿了吗?”APP首页,点击最上方的搜索框,输入关键词,即可查找想要了解的菜品和店家。

    5 系统实现系统实现部分对已开发好的系统功能的实现进行详细的描述,对每一个主要功能通过语言描述和页面截图对本系统进行全面的展示。
    5.1 店面界面import java.io.Serializable;public class store implements Serializable { private int id; private String name; private int score; private int image; private int type; private int sales; private int qisong; public store(int id, String name, int score, int image, int type, int sales, int qisong) { this.id = id; this.name = name; this.score = score; this.image = image; this.type = type; this.sales = sales; this.qisong = qisong; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } public int getImage() { return image; } public void setImage(int image) { this.image = image; } public int getType() { return type; } public void setType(int type) { this.type = type; } public int getSales() { return sales; } public void setSales(int sales) { this.sales = sales; } public int getQisong() { return qisong; } public void setQisong(int qisong) { this.qisong = qisong; }}
    5.2 storeActivity.java代码package com.example.a11469.food;import android.content.ContentValues;import android.content.DialogInterface;import android.content.Intent;import android.support.v7.app.AlertDialog;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.AdapterView;import android.widget.ImageView;import android.widget.ListView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;import java.util.Vector;public class StoreActivity extends AppCompatActivity { int flag=0; GoodsAdapter goodsAdapter; List<Goods> goodsList; ListView listView; List<Goods> myList; List<Evaluate> evaluateList; List<Evaluate> evList; Intent intent; Bundle bundle; int id; store s; String sname; ImageView im; TextView tv_name; TextView tv_want; TextView tv_pj; int image; EvaluateDBHelper evaluateDBHelper; EvaluateAdapter evaluateAdapter; GoodsDBHelper goodsDBHelper =new GoodsDBHelper(this,"goods.db",null,1); public static String name; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_store); intent = getIntent(); name=intent.getStringExtra("zhanghao"); bundle = intent.getExtras(); s = (store)bundle.get("Storer"); evaluateDBHelper= new EvaluateDBHelper(this,"evaluate.db",null,1); id = s.getId(); sname = s.getName(); image = s.getImage(); im=findViewById(R.id.Store_image); tv_name=(TextView) findViewById(R.id.Store_name); tv_want=findViewById(R.id.Store_wantfood); tv_pj=findViewById(R.id.Store_pingjia); im.setImageResource(image); tv_name.setText(sname); setTitle("店家界面"); listView=findViewById(R.id.Store_list); myList = new ArrayList<>(); goodsList = new ArrayList<>(); evList = new ArrayList<>(); evaluateList = new ArrayList<>(); Goods goods1 = new Goods(1,1,"34",R.drawable.jirou,"鸡胸肉+精致米饭",62,57,"鸡胸肉能量餐"); Goods goods2 = new Goods(2,1,"25",R.drawable.niu,"牛肉+精致米饭",50,44,"牛排能量餐"); Goods goods3 = new Goods(3,1,"42",R.drawable.xia,"牛油果+虾仁",14,12,"牛油果大虾沙拉"); Goods goods4 = new Goods(4,1,"36",R.drawable.kao,"圣女果+橙子+沙拉",15,14,"经典考伯沙拉"); Goods goods5 = new Goods(5,2,"30",R.drawable.ji,"鸡+精致米饭",87,72,"三杯鸡便当"); Goods goods6 = new Goods(6,2,"30",R.drawable.zhu,"猪肉+梅干菜+精致米饭",27,24,"梅干菜扣肉饭"); Goods goods7 = new Goods(7,2,"25",R.drawable.rou,"烤肉+精致米饭",24,23,"烤肉饭"); Goods goods8 = new Goods(8,2,"24",R.drawable.jipai,"奥尔良鸡排+精致米饭",24,12,"奥尔良鸡排饭"); Goods goods9 = new Goods(9,3,"20",R.drawable.sanxian,"鸡蛋+火腿+精致米饭",27,18,"解馋三鲜炒饭"); Goods goods10 = new Goods(10,3,"20",R.drawable.zhutourou,"猪头肉+精致米饭",45,40,"猪头肉炒饭"); Goods goods11 = new Goods(11,3,"18",R.drawable.huo,"火腿+精致米饭",39,26,"火腿炒饭"); Goods goods12 = new Goods(12,3,"15",R.drawable.jidan,"鸡蛋+精致米饭",78,74,"鸡蛋炒饭"); Goods goods13 = new Goods(13,4,"10",R.drawable.ydd,"奶+绿茶",35,35,"奶绿"); Goods goods14 = new Goods(14,4,"10",R.drawable.ydd,"奶+红茶",65,64,"奶茶"); Goods goods15 = new Goods(15,4,"15",R.drawable.ydd,"奶+乌龙茶",41,35,"乌龙奶茶"); Goods goods16 = new Goods(16,4,"10",R.drawable.ydd,"奶+四季春茶",78,34,"四季奶绿"); Goods goods17 = new Goods(17,5,"9",R.drawable.yeguo,"椰果+奶茶",44,42,"椰果奶茶"); Goods goods18 = new Goods(18,5,"11",R.drawable.xianyu,"鲜芋+奶茶",98,89,"鲜芋奶茶"); Goods goods19 = new Goods(19,5,"11",R.drawable.hongdou,"红豆+奶茶",36,35,"红豆奶茶"); Goods goods20 = new Goods(20,5,"10",R.drawable.zhengzhu,"珍珠+奶茶",68,66,"珍珠+奶茶"); Goods goods21 = new Goods(21,6,"13",R.drawable.nai,"奶霜+不知春茶",87,82,"奶霜不知春"); Goods goods22 = new Goods(22,6,"18",R.drawable.jin,"芒果+酸奶+西米+芦荟粒",97,92,"金玉甘露"); Goods goods23 = new Goods(23,6,"18",R.drawable.qing,"柠檬+绿茶",89,79,"青柠眷贵妃"); Goods goods24 = new Goods(24,6,"18",R.drawable.cao,"草莓+酸奶",65,64,"草莓小确幸"); Goods goods25 = new Goods(25,7,"9",R.drawable.xigua,"西瓜",69,67,"西瓜现切"); Goods goods26 = new Goods(26,7,"9",R.drawable.qian,"千禧",36,35,"千禧"); Goods goods27 = new Goods(27,7,"9",R.drawable.aaa,"波罗蜜",97,95,"波罗蜜"); Goods goods28 = new Goods(28,7,"9",R.drawable.cheng,"手剥橙",31,20,"手剥橙"); Goods goods29 = new Goods(29,8,"6",R.drawable.lizi,"砀山梨",85,75,"砀山梨"); Goods goods30 = new Goods(30,8,"8",R.drawable.tao,"猕猴桃",69,65,"猕猴桃两只"); Goods goods31 = new Goods(31,8,"8",R.drawable.pingguo,"苹果",69,59,"冰糖心苹果250g"); Goods goods32 = new Goods(32,7,"8",R.drawable.tizi,"红提",26,24,"红提250g"); Goods goods33 = new Goods(33,9,"11",R.drawable.liqie,"冰糖梨",90,88,"冰糖梨切"); Goods goods34 = new Goods(34,9,"12",R.drawable.migua,"蜜瓜",28,24,"沙漠蜜瓜"); Goods goods35 = new Goods(35,9,"14",R.drawable.yezi,"椰青",22,21,"泰国椰青"); Goods goods36 = new Goods(36,9,"16",R.drawable.guozi,"火龙果",27,26,"进口火龙果"); goodsList.add(goods1); goodsList.add(goods2); goodsList.add(goods3); goodsList.add(goods4); goodsList.add(goods5); goodsList.add(goods6); goodsList.add(goods7); goodsList.add(goods8); goodsList.add(goods9); goodsList.add(goods10); goodsList.add(goods11); goodsList.add(goods12); goodsList.add(goods13); goodsList.add(goods14); goodsList.add(goods15); goodsList.add(goods16); goodsList.add(goods17); goodsList.add(goods18); goodsList.add(goods19); goodsList.add(goods20); goodsList.add(goods21); goodsList.add(goods22); goodsList.add(goods23); goodsList.add(goods24); goodsList.add(goods25); goodsList.add(goods26); goodsList.add(goods27); goodsList.add(goods28); goodsList.add(goods29); goodsList.add(goods30); goodsList.add(goods31); goodsList.add(goods32); goodsList.add(goods33); goodsList.add(goods34); goodsList.add(goods35); goodsList.add(goods36); for(Goods g:goodsList) { if (g.getSid()==id) { myList.add(g); } } for(Goods g: goodsList){ ContentValues contentValues =new ContentValues(); contentValues.put("id",g.getId()); contentValues.put("sid",g.getSid()); contentValues.put("image",g.getImage()); contentValues.put("goodsName",g.getGoodName()+""); contentValues.put("sales",g.getLike1()); contentValues.put("price",g.getPrice()+""); contentValues.put("like1",g.getLike1()); contentValues.put("content",g.getContent()+""); goodsDBHelper.insert(contentValues); } evaluateList = evaluateDBHelper.select(); for(Evaluate e:evaluateList){ if(e.getSid()==id){ evList.add(e); } } evaluateAdapter = new EvaluateAdapter(this,evList); goodsAdapter = new GoodsAdapter(this,myList); listView.setAdapter(goodsAdapter); listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) { Intent intent = new Intent(StoreActivity.this,GoodsinformationActivity.class); Bundle bundle1= new Bundle(); bundle1.putSerializable("Goods_infor",myList.get(i)); intent.putExtras(bundle1); startActivity(intent); return true; } }); tv_want.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { listView.setAdapter(goodsAdapter); flag=1; } }); tv_pj.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { listView.setAdapter(evaluateAdapter); flag=2; } }); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, final int i, long l) { if(flag==1){ AlertDialog.Builder builder=new AlertDialog.Builder( StoreActivity.this); builder.setMessage("确定加入购物车嘛?") .setTitle("提示"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); Intent intent = new Intent(StoreActivity.this,ShopcarActivity.class); intent.putExtra("zhanghao",name); intent.putExtra("storename",s.getName()); intent.putExtra("storeid",s.getId()); Bundle bundle1= new Bundle(); bundle1.putSerializable("Shopcar",myList.get(i)); intent.putExtras(bundle1); startActivity(intent); } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss();}}); builder.create().show();} else if (flag==2){ } } }); }}
    5.3 storeAdapter代码package com.example.a11469.food;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;public class StoreAdapter extends BaseAdapter { private Context mContext; List<store> storeList; public StoreAdapter(Context context,List<store> list){ this.mContext = context; this.storeList = list; } public int getCount() { return storeList.size(); } @Override public Object getItem(int i) { return null; } @Override public long getItemId(int i) { return 0; } @Override public View getView(int i, View view, ViewGroup viewGroup) { view = LayoutInflater.from(mContext).inflate(R.layout.storeitem,null); ImageView store_image = view.findViewById(R.id.image); TextView store_name = view.findViewById(R.id.Sname); TextView store_xingji = view.findViewById(R.id.xingji); TextView store_sales = view.findViewById(R.id.sales); TextView store_score = view.findViewById(R.id.qisong); store_image.setImageDrawable(mContext.getResources().getDrawable(storeList.get(i).getImage())); store_name.setText(storeList.get(i).getName()); store_xingji.setText(storeList.get(i).getScore()+""); store_sales.setText(storeList.get(i).getSales()+""); store_score.setText(storeList.get(i).getQisong()+""); return view; }}
    5.4 店家数据库package com.example.a11469.food;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import java.util.ArrayList;import java.util.List;public class storeDBHelper extends SQLiteOpenHelper { private static final String DB_NAME = "store.db"; private static final String TBL_NAME = "store"; private SQLiteDatabase db; public storeDBHelper(Context context, String dbname, SQLiteDatabase.CursorFactory factory, int version) { super(context, dbname, factory, version); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { this.db = sqLiteDatabase; String sql = "create table store (id integer primary key autoincrement,name text,image integer,score integer,type integer,sales integer,qisong integer)"; sqLiteDatabase.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { } public void insert(ContentValues contentValues) { SQLiteDatabase sqLiteDatabase = getWritableDatabase(); sqLiteDatabase.insert(TBL_NAME, null, contentValues); sqLiteDatabase.close(); } public List<store> query(){ List<store> storeList = new ArrayList<>(); SQLiteDatabase sqLiteDatabase = getReadableDatabase(); Cursor cursor = sqLiteDatabase.query(TBL_NAME, null, null, null, null, null, null); while (cursor.moveToNext()){ int id = cursor.getInt(cursor.getColumnIndex("id")); String name = cursor.getString(cursor.getColumnIndex("name")); int image = cursor.getInt(cursor.getColumnIndex("image")); int score = cursor.getInt(cursor.getColumnIndex("score")); int type = cursor.getInt(cursor.getColumnIndex("score")); int sales = cursor.getInt(cursor.getColumnIndex("sales")); int qisong = cursor.getInt(cursor.getColumnIndex("qisong")); store store=new store(1,"赵家外卖",20,R.drawable.zhao,1,1,20); store.setId(id); store.setName(name); store.setImage(image); store.setScore(score); store.setType(type); store.setSales(sales); store.setQisong(qisong); storeList.add(store); } return storeList; } public void delete(int id) { if (db == null) db = getWritableDatabase(); db.delete(TBL_NAME, "id=?", new String[]{String.valueOf(id)}); } public void update(ContentValues values, int id) { db = getWritableDatabase(); db.update(TBL_NAME, values, "id=?", new String[]{String.valueOf(id)}); db.close(); }}
    5.5 商品实体代码package com.example.a11469.food;import java.io.Serializable;public class Goods implements Serializable { private int id; private int sid; private String price; private String goodName; private int image; private String content; private int sales; private int like1; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getSid() { return sid; } public void setSid(int sid) { this.sid = sid; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; } public String getGoodName() { return goodName; } public void setGoodName(String goodName) { this.goodName = goodName; } public int getImage() { return image; } public void setImage(int image) { this.image = image; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public int getSales() { return sales; } public void setSales(int sales) { this.sales = sales; } public int getLike1() { return like1; } public void setLike1(int like1) { this.like1 = like1; } public Goods(int id, int sid, String price, int image, String content, int sales, int like1, String goodName) { this.id = id; this.sid = sid; this.goodName=goodName; this.price = price; this.image = image; this.content = content; this.sales = sales; this.like1 = like1; }}
    5.6 商品数据库package com.example.a11469.food;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import java.util.ArrayList;import java.util.List;public class GoodsDBHelper extends SQLiteOpenHelper { private SQLiteDatabase db; private static final String DB_NAME="goods.db"; private static final String TBL_NAME="goodsTab"; public GoodsDBHelper(Context context, String daname, SQLiteDatabase.CursorFactory factory,int version){ super(context,daname,factory,version); } @Override public void onCreate(SQLiteDatabase db) { this.db=db; String sql ="create table goodsTab(id integer primary key autoincrement, sid integer,price text,image integer,content text,sales integer,like1 integer,goodsName text)"; db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } public void insert(ContentValues contentValues){ SQLiteDatabase sqLiteDatabase =getWritableDatabase(); sqLiteDatabase.insert("goodsTab",null,contentValues); sqLiteDatabase.close(); } public List<Goods> query(){ List<Goods> personList = new ArrayList<>(); SQLiteDatabase sqLiteDatabase = getReadableDatabase(); Cursor cursor = sqLiteDatabase.query(TBL_NAME,null,null,null,null,null,null); while (cursor.moveToNext()){ Goods goods = new Goods(1,1,"1",1,"c0",1,1,"ccxax"); goods.setId(cursor.getInt(cursor.getColumnIndex("id"))); goods.setSid(cursor.getInt(cursor.getColumnIndex("sid"))); goods.setPrice(cursor.getString(cursor.getColumnIndex("price"))); goods.setImage(cursor.getInt(cursor.getColumnIndex("image"))); goods.setContent(cursor.getString(cursor.getColumnIndex("content"))); goods.setSales(cursor.getInt(cursor.getColumnIndex("sales"))); goods.setLike1(cursor.getInt(cursor.getColumnIndex("like1"))); goods.setGoodName(cursor.getString(cursor.getColumnIndex("goodsName"))); personList.add(goods); } return personList; } public void delete(int id){ if(db==null) db = getWritableDatabase(); db.delete(TBL_NAME,"id=?",new String[]{String.valueOf(id)}); } public void update(ContentValues contentValues,int id){ SQLiteDatabase sqLiteDatabase = getWritableDatabase(); sqLiteDatabase.update("goodsTab",contentValues,"id=?",new String[]{id+""}); sqLiteDatabase.close(); }}
    5.7 商品适配器package com.example.a11469.food;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;public class GoodsAdapter extends BaseAdapter { private Context mContext; private List<Goods> mList; public GoodsAdapter(Context mContext, List<Goods> mList) { this.mContext = mContext; this.mList = mList; } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } public View getView(int i, View view, ViewGroup viewGroup) { view = LayoutInflater.from(mContext).inflate(R.layout.goodsitem,null); ImageView goods_image = view.findViewById(R.id.goods_image); TextView goods_name = view.findViewById(R.id.goods_name); TextView goods_like = view.findViewById(R.id.good_like); TextView goods_price = view.findViewById(R.id.goods_price); TextView goods_sales = view.findViewById(R.id.goods_sales); goods_image.setImageDrawable(mContext.getResources().getDrawable(mList.get(i).getImage())); goods_name.setText(mList.get(i).getGoodName()); goods_like.setText(mList.get(i).getLike1()+""); goods_price.setText(mList.get(i).getPrice()+""); goods_sales.setText(mList.get(i).getSales()+""); return view; }}
    5.8 商品详细信息package com.example.a11469.food;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;public class GoodsAdapter extends BaseAdapter { private Context mContext; private List<Goods> mList; public GoodsAdapter(Context mContext, List<Goods> mList) { this.mContext = mContext; this.mList = mList; } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } public View getView(int i, View view, ViewGroup viewGroup) { view = LayoutInflater.from(mContext).inflate(R.layout.goodsitem,null); ImageView goods_image = view.findViewById(R.id.goods_image); TextView goods_name = view.findViewById(R.id.goods_name); TextView goods_like = view.findViewById(R.id.good_like); TextView goods_price = view.findViewById(R.id.goods_price); TextView goods_sales = view.findViewById(R.id.goods_sales); goods_image.setImageDrawable(mContext.getResources().getDrawable(mList.get(i).getImage())); goods_name.setText(mList.get(i).getGoodName()); goods_like.setText(mList.get(i).getLike1()+""); goods_price.setText(mList.get(i).getPrice()+""); goods_sales.setText(mList.get(i).getSales()+""); return view; }}
    5.9 订单实体package com.example.a11469.food;import java.io.Serializable;public class Order implements Serializable { private int id; private int image; private String sname; private String goods_name; private String price; private int sid; private String uname; public Order(int id, int image, String sname, String goods_name, String price, int sid,String uname){ this.image =image; this.sname = sname; this.goods_name= goods_name; this.price = price; this.sid = sid; this.id = id; this.uname = uname; } public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getSid() { return sid; } public void setSid(int sid) { this.sid = sid; } public int getImage() { return image; } public void setImage(int image) { this.image = image; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getGoods_name() { return goods_name; } public void setGoods_name(String goods_name) { this.goods_name = goods_name; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; }}
    5.10 orderActivity代码package com.example.a11469.food;import android.content.DialogInterface;import android.content.Intent;import android.support.v7.app.AlertDialog;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.AdapterView;import android.widget.Button;import android.widget.ListView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;public class OrderActivity extends AppCompatActivity { TextView mine; TextView waimai; ListView listView1; OrderAdapter orderAdapter; List<User> userList; List<Order> OrderList; List<Order> OrderList1; Button bt_evaluate; Button bt_again; public static String name; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_form); OrderList = new ArrayList<>(); OrderList1 = new ArrayList<>(); mine = findViewById(R.id.form_mine); OrderDBHelper dbHelper = new OrderDBHelper(OrderActivity.this, "order.db", null, 1); waimai = findViewById(R.id.form_shop); bt_evaluate = findViewById(R.id.item_btnjudge); bt_again = findViewById(R.id.item_nextone); name = getIntent().getStringExtra("zhanghao"); OrderList = dbHelper.query(); for(Order o:OrderList){ if(name!=null){ if(name.equals(o.getUname())){ OrderList1.add(o); }} else{ OrderList1.clear(); } } listView1 = findViewById(R.id.form_list); orderAdapter = new OrderAdapter(this, OrderList1, OrderActivity.this); listView1.setAdapter(orderAdapter); mine.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(OrderActivity.this, MineActivity.class); intent.putExtra("zhanghao", name); startActivity(intent); } }); waimai.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(OrderActivity.this, ShouyeActivity.class); intent.putExtra("zhanghao", name); startActivity(intent); } }); listView1.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, final View view, int i, long l) { final int s = i; AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext()); builder.setMessage("确定删除吗?") .setTitle("提示"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int which) { dialogInterface.dismiss(); OrderDBHelper dbHelper2 = new OrderDBHelper(OrderActivity.this, "order.db", null, 1); dbHelper2.delete(OrderList1.get(s).getId()); OrderList1.clear(); OrderList1.addAll(dbHelper2.query()); orderAdapter.notifyDataSetInvalidated(); } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.dismiss(); } }); AlertDialog dialog = builder.create(); dialog.show(); } }); listView1.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) { Intent intent = new Intent(OrderActivity.this, OrderMoreActivity.class); intent.putExtra("zhanghao", name); Bundle bundle = new Bundle(); bundle.putSerializable("Orderer", OrderList1.get(i)); intent.putExtras(bundle); startActivity(intent); return true; } }); }}
    5.11 订单适配器package com.example.a11469.food;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.Button;import android.widget.ImageView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;public class OrderAdapter extends BaseAdapter { private Context mContext; OrderActivity orderActivity; List<Order> orderList; Order o; public OrderAdapter(Context context,List<Order> list,OrderActivity orderActivity){ this.orderActivity = orderActivity; this.mContext = context; this.orderList = list; } public int getCount() { return orderList.size(); } @Override public Object getItem(int i) { return null; } @Override public long getItemId(int i) { return 0; } @Override public View getView(final int i, View view, ViewGroup viewGroup) { view = LayoutInflater.from(mContext).inflate(R.layout.form_item_layout,null); ImageView image =view.findViewById(R.id.order_image); TextView shop_name = view.findViewById(R.id.item_shopname); TextView goods_name = view.findViewById(R.id.item_productname); View fenge = view.findViewById(R.id.fenge); TextView money = view.findViewById(R.id.item_money); Button judge = view.findViewById(R.id.item_btnjudge); Button nextone = view.findViewById(R.id.item_nextone); image.setImageDrawable(mContext.getResources().getDrawable(orderList.get(i).getImage())); shop_name.setText(orderList.get(i).getSname()); goods_name.setText(orderList.get(i).getGoods_name()); money.setText(orderList.get(i).getPrice()); judge.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(mContext,EvaluateActivity.class); Bundle bundle = new Bundle(); bundle.putSerializable("Evaluate",orderList.get(i)); intent.putExtras(bundle); mContext.startActivity(intent); } }); nextone.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(mContext,ShopcarActivity.class); Bundle bundle= new Bundle(); bundle.putSerializable("Order_Shopcar",orderList.get(i)); intent.putExtra("zhanghao",OrderActivity.name); intent.putExtras(bundle); mContext.startActivity(intent); } }); return view; }}
    5.12 订单数据库package com.example.a11469.food;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import java.util.ArrayList;import java.util.List;public class OrderDBHelper extends SQLiteOpenHelper { private SQLiteDatabase db; private static final String DB_NAME="order.db"; private static final String TBL_NAME="orderTab"; public OrderDBHelper(Context context, String daname, SQLiteDatabase.CursorFactory factory,int version){ super(context,daname,factory,version); } @Override public void onCreate(SQLiteDatabase db) { this.db=db; String sql ="create table orderTab(id integer primary key autoincrement, image integer,sname text,good_name text,price text,sid integer,uname text)"; db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } public void insert(ContentValues contentValues){ SQLiteDatabase sqLiteDatabase =getWritableDatabase(); sqLiteDatabase.insert("orderTab",null,contentValues); sqLiteDatabase.close(); } public List<Order> query(){ List<Order> personList = new ArrayList<>(); SQLiteDatabase sqLiteDatabase = getReadableDatabase(); Cursor cursor = sqLiteDatabase.query("orderTab",null,null,null,null,null,null); while (cursor.moveToNext()){ Order order = new Order(1,1,"12","23","ef",11,"1"); order.setId(cursor.getInt(cursor.getColumnIndex("id"))); order.setImage(cursor.getInt(cursor.getColumnIndex("image"))); order.setSname(cursor.getString(cursor.getColumnIndex("sname"))); order.setGoods_name(cursor.getString(cursor.getColumnIndex("good_name"))); order.setPrice(cursor.getString(cursor.getColumnIndex("price"))); order.setSid(cursor.getInt(cursor.getColumnIndex("sid"))); order.setUname(cursor.getString(cursor.getColumnIndex("uname"))); personList.add(order); } return personList; } public void delete(int id){ if(db==null) db = getWritableDatabase(); db.delete(TBL_NAME,"id=?",new String[]{String.valueOf(id)}); } public void update(ContentValues contentValues,int id){ SQLiteDatabase sqLiteDatabase = getWritableDatabase(); sqLiteDatabase.update("orderTab",contentValues,"id=?",new String[]{id+""}); sqLiteDatabase.close(); }}
    5.13 ordermoreActivity代码package com.example.a11469.food;import android.content.Intent;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.ImageView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;public class OrderMoreActivity extends AppCompatActivity { Button turnback; TextView storename; List<User> userList; Order o; int tupian; ImageView imageView; Intent intent; Bundle bundle; public static String loginname; TextView goodsname; TextView goodprice; TextView useraddress; TextView orderid; TextView person; int id; String goodsname1; String goodprice2; String storename2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.order_more); userList=new ArrayList<>(); intent=getIntent(); imageView=findViewById(R.id.order_more_img); bundle = intent.getExtras(); o=(Order)bundle.get("Orderer"); loginname=intent.getStringExtra("zhanghao"); storename=findViewById(R.id.order_more_storename); goodsname=findViewById(R.id.order_more_goodsname); goodprice=findViewById(R.id.order_more_price); useraddress=findViewById(R.id.order_more_address); person=findViewById(R.id.order_more_person); orderid=findViewById(R.id.order_more_orderid); final String name=getIntent().getStringExtra("zhanghao"); turnback=findViewById(R.id.order_more_turnback); person.setText(name); id=o.getId(); tupian=o.getImage(); storename2=o.getSname(); goodsname1=o.getGoods_name(); goodprice2=o.getPrice(); orderid.setText(String.valueOf(id)); goodprice.setText(goodprice2); goodsname.setText(goodsname1); storename.setText(storename2); imageView.setImageResource(tupian); UserDBHelper dbHelper = new UserDBHelper(OrderMoreActivity.this, "user2.db", null, 1); userList=dbHelper.selcet(); for(User u:userList){ if(u.getNumber().equals(name)){ useraddress.setText(u.getAddress()); break;} } turnback.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent=new Intent(OrderMoreActivity.this,OrderActivity.class); intent.putExtra("zhanghao",loginname); startActivity(intent); } }); }}
    6 系统测试与结果系统测试与结果部分主要对本系统已实现的所有功能进行测试,比较测试结果与预期结果,并做出分析,从而找出系统的bug,以便及时对系统进行修复。本部分还包括对测试思路和环境搭建以及测试的结果分析。
    6.1 测试思路和环境搭建本节将对本系统的测试思路以及环境搭建进行叙述。包括对系统测试的大致方向进行叙述,以及对本系统搭建所需要的硬件环境和软件环境的叙述。
    6.1.1 测试思路为了对系统做出更为全面的测试,本次测试将对已完成的系统功能进行测试,并对结果进行分析。具体的测试方案如下:

    功能测试:主要测试的功能有用户登录、用户注册、新闻搜索功能、个人信息修改功能
    安全性测试:主要测试系统的安全性,包括使用不同角色的用户登录,测试用户登录后角色所对应的功能是否正确

    6.1.2 环境搭建
    硬件环境

    硬盘:1TB内存:16GCPU:Inter(R)Core(TM) i7-6700HQ CPU @2.60GHz
    软件环境

    在本地(windows10系统)下搭建Java环境:安装JDK1.8,配置环境变量在本地(windows10系统)下搭建android 开发环境,安装android studio ,下载android开发Sdk。下载各种支持插件,运行库注册Bmob云服务器账号,在云端创建应用,输入应用包名,得到连接应用的开发密匙,并设计云端数据库,用作存储本地数据在Android Studio中创建应用,并搭建项目的整体框架,开始实施编码逻輯。并尝试连接Bmob云数据库,实现数据操作

    6.2 测试与结果分析根据上节中的测试思路对本系统进行黑盒测试,将所有可能的错误情况测试一遍,记录预期结果以及实际情况,并对测试的结果进行分析
    6.2.1 加入购物车功能测试测试内容:进入系统后,进行搜索新闻。









    点击菜品加入购物车
    菜品加入购物车



    6.2.2 搜索功能测试测试内容:进入修改信息界面,修改用户信息。









    在搜索框内输入空
    在搜索框内输入肯德基






    在搜索框内输入沙拉拉轻食
    沙拉拉轻食店家界面



    6.2.3 判断登录与否功能测试测试内容:进入修改信息界面,修改用户信息。









    不登陆直接点单
    提示登录






    登陆了注销以后再次点单
    提示登录






    登录以后查看订单
    未登录查看订单



    6 实习总结在实验的最初我们花了大量的时间来做需求分析。需求就像我们人生的航标一样,有了它,我们的开发才有方向,不然都是无用的。每天我们制定开发计划,一小步一小步的走,一行一行慢慢的写。看着一个个小小功能的实现与完善,我们都对他竖起大拇指。
    Android的UI布局是很重要的一环。布局模式以相对模式为主,线线布局模式可以在比较简单的include进行完成,最重要的一点就是:我们可以自己通过重写方法或者通过实现View或者Layout等类进行扩充项目需要的布局(或者控件),在学习界面中,我发现Android为我们提供了很好的类似反射机制,通过Layout文件夹下的配置文件,可以快速的形成界面,在配置文件可以设置属性或者样式都是很快捷方便。对比较特殊的界面也可以通过处理嵌入到指定的界面,同样你可以通过java代码直接创建View进行添加,不过这种方式比较复杂。对一些点击、选中、按键等处理的事件,界面之间的跳转Intent管理,通过Bundle对数据在界面之间进行传输
    在Android编程过程中巩固熟悉了Java的编程。由于Android应用程序的开发离不开Java的支持,所以基础的 Java知识是必须的。Android 系统是基于Linux的手机操作系统平台,要深入系统的学习Android,不仅仅是有 Java和Android应用开发,必须要具备Linux, C/C++高级编程才能深入的涉及Androi dFramework和Android内核开发。成为Android开发的高素质人才。所以,在后续对Android的学习中可能会看一-些较底层的书籍。由于这次实习时间较短,对于Android应用程序的高级编程讲的很少,是这次实习中的不足。
    当一个团队合作时,并不是遇到的每个团队,团队成员都令你满意、每个团队都可以融洽相处。这个团队之所以形成,主要目的为了成功完成这个项目。进入团队先要明白进入团队的目的,这个团队成立的目的。明白了这两点,许多在团队中遇到的问题都可以避开或很好的解决。所有成员都围绕如何能更好更完善的完成项目为主。当然在遇到必然可能影响到项目开发进度的问题时,还需及时解决。
    5 留言 2020-06-14 17:07:26 奖励55点积分
  • 利用网页打开本地exe文件

    首次接到“利用网页打开本地exe文件”这个任务时,还真有点摸不着头脑,后来细想,在淘宝上点击卖家的旺旺能开启本地的旺旺,这不就是利用的网页打开本地exe文件吗?
    了解这种实实在在存在的合理需求后,开始调查。经过网上搜索查询,主要归纳为两种实现方式,方式一:利用JS打开本地exe文件。一般浏览器,由于安全问题,都会禁止掉这个特性,这就导致部分浏览器不支持该种方式。方式二:利用浏览器外部协议(URL Procotol)打开本地exe文件。用这种方式实现,任何浏览器都兼容。在实际开发中,当然首选方式二。
    一、利用注册表文件将外部协议写入注册表[HKEY_CLASSES_ROOT\PCTV]@="PCTVProtocol""URL Protocol"="\"C:\\Program Files (x86)\\PCTV双模软终端_64位\\PCTV.exe\""[HKEY_CLASSES_ROOT\PCTV\DefaultIcon]@="\"C:\\Program Files (x86)\\PCTV双模软终端_64位\\PCTV.exe,1\"" [HKEY_CLASSES_ROOT\PCTV\shell] [HKEY_CLASSES_ROOT\PCTV\shell\open] [HKEY_CLASSES_ROOT\PCTV\shell\open\command]@="\"C:\\Program Files (x86)\\PCTV双模软终端_64位\\PCTV.exe\" \"%1\""将以上代码存入reg文件中,双击文件执行即可。在浏览器中输入 “pctv://” 或 “pctv://param1,param2” ,执行后即可打开对应路径下的exe文件。
    二、 在安装exe文件时将外部协议写入注册表在实际部署中,不会让客户安装完程序再手动单击注册表文件将安装路径写入注册表,最容易让人接受的方式就是在安装exe文件时将安装路径写入注册表。利用Inno Setup打包exe文件时,在脚本中加入如下代码即可:
    [Registry] Root:HKCR;Subkey:"PCTV";ValueType:string;ValueName:"URL Protocol";ValueData:"{app}\{#MyAppExeName}";Flags:createvalueifdoesntexist uninsdeletekeyRoot:HKCR;Subkey:"PCTV\DefaultIcon";ValueType:string;ValueData:"{app}\{#MyAppExeName}";Flags:createvalueifdoesntexist uninsdeletekey Root:HKCR;Subkey:"PCTV\shell";Flags:createvalueifdoesntexist uninsdeletekeyRoot:HKCR;Subkey:"PCTV\shell\open";Flags:createvalueifdoesntexist uninsdeletekeyRoot:HKCR;Subkey:"PCTV\shell\open\command";ValueType:string;ValueData:"{app}\{#MyAppExeName} ""%1""";Flags:createvalueifdoesntexist uninsdeletekey这样,在浏览器中输入 “pctv://” 或 “pctv://param1,param2” ,执行后即可打开对应路径下的exe文件。
    以上两种写入注册的方式,允许在外部协议中带参数。
    三、遇到的问题在利用外部协议打开本地exe文件时,通过查看日志记录,看到会出现路径不对的问题。通过查看代码在程序中用Environment.CurrentDirectory获取可执行文件的路径,但是通过浏览器打开exe文件时,Environment.CurrentDirectory获取的是浏览器exe文件的路径,这样在程序中就会报错。解决方法是将Environment.CurrentDirectory修改为Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory)即可。
    1 留言 2019-12-06 17:05:22 奖励15点积分
  • Android Studio使用POI读取及修改Word文档(.docx格式)

    一、说明上一篇文章(Android Studio使用POI读取及修改Word文档(.doc格式))使用poi对.doc格式的word文档进行了读取和更改,但很多情况下还需要在word文档中插入图片,这时就需要对.docx格式的word进行操作了。
    二、实现过程2.1 制作文书文书在源代码中可以直接看到,简单说明一下:文书有普通字段、表格、特定位置的图片,又在页眉页脚中加了普通字段和表格,基本满足对于word操作的所有情况。

    2.2 导包还是上篇中poi-3.9压缩资源包中的jar包,对.docx格式文档的操作用到XWPFDocument方法,使用到所有ooxml相关的jar包。

    2.3 build配置这次的build配置有点特殊,特别拿出来截图一下。就像上一篇说的一样,apache的很多配置在安卓是跑不通的,这次导包后,你会遇到方法过多,文件重复,基于jdk1.6以上版本的变异保存等一系列问题,可以按照下面的方法处理。当然不同的android studio版本可能也会有不同的处理方法,可以百度一下。

    权限还是储存权限,直接上代码吧,注解的也很详细。
    2.4 实现源码public class MainActivity extends AppCompatActivity { //创建生成的文件地址 private static final String newPath = "/storage/emulated/0/test.docx"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 0); } Button go = (Button) findViewById(R.id.go); go.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { initData(); } }); } private void initData() { Map<String, Object> map = new HashMap<String, Object>(); map.put("$TITLE$", "标题");//$TITLE$只是个标识符,你也可以用${},[]等等 map.put("$TXT1$", "表格第一行"); map.put("$CONTENT1$", "第一行内容"); map.put("$TXT2$", "表格第二行"); map.put("$CONTENT2$", "第二行内容"); map.put("$CONTENT3$", "页脚中的内容"); map.put("$TXT4$", "页脚表格第一行"); map.put("$CONTENT4$", "页脚第一行内容"); map.put("$TXT5$", "页脚表格第二行"); map.put("$CONTENT5$", "页脚第二行内容"); try { //读取示例文书 InputStream is = getAssets().open("test.docx"); //自定义的XWPFDocument,解决官方版图片不显示问题 CustomXWPFDocument document = new CustomXWPFDocument(is); //读取段落(一般段落,页眉页脚没办法读取) List<XWPFParagraph> listParagraphs = document.getParagraphs(); processParagraphs(listParagraphs, map); //读取页脚 List<XWPFFooter> footerList = document.getFooterList(); processParagraph(footerList, map); //处理表格 Iterator<XWPFTable> it = document.getTablesIterator(); while (it.hasNext()) {//循环操作表格 XWPFTable table = it.next(); List<XWPFTableRow> rows = table.getRows(); for (XWPFTableRow row : rows) {//取得表格的行 List<XWPFTableCell> cells = row.getTableCells(); for (XWPFTableCell cell : cells) {//取得单元格 if ("$IMG$".equals(cell.getText())) { //直接插入图片会在文档的最底端,所以要插在固定位置,要把图片放在表格里 //所以使用判断单元格,并清除单元格放置图片的方式来实现图片定位 cell.removeParagraph(0); XWPFParagraph pargraph = cell.addParagraph(); document.addPictureData(getAssets().open("1.png"), XWPFDocument.PICTURE_TYPE_PNG); document.createPicture(document.getAllPictures().size() - 1, 120, 120, pargraph); } List<XWPFParagraph> paragraphListTable = cell.getParagraphs(); processParagraphs(paragraphListTable, map); } } } FileOutputStream fopts = new FileOutputStream(newPath); document.write(fopts); if (fopts != null) { fopts.close(); } } catch (Exception e) { e.printStackTrace(); } } //处理页脚中的段落,其实就是用方法读取了下页脚中的内容,然后也会当做一般段落处理 private void processParagraph(List<XWPFFooter> footerList, Map<String, Object> map) { if (footerList != null && footerList.size() > 0) { for (XWPFFooter footer : footerList) { //读取一般段落 List<XWPFParagraph> paragraphs = footer.getParagraphs(); processParagraphs(paragraphs, map); //处理表格 List<XWPFTable> tables = footer.getTables(); for (int i = 0; i < tables.size(); i++) { XWPFTable xwpfTable = tables.get(i); List<XWPFTableRow> rows = xwpfTable.getRows(); for (XWPFTableRow row : rows) {//取得表格的行 List<XWPFTableCell> cells = row.getTableCells(); for (XWPFTableCell cell : cells) {//取得单元格 List<XWPFParagraph> paragraphListTable = cell.getParagraphs(); processParagraphs(paragraphListTable, map); } } } } } } //处理段落 public static void processParagraphs(List<XWPFParagraph> paragraphList, Map<String, Object> param) { if (paragraphList != null && paragraphList.size() > 0) { for (XWPFParagraph paragraph : paragraphList) { List<XWPFRun> runs = paragraph.getRuns(); for (XWPFRun run : runs) { String text = run.getText(0); if (text != null) { boolean isSetText = false; for (Map.Entry<String, Object> entry : param.entrySet()) { String key = entry.getKey(); if (text.indexOf(key) != -1) { isSetText = true; Object value = entry.getValue(); if (value instanceof String) {//文本替换 text = text.replace(key, value.toString()); } } } if (isSetText) { run.setText(text, 0); } } } } } }}
    2.5 效果展示
    本文转载自:https://blog.csdn.net/qq_21972583/article/details/82740281
    4 留言 2019-10-08 13:03:54 奖励16点积分
  • 三元组存储稀疏矩阵实现逆置

    一、实验说明1.1 稀疏矩阵压缩存储压缩存储值存储极少数的有效数据。使用{row,col,value}三元组(Trituple)存储 每一个有效数据,三元组按原矩阵中的位置,以行优先级先后顺序依次存放。
    1.2 矩阵逆置将原矩阵的行、列对换,也就是将[i][j]和[j][i]位置上的数据对换。
    二、实验背景2.1 三元组是什么?三元组是用来存储稀疏矩阵的一种压缩方式。

    2.2 那么为什么会用到三元组?二维数组表示的稀疏矩阵有着大量0存在,而这些0造成了大量的内存浪费,是我们的忽略对象(实际应用加密解密中可以使用其他我们用不到的数来进行混淆)。
    稀疏矩阵
    在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵;与之相反,若非0元素数目占大多数时,则称该矩阵为稠密矩阵。定义非零元素的总数比上矩阵所有元素的总数为矩阵的稠密度。
    特性

    稀疏矩阵其非零元素的个数远远小于零元素的个数,而且这些非零元素的分布也没有规律
    稀疏因子是用于描述稀疏矩阵的非零元素的比例情况。设一个n*m的稀疏矩阵A中有t个非零元素,则稀疏因子δδ的计算公式如下:δ=tn∗mδ=tn∗m(当这个值小于等于0.05时,可以认为是稀疏矩阵)

    矩阵压缩
    存储矩阵的一般方法是采用二维数组,其优点是可以随机地访问每一个元素,因而能够较容易地实现矩阵的各种运算,如转置运算、加法运算、乘法运算等。
    对于稀疏矩阵来说,采用二维数组的存储方法既浪费大量的存储单元用来存放零元素,又要在运算中花费大量的时间来进行零元素的无效计算。所以必须考虑对稀疏矩阵进行压缩存储。
    三、实验代码#include<stdio.h>#include<stdarg.h>#include<stdlib.h>#include<string.h>#include<malloc.h>#define MAXSIZE 12500//假设非零元个数的最大值为12500 #define MAX_ARRAY_DIM 8#define ElemType int#define OK 1typedef struct{ ElemType *base;//数组元素基址 ,由InitArray分配 int dim=2;//数组维数为二 int *bounds;//数组维界基址,由InitArray分配 int *constants; //数组映像函数常量基址,由InitArray分配 }Array;typedef struct{ int i,j;//该非零元的行下标和列下标 ElemType e;}Triple;//三元组类型typedef struct{ Triple data[MAXSIZE+1];//非零元三元组表,data[0]未用 int mu,nu,tu;}TSMatrix;//稀疏矩阵类型int b[10][10]={0};int a[10][10]={ 0,0,0,0,0,0,7,0,0,8, 5,0,0,0,0,0,0,0,0,3, 2,0,1,0,0,0,0,0,0,0, 0,0,4,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,6,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, };int FastTransposeSMatrix(TSMatrix M,TSMatrix &T) {//快速转置 int col,num[100],cpot[100],p,q,t,i,j,m,n; for(i=0;i<10;i++){ for(j=0;j<10;j++){ if(a[i][j]!=0){ M.data[M.tu].i=i; M.data[M.tu].j=j; M.data[M.tu].e=a[i][j]; M.tu++; } } } T.mu=M.nu; T.nu=M.mu; T.tu=M.tu;//建立同样大小的矩阵 if(T.tu){ for(col=0;col<=M.nu;++col) num[col]=0;//使每一列的非零元个数清空为零相当于初始化 for(t=0;t<M.tu;++t) ++num[M.data[t].j]; //求M中每一列含非零元个数 cpot[0]=0; //求第col列中第一个非零元在b.bata中的序号 for(col=1;col<M.nu;++col) cpot[col]=cpot[col-1]+num[col-1]; for(p=0;p<M.tu;++p){ col=M.data[p].j; q=cpot[col]; T.data[q].i=M.data[p].j; T.data[q].j=M.data[p].i; T.data[q].e=M.data[p].e; cpot[col]++; } } for(t=0;t<T.tu;t++){ m=T.data[t].i; n=T.data[t].j; b[m][n]=T.data[t].e; //输出非零值在原矩阵中的位置及数值 } for(i=0;i<T.mu;i++){ for(j=0;j<T.nu;j++) printf("%d\ ",b[i][j]); printf("\n"); } return 0;}int main(){ printf("矩阵转置前:<0,6,7>,<0,9,8>,<1,0,5>,<1,9,3>,<2,0,2>,<2,2,1>,<3,2,4>,<8,1,6>\n"); printf("矩阵转置后:<0,1,5>,<0,2,2>,<1,8,6>,<2,2,1>,<2,3,4>,<6,0,7>,<9,0,8>,<9,1,3>\n"); TSMatrix M,T; int x,y; M.mu=10; M.nu=10; M.tu=0; printf("转置前:\n"); for(x=0;x<M.mu;x++){ for(y=0;y<M.nu;y++){ printf("%d\ ",a[x][y]); } printf("\n"); } printf("转置后:\n"); FastTransposeSMatrix(M,T); return 0; }
    0 留言 2020-12-18 09:43:40 奖励30点积分
  • 基于C++的商品信息管理系统

    1 课题需求定义一个基类来维护所有商品的基本信息。 每种商品维护着三种基本信息,包括:商品库存量、商品价格以及商品销售量。另外, 定义一个派生 类来维护带有折扣的商品的信息。此时,每种商品除了包含商品库存量、商品价格以及商品销售量三种基本信息外,还包含一个折扣信息,用于表示该商品出售时的折扣。
    2 总体设计该系统的主要功能包括:商品信息录人、商品信息输出、商品销售、商品进货、统计库存不足商品、统计营业额、统计销量最高和销量最低的商品、统计营业额最高和营业额最低的商品。
    3 详细设计3.1 商晶信息录入在基类和派生类中都有一个成员函数来实现该功能。该成员函数包含一个形参,用于表示商品的种类。根据商品的种类来完成每种商品信息的初始化。对于基类,商品信息包括:商品数量和商品价格。在基类中,每种商品维护着三种信息;商品库存量、商品价格和商品销售量。三种信息分别用三个数组来存储,并作为类的数据成员存在。对于派生类,商品信息包括;商品数量、商品价格以及商品折扣。在派生类中,每种商品维护着四种信息:商品库存量、商品价格、商品销售量以及商品折扣。四种信息分别用四个数组来存储,并作为类的数据成员存在。在函数中进行信息录人时,用输人的商品数量来初始化商品库存量,用输人的商品价格来初始化商晶价格,并将商品销售量初始化为0。对于派生类,用输人的商品折扣来初始化商品折扣。
    3.2 商品信息输出在基类和派生类中都有一个成员函数来实现该功能。将所有商品的信息依次输出到屏幕上显示,每种商品信息显示一行。对于基类,输出的信息包括:商品库存量、商品价格和商品销售量。对于派生类,输出的信息包括:商品库存量、商品价格、商品销售量以及商品折扣。
    3.3 商品销售该功能由基类的一个单独的成员函数来实现。根据商品编号(编号从1开始)对某种商品进行销售,销售时需指定具体的商品销售数量。在对商品进行销售之前,需要对输人的商品编号和商品销售数量的信息进行合法性检测,只有输入数据合法时,才能进行商品销售。若输人数据合法,则根据具体的商品编号和商品销售数量对某种商品进行销售,销售成功后,需要相应的修改该商品的库存量和销售量的信息。注意:当库存量无法满足销售量需求时,同样不能进行商品销售。
    3.4 商品进货该功能由基类的一个单独的成员函数来实现。根据商品编号(编号从1开始)对某种商品进行进货,进货时需指定具体的商品进货数量。在对商品进行进货之前,需要对输人的商品编号和商品进货数量的信息进行合法性检测,只有输人数据合法时,才能进行商品进货。若输入数据合法,则根据具体的商品编号和商品进货数量对某种商品进行进货,进货成功后,需要相应的修改该商品的库存量信息。
    3.5 统计库存不足商品该功能由基类的一个单 独的成员函数来实现。 依次对所有商品的库存量进行检测,若某 种商品的库存量为0,则将该商品输出,每行输出一种商品。
    3.6 统计营业额在基类和派生类中都有一个成员函数来实现该功能。依次对每种商品的营业额进行统计,并输出该商品的营业额统计结果,每行输出一种商品。 对于基类,商品营业额计算公式为:商品价格X商品销售量。对于派生类,商品营业额计算公式为:商品价格X商品销售量X商品折扣。同时,将所有商品的营业额进行相加,在最后一行显示所有商品的总营业额。
    3.7 统计销量最高和销量最低的商品该功能由基类的一个单独的成员函数来实现。依次比较所有商品的销售量,从中找出销量最高和销量最低的商品。首先,定义两个变量分别用于存放最高销量和最低销量,初始时,将最高销量和最低销量都初始化为第一种商品的销售量。接下来,依次将其余商品的销售量与最高销量和最低销量进行比较。若当前商品的销售量大于最高销量,则将最高销量设置成该商品的销售量;此外,若当前商品的销售量小于最低销量,则将最低销量设置成该商品的销售量。
    3.8 统计营业额最高和营业额最低的商品在基类和派生类中都有一个成员函数来实现该功能。依次比较所有商品的营业额,从中找出营业额最高和营业额最低的商品。首先,定义两个变量分别用于存放最高营业额和最低营业额,初始时,将最高营业额和最低营业额初始化为第一种商品的营业额。接下来,依次将其余商品的营业额与最高营业额和最低营业额进行比较。若当前商品的营业额大于最高营业额,则将最高营业额设置成该商品的营业额;此外,若当前商品的营业额小于最低营业额,则将最低营业额设置成该商品的营业额。1587
    原文分析:
    Product::Product(){ num=NULL; price=NULL; sell=NULL; total=0;}/*对数据成员进行初始化。
    将商品种类初始化为0.将其余的指针成员初给化为空指针。
    Product::Product (int *_num,double *_price,int _total):total(_total){ num=new int[total]; price=new double[total]; sell=new int[total]; for(int i=0;i<total;++i) { num[i]=_num[i]; price[i]=_price[i]; sell[i]=0; }}
    对数据成员进行初始化。利用参数提供的商品种类对商品种类成员进行初始化。根据商品种类为其余指针成员开辟堆空间,并根据提供的形参对其进行初始化。
    Product::Product(const Product &prod):total(prod.total){ num =new int[total]; price=new double[total]; sell=new int[total]; for(int i=0;i<total;++i) { num[i]=num[i]; price[i]=price[i]; sell[i]=0; }}
    对数据成员进行初始化。利用参数提供的商品种类对商品种类成员进行初始化。根据商品种类为其余指针成员开辟堆空间,并根据提供的形参对其进行初始化。
    void Product::input(int _total){ total=_total; num=new int[total]; price=new double[total]; sell=new int[total]; for(int i=0;i<total;++i) { cout<<"输入第"<<i+1<<"种商品的信息(数量,价格):"; cin>>num[i]>>price[i]; sell[i]=0; }}
    利用参数提供的商品种类值对商品种类成员进行初始化,并根据商品种类为指针成员开辟堆空间。根据商品的种类依次输人每种商品的信息,信息包括:商品数量和商品价格。通过循环语句,每次循环输人一种商品的信息。
    void Product::output()const{ for(int i=0;i<total;++i) cout<<"[商品"<<i+1<<"]库存量:"<<num[i]<<",价格:"<<price[i]<<",销量:"<<sell[i]<<endl;}
    根据商品种类依次输出每种商品的信息信息包括:商品库存量,商品价格和商品销量。
    通过循环语句,每次循环输出一种商品的信息。
    void Product::sale(){ cout<<"请输入商品编号(1-"<<total<<")和销售数量(>0):"; int product,n; cin>>product>>n; if(n>num[product-1]) cout<<"商品"<<product<<"库存不足"<<endl; else { num[product-1]-=n; sell[product-1]+=n; cout<<"商品"<<product<<"销售成功"<<endl; }}
    需要对输人的商品编号和销售数量进行合法性检测,当输入数据合法时再根据具体的商品编号和销售数量对某种商品进行销售。此外,只有在某种商品的库存量能够满足销售数量的需求时,才能对该商品进行销售。
    void Product::stock(int total){ cout<<"请输入商品编号(1-"<<total<<")和销售数量(>0):"; int product,n; cin>>product>>n; if(n<0) cout<<"进货数量不能为负值!"<<endl; else { num[product-1]+=n; cout<<"商品"<<product<<"进货成功!"<<endl; }}
    需要对输入的商品编号和进货数量进行合法性检测,当输人数据合法时再根据具体的商品编号和进货数量对某种商品进行进货。
    void Product::lack()const{ for(int i=0;i<total;++i) if(num[i]==0) cout<<"商品"<<i+1<<"库存不足 !"<<endl;}
    依次对每种商品的库存量进行检测,并输出所有库存量为0的商品。通过循环语句,每次循环检测种商品的库存量。
    double Product::statistics ()const{ double sum=0.0; for(int i=0;i<total;++i) { cout<<"商品"<<i+1<<"营业额(元):"<<price[i]*sell[i]<<endl; sum+=price[i]*sell[i]; } return sum;}
    依次对每种商品的营业额进行统计,并输出该商品的营业额统计结果。商品营业额计算公式为:商品价格X商品销售量。通过循环语句,每次循环统计一种商品的营业额,并将其叠加到总营业额上。
    void Product::minmaxAmount()const{ int max_amount,min_amount; max_amount=sell[0]; min_amount=sell[0]; for (int i=1;i<total;++i) { if(sell[i]>max_amount) max_amount=sell[i]; if(sell[i]<min_amount) min_amount=sell[i]; }}
    依次将其余商品的销售量与最高销量和最低销量进行比较,若该商品的销售量大于最高销量,则将最高销量设置成该商品的销售量;另外,若该商品的销售量小于最低销量,则将最低销量设置成该商品的销售量。通过循环语句,每次循环比较一种商品的销售量。
    void Product::minmaxIncome()const{ double max_income,min_income; max_income=price[0]*sell[0]; min_income=price[0]*sell[0]; for(int i=1;i<total;++i) { if(price[i]*sell[i]>max_income) max_income=price[i]*sell[i]; if(price[i]*sell[i]<min_income) min_income=price[i]*sell[i]; }}
    依次将其余商品的营业额与最高营业额和最低营业额进行比较,若该商品的营业额大于最高营业额,则将最高营业额设置成该商品的营业额;另外,若该商品的营业额小于最低营业额,则将最低营业额设置成该商品的营业额。通过循环语句,每次循环比较一种 商品的营业额。
    DiscountProduct& DiscountProduct::opertor=(const DiscountProduct &dprod){ if(num!=NULL;) delete []num if(price!=NULL;) delete []price; if(sell!=NULL;) delete []sell; if(discount!=NULL;) delete []discount; total=prod.total; num=new int[total]; price=new double[total]; sell=new int[total]; discount=new double[total]; for(int i=0;i<total;++i) { num[i]=prod.num[i]; price[i]=prod.price[i]; sell[i]=prod.sell[i]; discount[i]=prod.discount[i]; } return *this;}
    首先,释放基类和派生类指针成员原有的堆空间。然后,再根据参数对象的商品种类成员初始化自身的商品种类成员,并根据商品种类为基类和派生类的指针成员开辟新的堆空间。最后根据参数对象的成员来初始化基类和派生类的成员。
    4 测试
    5 总结在本次实训中,通过课本与本次程序再一次的复习了,类的继承与派生的相关知识,并完成我的:商品信息管理系统设计。在编写程序过程中,遇到许许多多的困难,但是我每一次都是通过一次次的程序运行,将每一个错误逐一排除,遇到自己解决不了的就请教同学或者老师,在编写每一段程序的相关代码(不熟悉)都会去回顾一下课本或者老师上课所讲的ppt。
    在为期近两周的实训中,我体会颇多,学到了很多东西。我加强了对C++程序
    设计这门课程的认识,并且复习了自己以前学习到的知识。这些都使得我对计算机语言的学习有了更深入的认识!总之,通过这次实训,我收获颇丰,相信会为自己以后的学习和工作带来很大的好处。像职工信息表这样的程序设计,经历了平时在课堂和考试中不会出现的问题和考验。而这些问题,这并不是我们平时只靠课本,就可以轻易解诀的。
    所以,这次的实训锻炼了我们挑战难题,学会用已掌握的知识去解决具体问题的能力,进一步培养了独立思考问题和解决问题的能力。特别是学会了在Visual C++中 如何调试程序的方法。当然,老师的指导和同学的帮助也是不可忽视的,他们给了我许多提示和帮助,教会了我编译复杂程序的方法。
    我在编写程序之前,首先在心中有一个大体的框架。比如:每种商品都有3个基本信息,包括:商品库存量,商品价格以及商品库存量。然后,需要定义一个派生类(有折扣的商品含有折扣),用于表示该商品出售时的折扣。然后分八条来编写 分别是:商品信息录人、商品信息输出、商品销售、商品进货、统计库存不足商品、统计营业额、统计销量最高和销量最低的商品、统计营业额最高和营业额最低的商品。
    4 留言 2020-12-01 14:50:25 奖励42点积分
  • python实现对人脸的实时监测

    使用python实现对人脸的实时监测并将人脸保存到本地文件夹下1、先导入库【可能需要下载一下dlib包,注意一下版本是否对应】
    #导入库import cv2 import dlib import numpy as np2、开始编写#定义脸部特征检测器detector = dlib.getfrontal_face_detector() #得到人脸的正面cap = cv2.VideoCapture(0) #0代表打开摄像头frame_count = 0 #检测的帧数face_count = 0 #每一帧检测的人脸数margin = 0.2 # 边距比例while True:ret, frame = cap.read() #从摄像头获取的视频文件中读取第一帧if (ret != True): #判断摄像头是否正常打开并读取视频print(‘没有捕获到图像,请检查摄像头工作情况!’)breakinput_img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) #BGR转为RGBframe_count +=1 #帧数加一img_h,img_w, = np.shape(input_img) #获取图像的尺寸img_h:高,img_w:宽,第三个代表通道detected = detector(frame,1)#对当前帧检测faces = [] #存放当前帧中的脸部数量1代表的是上采样if len(detected) > 0: #当前帧检测到脸部,至少检测到一个 for i, d in enumerate(detected): #enumerate枚举函数,一个个的进行检测 face_count += 1#人脸数增加 # 脸部图像坐标与尺寸,分别表示左上角坐标,右下角坐标,和长宽,加一代表的是: x1, y1, x2, y2, w, h = d.left(), d.top(), d.right() + 1, d.bottom() + 1, d.width(), d.height() # 用边距做调整 #限制最小是0 xw1 = max(int(x1 - margin * w), 0) yw1 = max(int(y1 - margin * h), 0) #img_w-1是:限制最大的就是img-1 xw2 = min(int(x2 + margin * w), img_w - 1) yw2 = min(int(y2 + margin * h), img_h - 1); # 脸部图像坐标 face = frame[yw1:yw2 + 1, xw1:xw2 + 1, :]#加一是为了保存边界 file_name = "./dataset/valid/one/" + str(frame_count) +'_one'+str(i) + '.jpg'#文件路径 cv2.imwrite(file_name,face) #绘制人脸边框 cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2)#显示当前帧cv2.imshow("Face Detector", frame) # 按回车键终止视频检测if cv2.waitKey(1)&0xFF == 13: #1代表1ms,13代表回车键,27代表ESC键,ord('q') break3、打印运行结果print(‘已经完成了 {0} 帧检测,保存了 {1} 幅脸部图像’.format(frame_count, face_count))4、关闭摄像头和窗口cap.release()cv2.destroyAllWindows()
    2 留言 2020-10-14 14:36:47 奖励30点积分
  • 深度学习人脸性别识别

    1 实验目的
    了解机器学习的研究对象、研究内容、研究方法(包括理论基础和实验方法),和近年来的主要发展技术路线
    培养学生以机器学习理论为基础从事相关应用研究的能力,通过看——查阅资料、做——复现已有资料的实验或做一个机器学习应用课题、写——将自己的研究工作写成技术报告,完成整个大作业
    了解机器学习领域的新进展、新应用,并结合课堂教学和教材的内容,应用某一种或几种算法,解决问题并分析实验结果
    理解相关算法原理和关键技术,并以实验加以验证和实现
    撰写技术报告,将所做工作的方法、原理、实验设计、所得结果及其比较分析等写成技术报告,并附上主要参考文献,说明程序的运行环境和运行方法

    2 实验内容2.1 实验内容要求针对提供的人脸数据集,预测人脸性别。
    本次将提供 20000 多张已经分割的人脸图像,要求基于人脸图像自动识别该人性别。数据集的年龄从 1 岁覆盖到 100 多岁,包括了白种人、黄种人、黑种人等多种种族数据。数据集存在人脸姿态、光照、年龄等多种干扰,具有一定的挑战性。
    2.2 数据集数据集包括以下 csv 文件:

    train.csv:训练集,其中包括两列,第一列 id 是人脸图像的编号,即对应的文件名,第二列 label 是性别标签,0 表示男性,1 表示女性
    test.csv:测试集,只包括一列 id,即测试集中所有的人脸图像的编号。测试集中没有性别标签
    train文件夹:所有的训练图像,扩展名名.jpg,其命名与train.csv中的id命名一致
    test 文件夹:所有的测试图像,扩展名为jpg,其命名与test.csv中的id命名一致
    sampleSubmit.csv:提交文件的样本,其中包括两列,第一列 id 是所有测试集的人脸图像的编号,即对应的文件名,第二列 label 是模型输出的性别标签,0 表示男性,1 表示女性

    数据说明:

    id:图像文件名,其对应图像为 id.jpg
    label:图像标签,0 表示男性,1表示女性

    3 运行环境
    虚拟环境:Anaconda 4.5.4
    Python环境:Python 3.6.5
    编译器:vs code
    深度学习框架:pytorch1.5.1 , tensorflow2.3.0
    其他环境:jupyter, Kaggle notebook
    Python库:numpy,pandas,os,PIL,matplotlib

    4 算法原理与分析4.1 损失函数损失函数是一个非负实数函数,用来量化模型预测与真实标签之间的差异,下面介绍几种常用的损失函数:
    4.1.1 平方损失函数(MSE)平方损失函数较为容易理解,它直接测量机器学习模型的输出与实际结果之间的距离。这里可以定义机器学习模型的输出为y, 实际的结果为y_t,那么平方损失函数可以被定义为:

    4.1.2 交叉熵损失函数(CrossEntropy)二分类
    在二分的情况下,模型最后需要预测的结果只有两种情况,对于每个类别我们预测得到的概率分别为p和(1-p),此时表达式为:

    其中:

    y_i表示样本i的label,正类为1,负类为0
    p_i表示样本i预测为正的概率

    多分类
    多分类实际上是对二分类的扩展:

    其中:

    y_i表示第i类的标签
    p_i表示第i类的概率

    .2 梯度下降众所周知,在求最优解时常用到两个方法,两者均可用于线性/非线性拟合,梯度下降法通过不断地向梯度反方向迭代可以得到最优解,过程通常分为计算梯度和调整权值直至收敛
    4.2.1 随机梯度下降(SGD)随机梯度下降是指对于一个单样本计算损失,即随机一个样本更新一次梯度。

    这样的好处是计算f(x)的梯度和计算f(x+1)的梯度是可以并行计算的,大大提高了运行速度,而且其可以避免冗余数据的影响,但是并不是每次都朝着全局最优迭代,多以通常说地SGD都是mini-batch SGD。
    4.2.2 带动量的梯度下降(momentum)如下图所示,如果进行梯度下降法的一次迭代, 可以看到是慢慢摆动到最小值, 这种上下波动减慢了梯度下降法的速度, 理想的情况是,在纵轴上, 你希望学习慢一点, 因为你不想要这些摆动, 但是在横轴上,你希望加快学习,你希望快速从左向右移,移向最小值,移向红点。

    其基本的想法就是计算梯度的指数加权平均数,并利用该梯度更新你的权重。
    4.3 卷积神经网络基础的CNN由卷积(convolution),激活(activation)和池化(pooling)三种结构组成。CNN输出的结果是每幅图像的特定特征空间。当处理图像分类任务时,我们会把CNN输出的特征空间作为全连接层或全连接神经网络(fully connected neural network, FCN)的输入,用全连接层来完成从输入图像到标签集的映射,即分类。当然,整个过程最重要的工作就是如何通过训练数据迭代调整网络权重,也就是后向传播算法。目前主流的卷积神经网络(CNNs),比如VGG, ResNet都是由简单的CNN调整,组合而来。
    CNN层级结构

    数据输入层/ Input layer
    卷积计算层/ CONV layer
    ReLU激励层 / ReLU layer
    池化层 / Pooling layer
    全连接层 / FC layer

    卷积计算过程

    4.4 深度学习训练技巧4.4.1 划分验证集和K交叉验证模型什么时候达到最好,首先看对训练集的拟合程度,也就是通常所说的loss,但是并不是loss越小越好,更重要的是模型在新数据下的表现能力,也就是val_loss。我们通常将训练集划分出一部分作为验证集,来查看模型的泛化能力,但是这也会产生一个弊端,也就是没有充分的运用到训练数据,要知道,很多任务中的训练数据本身也并不多,所以我们这里采用k折交叉验证来最小化这个弊端。

    4.4.2 ReduceLROnPlateau配合EarlyStopping在训练过程中通过手工调整超参数费时费力,ReduceLROnPlateau通过监测验证集上的损失(val_loss)来动态的调整学习率,而且配合EarlyStopping,可以在val_loss多次不再变化后停止训练,更容易获取训练的最佳点。
    4.4.3 Dropoutdropout是指在深度学习网络的训练过程中,对于神经网络单元,按照一定的概率将其暂时从网络中丢弃。注意是暂时,对于随机梯度下降来说,由于是随机丢弃,故而每一个mini-batch都在训练不同的网络。
    4.4.4 Batch normalization在我们以前在神经网络训练中,只是对输入层数据进行归一化处理,却没有在中间层进行归一化处理。要知道,虽然我们对输入数据进行了归一化处理,但是输入数据经过σ ( W X + b ) 这样的矩阵乘法以及非线性运算之后,其数据分布很可能被改变,而随着深度网络的多层运算之后,数据分布的变化将越来越大。如果我们能在网络的中间也进行归一化处理,既可以加快训练速度,又可以避免采用其他正则化手段,还能提高模型精度。
    5 实验设计5.1 数据读入该任务给出的原始数据是由csv文件和相关的图片文件夹构成,操作起来很不方便,本文通过pandas的读取方法将csv文件里的图片名和标签读取到统一的txt文件中,如下图所示。

    通过继承pytorch里提供的DataSet类重写自己的数据集类,然后通过DataLoader打乱并生成batch。

    5.2 图像增强为了丰富图像训练集,更好的提取图像特征,泛化模型(防止模型过拟合),一般都会对数据图像进行数据增强。数据增强,常用的方式,就是旋转图像,剪切图像,改变图像色差,扭曲图像特征,改变图像尺寸大小,增强图像噪音(一般使用高斯噪音,盐椒噪音)等。
    归一化和标准化

    下边展示一些常用的图像增强方法:
    亮度变化

    随机灰度化

    随机旋转

    水平垂直翻转

    5.3 模型设计5.3.1 模型一模型1结构

    损失函数:BCELoss
    参数选择
    以1:4将训练集划分为验证集(validation)和训练集,以下将探讨学习率(learning_rata)、epoch、batch_size、优化器,正则化系数λ对验证集准确率的影响。

    总结
    在提高模型一精度的过程中,各种手段(包括数据增强,各种超参数调节均未取得较大进步,最高准确率只有89%,有理由确定该模型是影响精度的主要原因,猜测原因主要是模型深度还不够,可调节的参数不够多)
    5.3.2 模型二模型结构
    模型二通过使用小的3*3的卷积核进行处理,叠加5层,拉平后接全连接层,这样使得网络深度足够,有足够多的参数。下图是部分网路结构。

    该网络除了使用小的卷积核之外,还使用了dropout方法,这也是一种正则化方法,随机的让部分神经元失活,可以防止过拟合的出现。而且该网络在每一个卷积层的输出添加了Batch Normalization层,该层可以让网络训练过程中使得每一层神经网络的输入保持相同分布。
    训练过程
    在模型1的训练过程中通过手工调整超参数费时费力,在该模型中我们使用ReduceLROnPlateau,通过监测验证集上的损失(val_loss)来动态的调整学习率,而且配合EarlyStopping,可以在val_loss多次不再变化后停止训练,更容易获取训练的最佳点。
    实验表明,通过多次训练,该模型最佳可达到91.2%的准确率。
    5.3.3 模型三迁移学习
    模型迁移利用上千万的图象训练一个图象识别的系统,当我们遇到一个新的图象领域,就不用再去找几千万个图象来训练了,可以原来的图像识别系统迁移到新的领域,所以在新的领域只用几万张图片同样能够获取相同的效果。模型迁移的一个好处是可以和深度学习结合起来,我们可以区分不同层次可迁移的度,相似度比较高的那些层次他们被迁移的可能性就大一些。
    所以在遇到特征比较相似的情况时,可以使用预训练模型来达到自己的目的。
    VGG16迁移学习
    通过大量实践,并跑了多个预训练模型,发现对于人脸性别识别任务上VGG16效果最好,验证集上效果最高可到到92.7%,下表展示了VGG16训练过程中的部分数据。

    在此之前,或许因为训练经验不足,其他的预训练模型都没有产生理想的结果,也因为kaggle上gpu使用时间的限制和速度的限制,没有对其他模型进行大量的训练工作,也可能是其他模型识别的特征与该任务相差太大,训练结果见下表所示。

    5.4 模型融合在获取了大量的训练结果数据后,所谓三个臭皮匠,胜过诸葛亮,模型融合是一个很关键的步骤,能够大大提高精度。模型融合方法很多,下面介绍使用到的几个方法。
    5.4.1 投票如果是三个性能较差,但是高度不相关的模型:

    可以看到大大的提高了精确性。
    5.4.2 带权重的投票得到了一堆准确率差别挺大的数据,经过实验,随着低准确率样本数增多,投票过后的精度并不会再升高,反而不如用精确率最高的几组数据进行投票。可以想到这样一个场景:对于一道题,就算有2个成绩一般的人选A,反而你更宁愿相信成绩很好的人说选B,复原到该任务中,一个准确率很高的结果的票数远不止1票才合理。基于这种思想,将得分高的结果票数增多取得了比平均投票更好的效果。但是探索合适的权重仍然是一个复杂的工作,相信合适权重分配能取得更好的表现。
    6 实验结果与分析6.1 实验结果通过大量实验,模型一的最佳测试集结果为0.89,模型二的最佳表现为0.916,模型三的VGG16迁移学习最佳测试集结果可达到0.927,在通过投票和带权重的投票模型融合手段下,最佳测试集结果可达到0.93447。
    6.2 结果分析与展望对于这个结果,我觉得还存在很多不足,主要还有很多工作没有做完,主要有以下工作:

    在模型融合上,权重探索还不够多,而且单纯的对结果投票并没有对各个模型物尽其用,基于这个问题,我觉得可以将各个表现比较好的模型最后一个卷积层拉平的向量V保存下来,然后将几个模型的向量V进行拼接,通过一个或者多个全连接层进行训练,相信可以最大化的发掘出每个模型的优点
    对于一个不错的模型,因为gpu和设备的限制,只用到了部分数据,并且没有去做K交叉验证,所以应该还有一定的提升空间
    我一直有一个想法:模型输出的是为男性或者女性的概率,那么当模型输出值在0.45-0.55的时候,说明模型也觉得既像男性,又像女性,那么这个时候模型输出的结果属于“值得怀疑的结果”,我们可以对于这部分的输出再进行训练,提取更多的特征
    对于诸多复杂的预训练模型,没有进行大量而丰富的训练,并没有得出其最好的结果,相信其中还有很多潜力等待挖掘
    其次,对于各种trick还不够熟悉,对于模型的设计还有很多不足
    在对于图像数据清洗和处理上,在明知道有错误数据,但是却未来得及处理。

    我相信,随着自己不断地学习,以及条件地改善,这些遗留问题都会被一一解决,自己也能对该类问题更加游刃有余。
    7 心得体会通过这一次,我学习到了很多东西,首先就是将课堂所学用于实践,比如各种超参数调节,过拟合判断与处理,其次是学习到了各种深度学习框架地使用以及各种技巧,更重要的是培养了自己动手解决问题的能力和自信心。当然,在做大作业过程中也遇到了诸多困难,比如模型的设计,怎么才是好的模型呢?无从而知,只有从卷积网络的发展历程可以得知一二,其次就是gpu的限制,复杂模型一跑就是一个下午,很难在进行各种超参数调节。但是总的来说还是收获巨大!学习过程中才发现知识海洋之浩瀚,自己还需要努力学习,争取在这个领域贡献一份自己的力量!
    3 留言 2020-11-25 12:14:48 奖励56点积分
  • Http Server实现原理解读

    Tinyhttpd 是 J. David Blackstone 在1999年写的一个不到 500 行的超轻量型 Http Server,用来学习非常不错,可以帮助我们真正理解服务器程序的本质。官网:http://tinyhttpd.sourceforge.net
    每个函数的作用处理从套接字上监听到的一个 HTTP 请求,在这里可以很大一部分地体现服务器处理请求流程。

    bad_request:返回给客户端这是个错误请求,HTTP 状态吗 400 BAD REQUEST
    cat:读取服务器上某个文件写到 socket 套接字
    cannot_execute:主要处理发生在执行 cgi 程序时出现的错误
    error_die:把错误信息写到 perror 并退出
    execute_cgi:运行 cgi 程序的处理,也是个主要函数
    get_line:读取套接字的一行,把回车换行等情况都统一为换行符结束
    headers:把 HTTP 响应的头部写到套接字
    not_found:主要处理找不到请求的文件时的情况
    sever_file:调用 cat 把服务器文件返回给浏览器
    startup:初始化 httpd 服务,包括建立套接字,绑定端口,进行监听等
    unimplemented:返回给浏览器表明收到的 HTTP 请求所用的 method 不被支持

    建议源码阅读顺序:main —> startup —> accept_request —> execute_cgi,通晓主要工作流程后再仔细把每个函数的源码看一看。
    工作流程1) 服务器启动,在指定端口或随机选取端口绑定 httpd 服务
    2) 收到一个 HTTP 请求时(其实就是 listen 的端口 accpet 的时候),派生一个线程运行 accept_request 函数
    3) 取出 HTTP 请求中的 method (GET 或 POST) 和 url,。对于 GET 方法,如果有携带参数,则 query_string 指针指向 url 中 ?后面的 GET 参数
    4) 格式化 url 到 path 数组,表示浏览器请求的服务器文件路径,在 tinyhttpd 中服务器文件是在 htdocs 文件夹下。当 url 以 / 结尾,或 url 是个目录,则默认在 path 中加上 index.html,表示访问主页
    5) 如果文件路径合法,对于无参数的 GET 请求,直接输出服务器文件到浏览器,即用 HTTP 格式写到套接字上,跳到(10)。其他情况(带参数 GET,POST 方式,url 为可执行文件),则调用 excute_cgi 函数执行 cgi 脚本
    6) 读取整个 HTTP 请求并丢弃,如果是 POST 则找出 Content-Length. 把 HTTP 200 状态码写到套接字
    7) 建立两个管道,cgi_input 和 cgi_output, 并 fork 一个进程
    8) 在子进程中,把 STDOUT 重定向到 cgi_outputt 的写入端,把 STDIN 重定向到 cgi_input 的读取端,关闭 cgi_input 的写入端 和 cgi_output 的读取端,设置 request_method 的环境变量,GET 的话设置 query_string 的环境变量,POST 的话设置 content_length 的环境变量,这些环境变量都是为了给 cgi 脚本调用,接着用 execl 运行 cgi 程序
    9) 在父进程中,关闭 cgi_input 的读取端 和 cgi_output 的写入端,如果 POST 的话,把 POST 数据写入 cgi_input,已被重定向到 STDIN,读取 cgi_output 的管道输出到客户端,该管道输入是 STDOUT。接着关闭所有管道,等待子进程结束。这一部分比较乱,见下图说明:
    管道初始状态

    管道最终状态

    10) 关闭与浏览器的连接,完成了一次 HTTP 请求与回应,因为 HTTP 是无连接的
    3 留言 2019-04-24 10:50:47 奖励15点积分
  • 数据结构——二叉排序树

    1.定义(BSTTree)二叉排序树或者是一棵空树,或者是具有如下特性的二叉树:

    若它的左子树不空,则左子树上所有结点的值均小于根结点的值
    若它的右子树不空,则右子树上所有结点的值均大于根结点的值
    它的左、右子树也都分别是二叉排序树。

    注:只要有一个结点不满足就不是二叉排序树
    通常,取二叉链表作为二叉排序树的存储结构
    typedef struct BiTNode { TElemType data; struct BiTNode *lchild, *rchild; // 左右孩子指针} BiTNode, *BiTree;
    2.二叉排序树的查找算法
    若二叉排序树为空,则查找不成功
    否则:

    若给定值等于根结点的关键字,则查找成功若给定值小于根结点的关键字,则继续在左子树上进行查找若给定值大于根结点的关键字,则 继续在右子树上进行查找

    Status SearchBST (BiTree T, KeyType key, BiTree f, BiTree &p ) { // 在根指针 T 所指二叉排序树中递归地查找其 // 关键字等于 key 的数据元素,若查找成功, // 则返回指针 p 所指该数据元素的结点,并返回 // 函数值为 TRUE; if (!T){ p = f; return FALSE; // 查找不成功 } else if ( EQ(key, T->data.key) ){ p = T; return TRUE;//查找成功 } else if ( LT(key, T->data.key) ){ SearchBST (T->lchild, key, T, p ); // 在左子树中继续查找 } else{ SearchBST (T->rchild, key, T, p ); // 在右子树中继续查找 }} // SearchBST
    3.二叉排序树的插入算法
    根据动态查找表的定义,“插入”操作在查找不成功时才进行
    若二叉排序树为空树,则新插入的结点为新的根结点
    否则,新插入的结点必为一个新的叶子结点,其插入位置由查找过程得到

    4.二叉排序树的删除算法和插入相反,删除在查找成功之后进行,并且要求在删除二叉排序树上某个结点之后,仍然保持二叉排序树的特性。
    可分三种情况讨论:

    被删除的结点是叶子结点:其双亲结点中相应指针域的值改为“空”
    被删除的结点只有左子树或者只有右子树:其双亲结点的相应指针域的值改为 “指向被删除结点的左子树或右子树”
    被删除的结点既有左子树,也有右子树:以其中序前驱(左子树的最右无右子树的结点)替代之,然后再删除该前驱结点

    结论

    一个无序序列(10,18,3,8,12,2,7,3)可以通过构造一棵二叉排序树而变成一个有序序列,构造树的过程即为对无序序列进行排序的过程
    每次插入的新结点都是二叉排序树的叶子结点,在进行插入操作时,不必移动其它结点,仅需修改某个结点的指针由空变为非空即可。这就相当于在一个有序序列上插入一个元素而没有移动其它元素。这个特性告诉我们,对于需要经常插入和删除记录的有序表采用二叉排序树结构更为合适。

    5.查找性能分析
    值相同的 n 个关键字,构造所得的不同形态的各棵二叉排序树的平均查找长度的值不同
    不失一般性,假设长度为 n 的序列中有 k 个关键字小于第一个关键字,则必有 n-k-1 个关键字大于第一个关键字,由它构造的二叉排序树平均查找长度是 n 和 k 的函数
    3 留言 2020-10-23 08:51:23 奖励38点积分
  • 基于SSM框架的B/S微博系统的设计与实现

    第一章 绪 论时至今日,网络对于现代人来说,早已成为人类科技发展进步的桥梁,而通过网络衍生出的一系列产品也不断的冲击着人们的日常生活,截至2016年年底,我国网名数量达到7亿,有超多一半多人在使用网络,而它天涯咫尺的作用,不但消除了人与人地域上的距离,更是拉近了心灵的距离,沟通与互动变的异常频繁与重要。
    随着互联网新时代的来临,微博借着互联网的桥梁,逐渐进入网名的视野中,没有博客的长篇大论,也不需要严谨的逻辑层次,这使得网络中一大批的原创文章被生产发掘,短短几句话便可以在网络中激起千层浪,普通人也可能在一夜之间成为拥有数千万粉丝的“网红”。微博的便捷、原创、和草根性使它成为中国网民茶余饭后的网上生活。
    早在2006年3月,Obvious推出了Twitter服务,这个世界上最早同时也是最著名的微博系统,而在那是,微博也仅仅只是为了给好友发送手机短信,在中国,饭否网的上线标志中国微博的开端,之后腾讯滔滔、叽歪、嘀咕等微博的开荒者终究也都没能站在最后。
    2010年,我国的微博得到迅速发展,这一年,无论微博的用户还是影响力都达到前所未有的高度,以新浪门户为首的腾讯、新浪、搜狐等微博展现出全新的活力。到了2013年6月,中国微博用户规模高达3.31亿人,在微博中流动的信息有超过2亿多条。
    2010年11月,新浪微博推出群组功能,这个功能不但可以与好友实时联系,又可以随时发布最新信息,2012年添加新功能“悄悄关注”,在用户关注后不通知被关注用户,也不显示给被关注用户,2013年,微博推出包括iPhone和Android的移动客户端,新增“密友”功能,同年10月份新浪微博“粉丝服务平台”上线,粉丝服务平台帮助认证用户为订阅自己的用户提供精彩内容和互动服务,自此微博由“营销”向“营销+服务”转型!而新浪微博依旧延续这自己的名人效应,一个普通账号,在没有粉丝关注的情况下,发布的微博却很难被他人看到,如果需要在微博中求助,没有“名人大V”的帮助,很难被别人所注意到。想要在微博中寻求帮助,就需要微博提供更多的服务。而本系统通过使用积分悬赏功能使得用户可以通过积分悬赏自己的问题,来让更多的用户回答自己的问题,得到更多人的帮助。
    第二章 相关技术介绍2.1 架构概述B/S架构(Browser/Server,浏览器/服务器模式):是一种通过将浏览器作为客户端的网络结构模式,利用已经逐步成熟的web浏览器技术,结合浏览器的多种功能,使用浏览器来作为早先C/S(Client/Serve)架构下复杂的客户端,使用C/S架构使得用户的客户端得到统一,将软件系统的核心功能集中在服务器端,系统的升级和维护更加简单,开发人员只需要管理服务器就可以做到对如软件系统的更新和维护,B/S架构所带来的众多优点使得它成为将来信息化发展的主流方向。
    MVC模式:即模型(Model),视图(View),控制器(Controller)是一种软件开发的设计模式,Model主要负责分离出来的业务逻辑的数据交互,View主要负责数据的提交和展示,Controller主要负责获取请求和请求的转发。
    SSM框架(SpringMVC+Spring+Mybatis):由这三个开源框架组成的web项目的框架,是标准的MVC模式,spring MVC负责请求的转发和视图管理,spring实现业务对象管理和事务控制,mybatis作为持久化引擎,相对于早先流行的SSH(Struts+Spring+Hibernate)框架来说,SSM作为轻量级框架更适合开发中小型项目。
    2.2 关键技术简介前端技术
    JSP(Java Server Pages):嵌入了java代码的html页面,本质是一个servlet,它实现了在htmld的语法中扩展java代码,使用<% %>格式。
    JavaScript:是一种弱类型的脚本语言,由ECMAScript描述JavaScript的基本对象和语法,文档对象模型(DOM),浏览器对象模型(BOM)三部分组成。
    Ajax(Asynchronous Javascript And XML):异步的JavaScript和XML,实现前后台之间的异步交互,在不需要重新加载整个页面的前提下对页面上的部分内容做局部刷新。

    后台技术java:一种面向对象的编程语言,主要特性包括有

    简单性:抛弃了C++中复杂的语法和指针多继承等特征,开发人员不需要关注底层优化,只需要关注业务逻辑就行
    面向对象性,对程序员而言,只需要注意对应的数据和处理数据的方法,不用严格按照过程的方式来编写代码,因为java语言是面对对象的,因此也拥有面向对象的三大特征,继承、封装和多态
    跨平台性,java语言的跨平台性也就是JVM(java虚拟机)的跨平台性。Java文件编译后不会直接生成机器直接运行的二进制文件,而是编译成class文件,JVM会将编译的class文件根据不同操作系统下的JVM来生成对应系统的二进制文件,只需要编译一次,就可以在不同的平台上运行

    SpringMVC:是Spring框架提供的一个模块,通过实现MVC模式来地将数据、业务与展示进行分离,简化了Web开发。和其他MVC框架一样都是请求驱动,控制转发,它的核心Servlet是DispatcherServlet,即前端控制器,用来分发请求到控制器,它和Spring框架完全整合。这也使得SpringMVC成为目前非常流行的MVC框架。
    Spring:一款轻量级java开发框架,Spring框架有着分层的体系结构,因此可以使用Spring中 独立的任意部分。而它的架构依旧是内在稳定的,Spring提供了J2EE应用各层解决方案,贯穿于三层架构的每一层,但Spring依旧可以很好的兼容其他框架,本项目主要用到了Spring中IOC(控制反转)和AOP(面向切面编程)模块。
    Mybatis:是一个简化Java持久化层的轻量级开源框架。并且支持注解的Mapper,Mybatis消除了绝大部分的JDBC代码,使得java对象持久化到数据库的过程变的更加容易,相对于其他的java持久化框架,Mybatis的优点在于,消除了大量的JDBC冗余代码、简单易学、可见的Sql语句、提供了于Spring的整合,引入更好的性能。
    2.3 开发工具Tomcat服务器:是一个Web应用服务器,它是轻量且开源的,是中小型Web项目和开发调试和学习的首选。
    Oracle数据库(Oracle Database):是由Oracle公司开发的一款关系型数据库,是商业运用第一的关系型数据库,系统使用方便,功能强大,可移植性强。适用于各种大中小环境,在大数据时代,作为分布式数据库,它实现了分布式处理的功能,具有完整的数据管理功能、完备的关系型数据库、分布式处理功能。
    Eclipse开发工具:一个基于java开源的可扩展开发平台,它不但包括java集成开发环境,还包括插件开发环境,如SVN、CVS等团队开发插件。
    2.4 本章小结本章主要介绍了开发项目用到的一些主要技术,项目所使用的架构和设计模式,项目中所使用到的主要框架技术,项目在浏览器端展示用到的前端技术和展示方式,后台代码使用的开发语言,使用的服务器技术,数据持久层所使用的数据库等,在本章最后又介绍了开发使用到的开发工具。
    第三章 系统需求分析3.1 可行性分析3.1.1技术可行性在已有技术方面,为了统一客户端,消除因版本升级和维护带来的复杂性,因此采用成熟的B/S架构在项目的实现上完全可行,在开发语言和框架方面,java和j2ee体系的强大,可以让开发人员精心的构建web项目,以及一系列的开源框架,都为项目的可行性提供了强大的依据,在服务器方面,使用开源服务器Tomcat,足以支持该小型项目的正常使用,而不断发展的前端技术和前端框架可以制作精美的前台页面,提高用户的体验和交互,这在项目的页面展示技术上完全可行,强大的关系型数据库为项目数据的持久化提供强有力的后援。综上所述,日趋成熟化的java和j2ee体系、完全开源的java框架和服务器、功能强大的关系型数据库、运用Web前端技术提供用户交互页面,所以该项目在技术方面完全可行。
    3.1.2 经济可行性在互联网发展到信息时代的今天,单一获取信息的方式已经不能满足人们,人们每天接触的信息越来越多,而获取信息的形式也越来越多,但大多数获取信息的方式,留给用户交互的方式并不多,大都数情况下,人们只能被动获取,而很难找到自己的喜好和需求来获取信息,而本微博项目可以让人们获取实时热点信息和他们所关注的信息,实时与微博信息交互。而使用大量成熟技术与开源框架下,也使得小项目的开发更加简单,经济,高效,因微博而兴起的微博效应也能带来一定的经济效益。
    3.1.3 操作可行性微博系统使用B/S架构,用户不需要下载客户端,只需要用户有浏览器,就可以在浏览器上登陆微博系统,微博系统的界面颜值高,用户交互性高,用户操作简单方便,只需要了解基本的计算机操作就能使用,用户体验性高。因此在系统操作上完全可行。
    3.2 需求分析3.2.1 系统总体需求该微博系统主要由前台用户模块和后台管理模块组成,当用户进入首页时可以选择登陆或不登陆,登陆时可以使用已有账号登陆或注册新账号。用户未登陆时,在首页显示最近更新的热门微博,而登陆后的用户可以在首页看到 自己关注用户最近更新的微博。
    未登陆用户只能搜索查看微博信息和访问用户主页,登陆用户可以登陆系统后修改自己的基本信息例如签名、性别等,在验证用户信息后还可以修改密码和密保信息。以及修改用户头像和密码,编辑自己的个人主页,对微博进行点赞、评论、收藏等功能,还可以关注/取消关注用户,拉黑用户、私聊用户等操作。
    后台管理员可以查看系统所有的数据,包括用户、微博、评论、海螺、回答的总量,最近一个的数据库,最近一周的数据量。具体所具有的功能包括管理不良微博信息与不良账号,对微博、微博评论、海螺、回答等信息的删除和恢复功能,对不良账号的封禁等操作。
    微博查找模块:用户可以输入关键字来查找相对应的微博或查找用户。
    微博发布模块:用户点击发布,在内容中添加自己想要发送的内容,可以选择表情,也可以插入图片,但对输入字符数有着限制,同时还可以插入一张图片。
    微博评论模块:用户可以查看微博的评论,发布微博评论等。
    神奇海螺模块:用户可以发布一个神奇海螺,海螺主要用来记载用户提出的各种问题,由其他用户来查看并回答问题,当回复者的答案被提问者采纳后,回答者可以增加自己的海螺积分,不同的海螺积分有着不同的海螺称号。
    积分模块:用户每天登陆,发微博,做任务等方式可以增加自己的积分,不同的积分拥有不同的称号,神奇海螺模块的积分有着不同的称号,称号显示在用户名称的后面。
    好友模块:用户可以查看系统中其他的用户,找到自己喜爱的用户然后关注他们,关注后可以在好友模块中查看自己关注的用户,以及好友最近发布的微博等信息,也可以私信好友,发送私信信息给好友。
    3.2.2 用例图需求1. 当用户进入系统时候,可以选择登陆或注册用户,如果忘记了密码还可以通过密保问题来重置密码。

    2. 当用户登陆后,可以管理用户的个人基本信息,修改用户基础信息,修改用户密保信息。修改用户头像等功能。

    3. 用户微博管理系统,当用户登陆进去系统时,可以在首页发布微博,通过关键字搜索微博内容中关键字的微博信息。产看微博,包括查看个人微博、好友微博、推荐微博。

    4. 评论管理,评论管理依赖于微博模块,用户可以查看微博的评论,对微博信息发布评论,以及删除自己的评论。

    5. 海螺管理,用户登陆后可以在海螺模块发布海螺问题,发布问题时可以选择悬赏的积分数目,同时减少自己的积分数,用户可以参加回答他人的海螺问题,当回答的答案被采纳时,就可以获得用户悬赏的积分数。

    6. 好友管理,当用户登陆时可以关注系统推荐好友,也可以自己搜索用户,查看用户的主页面,添加关注或取消关注用户,还可以给用户发送私人信息,或者拉黑用户。

    7. 消息通知,当用户的关注,微博评论,点赞,收藏时调用消息通知。

    8. 管理员:管理员登陆系统,可以管理微博用户,对不良用户进行封禁和注销账户的操作,也可以恢复用户的状态,同时也可以对微博信息进行管理,如删除微博,恢复被管理员删除的微博信息等,对微博评论的删除和恢复等操作。

    第四章 系统功能设计4.1 系统类分析4.1.1 实体类实体类主要用来传递数据,主要包括User、Friend、PointAction、PointRecord、Weibo、WeiboCollect、WeiboComm、WeiboLike、WeiboTrans等,用户类中包括用户的基本信息,微博类中包括微博的基本信息同时包含实体用户类,好友类包括好友编号,好友创建时间与关注双方的用户类等,私信类包含私信双方的用户类与私信的基本属性,微博收藏类包含收藏的微博类与用户id等收藏属性,微博评论类包含微博类和用户类以及评论内容等属性,微博转发类包含用户类和微博类以及转发时间等属性,微博点赞类包含微博类和用户类以及点赞时间等属性。实体类之间的类关联关系如图所示。

    4.1.2 控制器类在controller层包含MainController、WeiboController、FrendController、AdminController四个JAVA类,在SpringMVC框架中主要用来接收浏览发送给服务器的请求和数据处理并控制请求的转发,将从Service层中获取的数据响应给浏览器端。MainController主要用发来接收来自用户相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    WeiboController主要用发来接收来自微博相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图4所示。

    FriendController主要用发来接收来自好友相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    AdminController主要用发来接收来自管理员相关页面中提交的表单或链接请求,并将请求的参数传递到Controller中对应的方法中,并获取到业务处理层层中返回的数据,携带数据响应给浏览器,在浏览器端显示数据,具体属性和方法如图所示。

    4.1.3 业务逻辑类在Service层中主要包含四个Service接口和他们的实现类,包括IUserService用来处理用户业务例如用户注册、登陆、修改个人信息等,如图4.6所示。IWeiboService用来处理微博相关的业务例如查找微博,发布微博,删除微博以及对微博的相关操作例如点赞、评论等功能的业务实现,如图4.7所示。IFriendService用来处理和用户好友相关的业务例如点赞、取消点赞、私信、拉黑等功能的业务实现,如图4.8所示。IAdmoinService用来处理和管理员相关的业务例如管理员登陆、图表展示、用户管理、微博管理、海螺管理功能的业务实现,如图4.8所示。

    图4.6用户业务处理类图

    图4.7微博业务处理类图

    图4.8微博控制器类图
    4.1.4 数据库交互类由于系统采用了MyBatis持久化框架,开发人员不需要关注和数据库之间具体的JDBC代码,而只需要处理业务逻辑,因此只需要在Dao层接口中声明方法而不需要写接口的实现类来实现方法,则是通过配置对应的配置文件,在配置文件中编写对应接口方法中的SQL语句和数据库交互。
    4.2 关键业务设计4.2.1 登陆系统
    图4.9系统登录顺序图
    用户进入主页后,可以在左边选择注册用户,或者在右边登陆系统,在注册模块,用户输入用户邮箱,密码,昵称等信息,当用户输入邮箱后会通过Ajax将用户输入的邮箱传到后台,查找用户邮箱是否已经被注册,如果被注册则在页面提示用户该邮箱已被注册,在输入基本信息点击注册后,保存用户注册信息。
    登陆功能:当用户输入邮箱地址和密码后,如果点击下次自动登陆密码在点击登陆后,判断用户的当前登陆时间和上次登陆时间是否为同一天,如果不是同一天则为用户增加登陆的 积分,然后跳转至微博首页。
    4.2.1 用户信息系统
    图4.10用户修改信息顺序图
    用户基本信息:在系统的首页中点击个人账号设置后,跳转到修改用户基本信息页面,用户可以修改这些基本信息,并将修改后的信息保存在数据库中。
    修改密码:在用户个人资料页面点击修改密码,跳转修改密码页面,用户可以输入用户的当前密码,系统判断密码是否正确,如果密码不正确,显示当面密码错误,如果输入密码正确,用户则可以输入新密码,点击修改后将修改后的密码更新到数据库中。
    修改用户头像:点击用户个人资料中修改头像,跳转至修改头像页面,用户选择头像文件,点击上传,将用户头像保存在服务器上,判断用户之前头像是为系统默认头像,如果不是就删除用户之前的头像图片,点击修改后将修改后的头像地址保存在数据库中。
    修改密保:用户先要根据之前设置的密保问题来填写答案,如果密保答案错误,提示用户密保答案错误,如果密保答案正确,用户可以输入新的密保问题和密保答案,点击修改保存用户新的密保问题和答案。
    4.2.2微博模块
    图4.11系统登录顺序图
    发布微博:用户可以在首页发布微博,在微博信息中可以插入表情,也可以选择插入一张图片,当用户点击发布后,浏览器发送请求将前台页面表单中微博信息和图片信息传入后台控制器,将图片信息保存在服务器中,在数据库中只保存图片路径,最后将微博信息保存在数据库中。
    搜索微博:用户可以在首页的搜索框中输入微博中提到的内容来搜索,系统在数据库中通过迷糊查询查询相关的微博信息。在页面中将搜索到的页面展示出来,并且将关键字标红显示。
    微博操作:用户可以查看个人微博、好友微博、收藏微博等信息,对微博的操作有点赞,转发,收藏,评论,删除等。
    4.2.3 好友模块关注功能:当用户登陆系统后可以在页面右侧的推荐用户中选择需要关注的用户,或者是通过页面中的搜索功能来搜索用户,对搜索到的用户进行关注。关注用户后个人关注+1,同时被关注用户粉丝数+1。
    取消关注:和关注功能类型,在导航栏中点击我的好友,在我的好友首页中查看我的关注好友,就能查看到我所关注的所有好友和好友数以及他们的个人信息,通过点击对应的取消关注就可以取消关注该用户,取消关注用户后个人关注数-1,同时被关注用户粉丝数-1。
    拉黑用户:即修改好友表中对应的记录状态,被拉黑用户所发布的私信和微博信息不会被拉黑用户所看到。
    私信:可以在页面的推荐用户上面查看当前登陆用户的未查看私信数,未读私信的数目通过数据库中对所有接收者为当前登陆用户的所有私信信息,且信息状态为未阅读的私信,将得到的数值展示在前台页面中显示。
    发送私信:用户先选择要发送的用户,输入需要发送的私信信息,可以在私信中插入表情,点击发送后会将私信信息发送给对应用户。同时增加提示该用户的未读私信数。
    搜索用户:在搜索用户页面中,用户可以输入用户昵称的关键字来模糊查询相关用户,并将查询到的用户展示在页面中,同时将用户输入的关键字标红显示。
    用户主页面:在页面中,点击任意一个用户的名称或头像都会跳转至对应用户的个人主页,用户的个人主页显示用户的个人信息和用户最近发布的微博,按照时间倒序排列,用户也可以对微博的点赞、转发、评论、收藏做操作。
    4.2.4 海螺模块发布:用户通过点击导航栏中的神奇海螺进入海螺主页,在海螺页面的上面是发布海螺的问题框,下面的可以插入的表情按钮和问题的悬赏积分数,中间的导航栏可以选择查看最新发布、已解决、待解决、我的海螺不同的筛选条件。
    查看海螺问题:用户点击任意海螺问题,系统跳转到展示海螺的具体信息的页面,上方展示发布用户的用户名、用户称号、发布日期、海螺问题、悬赏积分、问题的状态等,在中间显示问题的所有回复信息,回复人的用户名、称号、回答内容、回复日期,是否被采纳等。
    回答海螺问题:在海螺问题详细详细信息页面的底部可以回答海螺问题,在输入框中输入回复的答案,插入表情信息等,点击回复后将回复信息保存在数据库中。
    采纳问题答案:在海螺问题首页,用户通过点击我的海螺问题可以跳转至用户自己所发布的海螺问题页面,在自己所发布的海螺问题页面中可以选择自己认为最正确的答案,点击采纳后修改海螺状态,被采纳的答案变为采纳答案,增加采纳者的海螺积分。
    4.2.5 后台管理员模块管理员登陆:管理员可以通过在登陆页面中点击管理员登陆,跳转至管理员登陆页面。当管理员输入账号信息和密码信息后,如果用户名和密码正确就跳转到管理员首页。如果错误则给出提示。
    管理员首页:在管理员首页中上方显示导航栏,在页面内容方面,通过四张图表来显示微博、用户、评、海螺、评论、回答等的总数以及当月数,在下方的柱状图中则显示距离今天最近的7天的数目。
    用户管理首页:在页面上方显示导航栏,页面内容中显示所有用户的编号、邮箱、昵称、注册日期、上次登陆日期以及用户的状态,由于考虑到用户数量多以以及为了方便查找,因此在页面中一次显示10条用户数据,同时添加用户搜索功能,用户能够在输入用户昵称的关键字后来搜索用户,并且支持迷糊查询。 搜索用户:在用户管理页面输入用户昵称中的关键字来搜索用户,系统会将获取到的用户信息中昵 称为输入关键字的那部分显示为红色。
    用户封禁:管理员可以通过点击用户管理页面操作一栏中的封禁按钮来对违规用户的封禁,管理员可以输入封禁的天数,那么在这个日期之前,用户是不能正常登陆的,管理员也可以通过点击解封来提前解除封禁用户的操作。
    微博管理首页:管理员点击导航栏中的微博管理,系统会跳转至用户微博管理页面,显示系统中所用是微博信息,管理员也可以通过输入微博内容中的关键字来搜索微博,同时可以删除有不良信息的微博,或是恢复以被删除的微博。
    微博搜索:管理员可以通过输入微博内容中的关键字来搜索在微博中存在该关键字的微博,搜索到的微博内容中的关键字会使用红色标注出来。
    微博删除:在微博管理页面中,管理员可以根据微博的内容来判断微博是否违法等信息,如果微博信息中包含不良信息,管理员可以通过操作栏中的删除按钮来删除微博或者可以对已经删除的为微博做恢复操作。
    海螺管理首页:管理员点击导航栏中的海螺管理可以跳转至海螺管理页面,在页面的上方为导航栏,页面内容则显示所有的海螺信息、海螺搜索框、以及下方的分页框,海螺信息包括编号、发布人名称、海螺的内容、发布日期、海螺状态以及可以执行的操作。
    海螺搜索:在海螺管理首页中,考虑到海螺数量多不好查找的问题,因此设置了搜索功能,管理员可以能够在海螺搜索框中输入海螺内容中的关键字来搜索海螺问题,在展示搜索到的结果时会将搜索关键字使用红色标注出来,方便查看。
    4.3 数据库设计4.3.1 概述微博系统数据库表主要包括:

    1. 用户表:用来保存用户的个人信息,例如编号、昵称、姓名、密码。邮箱等信息,以用户编号作为主键,如表4-1
    2. 微博关注表:用来保存用户的关注用户信息,以关注ID作为主键,如表4-2所示
    3. 微博表:用来保存微博信息,例如微博发布时间,微博内容,微博点赞,转发,收藏数等,以微博编号作为主键,如表4-3所示
    4. 微博收藏表:用来保存用户收藏的微博信息,如微博编号,收藏时间等,以收藏编号作为主键,如表4-4所示
    5. 评论表:保存微博的评论消息,例如评论人编号、评论日期、评论内容等,以评论编号作为主键,如表4-5所示
    6. 积分表:用来保存用户获取积分的方式,例如通过每天的登陆发布微博等获取积分,以积分编号作为主键, 如表4-6所示
    7. 点赞表:用来保存微博的点赞信息,主要包括点赞人编号,点赞编号,点赞微博编号等信息。以点赞编号作为主键,如表4-7所示
    8. 积分流水表:用来保存用户获取积分的信息,主要包括积分编号、用户编号、获取日期等信息,以积分编号作为主键,如表4-8所示
    9. 转发表:用来保存用户转发的信息,主要有转发编号、转发人、微博编号等信息,以转发编号作为主键,如表4-9所示

    4.3.2 概念设计一个用户可以发布多条微博,因此用户表和微博表之间存在一对多的关系如图4.12所示。

    一条微博可以对应多个点赞、转发、收藏和评论,因此微博表和收藏、点赞、转发、收藏表之间存在一对多的对应关系,如图4.13所示。

    一个用户可以发布多个海螺,每个海螺问题可以对应多条评论。如图4.14所示。

    4.3.3 数据库表
    用户表:数据库表名USER_TAB,引用序列名SEQ_USER。


    关注表:数据库表FRIEND_TAB,引用序列名SEQ_FRIEND。


    微博表:数据库表名WEIBO_TAB,引用索引名SEQ_WEIBO。


    收藏表:数据库表名COLLECT_TAB,引用索引名SEQ_COLLECT。


    评论表:数据库表名COMM_TAB,引用索引名SEQ_COMM。


    积分表:数据库表名INTEGRAL_TAB,引用索引名SEQ_INTEGRAL。


    点赞表:数据库表名LIKE_TAB,引用索引名SEQ_LIKE。


    积分流水表:数据库表名RECORD_TAB,引用索引名SEQ_RECORD。


    转发表:数据库表名TRANS_TAB,引用索引名SEQ_TRANS。

    第五章 系统功能实现5.1 登陆系统在系统登陆注册页面当用户输入邮箱后会通过Ajax将用户输入的邮箱传到后台控制器,调用Service层中对应的方法,是Service方法中调用Dao层接口查找用户邮箱是否已经被注册,如果被注册则通过前台javaScript显示在页面提示用户该邮箱已被注册,在输入基本信息点击注册后,将会把用户输入的注册信息通过浏览器发送请求到后台控制器中,控制器控制请求的转发页面和将用户注册信息传给Service,在Service中初始化用户的一些基本信息,例如默认头像、默认状态、初始化用户积分等操作,组装用户数据源,调用Dao层方法保存用户注册信息如图5.1所示。核心代码如下:

    登陆功能:如果点击下次自动登陆密码在点击登陆后,先将用户名和密码信息传递到Service层做业务处理,再调用Dao层接口判断邮箱地址和密码信息是否正确,如果正确并且点击了下次登陆,就将邮箱名和密码保存在浏览器Cookie中,将登陆用户保存在Session中,在处理用户登陆的Service中判断用户的当前登陆时间和上次登陆时间是否为同一天,如果不是同一天则为用户增加登陆的积分,修改用户数据库中的积分数,然后跳转至微博首页,如图5.2所示。核心代码如下:



    5.2 用户信息系统用户基本信息:如图5.3所示当用户点击修改后浏览器会提交form表单,发送请求携带用户基本信息到SpringMVC的控制器中接收请求,并把form表单中的数据组装成User对象,作为参数传入Service层中对应的方法进行处理,调用Dao层接口修改用户的基本信息,并修改当前服务器中Session中的User对象信息。核心代码如下:


    修改密码:在用户个人资料页面点击修改密码,将会跳转至如图5-4所示的修改密码页面,用户可以输入用户的当前密码,前台页面通过Ajax发送异步请求,后台控制器接收请求,从数据库中获取用户当前密码是否正确,如果密码不正确,在前台页面中通过javaScript动态提示给用户当面密码错误,如果输入密码正确,用户则可以输入新密码,确认新密码,点击确认修改后浏览器提交form表单,将用户新密码传给后台Service层中对应的方法,在Service的方法中调用Dao层接口更改数据库中的用户密码,更改服务器Session中的用户信息。核心代码如下:


    修改用户头像:点击用户个人资料中修改头像,将会跳转至如图5.5所示的修改头像页面,用户选择头像文件,点击上传,form表单将图片传到后台控制器中,将用户头像保存在服务器上,判断用户之前头像是为系统默认头像,如果不是就删除用户之前的头像图片,并将用户新的头像地址保存在用户信息中,传入Service中调用Dao层接口修改数据库中用户的头像信息。核心代码如下:


    修改密保:当用户点击修改密保页面时浏览器跳转至修改密保页面如图5-6所示,用户先要根据之前设置的密保问题来填写答案,前台页面通过Ajax将用户输入的密保答案传入后台控制器,与数据库中用户的密保问题答案做判断,如果密保答案错误,在页面上通过javaScript展示用户密保答案错误,如果密保答案正确,用户可以输入新的密保问题和密保答案,点击修改提交form表单后,浏览器发送请求在后台数据库中修改用户的密保问题和答案。核心代码如下:


    5.3 微博模块发布微博:在用户主页如图5.7所示,用户可以发布微博,在微博信息中可以插入表情,也可以选择插入一张图片,在前台页面中,表情使用javaScript动态生成div标签并显示在页面上,当用户点击发布后,浏览器发送请求将前台页面表单中微博信息和图片信息传入后台控制器,将图片信息保存在服务器中,在数据库中只保存图片路径,从Session中获取当前登陆用户,组装微博数据源,将数据源传入Service中,调用Dao层接口将微博信息保存在数据库中。核心代码如下:


    搜索微博:如图5.8所示,在微博首页搜索框输入关键字点击搜索提交form表单,浏览器发送请求将关键字传到后台控制器中,在数据库中通过迷糊查询查询相关的微博信息。在Service层中具体方法中调用Dao层接口获得相关的微博信息,遍历集合,组装微博的基本信息和发布人对象,将微博中表情转换成对应的gif图片,调整微博时间格式为对应格式,修改微博内容中搜索关键字为红色显示,将修改好的数据返回到前台页面展示,如图5.9所示。核心代码如下:


    微博操作:点击微博的点赞,转发和收藏功能类型,通过Ajax将微博id提交请求到后台控制器,从Session中获取登陆用户的信息,传递参数到Service对应的方法中通过对微博不同的操作调用对应的Dao层接口将微博的点赞,转发和收藏信息保存在数据库中。点击评论后通过前台页面的点击事件跳转至JavaScript中通过Ajax发送异步请求到后台控制器中,将微博id传递到Service层中对应的方法中嗲用Dao层接口查找数据库中对应微博编号的评论信息,微博评论的分页通过对应的PageBean类控制,在数据库层通过SQL语句来控制分页要显示的条数,在控制器中传递json数据到前台页面展示。在评论信息的最后面可以发布用户自己的评论,可以添加表情,点击发表将form表单提交到后台控制器中,在后台代码中调用Dao层接口保存用户的评论信息。如图5.10所示。核心代码如下:


    个人微博:如图5.11所示:在页面上方导航栏可以点击进入用户个人主页查看我的微博,在页面中页面上方展示导航栏,下面显示用户的基本信息,中间显示用户所发布过的微博,点击微博插入的图片还可以通过JavaScript将图片放大,在页面的右侧展示系统为用户推荐的好友。点击我的微博按钮后发送的请求会被后台控制器接收,从服务器Session中获取当前登陆用户的用户id,传入Service层,在Service层中调用Dao层接口从数据库中获取当前登陆用户的微博信息组装成List集合,遍历List集合,更改微博数据源的日期格式,调用工具类将微博正文中的表情替换成对应的图片信息,在微博的分页中,使用了Oralce数据库中的伪列来获取对应区间的微博信息,实现数据库层的分页,将所有需要在页面上展示的数据传递给控制器,控制器再将数据封装在Response响应中传递到前台页面。核心代码如下:


    5.4 好友模块查看好友微博:通过点击页面导航栏中我的好友来跳转至好友微博页面,在后台控制器中先从Session中获得当前登陆用户的id值,传递给Service,在Service中调用Dao层方法先在数据库的好友表中查找对应用户的所有好友信息,然后再在数据库微博表中查看这些好友对应的微博信息按照微博发布时间倒序排列,将所有查找到的微博信息和发布用户信息封装成List集合,遍历List集合修改微博日期各式,微博表情字符转换为对应图片名称,将加工过后的集合传递给控制器,控制器将所有的组装好的数据响应到前台页面中,在前台页面中通过C标签遍历List展示微博数据如图5.12所示。核心代码如下:

    关注功能:当用户登陆系统后可以在页面右侧的推荐用户中选择需要关注的用户,点击关注后前台页面通过Ajax技术发送异步请求将被关注用户的用户id发送到后台控制器中,在控制器中获取Session中的登陆用户信息,将登陆用户的id和被关注用户的id传给Service层中对应的方法,在Service层对应的方法中做处理,组装好友信息数据源,调用Dao层接口保存用户的关注信息,同时更新用户的关注数,更新被关注用户的粉丝数,然后在前台页面中通过javaScript将页面文本信息关注修改为已关注。核心代码如下:

    取消关注:和关注功能类型,在导航栏中点击我的好友,在中间点击关注,就能查看到我所关注的所有好友和好友数以及他们的个人信息,如图5.13所示,点击取消关注,通过Ajax将好友id传递到后台控制器中,在控制器中获取请求中的用户信息,将好友id和用户id传入Service层中对应的方法,调用Dao层接口将对应用户id和好友id的好友信息删除掉,同时更改用户的关注数和被关注用户的粉丝数。然后返回响应在前台页面刷新好友列表。

    拉黑用户:和取消关注用户类似,只是在Dao层接口中,不是删除已有的用户记录而是逻辑删除,即修改好友表中对应的记录状态,被拉黑用户所发布的私信和微博信息不会被拉黑用户所看到。核心代码如下:


    私信:可以在页面的推荐用户上面查看当前登陆用户的未查看私信数,如图5.14所示,未读私信的数目通过数据库中对所有接收者为当前登陆用户的所有私信信息,且信息状态为未阅读的私信,将得到的数值传递给前台页面,前台页面中所有展示的数值通过EL表达式从服务器发送回来的响应中获取。点击未读私信或者通过图5.13中显示的好友列表中的私信按钮,跳转至用户私信页面。核心代码如下:


    查看私信:如图5.15所示,用户通过点击私信关注好友时,通过后台控制器跳转至我的私信用户页面,页面左侧显示和该用户最近的私信信息,页面右侧显示所有有过私信的用户列表,点击列表中的用户就可以直接私信这个用户,在私信中允许插入表情。在后台代码中,将要发送私信的用户id和从Session中获取的登陆用户id传递到Service层中对应的方法,调用Dao层接口在数据库中查找与该用户相关的所有私信记录同时修改和该用户的所有私信状态为以阅读,将获取的记录组装到集合中,遍历集合调用工具类修改私信的日期格式和文本中的表情格式,同时从数据库中获取和当前登陆用户有过私信记录的所有用户,将组装好的集合返回给控制器,控制器将数据响应给前台页面中,遍历集合中的数据,展示在前台JSP页面中。核心代码如下:


    发送私信:用户先选择要发送的用户,输入需要发送的私信信息,可以在私信中插入表情,点击发送后会提交form表单,浏览器发送请求到后台控制器中,控制器获取发送用户的id和接收用户的id,调用Service层中的方法,组装数据源为私信信息,设置私信信息为未阅读状态,在Service层中对应的方法中调用Dao层接口将私信信息保存在数据库中。核心代码如下:

    搜索用户:在搜索用户页面中,用户可以输入用户昵称的关键字来模糊查询相关用户,当用户输入要查询的用户昵称,浏览器发送请求携带关键字等信息跳转至控制器中特定的方法,在控制器方法内部调用Service中的方法处理逻辑,业务层调用Dao层接口中的查找方法查找用户昵称中包含有关键字的用户,将从数据库获得的对应用户组装成一个集合,遍历集合将用户昵称中包含的关键字改成红色,控制器返回响应跳转到搜索结果页面,遍历集合展示所有查找到的用户信息。如图5.16所示。核心代码如下:


    用户主页面:在页面中,点击任意一个用户的名称或头像都会跳转至对应用户的个人主页,用户的个人主页显示用户的个人信息和用户最近发布的微博,按照时间倒序排列,用户也可以对微博的点赞、转发、评论、收藏做操作。在后天代码中,当用户点击其他用户的头像或名称时,浏览器发送携带用户id的请求到后台控制器中。控制器调用Service中对应的方法,在Service方法中调用Dao层接口从数据库中查看用户的基本信息和用户的微博信息,将所有的数据存放在集合中。返回到控制器中,控制器携带数据返回到前台页面中做展示,如图5.17所示。核心代码如下:


    5.5 海螺模块发布:当用户进入海螺主页时,浏览器页面发送请求到后台控制器中,调用业务层中特定的方法,Service中调用Dao层接口在数据库中查找有关不同筛选条件的海螺问题,并将查找到的问题封装在集合中,通过控制器发送服务器响应,跳转到海螺首页,并循环展示所有的海螺问题。如果用户需要发布问题,在填写了问题描述和所要悬赏的积分数后,点击发布,浏览器提交表单数据到后台控制器中,在Service层中首先判断用户的海螺积分是否大于悬赏积分,如果小于悬赏积分就返回浏览器页面提示用户积分不足。如果积分足够就调用Dao层接口把用户的海螺问题保存在数据库中,同时减少用户的海螺积分修改用户基本信息。核心代码如下:


    查看海螺问题:用户点击海螺首页具体的问题时,浏览器发送请求给后台控制器中调用Service层对应的方法,在Service层中调用Dao层接口通过海螺问题的编号来查看海螺的具体信息,通过发布问题的用户id在数据库中查找对应的发布人信息,服务器返回响应到浏览器中,展示海螺的具体信息,上方展示发布用户的用户名、用户称号、发布日期、海螺问题、悬赏积分、问题的状态等,在中间显示问题的所有回复信息,回复人的用户名、称号、回答内容、回复日期,是否被采纳等,如图5.19所示。核心代码如下:


    回答海螺问题:在海螺问题详细详细信息页面的底部可以回答海螺问题,在输入框中输入回复的答案,插入表情信息,点击回复提交表单,浏览器请求携带表单数据到后台控制器中,被控制器中具体的方法接收,获取Session中的用户编号,组装回复信息的数据源,传递参数到Service中,在Service中调用Dao层接口保存海螺的回复信息,如图5.20所示。核心代码如下:


    采纳问题答案:在海螺问题首页,用户通过点击我的海螺问题可以跳转至用户自己所发布的海螺问题页面,在自己所发布的海螺问题页面中可以选择自己认为最正确的答案,被采纳的答案变为采纳答案,增加采纳者的海螺积分,如图5.21所示,在后台代码中,当用户点击采纳后,浏览器请求携带海螺问题id和问题回复信息被后台控制器中具体的方法接收,在控制器中调用Service层,业务层中调用Dao层接口修改数据库中海螺问题的状态为已解决,修改评论表中被采纳用户的评论状态为被采纳,刷新前台海螺问题页面。核心代码如下:


    5.6 后台管理员模块管理员登陆:管理员可以通过在登陆页面中点击管理员登陆,跳转至管理员登陆页面如图5.22所示,管理员登陆后,页面发送请求到后台控制器中,后台Controller层接收请求,将用户名和密码作为参数调用业务层中的方法,在Service层中调用Dao层接口和数据库中管理员账号表中查询,如果存在就返回管理员类,如果不存在,则抛出异常,异常层层上抛,在控制器层中接收,并将错误信息保存在方法的返回值中,在页面提示,如果用户名和密码正确,就跳转到Controller中管理员首页的处理方法中,在管理员首页的处理方法中获取需要显示的数据并展示。核心代码如下:


    管理员首页:登陆后,系统会跳转到管理员首页,在首页中上方显示导航栏,在页面内容方面,通过四张图表来显示微博、用户、评、海螺、评论、回答等的总数以及当月数,在下方的柱状图中则显示距离今天最近的7天的数目。后台首先在Controller层中跳转至管理员首页对应的处理方法中,调用Service层中对应的方法获取首页展示数据,并将获取到的数据组装到Map集合中,在服务层中调用Dao层中的方法来获取首页需要展示的用户、微博、评论、回答等数据保存在Map集合中,在控制层中获取到返回值数据并保存在服务器响应中,返回给前台页面使用EL表达式展示数据,如图5.23所示。核心代码如下:


    用户管理首页:后台代码通过调用Service层中对应的方法,Service方法里面则调用Dao层接口和数据库交互,获取数据库中所有的用户信息封装成List集合,返回给Controller层将用户List集合响应给浏览器,在页面中通过c标签遍历循环显示用户信息,页面的分页使用自定义分页类PageBean来保存分页信息,在数据库层做分页一次获取10条数据。核心代码如下:


    搜索用户:在用户管理页面输入用户昵称中的关键字来搜索用户,系统会将获取到的用户信息中昵称为输入关键字的那部分显示为红色。在后台代码中,点击搜索会将管理员输入的用户昵称关键字传给后台控制器中,在控制器中调用Service层中对应的搜索用户的方法,在Service层中调用Dao层接口在数据库中通过迷糊查询来获取用户。并将获取到的数据层层返回,在Controller中响应给前台页面,然后在页面中做展示,如图5.25所示。核心代码如下:


    用户封禁:管理员可以通过点击用户管理页面操作一栏中的封禁按钮来对违规用户的封禁,管理员可以输入封禁的天数,那么在这个日期之前,用户是不能正常登陆的,管理员也可以通过点击解封来提前解除封禁用户的操作,具体页面如图5.26所示。在后台代码中则是在Service层中调用Dao层接口,更改用户状态和封禁日期,解除封禁和封禁用户类似,因此不做具体说明。核心代码如下:


    微博管理首页:管理员点击导航栏中的微博管理,系统会跳转至用户微博管理页面,显示系统中所用是微博信息,管理员也可以通过输入微博内容中的关键字来搜索微博,同时可以删除有不良信息的微博,或是恢复以被删除的微博,微博管理页面如图5.27所示,在后台代码中,当用户点击导航栏中微博管理时,浏览器会发送相应的请求到SpringMVC框架的Controller中对用的更能处理方法中,在控制器中调用Service层中显示所有微博的方法中,在Service层中再调用Dao层方法获取所有的微博信息,在分页方面没有采用在前端页面中做分页的方法,而是在数据库中通过Oracle的伪列来做分页,一次获取10条数据,最后在Controller中将获取到的微博信息响应给浏览器,浏览器中通过c标签遍历显示微博信息。核心代码如下:


    微博搜索:管理员可以通过输入微博内容中的关键字来搜索在微博中存在该关键字的微博,搜索到的微博内容中的关键字会使用红色标注出来,具体页面如图5.28所示,在后台代码中,控制器中特定的方法接收浏览发送的搜索微博请求,调用Service层中对应的方法,将关键字作为参数传给Dao层接口中,在Dao层接口中查找数据库微博表中微博内容包含该关键字的微博信息,在数据库中则是通过模糊查询来查找对应微博。然后将查找到微博信息封装到List集合中,层层返回到Controller层中对应的方法,在方法中将数据响应给浏览器,浏览器接收响应在页面中通过c标签展示数据。核心代码如下:


    微博删除:在微博管理页面中,管理员可以根据微博的内容来判断微博是否违法等信息,如果微博信息中包含不良信息,管理员可以通过操作栏中的删除按钮来删除微博或者可以对已经删除的为微博做恢复操作,当用户点击删除时,页面会携带着微博编号等参数发送请求给服务器,请求会被控制器中对应的方法所接收,将微博编号作为参数嗲用Service中对应的方法,在方法中调用Dao层接口在修改数据库中对应微博编号的微博状态为已删除,恢复微博和删除类似,只是修改数据库中对应微博信息状态为正常即可。核心代码如下:

    海螺管理首页:管理员点击导航栏中的海螺管理可以跳转至海螺管理页面,如图5.29所示,在页面的上方为导航栏,页面内容则显示所有的海螺信息、海螺搜索框、以及下方的分页框,海螺信息包括编号、发布人名称、海螺的内容、发布日期、海螺状态以及可以执行的操作,在后台代码方面,当用户点击导航栏中的海螺管理时,浏览器发送请求,请求在控制器中被对应的方法接收,Service中调用Dao层接口在数据库海螺表中查找所有的海螺信息,保存在List集合中,遍历List集合组装发布人用户信息,修改日期格式等,最后将组装号的List集合返回到Controller中,响应给浏览器跳转海螺管理首页,通过使用c标签遍历显示查找到的海螺信息,因为系统中使用的分页方法一致,因此在这里不再赘述。核心代码如下:


    海螺搜索:在海螺管理首页中,考虑到海螺数量多不好查找的问题,因此设置了搜索功能,管理员可以通过在海螺搜索框中输入海螺内容中的关键字来搜索海螺问题,在展示搜索到的结果时会将搜索关键字使用红色标注出来,方便查看。具体页面如图5.30所示。在后台代码中,当用户输入关键字点击搜索时,浏览器发送请求到控制器中指定方法接收,在Controller中调用Service层中对应的方法处理业务逻辑,然后在Service层中调用Dao层接口通过模糊查询在数据库海螺表中查找对应的海螺信息,最后在Controller中将查找到的海螺信息响应给浏览器,在页面中通过c标签遍历展示数据。因为系统使用一样的分页方法,因此不做赘述。核心代码如下:


    第六章 总 结时光荏苒,岁月如梭,转眼之间为期半年的毕业设计以及论文的编写终于落下帷幕,回顾毕业设计的每一个阶段都使我受益匪浅,在毕设初期,经过了长达一周的深思熟虑之后我决定将B/S微博系统作为我这次毕业设计和论文的选题,因为平时对微博的接触以及近几年微博的火热程度,使我对微博系统的具体功能和优缺点都有了一个全体的把控,这也使得在之后系统功能设计时能够更加的得心应手,在毕业设计中期时进入代码编写阶段时,我选择了J2EE体系开发Web项目的B/S微博,使用面向对象语言JAVA作为开发语言,使用个人熟练掌握的SSM框架来搭建系统,SSM框架强大的功能减少了大量的冗余代码,使得系统代码的编写更加轻松,提高了系统的开发效率,然而开发一个系统并不是那么简单就能完成,在代码的编写阶段,问题接连出现,但在指导老师的指导下以及自己通过网上查阅资料,最终解决了这些问题,提高了自己代码的编写能力,同时也提高了自我学习能力,这对以后的生活学习和工作中都有着非同寻常的意义,在项目编写完成后的测试阶段,之前在编写阶段没有被发现的系统缺陷逐渐跃出水面,在每一次修复这些问题的时候,我都能感觉到自己的能力在慢慢提高,一个没有缺陷的系统是不存在的,经过对测试用例中的测试方案一一测试,然后修复掉一个又一个的缺陷后,微博系统中大多数可见性高的BUG都被修复了,但是在之后的使用中还会不断的完善系统让它变的更加成熟,而不单单只是作为一份毕设设计而存在。
    在这次毕业设计系统的开发中也让我看到了自身的一些问题,例如前端技术的不熟练导致在前台页面的修改和开发中浪费了大量的时间,微博系统中关键性的用户交互不够美观,用户体验性差,以及在系统类设计时没能正确的把控全局,设计出合理的接口,只能在后期代码编写阶段中不断的去完善。
    总而言之,在对微博系统的开发中,我学习到了很多以前没有注意到和忽略到的东西,也使我认识到了自身的一些缺陷,让我在以后的生活和工作中都能更好的认识自己,提高自己的能力,然后服务于社会,做一个对社会发展有帮助的人。
    参考文献[1] 贾文潇,邓俊杰. 基于Java的Web开发技术浅析[J]. 电子测试,2016
    [2] 李传扬. 微博分析系统的设计与实现[D]. 北京邮电大学 2015
    [3] 刘运臣. 网站设计与建设[M]. 清华大学出版社, 2008
    [4] 秦雅华. 基于WEB2.0的微博网站的设计与实现[D]. 北京工业大学 2012
    [5] 陈玲,夏汛. 利用Mybatis的动态SQL实现物理分页[J]. 数字技术与应用. 2011(11)
    [6] 萨师煊,王珊. 数据库系统概论(第三版)[M].北京:高等教育出版社,1998
    [7] 基于Java的数据库访问技术研究[J]. 科技资讯. 2009(04)
    [8] 张峰. 基于Ajax技术与J2EE框架的Web应用研究与实现[D]. 中国地质大学 2008
    [9] 基于Java多线程技术的网络编程[J]. 电脑编程技巧与维护. 2009(22)
    [10] 李威. 一种小型实用即时网络聊天通讯系统的设计[J]. 长江大学学报(自然科学版). 2011(12)
    [11] 钟睿祺. 基于微博嵌入小伙伴阅读网的分析与设计[D]. 华南理工大学 2011
    [12] 王少锋编著.面向对象技术UML教程[M]. 清华大学出版社, 2004
    [13] 徐春绵. 关于网站开发相关问题的探究[J]. 通讯世界. 2015(09)
    [14] 张宇,王映辉,张翔南. 基于Spring的MVC框架设计与实现[J]. 计算机工程. 2010(04)
    [15] 胡以谰,张立平. J2EE开发模式的选择[J]. 计算机系统应用. 2002(08)
    [16] 王丽爱. 《Java程序设计》课程网站的设计与实现[J]. 电脑知识与技术. 2016(27)
    [17] 荣艳冬. 关于Mybatis持久层框架的应用研究[J]. 信息安全与技术. 2015(12)
    8 留言 2019-12-20 17:48:45 奖励50点积分
  • 枚举并删除系统上PsSetCreateProcessNotifyRoutine回调

    背景我们学习内核 Rootkit 编程,那么肯定会接触到各种无 HOOK 回调函数的设置,这些回调函数都是官方为我们做好的接口,我们直接调用就好。这些回调使用方便,运行在底层,功能强大,而且非常稳定。很多杀软、游戏保护等就是设置这些回调,实现对计算机的监控的。
    既然可以设置回调,自然也可以删除回调。如果是自己程序设置的回调,当然可以很容易删除。但是,我们要做的是要枚举系统上存在的回调,不管是不是自己程序创建的,然后,并对这些回调进行删除,使其失效。
    本文要介绍的是枚举并删除系统上 PsSetCreateProcessNotifyRoutine 回调,支持 32 位和 64 位、Win7 到 Win10 全平台系统。现在,我把实现的过程和原理整理成文档,分享给大家。
    实现原理我们注册的进程回调,会存储在一个名为 PspCreateProcessNotifyRoutine 的数组里。 PspCreateProcessNotifyRoutine 可以理解成一个 PVOID 数组,它存储着系统里所有 PsSetCreateProcessNotifyRoutine 进程回调函数地址的加密地址。PspCreateProcessNotifyRoutine数组里的数据是加密的,要经过解密操作才可以获取正确的数据。
    PspCreateProcessNotifyRoutine 数组地址的获取首先,我们需要获取 PspCreateProcessNotifyRoutine 的地址。我们可以借助 WinDbg 帮助我们进行内核调试,下面是我们借助 WinDbg 逆向 Win10 x64 内核函数 PspSetCreateProcessNotifyRoutine 的代码:
    nt!PspSetCreateProcessNotifyRoutine+0x4c:fffff801`54b3b57c 33ff xor edi,edifffff801`54b3b57e 4c8d3dfb0bdfff lea r15,[nt!PspCreateProcessNotifyRoutine (fffff801`5492c180)]
    我们从上面发现,函数中会调用到 PspCreateProcessNotifyRoutine 的地址。所以,在 64 位系统中,我们可以通过扫描内存特征码,从而获取数组PspCreateProcessNotifyRoutine 的 4 字节偏移,再计算出它的地址。在 32 位系统中,通过扫描内存特征码,就可以直接获取 PspCreateProcessNotifyRoutine 的地址了。注意的是:不同系统上,特征码也会不同。下面是我们总结的特征码:




    Win7
    win8.1
    win10




    32 位
    C7450C
    B8
    BB


    64 位
    4C8D35
    4C8D3D
    4C8D3D



    其中,内核函数 PspSetCreateProcessNotifyRoutine 并不是导出函数,所以不能直接获取它的函数地址。使用WinDbg 逆向 PsSetCreateProcessNotifyRoutine 函数的代码如下(Win10 x64 系统):
    nt!PsSetCreateProcessNotifyRoutine:fffff800`042cb3c0 4533c0 xor r8d,r8dfffff800`042cb3c3 e9e8fdffff jmp nt!PspSetCreateProcessNotifyRoutine (fffff800`042cb1b0)
    我们从上面代码发现,内核函数 PsSetCreateProcessNotifyRoutine 里会调用到内核函数 PspSetCreateProcessNotifyRoutine。所以,在 32 位和 64 位系统中,我们可以通过扫描内存特征码,从而获取 PspSetCreateProcessNotifyRoutine 的 4 字节偏移,再计算出它的地址。注意的是:不同系统上,特征码也会不同。下面是我们总结的特征码:




    Win7
    win8.1
    win10




    32 位
    E8
    E8
    E8


    64 位
    E9
    E9
    E9



    要就是说,获取 PspCreateProcessNotifyRoutine 地址可以分成两步:

    首先,通过扫描特征码,从 PsSetCreateProcessNotifyRoutine 函数中获取 PspSetCreateProcessNotifyRoutine 函数的地址
    然后,通过扫描特征码,从 PspSetCreateProcessNotifyRoutine 函数中获取 PspCreateProcessNotifyRoutine 数组的地址

    那么,特征码的确定就变得至关重要了。
    PspCreateProcessNotifyRoutine 里数据的解密我们上面说,PspCreateProcessNotifyRoutine 里的数据是加密的,在 64 位系统和 32 位系统上的加密方式是不相同的,自然解密方式也不同。现在,我们分别介绍 32 位系统和 64 位系统下的解密方式。
    对于 32 位系统来说:PspCreateProcessNotifyRoutine 是一个 4 字节无符号类型的数组,数组大小最大为 8。我们使用 PspCreateProcessNotifyRoutine[i] 表示数组中的值,那么,32 位系统下的解密方式为:

    首先,数组的值 PspCreateProcessNotifyRoutine[i] 位运算“与” 0xFFFFFFF8
    然后,“与”运算之后的结果值再加上 4,结果就是一个存储着回调函数地址的地址

    对于 64 位系统来说:PspCreateProcessNotifyRoutine 是一个 8 字节无符号类型的数组,数组大小最大为 64。我们使用 PspCreateProcessNotifyRoutine[i] 表示数组中的值,那么,64 位系统下的解密方式为:
    数组的值 PspCreateProcessNotifyRoutine[i] 位运算“与” 0xFFFFFFFFFFFFFFF8,结果就是一个存储着回调函数地址的地址。
    删除回调我们可以通过上述介绍的方法,枚举系统中的回调函数。那么,要删除回调函数可以有 3 种方式:

    可以直接调用 PsSetCreateProcessNotifyRoutine 函数,传入回调函数地址,并设置删除回调函数标志为 TRUE,即可删除回调
    修改 PspCreateProcessNotifyRoutine 数组中的数据,使其指向我们自己定义的空回调函数地址。这样,当触发回调函数的时候,执行的是我们自己的空回调函数
    修改回调函数的前几字节内存数据,写入直接返回指令 RET,不进行任何操作

    编码实现遍历回调// 遍历回调BOOLEAN EnumNotifyRoutine(){ ULONG i = 0; PVOID pPspCreateProcessNotifyRoutineAddress = NULL; PVOID pNotifyRoutineAddress = NULL; // 获取 PspCreateProcessNotifyRoutine 数组地址 pPspCreateProcessNotifyRoutineAddress = GetPspCreateProcessNotifyRoutine(); if (NULL == pPspCreateProcessNotifyRoutineAddress) { DbgPrint("GetPspCreateProcessNotifyRoutine Error!\n"); return FALSE; } DbgPrint("pPspCreateProcessNotifyRoutineAddress=0x%p\n", pPspCreateProcessNotifyRoutineAddress); // 获取回调地址并解密#ifdef _WIN64 for (i = 0; i < 64; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)pNotifyRoutineAddress; DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } }#else for (i = 0; i < 8; i++) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspCreateProcessNotifyRoutineAddress + sizeof(PVOID) * i); pNotifyRoutineAddress = (PVOID)((ULONG)pNotifyRoutineAddress & 0xfffffff8); if (MmIsAddressValid(pNotifyRoutineAddress)) { pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pNotifyRoutineAddress + 4); DbgPrint("[%d]ullNotifyRoutine=0x%p\n", i, pNotifyRoutineAddress); } }#endif return TRUE;}
    移除回调// 移除回调NTSTATUS RemoveNotifyRoutine(PVOID pNotifyRoutineAddress){ NTSTATUS status = PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)pNotifyRoutineAddress, TRUE); if (!NT_SUCCESS(status)) { ShowError("PsSetCreateProcessNotifyRoutine", status); } return status;}
    获取 PspCreateProcessNotifyRoutine 数组地址// 获取 PspCreateProcessNotifyRoutine 数组地址PVOID GetPspCreateProcessNotifyRoutine(){ PVOID pPspCreateProcessNotifyRoutineAddress = NULL; RTL_OSVERSIONINFOW osInfo = { 0 }; UCHAR pFirstSpecialData[50] = { 0 }; ULONG ulFirstSpecialDataSize = 0; UCHAR pSecondSpecialData[50] = { 0 }; ULONG ulSecondSpecialDataSize = 0; // 获取系统版本信息, 判断系统版本 RtlGetVersion(&osInfo); if (6 == osInfo.dwMajorVersion) { if (1 == osInfo.dwMinorVersion) { // Win7#ifdef _WIN64 // 64 位 // E9 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; // 4C8D35 pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x35; ulSecondSpecialDataSize = 3;#else // 32 位 // E8 pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; // C7450C pSecondSpecialData[0] = 0xC7; pSecondSpecialData[1] = 0x45; pSecondSpecialData[2] = 0x0C; ulSecondSpecialDataSize = 3;#endif } else if (2 == osInfo.dwMinorVersion) { // Win8#ifdef _WIN64 // 64 位#else // 32 位#endif } else if (3 == osInfo.dwMinorVersion) { // Win8.1#ifdef _WIN64 // 64 位 // E9 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; // 4C8D3D pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x3D; ulSecondSpecialDataSize = 3;#else // 32 位 // E8 pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; // B8 pSecondSpecialData[0] = 0xB8; ulSecondSpecialDataSize = 1;#endif } } else if (10 == osInfo.dwMajorVersion) { // Win10#ifdef _WIN64 // 64 位 // E9 pFirstSpecialData[0] = 0xE9; ulFirstSpecialDataSize = 1; // 4C8D3D pSecondSpecialData[0] = 0x4C; pSecondSpecialData[1] = 0x8D; pSecondSpecialData[2] = 0x3D; ulSecondSpecialDataSize = 3;#else // 32 位 // E8 pFirstSpecialData[0] = 0xE8; ulFirstSpecialDataSize = 1; // BB pSecondSpecialData[0] = 0xBB; ulSecondSpecialDataSize = 1;#endif } // 根据特征码获取地址 pPspCreateProcessNotifyRoutineAddress = SearchPspCreateProcessNotifyRoutine(pFirstSpecialData, ulFirstSpecialDataSize, pSecondSpecialData, ulSecondSpecialDataSize); return pPspCreateProcessNotifyRoutineAddress;}
    根据特征码获取 PspCreateProcessNotifyRoutine 数组地址// 根据特征码获取 PspCreateProcessNotifyRoutine 数组地址PVOID SearchPspCreateProcessNotifyRoutine(PUCHAR pFirstSpecialData, ULONG ulFirstSpecialDataSize, PUCHAR pSecondSpecialData, ULONG ulSecondSpecialDataSize){ UNICODE_STRING ustrFuncName; PVOID pAddress = NULL; LONG lOffset = 0; PVOID pPsSetCteateProcessNotifyRoutine = NULL; PVOID pPspSetCreateProcessNotifyRoutineAddress = NULL; PVOID pPspCreateProcessNotifyRoutineAddress = NULL; // 先获取 PsSetCreateProcessNotifyRoutine 函数地址 RtlInitUnicodeString(&ustrFuncName, L"PsSetCreateProcessNotifyRoutine"); pPsSetCteateProcessNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName); if (NULL == pPsSetCteateProcessNotifyRoutine) { ShowError("MmGetSystemRoutineAddress", 0); return pPspCreateProcessNotifyRoutineAddress; } // 然后, 查找 PspSetCreateProcessNotifyRoutine 函数地址 pAddress = SearchMemory(pPsSetCteateProcessNotifyRoutine, (PVOID)((PUCHAR)pPsSetCteateProcessNotifyRoutine + 0xFF), pFirstSpecialData, ulFirstSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory1", 0); return pPspCreateProcessNotifyRoutineAddress; } // 获取偏移数据, 并计算地址 lOffset = *(PLONG)pAddress; pPspSetCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); // 最后, 查找 PspCreateProcessNotifyRoutine 地址 pAddress = SearchMemory(pPspSetCreateProcessNotifyRoutineAddress, (PVOID)((PUCHAR)pPspSetCreateProcessNotifyRoutineAddress + 0xFF), pSecondSpecialData, ulSecondSpecialDataSize); if (NULL == pAddress) { ShowError("SearchMemory2", 0); return pPspCreateProcessNotifyRoutineAddress; } // 获取地址#ifdef _WIN64 // 64 位先获取偏移, 再计算地址 lOffset = *(PLONG)pAddress; pPspCreateProcessNotifyRoutineAddress = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);#else // 32 位直接获取地址 pPspCreateProcessNotifyRoutineAddress = *(PVOID *)pAddress;#endif return pPspCreateProcessNotifyRoutineAddress;}
    指定内存区域的特征码扫描// 指定内存区域的特征码扫描PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize){ PVOID pAddress = NULL; PUCHAR i = NULL; ULONG m = 0; // 扫描内存 for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++) { // 判断特征码 for (m = 0; m < ulMemoryDataSize; m++) { if (*(PUCHAR)(i + m) != pMemoryData[m]) { break; } } // 判断是否找到符合特征码的地址 if (m >= ulMemoryDataSize) { // 找到特征码位置, 获取紧接着特征码的下一地址 pAddress = (PVOID)(i + ulMemoryDataSize); break; } } return pAddress;}
    程序测试在 Win7 32 位系统下,驱动程序正常执行:

    在 Win8.1 32 位系统下,驱动程序正常执行:

    在 Win10 32 位系统下,驱动程序正常执行:

    在 Win7 64 位系统下,驱动程序正常执行:

    在 Win8.1 64 位系统下,驱动程序正常执行:

    在 Win10 64 位系统下,驱动程序正常执行:

    总结要理解清楚获取 PspCreateProcessNotifyRoutine 地址的流程,其中,不同系统的内存特征码是不同的,要注意区分。大家也不用记忆这些特征码,如果需要用到,可以随时使用 WinDbg 来进行逆向查看就好。
    而且,不同位数系统中,对 PspCreateProcessNotifyRoutine 数组中的值解密方式也不相同,这个需要区分开来。
    删除回调常用就有 3 种方式,自己根据需要选择一种使用即可。
    参考参考自《Windows黑客编程技术详解》一书
    附录Win7 32 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:83fa573b 8bff mov edi,edi83fa573d 55 push ebp83fa573e 8bec mov ebp,esp83fa5740 807d0c00 cmp byte ptr [ebp+0Ch],083fa5744 53 push ebx83fa5745 56 push esi83fa5746 57 push edi83fa5747 0f84fa000000 je nt!PspSetCreateProcessNotifyRoutine+0x10a (83fa5847)nt!PspSetCreateProcessNotifyRoutine+0x12:83fa574d 648b3524010000 mov esi,dword ptr fs:[124h]83fa5754 66ff8e84000000 dec word ptr [esi+84h]83fa575b 33db xor ebx,ebx83fa575d c7450ce032f583 mov dword ptr [ebp+0Ch],offset nt!PspCreateProcessNotifyRoutine (83f532e0)nt!PspSetCreateProcessNotifyRoutine+0x29:83fa5764 ff750c push dword ptr [ebp+0Ch]83fa5767 e894bb0d00 call nt!ExReferenceCallBackBlock (84081300)83fa576c 8bf8 mov edi,eax83fa576e 85ff test edi,edi83fa5770 7439 je nt!PspSetCreateProcessNotifyRoutine+0x6e (83fa57ab)nt!PspSetCreateProcessNotifyRoutine+0x37:83fa5772 8b4f08 mov ecx,dword ptr [edi+8]83fa5775 e8e31ef2ff call nt!ExGetCallBackBlockRoutine (83ec765d)83fa577a 3b4508 cmp eax,dword ptr [ebp+8]83fa577d 7522 jne nt!PspSetCreateProcessNotifyRoutine+0x64 (83fa57a1)nt!PspSetCreateProcessNotifyRoutine+0x44:83fa577f 85c9 test ecx,ecx83fa5781 7509 jne nt!PspSetCreateProcessNotifyRoutine+0x4f (83fa578c)nt!PspSetCreateProcessNotifyRoutine+0x48:83fa5783 384d10 cmp byte ptr [ebp+10h],cl83fa5786 7519 jne nt!PspSetCreateProcessNotifyRoutine+0x64 (83fa57a1)nt!PspSetCreateProcessNotifyRoutine+0x4d:83fa5788 eb08 jmp nt!PspSetCreateProcessNotifyRoutine+0x55 (83fa5792)nt!PspSetCreateProcessNotifyRoutine+0x4f:83fa578c 807d1000 cmp byte ptr [ebp+10h],083fa5790 740f je nt!PspSetCreateProcessNotifyRoutine+0x64 (83fa57a1)nt!PspSetCreateProcessNotifyRoutine+0x55:83fa5792 8b450c mov eax,dword ptr [ebp+0Ch]83fa5795 57 push edi83fa5796 33c9 xor ecx,ecx83fa5798 e8abfeffff call nt!ExCompareExchangeCallBack (83fa5648)83fa579d 84c0 test al,al83fa579f 7547 jne nt!PspSetCreateProcessNotifyRoutine+0xab (83fa57e8)nt!PspSetCreateProcessNotifyRoutine+0x64:83fa57a1 8b450c mov eax,dword ptr [ebp+0Ch]83fa57a4 8bcf mov ecx,edi83fa57a6 e889bc0d00 call nt!ExDereferenceCallBackBlock (84081434)nt!PspSetCreateProcessNotifyRoutine+0x6e:83fa57ab 83450c04 add dword ptr [ebp+0Ch],483fa57af 43 inc ebx83fa57b0 83fb40 cmp ebx,40h83fa57b3 72af jb nt!PspSetCreateProcessNotifyRoutine+0x29 (83fa5764)nt!PspSetCreateProcessNotifyRoutine+0x78:83fa57b5 66ff8684000000 inc word ptr [esi+84h]83fa57bc 0fb78684000000 movzx eax,word ptr [esi+84h]83fa57c3 6685c0 test ax,ax83fa57c6 7516 jne nt!PspSetCreateProcessNotifyRoutine+0xa1 (83fa57de)nt!PspSetCreateProcessNotifyRoutine+0x8b:83fa57c8 8d4640 lea eax,[esi+40h]83fa57cb 3900 cmp dword ptr [eax],eax83fa57cd 740f je nt!PspSetCreateProcessNotifyRoutine+0xa1 (83fa57de)nt!PspSetCreateProcessNotifyRoutine+0x92:83fa57cf 6683be8600000000 cmp word ptr [esi+86h],083fa57d7 7505 jne nt!PspSetCreateProcessNotifyRoutine+0xa1 (83fa57de)nt!PspSetCreateProcessNotifyRoutine+0x9c:83fa57d9 e8a714e9ff call nt!KiCheckForKernelApcDelivery (83e36c85)nt!PspSetCreateProcessNotifyRoutine+0xa1:83fa57de b87a0000c0 mov eax,0C000007Ah83fa57e3 e9c5000000 jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0xab:83fa57e8 83c9ff or ecx,0FFFFFFFFh83fa57eb 807d1000 cmp byte ptr [ebp+10h],083fa57ef b8e433f583 mov eax,offset nt!PspCreateProcessNotifyRoutineCount (83f533e4)83fa57f4 7405 je nt!PspSetCreateProcessNotifyRoutine+0xbe (83fa57fb)nt!PspSetCreateProcessNotifyRoutine+0xb9:83fa57f6 b8e033f583 mov eax,offset nt!PspCreateProcessNotifyRoutineExCount (83f533e0)nt!PspSetCreateProcessNotifyRoutine+0xbe:83fa57fb f00fc108 lock xadd dword ptr [eax],ecx83fa57ff 8d049de032f583 lea eax,nt!PspCreateProcessNotifyRoutine (83f532e0)[ebx*4]83fa5806 8bcf mov ecx,edi83fa5808 e827bc0d00 call nt!ExDereferenceCallBackBlock (84081434)83fa580d 66ff8684000000 inc word ptr [esi+84h]83fa5814 0fb78684000000 movzx eax,word ptr [esi+84h]83fa581b 6685c0 test ax,ax83fa581e 7516 jne nt!PspSetCreateProcessNotifyRoutine+0xf9 (83fa5836)nt!PspSetCreateProcessNotifyRoutine+0xe3:83fa5820 8d4640 lea eax,[esi+40h]83fa5823 3900 cmp dword ptr [eax],eax83fa5825 740f je nt!PspSetCreateProcessNotifyRoutine+0xf9 (83fa5836)nt!PspSetCreateProcessNotifyRoutine+0xea:83fa5827 6683be8600000000 cmp word ptr [esi+86h],083fa582f 7505 jne nt!PspSetCreateProcessNotifyRoutine+0xf9 (83fa5836)nt!PspSetCreateProcessNotifyRoutine+0xf4:83fa5831 e84f14e9ff call nt!KiCheckForKernelApcDelivery (83e36c85)nt!PspSetCreateProcessNotifyRoutine+0xf9:83fa5836 8bcf mov ecx,edi83fa5838 e83a251700 call nt!ExWaitForCallBacks (84117d77)83fa583d 57 push edi83fa583e e8c88a0e00 call nt!AlpcpFreeBuffer (8408e30b)nt!PspSetCreateProcessNotifyRoutine+0x106:83fa5843 33c0 xor eax,eax83fa5845 eb66 jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0x10a:83fa5847 807d1000 cmp byte ptr [ebp+10h],083fa584b 7413 je nt!PspSetCreateProcessNotifyRoutine+0x123 (83fa5860)nt!PspSetCreateProcessNotifyRoutine+0x110:83fa584d ff7508 push dword ptr [ebp+8]83fa5850 e82bb0fdff call nt!MmVerifyCallbackFunction (83f80880)83fa5855 85c0 test eax,eax83fa5857 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x123 (83fa5860)nt!PspSetCreateProcessNotifyRoutine+0x11c:83fa5859 b8220000c0 mov eax,0C0000022h83fa585e eb4d jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0x123:83fa5860 33c0 xor eax,eax83fa5862 384510 cmp byte ptr [ebp+10h],al83fa5865 0f95c0 setne al83fa5868 50 push eax83fa5869 ff7508 push dword ptr [ebp+8]83fa586c e8a8fdffff call nt!ExAllocateCallBack (83fa5619)83fa5871 8bd8 mov ebx,eax83fa5873 85db test ebx,ebx83fa5875 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x141 (83fa587e)nt!PspSetCreateProcessNotifyRoutine+0x13a:83fa5877 b89a0000c0 mov eax,0C000009Ah83fa587c eb2f jmp nt!PspSetCreateProcessNotifyRoutine+0x170 (83fa58ad)nt!PspSetCreateProcessNotifyRoutine+0x141:83fa587e bee032f583 mov esi,offset nt!PspCreateProcessNotifyRoutine (83f532e0)83fa5883 33ff xor edi,edint!PspSetCreateProcessNotifyRoutine+0x148:83fa5885 6a00 push 083fa5887 8bcb mov ecx,ebx83fa5889 8bc6 mov eax,esi83fa588b e8b8fdffff call nt!ExCompareExchangeCallBack (83fa5648)83fa5890 84c0 test al,al83fa5892 7520 jne nt!PspSetCreateProcessNotifyRoutine+0x177 (83fa58b4)nt!PspSetCreateProcessNotifyRoutine+0x157:83fa5894 83c704 add edi,483fa5897 83c604 add esi,483fa589a 81ff00010000 cmp edi,100h83fa58a0 72e3 jb nt!PspSetCreateProcessNotifyRoutine+0x148 (83fa5885)nt!PspSetCreateProcessNotifyRoutine+0x165:83fa58a2 53 push ebx83fa58a3 e8638a0e00 call nt!AlpcpFreeBuffer (8408e30b)83fa58a8 b80d0000c0 mov eax,0C000000Dhnt!PspSetCreateProcessNotifyRoutine+0x170:83fa58ad 5f pop edi83fa58ae 5e pop esi83fa58af 5b pop ebx83fa58b0 5d pop ebp83fa58b1 c20c00 ret 0Chnt!PspSetCreateProcessNotifyRoutine+0x177:83fa58b4 33c9 xor ecx,ecx83fa58b6 41 inc ecx83fa58b7 807d1000 cmp byte ptr [ebp+10h],083fa58bb 7525 jne nt!PspSetCreateProcessNotifyRoutine+0x1a5 (83fa58e2)nt!PspSetCreateProcessNotifyRoutine+0x180:83fa58bd b8e433f583 mov eax,offset nt!PspCreateProcessNotifyRoutineCount (83f533e4)83fa58c2 f00fc108 lock xadd dword ptr [eax],ecx83fa58c6 a19830f583 mov eax,dword ptr [nt!PspNotifyEnableMask (83f53098)]83fa58cb a802 test al,283fa58cd 0f8570ffffff jne nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)nt!PspSetCreateProcessNotifyRoutine+0x196:83fa58d3 b89830f583 mov eax,offset nt!PspNotifyEnableMask (83f53098)83fa58d8 f00fba2801 lock bts dword ptr [eax],183fa58dd e961ffffff jmp nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)nt!PspSetCreateProcessNotifyRoutine+0x1a5:83fa58e2 b8e033f583 mov eax,offset nt!PspCreateProcessNotifyRoutineExCount (83f533e0)83fa58e7 f00fc108 lock xadd dword ptr [eax],ecx83fa58eb a19830f583 mov eax,dword ptr [nt!PspNotifyEnableMask (83f53098)]83fa58f0 a804 test al,483fa58f2 0f854bffffff jne nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)nt!PspSetCreateProcessNotifyRoutine+0x1bb:83fa58f8 b89830f583 mov eax,offset nt!PspNotifyEnableMask (83f53098)83fa58fd f00fba2802 lock bts dword ptr [eax],283fa5902 e93cffffff jmp nt!PspSetCreateProcessNotifyRoutine+0x106 (83fa5843)
    Win7 64 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:fffff800`042be1b0 48895c2408 mov qword ptr [rsp+8],rbxfffff800`042be1b5 48896c2410 mov qword ptr [rsp+10h],rbpfffff800`042be1ba 4889742418 mov qword ptr [rsp+18h],rsifffff800`042be1bf 57 push rdifffff800`042be1c0 4154 push r12fffff800`042be1c2 4155 push r13fffff800`042be1c4 4156 push r14fffff800`042be1c6 4157 push r15fffff800`042be1c8 4883ec20 sub rsp,20hfffff800`042be1cc 4533e4 xor r12d,r12dfffff800`042be1cf 418ae8 mov bpl,r8bfffff800`042be1d2 4c8be9 mov r13,rcxfffff800`042be1d5 418d5c2401 lea ebx,[r12+1]fffff800`042be1da 413ad4 cmp dl,r12bfffff800`042be1dd 0f840e010000 je nt!PspSetCreateProcessNotifyRoutine+0x141 (fffff800`042be2f1)nt!PspSetCreateProcessNotifyRoutine+0x33:fffff800`042be1e3 65488b3c2588010000 mov rdi,qword ptr gs:[188h]fffff800`042be1ec 83c8ff or eax,0FFFFFFFFhfffff800`042be1ef 660187c4010000 add word ptr [rdi+1C4h],axfffff800`042be1f6 4c8d358395d6ff lea r14,[nt!PspCreateProcessNotifyRoutine (fffff800`04027780)]nt!PspSetCreateProcessNotifyRoutine+0x4d:fffff800`042be1fd 418bc4 mov eax,r12dfffff800`042be200 4d8d3cc6 lea r15,[r14+rax*8]fffff800`042be204 498bcf mov rcx,r15fffff800`042be207 e8f4ecedff call nt!ExReferenceCallBackBlock (fffff800`0419cf00)fffff800`042be20c 33d2 xor edx,edxfffff800`042be20e 488bf0 mov rsi,raxfffff800`042be211 483bc2 cmp rax,rdxfffff800`042be214 743d je nt!PspSetCreateProcessNotifyRoutine+0xa3 (fffff800`042be253)nt!PspSetCreateProcessNotifyRoutine+0x66:fffff800`042be216 488bc8 mov rcx,raxfffff800`042be219 e89eb4c0ff call nt!ExGetCallBackBlockRoutine (fffff800`03ec96bc)fffff800`042be21e 493bc5 cmp rax,r13fffff800`042be221 7523 jne nt!PspSetCreateProcessNotifyRoutine+0x96 (fffff800`042be246)nt!PspSetCreateProcessNotifyRoutine+0x73:fffff800`042be223 48395110 cmp qword ptr [rcx+10h],rdxfffff800`042be227 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x80 (fffff800`042be230)nt!PspSetCreateProcessNotifyRoutine+0x79:fffff800`042be229 403aea cmp bpl,dlfffff800`042be22c 7407 je nt!PspSetCreateProcessNotifyRoutine+0x85 (fffff800`042be235)nt!PspSetCreateProcessNotifyRoutine+0x7e:fffff800`042be22e eb16 jmp nt!PspSetCreateProcessNotifyRoutine+0x96 (fffff800`042be246)nt!PspSetCreateProcessNotifyRoutine+0x80:fffff800`042be230 403aea cmp bpl,dlfffff800`042be233 7411 je nt!PspSetCreateProcessNotifyRoutine+0x96 (fffff800`042be246)nt!PspSetCreateProcessNotifyRoutine+0x85:fffff800`042be235 4c8bc6 mov r8,rsifffff800`042be238 498bcf mov rcx,r15fffff800`042be23b e8100ef5ff call nt!ExCompareExchangeCallBack (fffff800`0420f050)fffff800`042be240 33c9 xor ecx,ecxfffff800`042be242 3ac1 cmp al,clfffff800`042be244 7540 jne nt!PspSetCreateProcessNotifyRoutine+0xd6 (fffff800`042be286)nt!PspSetCreateProcessNotifyRoutine+0x96:fffff800`042be246 488bd6 mov rdx,rsifffff800`042be249 498bcf mov rcx,r15fffff800`042be24c e8bb2eeeff call nt!ExDereferenceCallBackBlock (fffff800`041a110c)fffff800`042be251 33d2 xor edx,edxnt!PspSetCreateProcessNotifyRoutine+0xa3:fffff800`042be253 4403e3 add r12d,ebxfffff800`042be256 4183fc40 cmp r12d,40hfffff800`042be25a 72a1 jb nt!PspSetCreateProcessNotifyRoutine+0x4d (fffff800`042be1fd)nt!PspSetCreateProcessNotifyRoutine+0xac:fffff800`042be25c 66019fc4010000 add word ptr [rdi+1C4h],bxfffff800`042be263 7517 jne nt!PspSetCreateProcessNotifyRoutine+0xcc (fffff800`042be27c)nt!PspSetCreateProcessNotifyRoutine+0xb5:fffff800`042be265 488d4750 lea rax,[rdi+50h]fffff800`042be269 483900 cmp qword ptr [rax],raxfffff800`042be26c 740e je nt!PspSetCreateProcessNotifyRoutine+0xcc (fffff800`042be27c)nt!PspSetCreateProcessNotifyRoutine+0xbe:fffff800`042be26e 663997c6010000 cmp word ptr [rdi+1C6h],dxfffff800`042be275 7505 jne nt!PspSetCreateProcessNotifyRoutine+0xcc (fffff800`042be27c)nt!PspSetCreateProcessNotifyRoutine+0xc7:fffff800`042be277 e834f5b6ff call nt!KiCheckForKernelApcDelivery (fffff800`03e2d7b0)nt!PspSetCreateProcessNotifyRoutine+0xcc:fffff800`042be27c b87a0000c0 mov eax,0C000007Ahfffff800`042be281 e916010000 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0xd6:fffff800`042be286 403ae9 cmp bpl,clfffff800`042be289 750a jne nt!PspSetCreateProcessNotifyRoutine+0xe5 (fffff800`042be295)nt!PspSetCreateProcessNotifyRoutine+0xdb:fffff800`042be28b f08305f196d6ffff lock add dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff800`04027984)],0FFFFFFFFhfffff800`042be293 eb08 jmp nt!PspSetCreateProcessNotifyRoutine+0xed (fffff800`042be29d)nt!PspSetCreateProcessNotifyRoutine+0xe5:fffff800`042be295 f08305e396d6ffff lock add dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff800`04027980)],0FFFFFFFFhnt!PspSetCreateProcessNotifyRoutine+0xed:fffff800`042be29d 418bc4 mov eax,r12dfffff800`042be2a0 488bd6 mov rdx,rsifffff800`042be2a3 498d0cc6 lea rcx,[r14+rax*8]fffff800`042be2a7 e8602eeeff call nt!ExDereferenceCallBackBlock (fffff800`041a110c)fffff800`042be2ac 66019fc4010000 add word ptr [rdi+1C4h],bxfffff800`042be2b3 7519 jne nt!PspSetCreateProcessNotifyRoutine+0x11e (fffff800`042be2ce)nt!PspSetCreateProcessNotifyRoutine+0x105:fffff800`042be2b5 488d4750 lea rax,[rdi+50h]fffff800`042be2b9 483900 cmp qword ptr [rax],raxfffff800`042be2bc 7410 je nt!PspSetCreateProcessNotifyRoutine+0x11e (fffff800`042be2ce)nt!PspSetCreateProcessNotifyRoutine+0x10e:fffff800`042be2be 33c0 xor eax,eaxfffff800`042be2c0 663987c6010000 cmp word ptr [rdi+1C6h],axfffff800`042be2c7 7505 jne nt!PspSetCreateProcessNotifyRoutine+0x11e (fffff800`042be2ce)nt!PspSetCreateProcessNotifyRoutine+0x119:fffff800`042be2c9 e8e2f4b6ff call nt!KiCheckForKernelApcDelivery (fffff800`03e2d7b0)nt!PspSetCreateProcessNotifyRoutine+0x11e:fffff800`042be2ce 33c0 xor eax,eaxfffff800`042be2d0 f0480fb11e lock cmpxchg qword ptr [rsi],rbxfffff800`042be2d5 740d je nt!PspSetCreateProcessNotifyRoutine+0x134 (fffff800`042be2e4)nt!PspSetCreateProcessNotifyRoutine+0x127:fffff800`042be2d7 483bc3 cmp rax,rbxfffff800`042be2da 7408 je nt!PspSetCreateProcessNotifyRoutine+0x134 (fffff800`042be2e4)nt!PspSetCreateProcessNotifyRoutine+0x12c:fffff800`042be2dc 488bce mov rcx,rsifffff800`042be2df e86cd4b9ff call nt!ExfWaitForRundownProtectionRelease (fffff800`03e5b750)nt!PspSetCreateProcessNotifyRoutine+0x134:fffff800`042be2e4 488bce mov rcx,rsifffff800`042be2e7 e864d7fbff call nt!IopDeallocateApc (fffff800`0427ba50)fffff800`042be2ec e9a9000000 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x141:fffff800`042be2f1 413aec cmp bpl,r12bfffff800`042be2f4 7419 je nt!PspSetCreateProcessNotifyRoutine+0x15f (fffff800`042be30f)nt!PspSetCreateProcessNotifyRoutine+0x146:fffff800`042be2f6 e8b51dfbff call nt!MmVerifyCallbackFunction (fffff800`042700b0)fffff800`042be2fb 413bc4 cmp eax,r12dfffff800`042be2fe 750a jne nt!PspSetCreateProcessNotifyRoutine+0x15a (fffff800`042be30a)nt!PspSetCreateProcessNotifyRoutine+0x150:fffff800`042be300 b8220000c0 mov eax,0C0000022hfffff800`042be305 e992000000 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0x15a:fffff800`042be30a 488bd3 mov rdx,rbxfffff800`042be30d eb03 jmp nt!PspSetCreateProcessNotifyRoutine+0x162 (fffff800`042be312)nt!PspSetCreateProcessNotifyRoutine+0x15f:fffff800`042be30f 498bd4 mov rdx,r12nt!PspSetCreateProcessNotifyRoutine+0x162:fffff800`042be312 498bcd mov rcx,r13fffff800`042be315 e846a8fbff call nt!ExAllocateCallBack (fffff800`04278b60)fffff800`042be31a 488bf0 mov rsi,raxfffff800`042be31d 493bc4 cmp rax,r12fffff800`042be320 7507 jne nt!PspSetCreateProcessNotifyRoutine+0x179 (fffff800`042be329)nt!PspSetCreateProcessNotifyRoutine+0x172:fffff800`042be322 b89a0000c0 mov eax,0C000009Ahfffff800`042be327 eb73 jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0x179:fffff800`042be329 418bfc mov edi,r12dfffff800`042be32c 4c8d354d94d6ff lea r14,[nt!PspCreateProcessNotifyRoutine (fffff800`04027780)]nt!PspSetCreateProcessNotifyRoutine+0x183:fffff800`042be333 8bc7 mov eax,edifffff800`042be335 4533c0 xor r8d,r8dfffff800`042be338 488bd6 mov rdx,rsifffff800`042be33b 498d0cc6 lea rcx,[r14+rax*8]fffff800`042be33f e80c0df5ff call nt!ExCompareExchangeCallBack (fffff800`0420f050)fffff800`042be344 413ac4 cmp al,r12bfffff800`042be347 7516 jne nt!PspSetCreateProcessNotifyRoutine+0x1af (fffff800`042be35f)nt!PspSetCreateProcessNotifyRoutine+0x199:fffff800`042be349 03fb add edi,ebxfffff800`042be34b 83ff40 cmp edi,40hfffff800`042be34e 72e3 jb nt!PspSetCreateProcessNotifyRoutine+0x183 (fffff800`042be333)nt!PspSetCreateProcessNotifyRoutine+0x1a0:fffff800`042be350 488bce mov rcx,rsifffff800`042be353 e8f8d6fbff call nt!IopDeallocateApc (fffff800`0427ba50)fffff800`042be358 b80d0000c0 mov eax,0C000000Dhfffff800`042be35d eb3d jmp nt!PspSetCreateProcessNotifyRoutine+0x1ec (fffff800`042be39c)nt!PspSetCreateProcessNotifyRoutine+0x1af:fffff800`042be35f 413aec cmp bpl,r12bfffff800`042be362 751c jne nt!PspSetCreateProcessNotifyRoutine+0x1d0 (fffff800`042be380)nt!PspSetCreateProcessNotifyRoutine+0x1b4:fffff800`042be364 f0011d1996d6ff lock add dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff800`04027984)],ebxfffff800`042be36b 8b056f91d6ff mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)]fffff800`042be371 a802 test al,2fffff800`042be373 7525 jne nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x1c5:fffff800`042be375 f00fba2d6291d6ff01 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)],1fffff800`042be37e eb1a jmp nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x1d0:fffff800`042be380 f0011df995d6ff lock add dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff800`04027980)],ebxfffff800`042be387 8b055391d6ff mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)]fffff800`042be38d a804 test al,4fffff800`042be38f 7509 jne nt!PspSetCreateProcessNotifyRoutine+0x1ea (fffff800`042be39a)nt!PspSetCreateProcessNotifyRoutine+0x1e1:fffff800`042be391 f00fba2d4691d6ff02 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`040274e0)],2nt!PspSetCreateProcessNotifyRoutine+0x1ea:fffff800`042be39a 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x1ec:fffff800`042be39c 488b5c2450 mov rbx,qword ptr [rsp+50h]fffff800`042be3a1 488b6c2458 mov rbp,qword ptr [rsp+58h]fffff800`042be3a6 488b742460 mov rsi,qword ptr [rsp+60h]fffff800`042be3ab 4883c420 add rsp,20hfffff800`042be3af 415f pop r15fffff800`042be3b1 415e pop r14fffff800`042be3b3 415d pop r13fffff800`042be3b5 415c pop r12fffff800`042be3b7 5f pop rdifffff800`042be3b8 c3 ret
    Win8.1 32 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:819b987e 8bff mov edi,edi819b9880 55 push ebp819b9881 8bec mov ebp,esp819b9883 83ec10 sub esp,10h819b9886 53 push ebx819b9887 8bd9 mov ebx,ecx819b9889 895df4 mov dword ptr [ebp-0Ch],ebx819b988c 56 push esi819b988d 57 push edi819b988e 84d2 test dl,dl819b9890 0f8567020a00 jne nt! ?? ::NNGAKEGL::`string'+0x70493 (81a59afd)nt!PspSetCreateProcessNotifyRoutine+0x18:819b9896 33f6 xor esi,esi819b9898 33ff xor edi,edi819b989a 46 inc esi819b989b 385508 cmp byte ptr [ebp+8],dl819b989e 756e jne nt!PspSetCreateProcessNotifyRoutine+0x90 (819b990e)nt!PspSetCreateProcessNotifyRoutine+0x22:819b98a0 8bd7 mov edx,edint!PspSetCreateProcessNotifyRoutine+0x24:819b98a2 8bcb mov ecx,ebx819b98a4 e8a9000000 call nt!ExAllocateCallBack (819b9952)819b98a9 8bc8 mov ecx,eax819b98ab 894dfc mov dword ptr [ebp-4],ecx819b98ae 85c9 test ecx,ecx819b98b0 0f846b030a00 je nt! ?? ::NNGAKEGL::`string'+0x705b7 (81a59c21)nt!PspSetCreateProcessNotifyRoutine+0x38:819b98b6 b8c8158681 mov eax,offset nt!PspCreateProcessNotifyRoutine (818615c8)819b98bb 8bdf mov ebx,edi819b98bd 8945f8 mov dword ptr [ebp-8],eaxnt!PspSetCreateProcessNotifyRoutine+0x42:819b98c0 8bd1 mov edx,ecx819b98c2 8bc8 mov ecx,eax819b98c4 57 push edi819b98c5 e88e24d9ff call nt!ExCompareExchangeCallBack (8174bd58)819b98ca 84c0 test al,al819b98cc 751c jne nt!PspSetCreateProcessNotifyRoutine+0x6c (819b98ea)nt!PspSetCreateProcessNotifyRoutine+0x50:819b98ce 8b45f8 mov eax,dword ptr [ebp-8]819b98d1 83c304 add ebx,4819b98d4 8b4dfc mov ecx,dword ptr [ebp-4]819b98d7 83c004 add eax,4819b98da 8945f8 mov dword ptr [ebp-8],eax819b98dd 81fb00010000 cmp ebx,100h819b98e3 72db jb nt!PspSetCreateProcessNotifyRoutine+0x42 (819b98c0)nt!PspSetCreateProcessNotifyRoutine+0x67:819b98e5 e941030a00 jmp nt! ?? ::NNGAKEGL::`string'+0x705c1 (81a59c2b)nt!PspSetCreateProcessNotifyRoutine+0x6c:819b98ea 807d0800 cmp byte ptr [ebp+8],0819b98ee 752b jne nt!PspSetCreateProcessNotifyRoutine+0x9d (819b991b)nt!PspSetCreateProcessNotifyRoutine+0x72:819b98f0 b8541cb481 mov eax,offset nt!PspCreateProcessNotifyRoutineCount (81b41c54)819b98f5 f00fc130 lock xadd dword ptr [eax],esi819b98f9 46 inc esi819b98fa a1441cb481 mov eax,dword ptr [nt!PspNotifyEnableMask (81b41c44)]819b98ff a802 test al,2819b9901 7437 je nt!PspSetCreateProcessNotifyRoutine+0xbc (819b993a)nt!PspSetCreateProcessNotifyRoutine+0x85:819b9903 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x87:819b9905 5f pop edi819b9906 5e pop esi819b9907 5b pop ebx819b9908 8be5 mov esp,ebp819b990a 5d pop ebp819b990b c20400 ret 4nt!PspSetCreateProcessNotifyRoutine+0x90:819b990e e86d000000 call nt!MmVerifyCallbackFunction (819b9980)819b9913 85c0 test eax,eax819b9915 742f je nt!PspSetCreateProcessNotifyRoutine+0xc8 (819b9946)nt!PspSetCreateProcessNotifyRoutine+0x99:819b9917 8bd6 mov edx,esi819b9919 eb87 jmp nt!PspSetCreateProcessNotifyRoutine+0x24 (819b98a2)nt!PspSetCreateProcessNotifyRoutine+0x9d:819b991b b8501cb481 mov eax,offset nt!PspCreateProcessNotifyRoutineExCount (81b41c50)819b9920 f00fc130 lock xadd dword ptr [eax],esi819b9924 46 inc esi819b9925 a1441cb481 mov eax,dword ptr [nt!PspNotifyEnableMask (81b41c44)]819b992a a804 test al,4819b992c 75d5 jne nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt!PspSetCreateProcessNotifyRoutine+0xb0:819b992e b8441cb481 mov eax,offset nt!PspNotifyEnableMask (81b41c44)819b9933 f00fba2802 lock bts dword ptr [eax],2819b9938 ebc9 jmp nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt!PspSetCreateProcessNotifyRoutine+0xbc:819b993a b8441cb481 mov eax,offset nt!PspNotifyEnableMask (81b41c44)819b993f f00fba2801 lock bts dword ptr [eax],1819b9944 ebbd jmp nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt!PspSetCreateProcessNotifyRoutine+0xc8:819b9946 b8220000c0 mov eax,0C0000022h819b994b ebb8 jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)nt! ?? ::NNGAKEGL::`string'+0x70493:81a59afd 648b3524010000 mov esi,dword ptr fs:[124h]81a59b04 66ff8e3c010000 dec word ptr [esi+13Ch]81a59b0b 33ff xor edi,edi81a59b0d b8c8158681 mov eax,offset nt!PspCreateProcessNotifyRoutine (818615c8)81a59b12 897df8 mov dword ptr [ebp-8],edi81a59b15 8945fc mov dword ptr [ebp-4],eaxnt! ?? ::NNGAKEGL::`string'+0x704ae:81a59b18 8bc8 mov ecx,eax81a59b1a e85bb2c2ff call nt!ExReferenceCallBackBlock (81684d7a)81a59b1f 8bd8 mov ebx,eax81a59b21 85db test ebx,ebx81a59b23 7426 je nt! ?? ::NNGAKEGL::`string'+0x704e1 (81a59b4b)nt! ?? ::NNGAKEGL::`string'+0x704bb:81a59b25 8bcb mov ecx,ebx81a59b27 e86a67d1ff call nt!ExGetCallBackBlockContext (81770296)81a59b2c 8bd0 mov edx,eax81a59b2e e86d67d1ff call nt!ExGetCallBackBlockRoutine (817702a0)81a59b33 3b45f4 cmp eax,dword ptr [ebp-0Ch]81a59b36 7509 jne nt! ?? ::NNGAKEGL::`string'+0x704d7 (81a59b41)nt! ?? ::NNGAKEGL::`string'+0x704ce:81a59b38 85d2 test edx,edx81a59b3a 7557 jne nt! ?? ::NNGAKEGL::`string'+0x70529 (81a59b93)nt! ?? ::NNGAKEGL::`string'+0x704d2:81a59b3c 385508 cmp byte ptr [ebp+8],dl81a59b3f 7458 je nt! ?? ::NNGAKEGL::`string'+0x7052f (81a59b99)nt! ?? ::NNGAKEGL::`string'+0x704d7:81a59b41 8b4dfc mov ecx,dword ptr [ebp-4]81a59b44 8bd3 mov edx,ebx81a59b46 e8e7b1c2ff call nt!ExDereferenceCallBackBlock (81684d32)nt! ?? ::NNGAKEGL::`string'+0x704e1:81a59b4b 8b4df8 mov ecx,dword ptr [ebp-8]81a59b4e 8b45fc mov eax,dword ptr [ebp-4]81a59b51 41 inc ecx81a59b52 83c004 add eax,481a59b55 894df8 mov dword ptr [ebp-8],ecx81a59b58 8945fc mov dword ptr [ebp-4],eax81a59b5b 83f940 cmp ecx,40h81a59b5e 72b8 jb nt! ?? ::NNGAKEGL::`string'+0x704ae (81a59b18)nt! ?? ::NNGAKEGL::`string'+0x704f6:81a59b60 0fbf863c010000 movsx eax,word ptr [esi+13Ch]81a59b67 40 inc eax81a59b68 6689863c010000 mov word ptr [esi+13Ch],ax81a59b6f 6685c0 test ax,ax81a59b72 7515 jne nt! ?? ::NNGAKEGL::`string'+0x7051f (81a59b89)nt! ?? ::NNGAKEGL::`string'+0x7050a:81a59b74 8d4670 lea eax,[esi+70h]81a59b77 3900 cmp dword ptr [eax],eax81a59b79 740e je nt! ?? ::NNGAKEGL::`string'+0x7051f (81a59b89)nt! ?? ::NNGAKEGL::`string'+0x70511:81a59b7b 6639be3e010000 cmp word ptr [esi+13Eh],di81a59b82 7505 jne nt! ?? ::NNGAKEGL::`string'+0x7051f (81a59b89)nt! ?? ::NNGAKEGL::`string'+0x7051a:81a59b84 e8fb79c8ff call nt!KiCheckForKernelApcDelivery (816e1584)nt! ?? ::NNGAKEGL::`string'+0x7051f:81a59b89 b87a0000c0 mov eax,0C000007Ah81a59b8e e972fdf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)nt! ?? ::NNGAKEGL::`string'+0x70529:81a59b93 807d0800 cmp byte ptr [ebp+8],081a59b97 74a8 je nt! ?? ::NNGAKEGL::`string'+0x704d7 (81a59b41)nt! ?? ::NNGAKEGL::`string'+0x7052f:81a59b99 8b4dfc mov ecx,dword ptr [ebp-4]81a59b9c 33d2 xor edx,edx81a59b9e 53 push ebx81a59b9f e8b421cfff call nt!ExCompareExchangeCallBack (8174bd58)81a59ba4 84c0 test al,al81a59ba6 7499 je nt! ?? ::NNGAKEGL::`string'+0x704d7 (81a59b41)nt! ?? ::NNGAKEGL::`string'+0x7053e:81a59ba8 83c8ff or eax,0FFFFFFFFh81a59bab b9541cb481 mov ecx,offset nt!PspCreateProcessNotifyRoutineCount (81b41c54)81a59bb0 807d0800 cmp byte ptr [ebp+8],081a59bb4 7405 je nt! ?? ::NNGAKEGL::`string'+0x70551 (81a59bbb)nt! ?? ::NNGAKEGL::`string'+0x7054c:81a59bb6 b9501cb481 mov ecx,offset nt!PspCreateProcessNotifyRoutineExCount (81b41c50)nt! ?? ::NNGAKEGL::`string'+0x70551:81a59bbb f00fc101 lock xadd dword ptr [ecx],eax81a59bbf 8b45f8 mov eax,dword ptr [ebp-8]81a59bc2 8bd3 mov edx,ebx81a59bc4 8d0c85c8158681 lea ecx,nt!PspCreateProcessNotifyRoutine (818615c8)[eax*4]81a59bcb e862b1c2ff call nt!ExDereferenceCallBackBlock (81684d32)81a59bd0 0fbf863c010000 movsx eax,word ptr [esi+13Ch]81a59bd7 40 inc eax81a59bd8 6689863c010000 mov word ptr [esi+13Ch],ax81a59bdf 6685c0 test ax,ax81a59be2 7515 jne nt! ?? ::NNGAKEGL::`string'+0x7058f (81a59bf9)nt! ?? ::NNGAKEGL::`string'+0x7057a:81a59be4 8d4670 lea eax,[esi+70h]81a59be7 3900 cmp dword ptr [eax],eax81a59be9 740e je nt! ?? ::NNGAKEGL::`string'+0x7058f (81a59bf9)nt! ?? ::NNGAKEGL::`string'+0x70581:81a59beb 6639be3e010000 cmp word ptr [esi+13Eh],di81a59bf2 7505 jne nt! ?? ::NNGAKEGL::`string'+0x7058f (81a59bf9)nt! ?? ::NNGAKEGL::`string'+0x7058a:81a59bf4 e88b79c8ff call nt!KiCheckForKernelApcDelivery (816e1584)nt! ?? ::NNGAKEGL::`string'+0x7058f:81a59bf9 33f6 xor esi,esi81a59bfb 33c0 xor eax,eax81a59bfd 46 inc esi81a59bfe 8bce mov ecx,esi81a59c00 f00fb10b lock cmpxchg dword ptr [ebx],ecx81a59c04 85c0 test eax,eax81a59c06 740d je nt! ?? ::NNGAKEGL::`string'+0x705ab (81a59c15)nt! ?? ::NNGAKEGL::`string'+0x7059e:81a59c08 3bc6 cmp eax,esi81a59c0a 7409 je nt! ?? ::NNGAKEGL::`string'+0x705ab (81a59c15)nt! ?? ::NNGAKEGL::`string'+0x705a2:81a59c0c 8bd0 mov edx,eax81a59c0e 8bcb mov ecx,ebx81a59c10 e87d36ccff call nt!ExfWaitForRundownProtectionRelease (8171d292)nt! ?? ::NNGAKEGL::`string'+0x705ab:81a59c15 57 push edi81a59c16 53 push ebx81a59c17 e8f4a3ddff call nt!ExFreePoolWithTag (81834010)81a59c1c e9e2fcf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x85 (819b9903)nt! ?? ::NNGAKEGL::`string'+0x705b7:81a59c21 b89a0000c0 mov eax,0C000009Ah81a59c26 e9dafcf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)nt! ?? ::NNGAKEGL::`string'+0x705c1:81a59c2b 57 push edi81a59c2c ff75fc push dword ptr [ebp-4]81a59c2f e8dca3ddff call nt!ExFreePoolWithTag (81834010)81a59c34 b80d0000c0 mov eax,0C000000Dh81a59c39 e9c7fcf5ff jmp nt!PspSetCreateProcessNotifyRoutine+0x87 (819b9905)
    Win8.1 64 位 PspSetCreateProcessNotifyRoutinelkd> uf PspSetCreateProcessNotifyRoutineFlow analysis was incomplete, some code may be missingnt!PspSetCreateProcessNotifyRoutine:fffff803`10775bc4 48895c2408 mov qword ptr [rsp+8],rbxfffff803`10775bc9 48896c2410 mov qword ptr [rsp+10h],rbpfffff803`10775bce 4889742418 mov qword ptr [rsp+18h],rsifffff803`10775bd3 57 push rdifffff803`10775bd4 4154 push r12fffff803`10775bd6 4155 push r13fffff803`10775bd8 4156 push r14fffff803`10775bda 4157 push r15fffff803`10775bdc 4883ec20 sub rsp,20hfffff803`10775be0 4533f6 xor r14d,r14dfffff803`10775be3 418ae8 mov bpl,r8bfffff803`10775be6 4c8be1 mov r12,rcxfffff803`10775be9 418d7e01 lea edi,[r14+1]fffff803`10775bed 84d2 test dl,dlfffff803`10775bef 0f85c52e0b00 jne nt! ?? ::NNGAKEGL::`string'+0x79eba (fffff803`10828aba)nt!PspSetCreateProcessNotifyRoutine+0x31:fffff803`10775bf5 4584c0 test r8b,r8bfffff803`10775bf8 7577 jne nt!PspSetCreateProcessNotifyRoutine+0xad (fffff803`10775c71)nt!PspSetCreateProcessNotifyRoutine+0x36:fffff803`10775bfa 418bd6 mov edx,r14dnt!PspSetCreateProcessNotifyRoutine+0x39:fffff803`10775bfd 498bcc mov rcx,r12fffff803`10775c00 e8ab000000 call nt!ExAllocateCallBack (fffff803`10775cb0)fffff803`10775c05 488bf0 mov rsi,raxfffff803`10775c08 4885c0 test rax,raxfffff803`10775c0b 0f84ef2f0b00 je nt! ?? ::NNGAKEGL::`string'+0x7a000 (fffff803`10828c00)nt!PspSetCreateProcessNotifyRoutine+0x4d:fffff803`10775c11 418bde mov ebx,r14dfffff803`10775c14 4c8d3de5b1deff lea r15,[nt!PspCreateProcessNotifyRoutine (fffff803`10560e00)]nt!PspSetCreateProcessNotifyRoutine+0x57:fffff803`10775c1b 8bc3 mov eax,ebxfffff803`10775c1d 4533c0 xor r8d,r8dfffff803`10775c20 488bd6 mov rdx,rsifffff803`10775c23 498d0cc7 lea rcx,[r15+rax*8]fffff803`10775c27 e874b8c3ff call nt!ExCompareExchangeCallBack (fffff803`103b14a0)fffff803`10775c2c 84c0 test al,alfffff803`10775c2e 750c jne nt!PspSetCreateProcessNotifyRoutine+0x78 (fffff803`10775c3c)nt!PspSetCreateProcessNotifyRoutine+0x6c:fffff803`10775c30 03df add ebx,edifffff803`10775c32 83fb40 cmp ebx,40hfffff803`10775c35 72e4 jb nt!PspSetCreateProcessNotifyRoutine+0x57 (fffff803`10775c1b)nt!PspSetCreateProcessNotifyRoutine+0x73:fffff803`10775c37 e9ce2f0b00 jmp nt! ?? ::NNGAKEGL::`string'+0x7a00a (fffff803`10828c0a)nt!PspSetCreateProcessNotifyRoutine+0x78:fffff803`10775c3c 4084ed test bpl,bplfffff803`10775c3f 7545 jne nt!PspSetCreateProcessNotifyRoutine+0xc2 (fffff803`10775c86)nt!PspSetCreateProcessNotifyRoutine+0x7d:fffff803`10775c41 f0013d005a1b00 lock add dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff803`1092b648)],edifffff803`10775c48 8b05ce541b00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)]fffff803`10775c4e a802 test al,2fffff803`10775c50 7450 je nt!PspSetCreateProcessNotifyRoutine+0xde (fffff803`10775ca2)nt!PspSetCreateProcessNotifyRoutine+0x8e:fffff803`10775c52 33c0 xor eax,eaxfffff803`10775c54 488b5c2450 mov rbx,qword ptr [rsp+50h]fffff803`10775c59 488b6c2458 mov rbp,qword ptr [rsp+58h]fffff803`10775c5e 488b742460 mov rsi,qword ptr [rsp+60h]fffff803`10775c63 4883c420 add rsp,20hfffff803`10775c67 415f pop r15fffff803`10775c69 415e pop r14fffff803`10775c6b 415d pop r13fffff803`10775c6d 415c pop r12fffff803`10775c6f 5f pop rdifffff803`10775c70 c3 retnt!PspSetCreateProcessNotifyRoutine+0xad:fffff803`10775c71 e87e000000 call nt!MmVerifyCallbackFunction (fffff803`10775cf4)fffff803`10775c76 85c0 test eax,eaxfffff803`10775c78 0f84782f0b00 je nt! ?? ::NNGAKEGL::`string'+0x79ff6 (fffff803`10828bf6)nt!PspSetCreateProcessNotifyRoutine+0xba:fffff803`10775c7e 488bd7 mov rdx,rdifffff803`10775c81 e977ffffff jmp nt!PspSetCreateProcessNotifyRoutine+0x39 (fffff803`10775bfd)nt!PspSetCreateProcessNotifyRoutine+0xc2:fffff803`10775c86 f0013db7591b00 lock add dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff803`1092b644)],edifffff803`10775c8d 8b0589541b00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)]fffff803`10775c93 a804 test al,4fffff803`10775c95 75bb jne nt!PspSetCreateProcessNotifyRoutine+0x8e (fffff803`10775c52)nt!PspSetCreateProcessNotifyRoutine+0xd3:fffff803`10775c97 f00fba2d7c541b0002 lock bts dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)],2fffff803`10775ca0 ebb0 jmp nt!PspSetCreateProcessNotifyRoutine+0x8e (fffff803`10775c52)nt!PspSetCreateProcessNotifyRoutine+0xde:fffff803`10775ca2 f00fba2d71541b0001 lock bts dword ptr [nt!PspNotifyEnableMask (fffff803`1092b11c)],1fffff803`10775cab eba5 jmp nt!PspSetCreateProcessNotifyRoutine+0x8e (fffff803`10775c52)
    Win10 32 位 PspSetCreateProcessNotifyRoutinekd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:81e1c9fe 8bff mov edi,edi81e1ca00 55 push ebp81e1ca01 8bec mov ebp,esp81e1ca03 83ec10 sub esp,10h81e1ca06 53 push ebx81e1ca07 8bd9 mov ebx,ecx81e1ca09 895df4 mov dword ptr [ebp-0Ch],ebx81e1ca0c 56 push esi81e1ca0d 57 push edi81e1ca0e 84d2 test dl,dl81e1ca10 0f85d11b0a00 jne nt! ?? ::NNGAKEGL::`string'+0x69bdb (81ebe5e7)nt!PspSetCreateProcessNotifyRoutine+0x18:81e1ca16 33ff xor edi,edi81e1ca18 385508 cmp byte ptr [ebp+8],dl81e1ca1b 7560 jne nt!PspSetCreateProcessNotifyRoutine+0x7f (81e1ca7d)nt!PspSetCreateProcessNotifyRoutine+0x1f:81e1ca1d 8bd7 mov edx,edint!PspSetCreateProcessNotifyRoutine+0x21:81e1ca1f 8bcb mov ecx,ebx81e1ca21 e89a000000 call nt!ExAllocateCallBack (81e1cac0)81e1ca26 8945f8 mov dword ptr [ebp-8],eax81e1ca29 85c0 test eax,eax81e1ca2b 0f84d81c0a00 je nt! ?? ::NNGAKEGL::`string'+0x69cfd (81ebe709)nt!PspSetCreateProcessNotifyRoutine+0x33:81e1ca31 bb30dccb81 mov ebx,offset nt!PspCreateProcessNotifyRoutine (81cbdc30)81e1ca36 8bf7 mov esi,edint!PspSetCreateProcessNotifyRoutine+0x3a:81e1ca38 57 push edi81e1ca39 8bd0 mov edx,eax81e1ca3b 8bcb mov ecx,ebx81e1ca3d e8921dd6ff call nt!ExCompareExchangeCallBack (81b7e7d4)81e1ca42 84c0 test al,al81e1ca44 7516 jne nt!PspSetCreateProcessNotifyRoutine+0x5e (81e1ca5c)nt!PspSetCreateProcessNotifyRoutine+0x48:81e1ca46 8b45f8 mov eax,dword ptr [ebp-8]81e1ca49 83c604 add esi,481e1ca4c 83c304 add ebx,481e1ca4f 81fe00010000 cmp esi,100h81e1ca55 72e1 jb nt!PspSetCreateProcessNotifyRoutine+0x3a (81e1ca38)nt!PspSetCreateProcessNotifyRoutine+0x59:81e1ca57 e9b71c0a00 jmp nt! ?? ::NNGAKEGL::`string'+0x69d07 (81ebe713)nt!PspSetCreateProcessNotifyRoutine+0x5e:81e1ca5c 807d0800 cmp byte ptr [ebp+8],081e1ca60 7529 jne nt!PspSetCreateProcessNotifyRoutine+0x8d (81e1ca8b)nt!PspSetCreateProcessNotifyRoutine+0x64:81e1ca62 f0ff05d8bdff81 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineCount (81ffbdd8)]81e1ca69 a1c4bdff81 mov eax,dword ptr [nt!PspNotifyEnableMask (81ffbdc4)]81e1ca6e a802 test al,281e1ca70 7435 je nt!PspSetCreateProcessNotifyRoutine+0xa9 (81e1caa7)nt!PspSetCreateProcessNotifyRoutine+0x74:81e1ca72 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x76:81e1ca74 5f pop edi81e1ca75 5e pop esi81e1ca76 5b pop ebx81e1ca77 8be5 mov esp,ebp81e1ca79 5d pop ebp81e1ca7a c20400 ret 4nt!PspSetCreateProcessNotifyRoutine+0x7f:81e1ca7d e86c000000 call nt!MmVerifyCallbackFunction (81e1caee)81e1ca82 85c0 test eax,eax81e1ca84 742d je nt!PspSetCreateProcessNotifyRoutine+0xb5 (81e1cab3)nt!PspSetCreateProcessNotifyRoutine+0x88:81e1ca86 33d2 xor edx,edx81e1ca88 42 inc edx81e1ca89 eb94 jmp nt!PspSetCreateProcessNotifyRoutine+0x21 (81e1ca1f)nt!PspSetCreateProcessNotifyRoutine+0x8d:81e1ca8b f0ff05d4bdff81 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineExCount (81ffbdd4)]81e1ca92 a1c4bdff81 mov eax,dword ptr [nt!PspNotifyEnableMask (81ffbdc4)]81e1ca97 a804 test al,481e1ca99 75d7 jne nt!PspSetCreateProcessNotifyRoutine+0x74 (81e1ca72)nt!PspSetCreateProcessNotifyRoutine+0x9d:81e1ca9b b8c4bdff81 mov eax,offset nt!PspNotifyEnableMask (81ffbdc4)81e1caa0 f00fba2802 lock bts dword ptr [eax],281e1caa5 ebcb jmp nt!PspSetCreateProcessNotifyRoutine+0x74 (81e1ca72)nt!PspSetCreateProcessNotifyRoutine+0xa9:81e1caa7 b8c4bdff81 mov eax,offset nt!PspNotifyEnableMask (81ffbdc4)81e1caac f00fba2801 lock bts dword ptr [eax],181e1cab1 ebbf jmp nt!PspSetCreateProcessNotifyRoutine+0x74 (81e1ca72)nt!PspSetCreateProcessNotifyRoutine+0xb5:81e1cab3 b8220000c0 mov eax,0C0000022h81e1cab8 ebba jmp nt!PspSetCreateProcessNotifyRoutine+0x76 (81e1ca74)
    Win10 64 位 PspSetCreateProcessNotifyRoutinekd> uf PspSetCreateProcessNotifyRoutinent!PspSetCreateProcessNotifyRoutine:fffff800`a09a4530 48895c2408 mov qword ptr [rsp+8],rbxfffff800`a09a4535 48896c2410 mov qword ptr [rsp+10h],rbpfffff800`a09a453a 4889742418 mov qword ptr [rsp+18h],rsifffff800`a09a453f 57 push rdifffff800`a09a4540 4154 push r12fffff800`a09a4542 4155 push r13fffff800`a09a4544 4156 push r14fffff800`a09a4546 4157 push r15fffff800`a09a4548 4883ec20 sub rsp,20hfffff800`a09a454c 8ada mov bl,dlfffff800`a09a454e 8bf2 mov esi,edxfffff800`a09a4550 d0eb shr bl,1fffff800`a09a4552 4c8be1 mov r12,rcxfffff800`a09a4555 80e301 and bl,1fffff800`a09a4558 f6c201 test dl,1fffff800`a09a455b 0f8559770900 jne nt! ?? ::NNGAKEGL::`string'+0x6752a (fffff800`a0a3bcba)nt!PspSetCreateProcessNotifyRoutine+0x31:fffff800`a09a4561 84db test bl,blfffff800`a09a4563 7573 jne nt!PspSetCreateProcessNotifyRoutine+0xa8 (fffff800`a09a45d8)nt!PspSetCreateProcessNotifyRoutine+0x35:fffff800`a09a4565 488bd6 mov rdx,rsifffff800`a09a4568 498bcc mov rcx,r12fffff800`a09a456b e8a0000000 call nt!ExAllocateCallBack (fffff800`a09a4610)fffff800`a09a4570 488bf0 mov rsi,raxfffff800`a09a4573 4885c0 test rax,raxfffff800`a09a4576 0f8400780900 je nt! ?? ::NNGAKEGL::`string'+0x675ec (fffff800`a0a3bd7c)nt!PspSetCreateProcessNotifyRoutine+0x4c:fffff800`a09a457c 33ff xor edi,edifffff800`a09a457e 4c8d3dfb0bdfff lea r15,[nt!PspCreateProcessNotifyRoutine (fffff800`a0795180)]nt!PspSetCreateProcessNotifyRoutine+0x55:fffff800`a09a4585 498d0cff lea rcx,[r15+rdi*8]fffff800`a09a4589 4533c0 xor r8d,r8dfffff800`a09a458c 488bd6 mov rdx,rsifffff800`a09a458f e8fce9bfff call nt!ExCompareExchangeCallBack (fffff800`a05a2f90)fffff800`a09a4594 84c0 test al,alfffff800`a09a4596 750c jne nt!PspSetCreateProcessNotifyRoutine+0x74 (fffff800`a09a45a4)nt!PspSetCreateProcessNotifyRoutine+0x68:fffff800`a09a4598 ffc7 inc edifffff800`a09a459a 83ff40 cmp edi,40hfffff800`a09a459d 72e6 jb nt!PspSetCreateProcessNotifyRoutine+0x55 (fffff800`a09a4585)nt!PspSetCreateProcessNotifyRoutine+0x6f:fffff800`a09a459f e9e2770900 jmp nt! ?? ::NNGAKEGL::`string'+0x675f6 (fffff800`a0a3bd86)nt!PspSetCreateProcessNotifyRoutine+0x74:fffff800`a09a45a4 84db test bl,blfffff800`a09a45a6 7540 jne nt!PspSetCreateProcessNotifyRoutine+0xb8 (fffff800`a09a45e8)nt!PspSetCreateProcessNotifyRoutine+0x78:fffff800`a09a45a8 f0ff051d481d00 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineCount (fffff800`a0b78dcc)]fffff800`a09a45af 8b0583441d00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)]fffff800`a09a45b5 a802 test al,2fffff800`a09a45b7 744b je nt!PspSetCreateProcessNotifyRoutine+0xd4 (fffff800`a09a4604)nt!PspSetCreateProcessNotifyRoutine+0x89:fffff800`a09a45b9 33c0 xor eax,eaxnt!PspSetCreateProcessNotifyRoutine+0x8b:fffff800`a09a45bb 488b5c2450 mov rbx,qword ptr [rsp+50h]fffff800`a09a45c0 488b6c2458 mov rbp,qword ptr [rsp+58h]fffff800`a09a45c5 488b742460 mov rsi,qword ptr [rsp+60h]fffff800`a09a45ca 4883c420 add rsp,20hfffff800`a09a45ce 415f pop r15fffff800`a09a45d0 415e pop r14fffff800`a09a45d2 415d pop r13fffff800`a09a45d4 415c pop r12fffff800`a09a45d6 5f pop rdifffff800`a09a45d7 c3 retnt!PspSetCreateProcessNotifyRoutine+0xa8:fffff800`a09a45d8 e837020000 call nt!MmVerifyCallbackFunction (fffff800`a09a4814)fffff800`a09a45dd 85c0 test eax,eaxfffff800`a09a45df 7584 jne nt!PspSetCreateProcessNotifyRoutine+0x35 (fffff800`a09a4565)nt!PspSetCreateProcessNotifyRoutine+0xb1:fffff800`a09a45e1 b8220000c0 mov eax,0C0000022hfffff800`a09a45e6 ebd3 jmp nt!PspSetCreateProcessNotifyRoutine+0x8b (fffff800`a09a45bb)nt!PspSetCreateProcessNotifyRoutine+0xb8:fffff800`a09a45e8 f0ff05d9471d00 lock inc dword ptr [nt!PspCreateProcessNotifyRoutineExCount (fffff800`a0b78dc8)]fffff800`a09a45ef 8b0543441d00 mov eax,dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)]fffff800`a09a45f5 a804 test al,4fffff800`a09a45f7 75c0 jne nt!PspSetCreateProcessNotifyRoutine+0x89 (fffff800`a09a45b9)nt!PspSetCreateProcessNotifyRoutine+0xc9:fffff800`a09a45f9 f00fba2d36441d0002 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)],2fffff800`a09a4602 ebb5 jmp nt!PspSetCreateProcessNotifyRoutine+0x89 (fffff800`a09a45b9)nt!PspSetCreateProcessNotifyRoutine+0xd4:fffff800`a09a4604 f00fba2d2b441d0001 lock bts dword ptr [nt!PspNotifyEnableMask (fffff800`a0b78a38)],1fffff800`a09a460d ebaa jmp nt!PspSetCreateProcessNotifyRoutine+0x89 (fffff800`a09a45b9)
    Win7 32 位 PsSetCreateProcessNotifyRoutinelkd> uf PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:83fa5720 8bff mov edi,edi83fa5722 55 push ebp83fa5723 8bec mov ebp,esp83fa5725 6a00 push 083fa5727 ff750c push dword ptr [ebp+0Ch]83fa572a ff7508 push dword ptr [ebp+8]83fa572d e809000000 call nt!PspSetCreateProcessNotifyRoutine (83fa573b)83fa5732 5d pop ebp83fa5733 c20800 ret 8
    Win7 64 位 PsSetCreateProcessNotifyRoutinelkd> u PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:fffff800`042be3c0 4533c0 xor r8d,r8dfffff800`042be3c3 e9e8fdffff jmp nt!PspSetCreateProcessNotifyRoutine (fffff800`042be1b0)
    Win8.1 32 位 PsSetCreateProcessNotifyRoutinelkd> uf PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:811617d2 8bff mov edi,edi811617d4 55 push ebp811617d5 8bec mov ebp,esp811617d7 8a550c mov dl,byte ptr [ebp+0Ch]811617da 8b4d08 mov ecx,dword ptr [ebp+8]811617dd 6a00 push 0811617df e89a000000 call nt!PspSetCreateProcessNotifyRoutine (8116187e)811617e4 5d pop ebp811617e5 c20800 ret 8
    Win8.1 64 位 PsSetCreateProcessNotifyRoutinelkd> u PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:fffff803`10775b00 4533c0 xor r8d,r8dfffff803`10775b03 e9bc000000 jmp nt!PspSetCreateProcessNotifyRoutine (fffff803`10775bc4)
    Win10 32 位 PsSetCreateProcessNotifyRoutinekd> uf PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:81e1c8d4 8bff mov edi,edi81e1c8d6 55 push ebp81e1c8d7 8bec mov ebp,esp81e1c8d9 8a550c mov dl,byte ptr [ebp+0Ch]81e1c8dc 8b4d08 mov ecx,dword ptr [ebp+8]81e1c8df 6a00 push 081e1c8e1 e818010000 call nt!PspSetCreateProcessNotifyRoutine (81e1c9fe)81e1c8e6 5d pop ebp81e1c8e7 c20800 ret 8
    Win10 64 位 PsSetCreateProcessNotifyRoutinekd> u PsSetCreateProcessNotifyRoutinent!PsSetCreateProcessNotifyRoutine:fffff800`a09a4460 33c0 xor eax,eaxfffff800`a09a4462 84d2 test dl,dlfffff800`a09a4464 448d4001 lea r8d,[rax+1]fffff800`a09a4468 410f45c0 cmovne eax,r8dfffff800`a09a446c 8bd0 mov edx,eaxfffff800`a09a446e e9bd000000 jmp nt!PspSetCreateProcessNotifyRoutine (fffff800`a09a4530)
    5 留言 2019-03-16 10:05:20 奖励12点积分
显示 30 到 45 ,共 15 条

热门回复

  • 线程同步之临界区
    程序还算比较简单的,谢谢分享
    2019-04-20 12:24:58 thumb_up( 1 )
  • 基于WinPcap实现的UDP发包程序
    如果在vs 编译时出现 无法找到元数据文件 platform.winmd 应该怎么解决
    2021-03-23 09:58:16 thumb_up( 2 )
  • 机器视觉MIL Mod匹配
    You can also search within the full angular range of 360� from the nominal angle specified with M_ANGLE. Use the MmodControl()�M_ANGLE_DELTA_POS and M_ANGLE_DELTA_NEG control types to specify the angular range in the counter-clockwise and clockwise direction from the nominal angle, respectively; the default for both is 180�. The angular range limits the possible angles which can be returned as results for an occurrence. Note that the actual angle of the occurrence does not affect search speed. If you need to search for a model at discrete angles only (for example, at intervals of 90 degrees), it is typically more efficient to define several models with different expected angles, than to search through the full angular range. By default, calculations specific to angular-range search strategies are enabled. If you expect that the occurrences sought are close to the specified nominal angle, you can disable these calculations using MmodControl() with M_SEARCH_ANGLE_RANGE set to M_DISABLE. When disabled, you must specify a good nominal angle for each model, which is within the model's angular range. You can restrict which candidates are returned as occurrences by narrowing the angular-range. Note that M_SEARCH_ANGLE_RANGE must be enabled to search for a rotation-invariant non-synthetic model (for example, an image-type model of a circle).
    2021-03-20 13:27:51 thumb_up( 2 )
  • 基于SSM的超市订单管理系统
    可以用吗,怎么用的
    2021-04-14 15:44:50 thumb_up( 1 )
eject