关于px、em、rem、rpx、upx的知识

该日志由 samool 发表于 2020-09-30 11:11:48

PX

px像素(Pixel)。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。

PX特点

    1. IE无法调整那些使用px作为单位的字体大小;
    1. 国外的大部分网站能够调整的原因在于其使用了em或rem作为字体单位;
    1. Firefox能够调整px和em,rem,但是96%以上的中国网民使用IE浏览器(或内核)。

EM

em是相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸。

EM特点

    1. em的值并不是固定的;
    1. em会继承父级元素的字体大小。

注意:任意浏览器的默认字体高都是16px。所有未经调整的浏览器都符合: 1em=16px。那么12px=0.75em,10px=0.625em。为了简化font-size的换算,需要在css中的body选择器中声明Font-size=62.5%,这就使em值变为 16px*62.5%=10px, 这样12px=1.2em, 10px=1em, 也就是说只需要将你的原来的px数值除以10,然后换上em作为单位就行了。

所以我们在写CSS的时候,需要注意两点:

    1. body选择器中声明Font-size=62.5%;
    1. 将你的原来的px数值除以10,然后换上em作为单位;
    1. 重新计算那些被放大的字体的em数值。避免字体大小的重复声明。

也就是避免1.2 * 1.2= 1.44的现象。比如说你在#content中声明了字体大小为1.2em,那么在声明p的字体大小时就只能是1em,而不是1.2em, 因为此em非彼em,它因继承#content的字体高而变为了1em=12px。

REM

rem是CSS3新增的一个相对单位(root em,根em),这个单位引起了广泛关注。这个单位与em有什么区别呢?区别在于使用rem为元素设定字体大小时,仍然是相对大小,但相对的只是HTML根元素。这个单位可谓集相对大小和绝对大小的优点于一身,通过它既可以做到只修改根元素就成比例地调整所有字体大小,又可以避免字体大小逐层复合的连锁反应。目前,除了IE8及更早版本外,所有浏览器均已支持rem。对于不支持它的浏览器,应对方法也很简单,就是多写一个绝对单位的声明。这些浏览器会忽略用rem设定的字体大小。下面就是一个例子:

p {font-size:14px; font-size:.875rem;}
注意: 选择使用什么字体单位主要由你的项目来决定,如果你的用户群都使用最新版的浏览器,那推荐使用rem,如果要考虑兼容性,那就使用px,或者两者同时使用。

px 与 rem 的选择?

对于只需要适配少部分手机设备,且分辨率对页面影响不大的,使用px即可 。

对于需要适配各种移动设备,使用rem,例如只需要适配iPhone和iPad等分辨率差别比较挺大的设备。

rpx

rpx 是微信小程序解决自适应屏幕尺寸的尺寸单位。微信小程序规定屏幕的宽度为750rpx。

无论是在iPhone6上面还是其他机型上面都是750rpx的屏幕宽度,拿iPhone6来讲,屏幕宽度为375px,把它分为750rpx后, 1rpx = 0.5px。

微信小程序同时也支持rem尺寸单位, rem 规定屏幕的宽度为20rem, 所以 1rem = (750/20)rpx = 37.5 rpx

UPX

uni-app 使用 upx 作为默认尺寸单位, upx 是相对于基准宽度的单位,可以根据屏幕宽度进行自适应。uni-app 规定屏幕基准宽度750upx。

开发者可以通过设计稿基准宽度计算页面元素 upx 值,设计稿 1px 与框架样式 1upx 转换公式如下:

设计稿 1px / 设计稿基准宽度 = 框架样式 1upx / 750upx

upx在2018年就推出了,其初衷是为了让微信的rpx机制可以跨全端使用。

在普通css写法里,upx会被编译器编译。但动态绑定时,upx无法被准确编译,此时官方提出了使用uni.upx2px()方法,用来动态计算。

该日志标签: 浏览器, 移动, 选择, 元素, 尺寸, 字体, 网站, 设计, 手机, 方法, 程序, 宽度, css, 长度, 中国, 特点, root, 微信, 用户, 时候, rem, upx, rpx, 单位

CentOS7 + 宝塔环境搭建 Gogs 私人Git 仓库

该日志由 samool 发表于 2020-09-01 16:41:00

安装文档 https://gogs.io/docs/installation

我这里的安装环境:

CentOS7 + 宝塔面板 + Nginx + Mysql5.7

git 版本 1.8.3.1

Gogs 版本 0.11.66 @ 2018-09-16

安装包下载地址列表:

官方:

https://gogs.io/docs/installation/install_from_binary.html

github

https://github.com/gogs/gogs/releases

找到 Linux amd64 的下载地址

https://dl.gogs.io/0.11.66/gogs_0.11.66_linux_amd64.zip

安装宝塔面板

**

yum install -y wget && wget -O install.sh http://download.bt.cn/install/install.sh && sh install.sh

我这里选择的环境是

Mysql5.7 + PHP7.2 + Nginx 1.14.0 + phpmyadmin4.x + Pure-Ftpd 1.0.47

安装Git (已经安装的跳过)

**

yum install -y git

查看Git版本

**

git --version

创建一个git用户组

**

groupadd git

创建一个git用户并添加到git用户组

**

useradd -g git git

修改git用户的密码

**

passwd git

创建好了之后, 切换到Git身份

**

su git

这时候已经是git用户了, 进入git的home目录

**

cd ~

下载gogs安装压缩包, 双引号里的下载地址替换为官方最新版本的下载地址

**

wget -c "https://dl.gogs.io/0.11.66/gogs_0.11.66_linux_amd64.zip" -O gogs.zip

解压安装包

**

unzip gogs.zip

进入gogs目录

**

cd gogs

启动安装程序

**

./gogs web
在宝塔面板->安全-> 添加3000端口, 允许访问

回到桌面, 用浏览器打开安装向导页面, 根据自己情况设置

**

http://192.168.1.101:3000

安装完成后, 回到终端, 退出当前的git用户

**

exit

为gogs添加服务,开机启动

**

# 创建服务配置文件
vim /lib/systemd/system/gogs.service

加入以下内容, 保存退出:

**

[Unit]
Description=Gogs
After=syslog.target
After=network.target
After=mysqld.service

[Service]
# Modify these two values and uncomment them if you have
# repos with lots of files and get an HTTP error 500 because
# of that
###
#LimitMEMLOCK=infinity
#LimitNOFILE=65535
Type=simple
User=git
Group=git
WorkingDirectory=/home/git/gogs
ExecStart=/home/git/gogs/gogs web
Restart=always
Environment=USER=git HOME=/home/git

[Install]
WantedBy=multi-user.target

设置开机启动

**

systemctl enable gogs.service

并手动启动服务

**

systemctl start gogs

查看服务状态

**

systemctl status gogs

在宝塔面板上配置域名访问(反向代理)

这个时候就可以通过域名访问了, https证书在宝塔界面配置部署即可, 不用改gogs配置

使用域名访问搭建好的gogs

该日志标签: 代理, 浏览器, 目录, 来源, 服务, 桌面, 内容, 文件, 访问, 密码, 程序, 链接, c, 列表, 安装, 界面, 域名, web, 环境, get, cd, error, 商业, 端口, 宝塔, , 用户, git, gogs, home

一个简单的浏览器

该日志由 samool 发表于 2011-08-07 14:54:33

package com.iame.mybrowser;  
  
import android.app.Activity;  
import android.app.AlertDialog;  
import android.content.DialogInterface;  
import android.graphics.Bitmap;  
import android.os.Bundle;  
import android.view.KeyEvent;  
import android.view.LayoutInflater;  
import android.view.View;  
import android.view.Window;  
import android.view.View.OnClickListener;  
import android.webkit.JsPromptResult;  
import android.webkit.JsResult;  
import android.webkit.WebChromeClient;  
import android.webkit.WebView;  
import android.webkit.WebViewClient;  
import android.widget.Button;  
import android.widget.EditText;  
import android.widget.TextView;  
  
public class MyBrowser extends Activity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        getWindow().requestFeature(Window.FEATURE_PROGRESS);//��title bar��������   
        setContentView(R.layout.main);  
        final WebView webView=(WebView)findViewById(R.id.browser);  
        final EditText text=(EditText)findViewById(R.id.url);  
        Button go=(Button)findViewById(R.id.go);  
        webView.getSettings().setJavaScriptEnabled(true);  
        webView.setWebViewClient(new WebViewClient(){  
            @Override  
            public void onPageFinished(WebView view, String url) {  
                super.onPageFinished(view, url);  
            }  
  
            @Override  
            public void onPageStarted(WebView view, String url, Bitmap favicon) {  
                text.setText(url);  
                super.onPageStarted(view, url, favicon);  
            }  
        });  
        webView.setWebChromeClient(new WebChromeClient(){  
            @Override  
            public boolean onJsAlert(WebView view, String url, String message,  
                    final JsResult result) {  
                AlertDialog.Builder b = new AlertDialog.Builder(MyBrowser.this);  
                b.setTitle("Alert");  
                b.setMessage(message);  
                b.setPositiveButton(android.R.string.ok,  
                        new AlertDialog.OnClickListener() {  
                            public void onClick(DialogInterface dialog, int which) {  
                                result.confirm();  
                            }  
                        });  
                b.setCancelable(false);  
                b.create();  
                b.show();  
                return true;  
            };  
  
            @Override  
            public boolean onJsConfirm(WebView view, String url, String message,  
                    final JsResult result) {  
                AlertDialog.Builder b = new AlertDialog.Builder(MyBrowser.this);  
                b.setTitle("Confirm");  
                b.setMessage(message);  
                b.setPositiveButton(android.R.string.ok,  
                        new AlertDialog.OnClickListener() {  
                            public void onClick(DialogInterface dialog, int which) {  
                                result.confirm();  
                            }  
                        });  
                b.setNegativeButton(android.R.string.cancel,  
                        new DialogInterface.OnClickListener() {  
                            public void onClick(DialogInterface dialog, int which) {  
                                result.cancel();  
                            }  
                        });  
                b.setCancelable(false);  
                b.create();  
                b.show();  
                return true;  
            };  
  
            @Override  
            public boolean onJsPrompt(WebView view, String url, String message,  
                    String defaultValue, final JsPromptResult result) {  
                final LayoutInflater factory = LayoutInflater.from(MyBrowser.this);  
                final View v = factory.inflate(R.layout.javascript_prompt_dialog, null);  
                ((TextView) v.findViewById(R.id.prompt_message_text)).setText(message);  
                ((EditText) v.findViewById(R.id.prompt_input_field))  
                        .setText(defaultValue);  
  
                AlertDialog.Builder b = new AlertDialog.Builder(MyBrowser.this);  
                b.setTitle("Prompt");  
                b.setView(v);  
                b.setPositiveButton(android.R.string.ok,  
                        new AlertDialog.OnClickListener() {  
                            public void onClick(DialogInterface dialog, int which) {  
                                String value = ((EditText) v.findViewById

(R.id.prompt_input_field)).getText().toString();  
                                result.confirm(value);  
                            }  
                        });  
                b.setNegativeButton(android.R.string.cancel,  
                        new DialogInterface.OnClickListener() {  
                            public void onClick(DialogInterface dialog, int which) {  
                                result.cancel();  
                            }  
                        });  
                b.setOnCancelListener(new DialogInterface.OnCancelListener() {  
                            public void onCancel(DialogInterface dialog) {  
                                result.cancel();  
                            }  
                        });  
                b.show();  
                return true;  
            };  
  
            @Override  
            public void onProgressChanged(WebView view, int newProgress) {  
                MyBrowser.this.getWindow().setFeatureInt(Window.FEATURE_PROGRESS, newProgress * 100);  
                super.onProgressChanged(view, newProgress);  
            }  
  
            @Override  
            public void onReceivedTitle(WebView view, String title) {  
                MyBrowser.this.setTitle(title);  
                super.onReceivedTitle(view, title);  
            }  
        });  
        go.setOnClickListener(new OnClickListener(){  
            public void onClick(View view) {  
                String url=text.getText().toString();  
                webView.loadUrl(url);  
            }  
        });  
        webView.loadUrl("file:///android_asset/index.html");  
    }  
    public boolean onKeyDown(int keyCode, KeyEvent event) {  
        WebView mWebView=(WebView)findViewById(R.id.browser);  
        if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {  
            mWebView.goBack();  
            return true;  
        }  
        return super.onKeyDown(keyCode, event);  
    }  
}  

该日志标签: 浏览器, browser, Android

关于ie双击变成了创建快捷方式的解决办法

该日志由 samool 发表于 2010-07-13 21:03:17

故障现象:
双击IE图标时,它并不是打开IE浏览器,而是在桌面创建了一个快捷方式。

故障原因:
打开注册表,在注册表的HKEY_CLASSES_ROOT中寻找OpenHomePage项,发现里面有个字符串LegacyDisable值为空,这就是问题所在。

解决方法:
把这个这个字符串LegacyDisable删除

数据项位置:
HKEY_CLASSES_ROOT\CLSID\{871C5380-42A0-1069-A2EA-08002B30309D}\shell\OpenHomePage

要删除的数据项:
LegacyDisable

该日志标签: 浏览器, ie

微软发布IE浏览器安全补丁

该日志由 samool 发表于 2008-12-18 08:38:15

微软Internet Explorer浏览器出现容易让黑客入侵的严重安全漏洞,该公司称即将发布紧急补丁程序来填补这个漏洞。互联网专家说,微软IE 7.0浏览器爆发的漏洞已被黑客利用制作恶意网页,传播木马病毒。微软说,补丁程序在周三格林尼治标准时间1800(北京时间周四凌晨两点)发布给用户。 互联网专家建议电脑用户近期最好换用其它浏览器,直到微软发布该漏洞的补丁程序。

专家称,用户在上网时,电脑不知不觉中毒,黑客就可达到窃取用户个人信息之目的。 微软近日已正式在本身网上发布公告,确认IE 7.0浏览器中新发现了一个严重漏洞,该漏洞存在于所有版本的浏览器当中--即从IE 5.0版本到IE 8 beta 2版本--都受到影响。 微软公告说,微软将继续监测和调查对此漏洞的安全威胁,可能在下月安全更新或根据客户需要额外发布解决方案。

电脑安全专家表示,就目前而言,黑客利用微软IE 7.0浏览器漏洞主要是想窃取网民的游戏账号。 趋势科技(Trend Micro)安全专家费格森说,该漏洞有可能被黑客用于经济犯罪,这更加令人担心。

新闻来源:BBC
查看:Microsoft Security Bulletin MS08-078 - Critical

该日志标签: 补丁, 浏览器, ie

Maxthon被鬼缠身,转投FireFox怀抱

该日志由 samool 发表于 2007-11-09 09:42:46

本来我一直喜欢Maxthon(傲游浏览器,又名马桶)的,拦截广告功能,网络收藏夹功能,还有标签功能都是我的喜欢的,但是最近不知何故浏览器老假死,稍微多打开几个窗口CPU就是100%,郁闷的很。

前段时间用了用FireFox(火狐)浏览器,速度貌似比马桶慢点,但是至少不会假死,举例说明吧,像打开google adsense后台,想显示详细统计报表,因为google其它是用Web2.0做的,JS非常多,在马桶下绝对卡机,半天没响应,CPU就满了,过不了一会整个浏览就挂,或许马桶的核心是IE的原因吧,要受IE很多的限制,IE核心本身的缺陷也会累加到马桶身上,还有从百度搜索网页,只要打开某些带毒的网页(其实就是包含病毒脚本),马桶也会假死,不知道这个是NOD32的原因,具体还是马桶的原因说不清楚,反正最近一段时间就是老崩溃,最近还提交错误信息,都提交过N次了,问题还是没解决。

怎么办?放弃马桶,转投Firefox怀抱。希望马桶一路走好,要做一个完美的浏览器,不光光是做一个漂亮的壳,还是要开发自己核心。当然我们不排除目前马桶已经做的很不错了,但是老假死的问题确实让我受不了,暂时用一段时间FireFox吧,听说FF3.0要出来了,期待中。。。

最新版FireFox下载页面

该日志标签: 浏览器, maxthon, firefox

如何使用BHO定制你的Internet Explorer浏览器

该日志由 samool 发表于 2007-10-15 10:26:07

英文原文:Browser Helper Objects: The Browser the Way You Want It

一、简介
  有时,你可能需要一个定制版本的浏览器。在这种情况下,你可以自由地把一些新颖但又不标准的特征增加到一个浏览器上。结果,你最终有的只是一个新但不标准的浏览器。Web浏览器控件只是浏览器的分析引擎。这意味着仍然存在若干的与用户接口相关的工作等待你做――增加一个地址栏,工具栏,历史记录,状态栏,频道栏和收藏夹等。如此,要产生一个定制的浏览器,你可以进行两种类型的编程――一种象微软把Web浏览器控件转变成一个功能齐全的浏览器如Internet Explorer;一种是在现有的基础上加一些新的功能。如果有一个直接的方法定制现有的Internet Explorer该多好?BHO(Browser Helper Objects,我译为"浏览器帮助者对象",以下皆简称BHO)正是用来实现此目的的。

二、关于软件定制
  以前,定制一个软件的行为主要是通过子类化方法实现的。 通过这种办法,你可以改变一个窗口的外表与行为。子类化虽然被认为是一种有点暴力方式――受害者根本不知道发生的事情――但它还是长时间以来的唯一的选择。
  随着微软Win32 API的到来,进程间子类化不再被鼓励使用并愈发变得困难起来。当然,如果你是勇敢的--指针从未吓倒你,而最重要的是,如果你已经游刃于系统钩子之间,你可能觉得这一问题太简单了。 但是情形并不总是这样。暂放下这点不管,问题在于每一个进程运行在自己的地址空间中,而且打破进程边界略微有些不正确性。 另一方面, 你可能需要对定制进行更好的管理。更经常情况下,定制可能是程序本身强烈要求实现的。
  在后者情况下,已安装的软件只需在既定的磁盘位置查询另外的组件模块,然后装载、设定初值,最后让它们自由地按照既定的设计工作。这正是Internet Explorer浏览器和它的BHO所要实现的。

三、什么是BHO?
  从某种观点看,Internet Explorer同普通的Win32程序没有什么两样。借助于BHO,你可以写一个进程内COM对象,这个对象在每次启动时都要加载。这样的对象会在与浏览器相同的上下文中运行,并能对可用的窗口和模块执行任何行动。例如,一个BHO能够探测到典型的事件,如GoBack、GoForward、DocumentComplete等;另外BHO能够存取浏览器的菜单与工具栏并能做出修改,还能够产生新窗口来显示当前网页的一些额外信息,还能够安装钩子以监控一些消息和动作。简而言之, BHO的工作如我们打入浏览器领地的一位间谍(注意这是微软允许的合法工作)。
  在进一步了解BHO细节之前,有几点我需要进一步阐述。首先,BHO对象依托于浏览器主窗口。实际上,这意味着一旦一个浏览器窗口产生,一个新的BHO对象实例就要生成。任何 BHO对象与浏览器实例的生命周期是一致的。其次, BHO仅存在于Internet Explorer 4.0及以后版本中。
如果你在使用Microsoft Windows? 98, Windows 2000, Windows 95, or Windows NT版本4.0 操作系统的话,也就一块运行了活动桌面外壳4.71,BHO也被 Windows资源管理器所支持。 BHO是一个COM进程内服务,注册于注册表中某一键下。在启动时,Internet Explorer查询那个键并把该键下的所有对象预以加载。
  Internet Explorer浏览器初始化这一对象并要求某一接口功能。如果发现这一接口, Internet Explorer使用其提供的方法传递 IUnknown 指针到BHO对象。见图一:


图一 ie浏览器如何装入和初始化BHO对象,BHO场所(site)是用于实现通信的COM接口

  浏览器可能在注册表中发现一系列的CLSID,并由此为每个CLSID建立一个进程中实例。结果是,这些对象被装载至浏览器上下文中并运行起来,好象它们是本地组件一样。但是,由于Internet Explorer的COM特性,即使被装入到它的进程空间中于事(你的野心实现)也不一定会有多大帮助。用另一说法, BHO的确能够做许多潜在的有用的事情,如子类化组成窗口或者安装线程局部钩子,但是它确实远离浏览器的核心活动。为了钩住浏览器的事件或者自动化浏览器,BHO需要建立一个私有的基于COM的通讯通道。为此,该BHO应该实现一个称为IObjectWithSite的接口。事实上,通过接口IobjectWithSite, Internet Explorer 可以传递它的IUnknown 接口。BHO反过来能够存储该接口并进一步查询更专门的接口,如IWebBrowser2、IDispatch和IConnectionPointContainer。
  另外一种分析BHO对象的途径与Internet Explorer外壳扩展有关。我们知道,一个WINDOWS外壳扩展即是一个进程内的COM服务器,它在Windows资源管理器执行某种动作时装入内存――如显示上下文菜单。通过建立一个实现几个COM接口的COM模块,你就给上下文菜单加上一些项并能预以正确处理。一个外壳扩展必须以Windows资源管理器能够发现的方法注册。一个BHO对象遵循同样的模式――唯一的改变在于要实现的接口。然而,尽管实现方式有所不同,外壳扩展与 BHO 仍有许多共同的特点。如下表一:

表一 外壳扩展与 BHO相近特性比较
特性 外壳扩展 BHO对象
加载者 Windows资源管理器 Internet Explorer(和外壳4.17及以上版本的Windows资源管理器)
击活动作 在某类文档上的用户动作(即单击右键) 打开浏览器窗口
何时卸载 参考计数达到0的几秒之后 导致它加载的窗口关闭时
实现形式 COM进程中DLL COM 进程中 DLL
注册需求 常常是为一个COM服务器设置的入口处,另加的入口依赖于外壳类型及它要应用至的文档类型 常常是为一个COM服务器设置的入口处,另加一个把它申请为BHO的注册入口
接口需求 依赖于外壳扩展的类型 IObjectWithSite


如果你对SHELL扩展编程有兴趣的话,可以参考MSDN有关资料。

四、BHO的生存周期
  前面已经说过,BHO不仅仅为Internet Explorer所支持。如果你在使用外壳 4.71或者更高版本,你的BHO对象也会被Windows资源管理器所加载。下表二展示了我们可以使用的不同版本的外壳产品情况,Windows外壳版本号存于库文件shell32.dll中。

表二 不同版本的Windows外壳对于BHO的支持情况
外壳版本 安装的产品 BHO的支持情况
4.00 Windows 95,Windows NT 4.0 带或不带 Internet Explorer 4.0 或更老版本。 注意没有安装外壳更新 Internet Explorer 4.0
4.71 Windows 95,Windows NT 4.0 带Internet Explorer 4.0 和活动桌面外壳更新 Internet Explorer 与Windows 资源管理器
4.72 Windows 98 Internet Explorer与Windows 资源管理器
5.00 Windows 2000 Internet Explorer与Windows 资源管理器


  BHO对象随着浏览器主窗口的显示而装入,随着浏览器主窗口的销毁而缷载。如果你打开多个浏览器窗口,多个BHO实例也一同产生。
  无论浏览器以什么样的命令行启动,BHO对象都被加载。举例来说,即使你只是想要见到特定的 HTML 页或一个给定的文件夹,BHO对象也被加载。一般地,当 explorer.exe 或 iexplore.exe 运行的时候,BHO都要被考虑在内。如果你设置了"Open each folder in its own window"(对每一个文件夹以一个独立窗口打开)文件夹选项,那么你每次打开一个文件夹,BHO对象都要被加载。见图二。


图二 经过这样设置,你每次打开一个文件夹时,执行一个独立的explorer.exe实例,并装入已注册的BHO对象。

  但是注意,这种情形仅适于当你从桌面上的"我的电脑"图标中打开文件夹的情况。在这种情况下,每次你移到另外一个文件夹时外壳都要调用explorer.exe。这种情况在你同时用两个窗格进行浏览时是不会发生的。事实上,当你改变文件夹时,外壳是不会启动浏览器的新的实例的而仅是简单创建嵌入视图对象的另外一个实例。奇怪的是,如果你在地址栏中输入一个新的名字来改变文件夹时,在同一个窗口中同样可以达到浏览之目的,无论Windows资源管理器视图是单个的还是双视图形式。
  对于Internet Explorer的情形,事情要更简单一些。只有你显式地多次运行iexplore.exe浏览器时,你才有多个Internet Explorer的拷贝。当你从Internet Explorer中打开新的窗口时,每一个窗口在一个新的线程中被复制而不是创建一个新的进程,因此也就不需要重新载入BHO对象。
  首先,BHO最有趣的地方是,它是极度动态的。每次Windows资源管理器或者Internet Explorer打开,装载器从注册表中读取已安装的BHO对象的CLSID然后处理它们。如果你在打开的浏览器多个实例中间编辑注册表的话,你可以随着多个浏览器拷贝的载入而装入多个不同的BHO。 这就是说,如果你选择从头创建一个新的属于自己的浏览器,那么你可以把它内嵌在一个Visual Basic或者MFC框架窗口中。同时你有相当的机会来灵活安排浏览程序。如果它们能满足你的需要的话,你可以依赖于Internet Explorer的强大的功能并且加上你想要的尽可能多的插件。

五、关于IObjectWithSite接口
  从一个高起点来看,BHO即是一个DLL,它能够依附于Internet Explorer浏览器的一个新建的实例,在某些情况下也适用于Windows资源管理器。
  一般地,一个场所(site)是一个中间对象,它位于容器对象与被包容对象之间。通过它,容器对象管理被包容对象的内容,也因此使得对象的内部功能可用。为此,容器方要实现接口IoleClientSite,被包容对象要实现接口IOleObject 。通过调用IOleObject提供的方法,容器对象使得被包容对象清楚地了解其HOST的环境。
  一旦容器对象成为Internet Explorer(或是具有WEB能力的Windows资源管理器),被包容对象只需实现一个轻型的IObjectWithSite接口。该接口提供了以下方法:

表三 IObjectWithSite定义
方法 描述
HRESULT SetSite(IUnknown* pUnkSite) 接收ie浏览器的IUnknown指针。典型实现是保存该指针以备将来使用。.
HRESULT GetSite(REFIID riid, void** ppvSite) 从通过SetSite()方法设置的场所中接收并返回指定的接口,典型实现是查询前面保存的接口指针以进一步取得指定的接口。


  对BHO 的唯一严格的要求正在于必须实现这一个接口。 注意你应该避免在调用以上任何一个函数时返回E_NOTIMPL 。 要么你不实现这一接口,要么应保证在调用这些方法时进行正确地编码。

六、构造自己的BHO对象
  一个BHO对象就是一个进程中服务器DLL,选用ATL创建它是再恰当不过的了。我们选择ATL的另外一个原因是因为它已经提供了缺省的而且提供了IObjectWithSite接口的足够好的实现。另外,在ATL COM 向导本地支持的已定义好的对象类型当中,有一个,就是Internet Explorer对象,这正是一个BHO应该具有的类型。一个 ATL Internet Explorer 对象,事实上是一个简单对象――也就是说,是一个支持IUnknown和自注册,还有接口IObjectWithSite的COM 服务器。如果你在ATL工程中添加一个这样的对象,并调用相应的类CViewSource,你将从向导中得到下列代码:

class ATL_NO_VTABLE CViewSource :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CViewSource, &CLSID_ViewSource>,
public IObjectWithSiteImpl<CViewSource>,
public IDispatchImpl<IViewSource, &IID_IViewSource,
&LIBID_HTMLEDITLib>
  正如你所见,向导已经使类从接口IObjectWithSiteImpl继承,这是一个ATL模板类,它提供了接口IObjectWithSite的基本实现。一般情况下,没有必要重载成员函数GetSite()。取而代之的是, SetSite() 实现代码经常需要加以定制。ATL实际上仅仅把一个IUnknown接口指针存储在成员变量m_spUnkSite中。
  在文章的剩余部分,我将讨论一个 BHO 的相当复杂而丰富的例子。该BHO对象将依附于Internet Explorer,并显示一个文本框来显示当前正浏览的网页源码。 该代码窗口将 随着你改变网页而自动更新,如果浏览器显示的不是一个HTML网页时,它将变灰。你对于原始HTML代码的任何改动立即反映在浏览器中。HTML (DHTML)使得这一看似魔术般的实现成为可能。该代码窗口可被隐藏和通过按动热键重现。 在可见情况下,它与Internet Explorer共享整个桌面空间,见图三。


图三 BHO对象在使用中。它依附于Internet Explorer,并显示一个窗口来显示当前正浏览的网页源码。还允许你源码进行修改。

  本例子的关键点在于存取Internet Explorer的浏览机制,其实它只不过是WebBrowser控件的一个实例而已。这个例子可以分解为以下五步来实现:

探测谁在装入这个对象,是Internet Explorer还是Windows资源管理器;
获取接口IWebBrowser2以实现Web浏览器对象;
捕捉Web浏览器的特定事件;
存取当前文档对象,确定它是一份HTML类型的文件;
管理对话框窗口以实现HTML源码的显示;
  第一个步骤是在DllMain()中完成的。SetSite()是取得指向WebBrowser对象指针的适当位置。请详细分析以下步骤。

七、探测谁在调用这个对象
  如前所述,一个BHO对象会被Internet Explorer或者Windows资源管理器(前提:外壳版本4.71或者更高)所加载。所以我专门设计了一个BHO来处理HTML网页,因此这个BHO与资源管理器毫无关系。如果一个Dll不想被调用者一起加载,只需在DllMain()中实现了探明谁在调用该对象后返回FALSE即可。参看下面代码:

if (dwReason == DLL_PROCESS_ATTACH)
{
TCHAR pszLoader[MAX_PATH];

//返回调用者模块的名称,第一个参数应为NULL,详见msdn。
GetModuleFileName(NULL, pszLoader, MAX_PATH);
_tcslwr(pszLoader);
if (_tcsstr(pszLoader, _T("explorer.exe")))
return FALSE;
}
一旦知道了当前进程是Windows资源管理器,可立即退出。
  注意,再多加一些条件语句是危险的!事实上,另外一些进程试图装入该DLL时将被放弃。如果你做另外一个试验,比方说针对Internet Explorer的执行文件iexplorer.exe,这时第一个受害者就是regsvr32.exe(该程序用于自动注册对象)。

if (!_tcsstr(pszLoader, _T("iexplore.exe")))
  你不能够再次注册该DLL库了。 事实上,当 regsvr32.exe 试图装入DLL以激活函数DllRegisterServer()时,该调用将被放弃。

八、与Web浏览器取得联系
  SetSite()方法正是BHO对象被初始化的地方,此外,在这个方法中你可以执行所有的仅仅允许发生一次的任务。当你用Internet Explorer打开一个URL时,你应该等待一系列的事件以确保要求的文档已完全下载并被初始化。唯有在此时,你才可以通过对象模型暴露的接口(如果存在的话)存取文档内容。这就是说你要取得一系列的指针。第一个就是指向IWebBrowser2(该接口用来生成WebBrowser对象)的指针。第二个指针与事件有关。该模块必须作为一个浏览器的事件侦听器来实现,目的是为接收下载以及与文档相关的事件。下面用ATL灵敏指针加以封装:

CComQIPtr< IWebBrowser2, &IID_IWebBrowser2> m_spWebBrowser2;
CComQIPtr<IConnectionPointContainer,
&IID_IConnectionPointContainer> m_spCPC;
源代码部分如下所示:

HRESULT CViewSource::SetSite(IUnknown *pUnkSite)
{
// 检索并存储 IWebBrowser2 指针
m_spWebBrowser2 = pUnkSite;
if (m_spWebBrowser2 == NULL)
return E_INVALIDARG;
//检索并存储 IConnectionPointerContainer指针
m_spCPC = m_spWebBrowser2;
if (m_spCPC == NULL)
return E_POINTER;
//检索并存储浏览器的句柄HWND. 并且安装一个键盘钩子备后用
RetrieveBrowserWindow();
// 为接受事件通知连接到容器
return Connect();
}
  为了取得IWebBrowser2接口指针,你可以进行查询。当然也可以在事件刚刚发生时查询IConnectionPointContainer。这里,SetSite()检索了浏览器的句柄HWND,并且在当前线程中安装了一个键盘钩子。HWND用于后面Internet Explorer窗口的移动或尺寸调整。这里的钩子用来实现热键功能,用户可以按动热键来显示/隐藏代码窗口。

九、从Internet Explorer浏览器取得事件
  当你导向一个新的URL时,浏览器最需要完成的是两种事件:下载文档并为之准备HOST环境。也就是说,它必须初始化某对象并使该对象从外部可以利用。针对不同的文档类型,或者装入一个已注册的Microsoft ActiveX? 服务器来处理该文档(如Word对于.doc文件的处理)或者初始化一些内部组件来分析文档内容并生成和显示该文档。对于HTML网页就是这样,其内容由于DHTML对象作用而变得可用。当文档全部下载结束,DownloadComplete事件被激活。这并不是说,这样利用对象模型就可以安全地管理文档的内容了。事实上,DocumentComplete 事件仅指明一切已经结束,文档已准备好了 (注意DocumentComplete事件仅在你第一次存取URL时到达,如果你执行了刷新动作,你仅仅收到一个DocumentComplete事件)。
  为了截获浏览器发出的事件, BHO需要通过IConnectionPoint 接口连接到浏览器上 并且实现传递接口IDispatch指针以处理各种事件。现在利用前面取得的IConnectionPointContainer指针来调用FindConnectionPoint方法――它返回一个指针指向连接点对象(正是通过这个连接点对象来取得要求的外向接口,此时是DIID_DWebBrowserEvent2)。 下列代码显示了连接点的发生情况:

HRESULT CViewSource::Connect(void)
{
HRESULT hr;
CComPtr<IConnectionPoint> spCP;
//为Web浏览器事件而接收(receive)连接点
hr = m_spCPC->FindConnectionPoint(DIID_DWebBrowserEvent2, &spCP);
if (FAILED(hr))
return hr;
// 把事件处理器传递到容器。每次事件发生容器都将激活我们实现的IDispatch接口上的相应的函数。
hr = spCP->Advise( reinterpret_cast<IDispatch*>(this), &m_dwCookie);
return hr;
}
  通过调用接口IConnectionPoint的Advise() 方法, BHO告诉浏览器它对它产生的事件很感兴趣。 由于COM事件处理机制,所有这些意味着BHO把IDispatch接口指针提供给浏览器。浏览器将回调IDispatch接口的Invoke() 方法,以事件的ID值作为第一参数:

HRESULT CViewSource::Invoke(DISPID dispidMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
VARIANT* pvarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
if (dispidMember == DISPID_DOCUMENTCOMPLETE) {
OnDocumentComplete();
m_bDocumentCompleted = true;
}
:
}
  切记,当事件不再需要时,应该使之与浏览器分离。如果你忘记了做这件事情,BHO对象将被锁定,即使在你关闭浏览器窗口之后。很明显,实现分离的最佳时机是收到事件OnQuit时。

十、存取文档对象
  此时,该BHO已经有一个参照指向Internet Explorer的Web浏览器控件并被连接到浏览器控件以接收所有它产生的事件。当网页被全部下载并正确初始化后,我们就可以通过DHTML文档模型存取它。Web浏览器的文档属性返回一个指向文档对象的IDispatch接口的指针:

CComPtr<IDispatch> pDisp;
HRESULT hr = m_spWebBrowser2->get_Document(&pDisp);
  get_Document() 方法取得的仅仅是一个接口指针。我们要进一步确定在IDispatch 指针背后存在一个HTML文档对象。用VB实现的话,可以用下面代码:

Dim doc As Object
Set doc = WebBrowser1.Document
If TypeName(doc)="HTMLDocument" Then
'' 获取文档内容并予以显示
Else
'' Disable the display dialog
End If
  现在要了解一下get_Document()返回的IDispatch指针 。Internet Explorer不仅仅是一个HTML浏览器,而且还是一个ActiveX文档容器。 这样一来,难以保证当前浏览对象就是一个HTML文档。不过办法还是有的――你想,如果IDispatch指针真正指向一个HTML文档,查询IHTMLDocument2 接口一定成功。
IHTMLDocument2接口包装了DHTML对象模型用来展现HTML页面的所有功能。下面代码实现这些功能:

CComPtr<IDispatch> pDisp;
HRESULT hr = m_spWebBrowser2->get_Document(&pDisp);
CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> spHTML;
spHTML = pDisp;
if (spHTML) {
// 获取文档内容并予以显示
}
else {
// disable the Code Window controls
}
如果IHTMLDocument2接口查询失败,spHTML指针将是NULL。
  现在考虑如何获得当前显示窗口的源代码。正如一个HTML页把它所有的内容封装在标签<BODY>中,DHTML对象模型要求你取得一个指向Body对象的指针:

CComPtr<IHTMLElement> m_pBody;
hr = spHTML->get_body(&m_pBody);
  奇怪的是,DHTML对象模型不让你取得标签<BODY>之前的原始内容,如<HEAD>。其内容被处理并存于一些属性中,但你还是不能从HTML原始文件中提取这部分的RAW文本。这过,仅从BODY部分取得的内容足够了。为了取得包含在<BODY>…</BODY>间的HTML代码部分,可以把outerHTML属性内容读取到一个BSTR变量中:

BSTR bstrHTMLText;
hr = m_pBody->get_outerHTML(&bstrHTMLText);
  在此基础上,在代码窗口中显示源码就是一种简单的事情了:生成一个窗口,进行字符的UNICODE至ANSI转化和设置编辑框控件的问题。下面代码实现这些功能:

HRESULT CViewSource::GetDocumentContent()
{
USES_CONVERSION;

// 获取 WebBrowser的文档对象
CComPtr<IDispatch> pDisp;
HRESULT hr = m_spWebBrowser2->get_Document(&pDisp);
if (FAILED(hr))
return hr;

// 确保我们取得的是一个IHTMLDocument2接口指针
//让我们查询一下 IHTMLDocument2 接口 (使用灵敏指针)
CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> spHTML;
spHTML = pDisp;

// 抽取文档源代码
if (spHTML)
{
// 取得BODY 对象
hr = spHTML->get_body(&m_pBody);
if (FAILED(hr))
return hr;
// 取得HTML 文本
BSTR bstrHTMLText;
hr = m_pBody->get_outerHTML(&bstrHTMLText);
if (FAILED(hr))
return hr;
// 进行文本的Unicode到 ANSI的转换
LPTSTR psz = new TCHAR[SysStringLen(bstrHTMLText)];
lstrcpy(psz, OLE2T(bstrHTMLText));
// 文本进行相应的调整
HWND hwnd = m_dlgCode.GetDlgItem(IDC_TEXT);
EnableWindow(hwnd, true);
hwnd = m_dlgCode.GetDlgItem(IDC_APPLY);
EnableWindow(hwnd, true);

// 设置代码窗口中的文本
m_dlgCode.SetDlgItemText(IDC_TEXT, psz);
delete [] psz;
}
else // 文档不是一个 HTML 页
{
m_dlgCode.SetDlgItemText(IDC_TEXT, "");
HWND hwnd = m_dlgCode.GetDlgItem(IDC_TEXT);
EnableWindow(hwnd, false);
hwnd = m_dlgCode.GetDlgItem(IDC_APPLY);
EnableWindow(hwnd, false);
}

return S_OK;
}
  因为我要运行这段代码来响应DocumentComplete事件通知,每个新的页自动地而且敏捷地被处理。DHTML对象模型使你能够随意修改网页的结构,但这一变化在按F5刷新后全部复原。你还要处理一下DownloadComplete事件以刷新代码窗口 (注意, DownloadComplete 事件发生在 DocumentComplete事件之前)。你应该忽略网页的首次DownloadComplete事件,而是在执行刷新动作时才关注这一事件。布尔成员变量m_bDocumentCompleted正是用来区别这两种情形的。

十一、管理代码窗口
  用来显示当前HTML页原始码的代码窗口涉及另外一个ATL 基本编程问题-对话框窗口,它位于ATL对象向导的"Miscellaneous"选项卡下。
  我调整了代码窗口的大小来响应WM_INITDIALOG消息,使它占居桌面空间的下部区域,正好是在任务栏的上面。在浏览器启动时你可以选择显示或不显示这个窗口。缺省情况下是显示的,但这可以通过清除"Show window at startup"复选框项来实现。当然喜欢的话,你可以随时关闭。按键F12即可重新显示代码窗口。F12是通过在SetSite()中安装的键盘钩子实现的。启动环境存于WINDOWS注册表中,我选择外壳库文件shlwapi.dll中函数SHGetValue来实现注册表的读写操作。这同使用Reg开头的Win32函数操作相比,简单极了。请看:

DWORD dwType, dwVal;
DWORD dwSize = sizeof(DWORD);
SHGetValue(HKEY_CURRENT_USER, _T("Software\\MSDN\\BHO"), _T("ShowWindowAtStartup"), &dwType, &dwVal, &dwSize);
这个DLL文件是同Internet Explorer 4.0 和活动桌面的诞生一起产生的,是WIN98及以后版本的标准组成,你可以放心使用。

十二、注册BHO对象
  因为BHO 是一个COM 服务器,所以既应该作为COM 服务器注册又应该作为BHO对象注册。ATL向导自动生成.rgs文件,第一种情况的注册就免除了。下面的文件代码段是用来实现作为BHO对象注册的(CLSID为例中生成)。

HKLM {
SOFTWARE {
Microsoft {
Windows {
CurrentVersion {
Explorer {
''BHO'' {
ForceRemove {1E1B2879-88FF-11D2-8D96-D7ACAC95951F}
}}}}}}}
  注意ForceRemove一词能够实现在卸载对象时删除这一行相应的键值。BHO键下聚集了所有的BHO对象。对于这么多的一串家伙是从来不作缓冲调用的。这样以来,安装与测试BHO就是不费时的事情了。

十三、总结
  本文描述了BHO对象,通过它你可以把自己的代码注入浏览器的地址空间中。你必须做的事情是写一个支持IObjectWithSite 接口的COM 服务器。在这一点上,你的BHO对象可以实现浏览器机制范围内的各种合法目的。本文所及示例涉及了COM事件,DHTML对象模型以及WEB浏览器编程接口。虽然内容稍宽一些,但它正显示了现实世界中的BHO对象的应用。如,你想知道浏览器在显示什么,那么您就需要了解接收事件并要熟悉WEB浏览器才行。
  另外:Windows资源管理器也是与BHO对象交互的,这一点在编程时要特别注意。本文所附源程序为MSDN所带,在Windows2000/VC6下调试通过(编译通过后,重新启动IE即得到结果)。

该日志标签: 浏览器, ie, 插件, bho

Browser Helper Object,浏览器辅助对象BHO介绍

该日志由 samool 发表于 2007-10-15 10:17:07

BHO(Browser Helper Object,浏览器辅助对象,简称BHO)

    BHO是微软推出的作为浏览器对第三方程序员开放交互接口的业界标准,通过简单的代码就可以进入浏览器领域的“交互接口”(INTERACTIVED Interface)。通过这个接口,程序员可以编写代码获取浏览器的行为,比如“后退”、“前进”、“当前页面”等,利用BHO的交互特性,程序员还可以用代码控制浏览器行业,比如修改替换浏览器工具栏,添加自己的程序按钮等(见图1)。这些在系统看来都是没有问题的。BHO原来的目的是为了更好的帮助程序员打造个性化浏览器,以及为程序提供更简洁的交互功能,现在很多IE个性化工具就是利用BHO的来实现。

    “浏览器劫持”是一种不同于普通病毒木马感染途径的网络攻击手段,而是使用各种技术(如DLL插件等)插件对用户的浏览器进行篡改。安装后,它们会成为浏览器的一部分,可以直接控制浏览器进行指定的操作,根据需要,可以让你打开指定的网站,甚至是收集你系统中的各种私密信息。最可怕的是只有当浏览器已经被劫持了,你才会发现,反应过来,原来电脑已经出现了问题。比如IE主页被改,开机就会弹出广告等等。目前,浏览器劫持已经成为Internet用户最大的威胁之一。其实“浏览器劫持”就是通过BHO的技术手段进入你的系统的,而这种技术是合法的。

    从某种观点看,Internet Explorer同普通的Win32程序没有什么两样。借助于BHO,你可以写一个进程内COM对象,这个对象在每次启动时都要加载。这样的对象会在与浏览器相同的上下文中运行,并能对可用的窗口和模块执行任何行动。例如,一个BHO能够探测到典型的事件,如GoBack、GoForward、DocumentComplete等;另外BHO能够存取浏览器的菜单与工具栏并能做出修改,还能够产生新窗口来显示当前网页的一些额外信息,还能够安装钩子以监控一些消息和动作。
    
    BHO对象依托于浏览器主窗口。实际上,这意味着一旦一个浏览器窗口产生,一个新的BHO对象实例就要生成。任何 BHO对象与浏览器实例的生命周期是一致的。其次, BHO仅存在于Internet Explorer 4.0及以后版本中。
如果你在使用Microsoft Windows? 98, Windows 2000, Windows 95, or Windows NT版本4.0 操作系统的话,也就一块运行了活动桌面外壳4.71,BHO也被 Windows资源管理器所支持。 BHO是一个COM进程内服务,注册于注册表中某一键下。在启动时,Internet Explorer查询那个键并把该键下的所有对象预以加载。

该日志标签: 微软, 浏览器, 插件, bho, 接口

阻断弹出式广告的BHO

该日志由 samool 发表于 2007-10-15 09:36:32

随着网络免费的大潮的退去,网站变得越来越商业化。浏览一些常去的网站,每看一个页面都会弹出N多的广告窗口,而且都是花花绿绿的Flash和Gif小动画,浪费带宽(我在家还是拨号上网),同时干扰了正常的阅读,非常讨厌。那么如何才能将这些广告屏蔽掉呢?答案就是Browser Helper Object(简称BHO)。

BHO实际上也是一个简单的IE扩展COM组件,它和其它COM组件的区别就在于其它扩展需要一些用户的手工操作,如点击菜单,点击工具条按钮,在地址栏输入 网址等等触发动作才会被IE加载。而BHO则不同,每当IE启动时,都会自动去加载BHO而无须任何触发条件,另外BHO还可以监听IE的各类事件的通知消息,比如窗 口大小的变化,下载是否完成等事件。

由于BHO可以在一启动IE就被加载,并能监听各种事件,我们就可以使用BHO扩展实现限制用户浏览某些色情网站,或者搜集用户浏览喜好信息等功能。接下来, 我们就来实现一个能够阻断广告弹出的BHO扩展。

delphi代码
  1. 创建COM组件   
  2.   
  3.        新建一个ActiveX Library,保存为IEBHO.dpr,然后新建一个名为TIEAdvBHO的COM Object,然后保存生成的文件为CIEBHO.pas,作为BHO扩展,需要实现两 个接口IObjectWithSite和IDispatch,其中 IObjectWithSite接口同前面的工具条扩展一样可以用来获得浏览器的接口,而IDispatch接口,则被用来监听浏览器的事件。下面就是BHO扩展的类定义:   
  4.   
  5. type  
  6.   TTIEAdvBHO = class(TComObject, IObjectWithSite, IDispatch)   
  7.   private  
  8.     FIESite: IUnknown;   
  9.     FIE: IWebBrowser2;   
  10.     FCPC: IConnectionPointContainer;   
  11.     FCP: IConnectionPoint;   
  12.     FCookie: Integer;   
  13.   protected  
  14.     //IObjectWithSite接口方法定义  
  15.     function SetSite(const pUnkSite: IUnknown): HResult; stdcall;   
  16.     function GetSite(const riid: TIID; out site: IUnknown): HResult; stdcall;   
  17.     //IDispatch接口方法定义  
  18.     function GetTypeInfoCount(out Count: Integer): HResult; stdcall;   
  19.     function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult;   
  20.       stdcall;   
  21.     function GetIDsOfNames(const IID: TGUID; Names: Pointer;   
  22.       NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;   
  23.     function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;   
  24.       Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;   
  25.       stdcall;   
  26.     //阻断广告弹出事件处理过程  
  27. procedure DoNewWindow2(var ppDisp: IDispatch; var Cancel: WordBool);   
  28.     procedure DoBeforeNavigate2(const pDisp: IDispatch; var URL: OleVariant; var Flags: OleVariant; var TargetFrameName: OleVariant; var PostData: OleVariant;var Headers: OleVariant; var Cancel: WordBool);   
  29. end;   
  30.     
  31.   
  32. IObjectWithSite的接口的实现   
  33.   
  34.     
  35.   
  36. 先看IObjectWithSite的接口的实现,当IE加载BHO扩展后,会调用BHO的扩展,把自身的IUnknown接口作为参数pUnkSite传给扩展,BHO扩展应该从pUnkSite参数中获得浏览器接口IWebBrowser2,同时为了监听浏览器的事件,还需要获得事件链接点接口,IE的支持的事件都定义在DWebBrowserEvents2的双接口中,使用链接点的Advise方法建立对IE事件的监听,注意Advise方法调用后 会返回一个Cookie,需要保存Cookie,后面在退出IE时,需要Cookie作为参数来断开对IE事件的监听。   
  37.   
  38. function TTIEAdvBHO.SetSite(const pUnkSite: IInterface): HResult;   
  39. begin  
  40.   Result := E_FAIL;   
  41.   //保存接口  
  42.   FIESite := pUnkSite;   
  43.   if not Supports(FIESite, IWebBrowser2, FIE) then  
  44. Exit;   
  45.   //获得事件连接点  
  46.   if not Supports(FIE, IConnectionPointContainer, FCPC) then  
  47.     Exit;   
  48.   FCPC.FindConnectionPoint(DWebBrowserEvents2, FCP);   
  49.   //监听事件  
  50.   FCP.Advise(Self, FCookie);   
  51.   Result := S_OK;   
  52. end;   
  53. 后面IE有时会调用IObjectWithSite接口的GetSite方法获得需要的接口,这时可以将保存的接口返回。   
  54.   
  55. function TTIEAdvBHO.GetSite(const riid: TIID;   
  56.   out site: IInterface): HResult;   
  57. begin  
  58.   if Supports(FIESite, riid,site) then  
  59.     Result := S_OK   
  60.   else  
  61.     Result:= E_NOINTERFACE;   
  62. end;   
  63.     
  64.   
  65. IDispatch接口的实现   
  66.   
  67.     
  68.   
  69. 前面我们在SetSite中建立了对IE事件的监听,建立事件监听后每当IE产生了新的事件,它就会调用扩展的IDispatch接口的Invoke方法通知扩展发生的事件类型以及事件参数,并请求扩展对事件进行处理。因此对于BHO扩展来说,IDispatch接口的Invoke方法是必须实现的,而其它的GetTypeInfoCount,GetTypeInfo和GetIDsOfNames方法都无须实现,只要返回结果为E_NOTIMPL,表示未实现该方法就可以了。   
  70.   
  71.     
  72.   
  73. function TTIEAdvBHO.GetIDsOfNames(const IID: TGUID; Names: Pointer;   
  74.   NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;   
  75. begin  
  76.   Result := E_NOTIMPL;   
  77. end;   
  78.     
  79. function TTIEAdvBHO.GetTypeInfo(Index, LocaleID: Integer;   
  80.   out TypeInfo): HResult;   
  81. begin  
  82.   Result := E_NOTIMPL;   
  83.   pointer(TypeInfo) := nil;   
  84. end;   
  85.     
  86. function TTIEAdvBHO.GetTypeInfoCount(out Count: Integer): HResult;   
  87. begin  
  88.   Result := E_NOTIMPL;   
  89.   Count := 0;   
  90. end;   
  91.     
  92.   
  93. 事件的监听   
  94.   
  95.     
  96.   
  97. IE支持的事件都定义在DWebEvents2接口中,如下:   
  98.   
  99.   DWebBrowserEvents2 = dispinterface   
  100.     ['{34A715A0-6587-11D0-924A-0020AFC7AC4D}']   
  101.     procedure StatusTextChange(const Text: WideString); dispid 102;   
  102.     procedure ProgressChange(Progress: Integer; ProgressMax: Integer); dispid 108;   
  103.     procedure CommandStateChange(Command: Integer; Enable: WordBool); dispid 105;   
  104.     procedure DownloadBegin; dispid 106;   
  105.     procedure DownloadComplete; dispid 104;   
  106.     procedure TitleChange(const Text: WideString); dispid 113;   
  107.     procedure PropertyChange(const szProperty: WideString); dispid 112;   
  108. procedure BeforeNavigate2(const pDisp: IDispatch; var URL: OleVariant; var Flags:   
  109.  OleVariant; var TargetFrameName: OleVariant; var PostData: OleVariant;                                  
  110. var Headers: OleVariant; var Cancel: WordBool); dispid 250;   
  111.     procedure NewWindow2(var ppDisp: IDispatch; var Cancel: WordBool); dispid 251;   
  112.     procedure NavigateComplete2(const pDisp: IDispatch; var URL: OleVariant); dispid 252;   
  113.     procedure DocumentComplete(const pDisp: IDispatch; var URL: OleVariant); dispid 259;   
  114.     procedure OnQuit; dispid 253;   
  115.     procedure OnVisible(Visible: WordBool); dispid 254;   
  116.     procedure OnToolBar(ToolBar: WordBool); dispid 255;   
  117.     procedure OnMenuBar(MenuBar: WordBool); dispid 256;   
  118.     procedure OnStatusBar(StatusBar: WordBool); dispid 257;   
  119.     procedure OnFullScreen(FullScreen: WordBool); dispid 258;   
  120.     procedure OnTheaterMode(TheaterMode: WordBool); dispid 260;   
  121.   end;   
  122.     
  123.   
  124. 可以看到每个事件中的后面都有一个dispid关键加上数字如 258 ,260等等。Dispid的数字就是事件类型的标识符号。IDispatch的Invoke方法定义如下:   
  125.     function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;   
  126. Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;   
  127.   
  128. 当IE调用Invoke方法时,会设定DispId参数为事件的标识符号,这样我们就可以知道IE发生了什么事件。对于要实现 的阻断广告窗口弹出来说,我们只需关心BeforeNavigate2和OnQuit事件就可以了,因为当广告窗口弹出前,会激发 IE的BeforeNavigate2事件,而弹出式窗口一般没有工具条,所以只要BeforeNavigate2事件中判断当前页面是否有 工具条就可以判断是否是弹出窗口,并予以禁止。而当IE退出时,会激发OnQuit事件,在OnQuit事件中应该断开事件 监听,同时清理分配的资源。下面就是截获BeforeNavigate2和OnQuit事件的Invoke方法的实现:   
  129.   
  130. procedure BuildPositionalDispIds(pDispIds: PDispIdList; const dps: TDispParams);   
  131. var  
  132.   i: integer;   
  133. begin  
  134.   Assert(pDispIds <> nil);   
  135.   for i := 0 to dps.cArgs - 1 do  
  136.     pDispIds^[i] := dps.cArgs - 1 - i;   
  137.   if (dps.cNamedArgs <= 0then  
  138.     Exit;   
  139.   for i := 0 to dps.cNamedArgs - 1 do  
  140.     pDispIds^[dps.rgdispidNamedArgs^[i]] := i;   
  141. end;   
  142.     
  143.   
  144. function TTIEAdvBHO.Invoke(DispID: Integer; const IID: TGUID;   
  145.   LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,   
  146.   ArgErr: Pointer): HResult;   
  147. var  
  148.   dps: TDispParams absolute Params;   
  149.   bHasParams: boolean;   
  150.   pDispIds: PDispIdList;   
  151.   iDispIdsSize: integer;   
  152. begin  
  153.   pDispIds := nil;   
  154.   iDispIdsSize := 0;   
  155.   bHasParams := (dps.cArgs > 0);   
  156.   if (bHasParams) then  
  157.   begin  
  158.     iDispIdsSize := dps.cArgs * SizeOf(TDispId);   
  159.     GetMem(pDispIds, iDispIdsSize);   
  160.   end;   
  161.   try  
  162.     if (bHasParams) then  
  163.       BuildPositionalDispIds(pDispIds, dps);   
  164.     Result := S_OK;   
  165.     case DispId of  
  166.       250://BeforeNaviage2事件id  
  167.         begin  
  168.           DoBeforeNavigate2(IDispatch(dps.rgvarg^[pDispIds^[0]].dispval),   
  169.               POleVariant(dps.rgvarg^[pDispIds^[1]].pvarval)^,   
  170.               POleVariant(dps.rgvarg^[pDispIds^[2]].pvarval)^,   
  171.               POleVariant(dps.rgvarg^[pDispIds^[3]].pvarval)^,   
  172.               POleVariant(dps.rgvarg^[pDispIds^[4]].pvarval)^,   
  173.               POleVariant(dps.rgvarg^[pDispIds^[5]].pvarval)^,   
  174.               dps.rgvarg^[pDispIds^[6]].pbool^);   
  175.         end;   
  176.       253://OnQuit事件ID  
  177.         begin  
  178.           FCP.Unadvise(FCookie);   
  179.         end;   
  180.     else  
  181.       Result := DISP_E_MEMBERNOTFOUND;   
  182.     end;   
  183.   finally  
  184.     if (bHasParams) then  
  185.       FreeMem(pDispIds, iDispIdsSize);   
  186.   end;   
  187. end;   
  188.     
  189.   
  190. 在Invoke方法中,Params参数包含了被激发的事件包含的参数的数目以及参数的值,而BuildPositionalDispIds 则从Params参数中提取参数值,并放到数组中,然后在BeforeNavigate2事件中,调用DoBeforeNavigate2过程对 事件进行处理,事件参数作为过程参数被传递过去,下面是具体禁止弹出网页的DoBeforeNavigate2的处理过程:   
  191.   
  192. procedure TTIEAdvBHO.DoBeforeNavigate2(const pDisp: IDispatch; var URL,   
  193.   Flags, TargetFrameName, PostData, Headers: OleVariant;   
  194.   var Cancel: WordBool);   
  195. begin  
  196.   if FIE.ToolBar=0 then FIE.Quit;   
  197. end;   
  198.     
  199.   
  200. 在过程中,首先,调用IWebBrowser2接口的Toolbar属性判断页面是否有工具条,如果没有,则调用IE的退出方法关闭弹出窗口。另外在Invoke中还在OnQuit事件激发时,调用事件连接点的UnAdvise方法,断开事件监听。   
  201.   
  202.  注册扩展   
  203.   
  204.  注册扩展非常简单,只要在注册表中关键字HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion   
  205. \explorer\Browser Helper Objects\下添加值为扩展的Guid的字符串形式的下级关键字就可以了。   
  206.   
  207.  type  
  208.   
  209.   TIEAdvBHOFactory = class(TComObjectFactory)   
  210.   public  
  211.     procedure UpdateRegistry(Register: Boolean); override;   
  212.   end;   
  213.     
  214. { TIEAdvBHOFactory }  
  215.     
  216. procedure TIEAdvBHOFactory.UpdateRegistry(Register: Boolean);   
  217. begin  
  218.   inherited;   
  219.   if Register then  
  220.     CreateRegKeyValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objects\'                       + GuidToString(ClassID), '', '') 
  221.   else 
  222.     DeleteRegKeyValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objects\'                       + GuidToString(ClassID), ''); 
  223. end; 
  224.   
  225. initialization 
  226.   TIEAdvBHOFactory.Create(ComServer, TTIEAdvBHO, Class_TIEAdvBHO, 
  227.     'TIEAdvBHO', '', ciMultiInstance, tmApartment);   
  228. end.   
  229.     
  230.   
  231. 注册扩展后,打开浏览器浏览新浪网站(http://www.sina.com.cn),你会发现平时讨厌的弹出广告窗口都消失了。  
  232.   
  233. 文章来源:http://delphi.sharpplus.com/Delphi/bho.htm  

该日志标签: delphi, 浏览器, 插件, bho

Mozilla Firefox 2.0.0.8 RC2 简体中文版

该日志由 samool 发表于 2007-10-10 16:39:43

Mozilla Firefox是一个自由的,开放源码的浏览器,适用于 Windows、Linux 和MacOS X平台,它体积小速度快,还有其它一些高级特征.
主要特性有:标签式浏览,使上网冲浪更快;可以禁止弹出式窗口;自定制工具栏;扩展管理;更好的搜索特性;快速而方便的侧栏.

下载页面:Mozilla Firefox 2.0.0.8 RC2 简体中文版

中文版RC2也出了,估计正式版就在前面了,更新暂时未知

该日志标签: 浏览器, 中文版, firefox