月度归档:2010年05月

TCP 状态机 理解TCP重要的环节

1.问:TCP使用有限长的字段来记录流序号。研究协议规范,找出在两台机器之间允许任意长度流传输的方法

答:目前我只知道使用,时间戳选项来解决这个问题,具体的还不太清楚,有待提高

2.问:TCP有一个选项允许接收方指明它愿意接受的最大报文段长度。在TCP已具有窗口通告机制的情况下,为什么还要支持这个选项来规定最大报文段长度?

答:第一,最大报文段长度规定了最大报文的长度,避免在物理网络上对TCP数据报进行分组,以提高传输效率。

第二,在避免糊涂窗口综合证时还使用了最大报文段长度做为量度。

3.问:在什么样的时延,带宽,负载以及分组丢失率情况下,TCP没有必要重传大量的数据?

答:时延低且稳定,带宽高,负载低,分组丢失率低的情况下(我不太明白这题的意思,可能不太对 🙂 )

4.问:一个丢失的TCP确认并不一定会导致重传,试解释原因。

答:第一种,TCP采用累积确认,如果发送方确认丢失也不会重传。

第二种,两台主机通信,主机1主动关闭发送FIN,主机2 发送确认,但是确认丢失。主机2被动关闭发送FIN,ACK.主机1确定,并进入TIMED_WAIT,这个丢失的确认不用重传,因为主机2发送关闭时,包含有主机1发送的信息,所以已经可以确定收到。

5.问:考虑检验和的计算。假设报文段中的检验和字段并未置零,而检验和计算的结果却是零。你能从中得出什么结论?

答:结论是这次的检验和应该是上一次检验和减1

6.问:如果两个程序使用TCP来发送数据,但每次只发送含一个字符的报文段,那么它们的数据最多有多大的网络带宽利用率?

答:首先是多层封装,TCP封在IP报里,IP报封在帧中。

帧:最少要6octet 源地址,6octet 目的地址,2octet帧类型,和4octet的CRC,一共是18octet (1octet=8bit)

IP数据报与TCP数据报的报头最少都是20byte,两种加起来是40byte,加上18byte+1byte的数据。

1/(40+18+1)=0.016949 约合1.7%

Tomato Dualwan 做 SSH 加密 代理

先来表达一下不满,有的单位的网管就是吃多了。封这个,封那个,没事找事。有本事你就别开外网,开了外网总是有办法穿透的。就像某党弄的网络长城一样,有真正长城那么好用吗????!!!!!!!!

下面是正题。

虽说TT全功能版是有VPN服务器功能的,但是已经被封,用不了,因为端口是不能变的。所以有能用SSH功能了。TT无法在管理WEB中加入多个用户,所以只能到SHELL里操作。

先启动WEB管理中的 系统管理->JFFS2设置 开启 并格式化

再到 系统管理->访问设置 中改掉原来的密码。改成你想新建立的用户使用的密码(因为密码存储是加密的,所以要得到密码加密后的结果)

连接SSH 键入命令 cat /etc/shadow

root:$1$TFGPEA$g/l3FMuNtLsgRjQLOA/:0:0:99999:7:0:0:
admin:$1$TFGPEA$g/l3FMuNtLsgRjQLOA/:0:0:99999:7:0:0:
nobody:*:0:0:99999:7:0:0:
nas:*:0:0:99999:7:0:0:

复制第一行root: 和后边冒号之间的安符串,不包括前后的冒号,这时就是$1$TFGPEA$g/l3FMuNtLsgRjQLOA/
然后到 系统管理->访问设置 把密码再改回原来的

然后复制密码文件到/jffs 目录 cp /etc/shadow /jffs/shadow

编辑密码文件 vi /jffs/shadow 在最下边一行加入ssh:$1$TFGPEA$g/l3FMuNtLsgRjQLOA/:0:0:99999:7:0:0:

ssh后边的冒号之间是刚才记下来的密码.

最后就是到 系统管理->脚本设置->开机时 输入以下内容

sleep 30
echo ssh:x:101: >> /etc/group
echo ssh:x:101:101:ssh:/tmp/home/ssh:/bin/sh >> /etc/passwd
cp /jffs/shadow /etc/shadow
mkdir /tmp/home/ssh
chown ssh:ssh /tmp/home/ssh

基本上就完了,路由开启后30秒会自动建立新用户。

SSH代理的好处是加密,我在这个要代理的朋友那里试过socks代理,结果是用不了,应该进行了包过滤。那可是世界500强的单位设备一定是不俗.http更是不用说了。SSH以出色的加密传输打败了那要死的设备(应该是人)

缺点:看到最后的语句,可能有人会问,组和用户可以用echo来加入,密码为什么要复制文件呢。答案是,我试过了密码用重定向加入到文件中,结果进去一看,是不对的。所以用了这个复制文件的笨方法。所有用户的密码全都在这里。也就是说,你在WEB管理页改了密码,在每次开机30秒,就会变成原来的密码(就是这个文件里写的)

最后到 系统管理->访问设置 中开启SSH 并先取 使用密码登陆 设定远程端口(我用的80,只要能上网的地方,就行)  

linux 命令行 共享 无线网络

这几天改了路由器,前面的贴子里写了。刷了TOMATO DUALWAN的固件,可以双卡合并,但是这个固件不支持中继,我的笔记本还要用无线。怎么能合并无线网和有线网,又能让笔记本上合并后的无线网呢?

答案是,共享一台电脑上的无线网卡,转成有线,插在路由的WAN2上,这样路由的无线就可以AP了。

下面来说说如何在LINUX下共享无线网络。

具体过程
1. 打开无线网卡

ifup wlan0
2. 列出区域内的无线网络

iwlist wlan0 scan
3. 假设要连接到网络MyHome(即essid为MyHome的网络),那么输入命令

iwconfig wlan0 essid "MyHome"
如果网络是加密的,密码是0123456789,那么就输入命令

iwconfig wlan0 essid "MyHome" key 0123456789
4. 如果正常的话,输入

iwconfig wlan0
就可以看到连接正常的各项参数了。

5. 如果是用DHCP获取IP的,那么用dhclient或dhcpcd获取ip

dhclient wlan0

dhcpcd wlan0
6. 现在无线网卡应该可以正常使用了

7.设置转发

#!/bin/sh  
# Name: nat.sh  
# Author: Bixuan  
# Date: 2003/04/04  
  
echo "Your user the iptables firewall.";  
ETH="wlan0"  #外网的网卡
SRC="192.168.3.0/24" #内网的IP段
   
  
case $1 in  
   start)  
       echo 1 > /proc/sys/net/ipv4/ip_forward  
       /sbin/modprobe ip_tables  
       /sbin/modprobe iptable_filter  
        /sbin/modprobe iptable_nat  
        /sbin/modprobe ip_conntrack  
        /sbin/modprobe ip_conntrack_ftp  
        /sbin/modprobe ip_nat_ftp  
        /sbin/iptables -F INPUT  
        /sbin/iptables -F FORWARD  
        /sbin/iptables -F POSTROUTING -t nat  
        /sbin/iptables -P FORWARD DROP  
        /sbin/iptables -t nat -A POSTROUTING -o $ETH -s $SRC -j MASQUERADE  
        /sbin/iptables -A FORWARD -i $ETH -m state –state ESTABLISHED,RELATED -j ACCEPT  
        /sbin/iptables -A FORWARD -s $SRC -j ACCEPT  
        echo "Nat is strating …… [ OK ]"  
        ;;  
   stop)  
echo 0 > /proc/sys/net/ipv4/ip_forward  
       ;;  
    *)  
        echo $"Usage: $0 {start|stop}";  
esac
 

我以写成脚本nat

浏览器 定制 扩展 (转)

前言

  由于本人在开发中经常要在程序中嵌入浏览器,为了符合自己的需求经常要对浏览器进行扩展和定制, 解决这些问题需在网上找资料和学习的过程,我想可能很多开发者或许会遇到同样的问题,特写此文,以供大家参考。

在MFC中使用浏览器

  在MFC中微软为我们提供了CHtmlView、CDHtmlDialog类让我们的程序很方便的嵌入浏览器和进行浏览器的二次开发,这比直 接使用WebBrowser控件要方便很多,所以本文中讨论的浏览器的问题都是针对CHtmlView来讨论的。文中将提到一个类CLhpHtmlView, 它是CHtmlView的派生类,文中提及的扩展或定制都将在CLhpHtmlView类(或派生类)上实现。

怎样扩展或定制浏览器

  浏览器定义了一些扩展接口(如IDocHostUIHandler可以定制浏览器界面有关的行为),以便开发者进行定制和扩展。浏览 器会在需要的时候向他的控制站点查询这些接口,在控制站点里实现相应的接口就可以进行相应的扩展。在MFC7.01类 库中,CHtmlView使用的控制站点是CHtmlControlSite的,在CHtmlControlSite类中 只实现了接口IDocHostUIHandler, 而要实现更多的扩展接口,必须用自定义的控制站类来取代CHtmlControlSite,在下文中提及的类CDocHostSite即为自定义 的控制站类。

关于接口的介绍请参考:

http://dev.csdn.net/develop/article/48/48483.shtm 

如何使自定义的控制站点来替换默认的控制站点呢?在MFC7.0中只需重载CHtmlView的虚函数CreateControlSite即可:
 

BOOL CLhpHtmlView::CreateControlSite(COleControlContainer * pContainer, 
COleControlSite ** ppSite, UINT /*nID*/, REFCLSID /*clsid*/)
{
	*ppSite = new CDocHostSite(pContainer, this);// 创建自己的控制站点实例
	return (*ppSite) ? TRUE : FALSE;
}

VC6.0要替换控制站要复杂的多,这里就不讨论了,如需要6.0版本的请给我发邮件到yourshine@21cn.com。

定制鼠标右键弹出出菜单

  要定制浏览器的鼠标右键弹出菜单,必须在自定义的控制站点类中实现IDocHostUIHandler2接口,并且IE的 版本是5.5或以上。在接口IDocHostUIHandler2的ShowContextMenu方法中调用浏览器类的OnShowContextMenu虚函数,我们 在浏览器类的派生类重载此虚函数即可实现右键菜单的定制,参见代码
 

HRESULT CDocHostSite::XDocHostUIHandler::ShowContextMenu(DWORD dwID,
                                                         POINT * ppt,
                                                         IUnknown * pcmdtReserved,
                                                         IDispatch * pdispReserved)
{
	METHOD_PROLOGUE(CDocHostSite, DocHostUIHandler);
	return pThis->m_pView->OnShowContextMenu( dwID, ppt, pcmdtReserved,pdispReserved );
}

HRESULT CLhpHtmlView::OnShowContextMenu(DWORD dwID, 
                                        LPPOINT ppt,
                                        LPUNKNOWN pcmdtReserved, 
                                        LPDISPATCH pdispReserved)
{
	HRESULT result = S_FALSE;

	switch(m_ContextMenuMode)
	{
	case NoContextMenu:			// 无菜单
		result=S_OK;
		break;
	case DefaultMenu:				// 默认菜单
		break;
	case TextSelectionOnly:			// 仅文本选择菜单
		if(!(dwID == CONTEXT_MENU_TEXTSELECT || dwID == CONTEXT_MENU_CONTROL))
			result=S_OK;
		break;
	case CustomMenu:				// 自定义菜单
		if(dwID!=CONTEXT_MENU_TEXTSELECT)
			result=OnShowCustomContextMenu(ppt,pcmdtReserved,pdispReserved);
		break;
	}

	return result;
}		

在CLhpHtmlView中定义的枚举类型CONTEXT_MENU_MODE举出了定制右键弹出菜单的四种类型
 

enum CONTEXT_MENU_MODE		// 上下文菜单
{
	NoContextMenu,		// 无菜单
	DefaultMenu,		// 默认菜单
	TextSelectionOnly,		// 仅文本选择菜单
	CustomMenu		// 自定义菜单
};

  通过CLhpHtmlView的函数SetContextMenuMode来设置右键菜单的类型。如果设定的右键弹出菜单是“自定义菜单”类型, 我们只要在CLhpHtmlView的派生类中重载OnShowCustomContextMenu虚函数即可,如下代码 CDemoView是CLhpHtmlView的派生类

HRESULT CDemoView::OnShowCustomContextMenu(LPPOINT ppt, LPUNKNOWN pcmdtReserved,LPDISPATCH pdispReserved)
{
	if ((ppt==NULL)||(pcmdtReserved==NULL)||(pcmdtReserved==NULL))
		return S_OK;

	HRESULT hr=0;
	IOleWindow *oleWnd=NULL;
    	hr=pcmdtReserved->QueryInterface(IID_IOleWindow, (void**)&oleWnd);
	if((hr != S_OK)||(oleWnd == NULL))
		return S_OK;

	HWND hwnd=NULL;
	hr=oleWnd->GetWindow(&hwnd);
	if((hr!=S_OK)||(hwnd==NULL))
	{
		oleWnd->Release();
		return S_OK;
	}

	IHTMLElementPtr	pElem=NULL;
	hr = pdispReserved->QueryInterface(IID_IHTMLElement, (void**)&pElem);
	if(hr != S_OK)
	{
		oleWnd->Release();
		return S_OK;
	}

	IHTMLElementPtr	pParentElem=NULL;

	_bstr_t	tagID;
	BOOL go=TRUE;

	pElem->get_id(&tagID.GetBSTR());
	
	while(go && tagID.length()==0)
	{
		hr=pElem->get_parentElement(&pParentElem);
		if(hr==S_OK && pParentElem!=NULL)
		{
			pElem->Release();
			pElem=pParentElem;
			pElem->get_id(&tagID.GetBSTR());
		}
		else
			go=FALSE;
	};
	if(tagID.length()==0)
		tagID="no id";

	CMenu Menu,SubMenu;
	Menu.CreatePopupMenu();
	
	CString strTagID = ToStr(tagID);

	if(strTagID == "red")
		Menu.AppendMenu(MF_BYPOSITION, ID_RED, "您点击的是红色");
	else if(strTagID == "green")
		Menu.AppendMenu(MF_BYPOSITION, ID_GREEN, "您点击的是绿色");
	else if(strTagID == "blue")
		Menu.AppendMenu(MF_BYPOSITION, ID_BLUE, "您点击的是蓝色");
	else
		Menu.AppendMenu(MF_BYPOSITION, ID_NONE, "你点了也白点,请在指定的地方点击");
		
	int MenuID=Menu.TrackPopupMenu(TPM_RETURNCMD|TPM_LEFTALIGN|TPM_RIGHTBUTTON,ppt->x, ppt->y, this);
	switch(MenuID)
	{
	case ID_RED:
		MessageBox("红色");
		break;
	case ID_GREEN:
		MessageBox("红色");
		break;
	case ID_BLUE:
		MessageBox("红色");
		break;
	case ID_NONE:
		MessageBox("haha");
		break;
	}

	oleWnd->Release();
	pElem->Release();

	return S_OK;
}		


实现脚本扩展(很重要的external接口)

  在你嵌入了浏览器的工程中,如果网页的脚本中能调用C++代码,那将是一件很惬意的事情,要实现这种交互,就必须实现脚本扩展。实现脚本扩展就是在程序中实现一个IDispatch接口,通过CHtmlView类的OnGetExternal虚函数返回此接口指针,这样就可以在脚本中通过window.external.XXX(关键字window可以省略)来 引用接口暴露的方法或属性(XXX为方法或属性名)。在MFC中从CCmdTarget派生的类都可以实现自动化,而不必在MFC工程中引入繁杂的ATL。从CCmdTarget派生的类实现自动化接口的时候不要忘了在构造函数中调用EnableAutomation函数。
  要使虚函数OnGetExternal发挥作用必须在 自定义的控制站点类中实现IDocHostUIHandler,在接口IDocHostUIHandler的GetExternal方法中调用浏览器类的OnGetExternal虚函数,我们在浏览器类的派生类重载OnGetExternal虚函数, 通过参数lppDispatch返回一个IDispatch指针,这样脚本中引用window.external时就是引用的返回的接口,参见代码

HRESULT CDocHostSite::XDocHostUIHandler::GetExternal(IDispatch ** ppDispatch)
{
	METHOD_PROLOGUE(CDocHostSite, DocHostUIHandler);
	return pThis->m_pView->OnGetExternal( ppDispatch );
}

CLhpHtmlView::CLhpHtmlView(BOOL isview)
{
	......
	EnableAutomation();// 允许自动化
}

HRESULT CLhpHtmlView::OnGetExternal(LPDISPATCH *lppDispatch)
{
	*lppDispatch = GetIDispatch(TRUE);// 返回自身的IDispatch接口
	return S_OK;
}      

请注意上面代码中,在OnGetExternal返回的是自身IDispatch接口, 这样就不比为脚本扩展而另外写一个从CCmdTarget派生的新类, CLhpHtmlView本身就是从CCmdTarget派生,直接在上面实现接口就是。

下用具体示例来说明怎样实现脚本扩展

示例会在网页上点击一个按钮而使整个窗口发生抖动

从CLhpHtmlView派生一个类CDemoView,在类中实现IDispatch, 并通过IDispatch暴露方法WobbleWnd

---------------------------------------------------------------------------
文件 DemoView.h
---------------------------------------------------------------------------
.......
class CDemoView : public CLhpHtmlView
{
	......
	DECLARE_DISPATCH_MAP() // 构建dispatch映射表以暴露方法或属性
	......
	void WobbleWnd();// 抖动窗口
};

---------------------------------------------------------------------------
文件 DemoView.cpp
---------------------------------------------------------------------------

......

// 把成员函数映射到Dispatch映射表中,暴露方法给脚本
BEGIN_DISPATCH_MAP(CDemoView, CLhpHtmlView)
	DISP_FUNCTION(CDemoView, "WobbleWnd", WobbleWnd, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()

......

void CDemoView::WobbleWnd()
{
	// 在这里实现抖动窗口
	......
}

---------------------------------------------------------------------------
文件 Demo.htm
---------------------------------------------------------------------------

...... onclick="external.WobbleWnd()" ......		

这里我要介绍一下DISP_FUNCTION宏,它的作用是将一个函数映射到Dispatch映射表中,我们看

DISP_FUNCTION(CDemoView, "WobbleWnd", WobbleWnd, VT_EMPTY, VTS_NONE)

CDemoView是宿主类名, "WobbleWnd"是暴露给外面的名字(脚本调用时使用的名字), VT_EMPTY是返回值得类型为空,VTS_NONE说明此方法没有参数,如果要映射的函数有返回值和参数该 如何映射,通过下面举例来说明
 

DISP_FUNCTION(CCalendarView,"TestFunc",TestFunc,VT_BOOL,VTS_BSTR VTS_I4 VTS_I4)

BOOL TestFunc(LPCSTR param1, int param2, int param3)
{
	.....
}		

  参数表VTS_BSTR VTS_I4 VTS_I4是用空格分隔,他们的类型映射请参考MSDN,这要提醒的是不要把VTS_BSTR与CString对应,而应与LPCSTR对应。

C++代码中如何调用网页脚本中的函数

  IHTMLDocument2::scripts属性表示HTML文档中所有脚本对象。使用脚本对象的IDispatch接口的GetIDsOfNames方法可以得到脚本函数的 DispID,得到DispID后,使用IDispatch的Invoke函数可以调用对应的脚本函数。CLhpHtmlView提供了方便的调用JavaScript的函数,请参考CLhpHtmlView中有关键字“JScript”的代码。

定制消息框的标题

  我们在脚本中调用alert弹出消息框时,消息框的标题是微软预定义的“Microsoft Internet Explorer”,如下图:

  在自定义的控制站点类中实现IDocHostShowUI接口,在接口的ShowMessage方法中调用浏览器的OnShowMessage,我们重载 OnShowMessage虚函数即可定制消息框的标题,实现代码如下:
 

// 窗口标题"Microsoft Internet Explorer"的资源标识
#define IDS_MESSAGE_BOX_TITLE 2213
HRESULT CLhpHtmlView::OnShowMessage(HWND hwnd,
                                    LPOLESTR lpstrText,
                                    LPOLESTR lpstrCaption,
                                    DWORD dwType,
                                    LPOLESTR lpstrHelpFile,
                                    DWORD dwHelpContext,
                                    LRESULT * plResult)
{
    //载入Shdoclc.dll 和IE消息框标题字符串
    HINSTANCE hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL"));
    if (hinstSHDOCLC == NULL)
        return S_FALSE;

	CString strBuf,strCaption(lpstrCaption);
	strBuf.LoadString(hinstSHDOCLC, IDS_MESSAGE_BOX_TITLE);

    // 比较IE消息框标题字符串和lpstrCaption
    // 如果相同,用自定义标题替换
	if(strBuf==lpstrCaption)
        strCaption = m_DefaultMsgBoxTitle;

    // 创建自己的消息框并且显示
    *plResult = MessageBox(CString(lpstrText), strCaption, dwType);

    //卸载Shdoclc.dll并且返回
    FreeLibrary(hinstSHDOCLC);
    return S_OK;
}		

从代码中可以看到通过设定m_DefaultMsgBoxTitle的值来改变消息宽的标题,修改此值是同过SetDefaultMsgBoxTitle来实现

void CLhpHtmlView::SetDefaultMsgBoxTitle(CString strTitle)
{
	m_DefaultMsgBoxTitle=strTitle;
}


怎样定制、修改浏览器向Web服务器发送的HTTP请求头

  在集成了WebBrowser控件的应用中,Web服务器有时可能希望客户端(浏览器)发送的HTTP请求中附带一些额外的信息或自定义的 HTTP头字段,这样就必须在浏览器中控制向Web服务器发送的HTTP请求。 下面是捕获的一个普通的用浏览器发送的HTTP请求头:

GET /text7.htm HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, 
application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://localhost
Accept-Language: en-us
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Poco 0.31; LHP Browser 1.01; 
.NET CLR 1.1.4322)
Host: localhost
Connection: Keep-Alive

CHtmlView的 
void Navigate2(
   LPCTSTR lpszURL,
   DWORD dwFlags = 0,
   LPCTSTR lpszTargetFrameName = NULL,
   LPCTSTR lpszHeaders = NULL,
   LPVOID lpvPostData = NULL,
   DWORD dwPostDataLen = 0 
);

函数参数lpszHeaders可以指定HTTP请求头,示例如下:

Navigate2(_T("http://localhost"),NULL,NULL, "MyDefineField: TestValue");

我们捕获的HTTP头如下:

怎样修改浏览器标识

  在HTTP请求头中User-Agent字段表明了浏览器的版本以及操作系统的版本等信息。WEB服务器经常需要知道用户请求页面时是来自IE还是来自自己的客户端中的WebBrowser控件, 以便分开处理,而WebBrowser控件向WEB服务器发送的浏览器标识(User-Agent字段)跟用IE发送的是一样的,怎样区分自己的浏览器和IE呢? 微软没有提供现成的方法,要自己想法解决。 前面讨论的定制HTTP请求头就是为这一节准备的。 思路是这样的: 在自己的浏览器里处理每一个U页面请求,把请求头User-Agent改成自己想要的。 在CHtmlView的OnBeforeNavigate2虚函数里来修改HTTP请求是再好不过了,

#define WM_NVTO		(WM_USER+1000)

class NvToParam
{
public:
	CString URL;
	DWORD Flags;
	CString TargetFrameName;
	CByteArray PostedData;
	CString Headers;
};


void CDemoView::OnBeforeNavigate2(LPCTSTR lpszURL, 
                                  DWORD nFlags, 
                                  LPCTSTR lpszTargetFrameName, 
                                  CByteArray& baPostedData, 
                                  LPCTSTR lpszHeaders, 
                                  BOOL* pbCancel)
{
	CString strHeaders(lpszHeaders);
	if(strHeaders.Find("User-Agent:LHPBrowser 1.0") < 0)// 检查头里有没有自定义的User-Agent串
	{
		*pbCancel = TRUE;// 没有,取消这次导航

		if(!strHeaders.IsEmpty())
			strHeaders += "rn";
		strHeaders += "User-Agent:LHPBrowser 1.0";// 加上自定义的User-Agent串

		NvToParam* pNvTo = new NvToParam;
		pNvTo->URL = lpszURL;
		pNvTo->Flags = nFlags;
		pNvTo->TargetFrameName = lpszTargetFrameName;
		baPostedData.Copy(pNvTo->PostedData);
		pNvTo->Headers = strHeaders;

		// 发送一个自定义的导航消息,并把参数发过去
		PostMessage(WM_NVTO,(WPARAM)pNvTo);
		return;
	}

	CHtmlView::OnBeforeNavigate2(lpszURL, 
	                             nFlags, 
	                             lpszTargetFrameName, 
	                             baPostedData, 
	                             lpszHeaders, 
	                             pbCancel);
}

LRESULT CDemoView::OnNvTo(WPARAM wParam, LPARAM lParam)
{
	NvToParam* pNvTo = (NvToParam*)wParam;
	Navigate2((LPCTSTR)pNvTo->URL, 
	           pNvTo->Flags, 
	           pNvTo->PostedData, 
	           (LPCTSTR)pNvTo->TargetFrameName, 
	           (LPCTSTR)pNvTo->Headers);
	delete pNvTo;
	return 1;
}

  在OnBeforeNavigate2中如果发现没有自定义的User-Agent串,就加上这个串,并取消本次导航,再Post一个消息(一定要POST,让OnBeforeNavigate2跳出以后再进行导航 ),在消息中再次导航,再次导航时请求头已经有了自己的标识,所以能正常的导航。

去掉讨厌的异常警告

  在程序中使用了CHtmlView以后,我们在调整窗口大小的时候经常会看到输出窗口输出的异常警告: ReusingBrowser.exe 中的 0x77e53887 处最可能的异常: Microsoft C++ exception: COleException @ 0x0012e348 。

Warning: constructing COleException, scode = DISP_E_MEMBERNOTFOUND($80020003).

这是由于CHtmlView在处理WM_SIZE消息时的一点小问题引起的,采用如下代码处理WM_SIZE消息就不会有此警告了

void CLhpHtmlView::OnSize(UINT nType, int cx, int cy)
{
	CFormView::OnSize(nType, cx, cy);

	if (::IsWindow(m_wndBrowser.m_hWnd)) 
	{ 
		CRect rect; 
		GetClientRect(rect); 
		// 就这一句与CHtmlView的不同
		::AdjustWindowRectEx(rect, GetStyle(), FALSE, WS_EX_CLIENTEDGE);
		m_wndBrowser.SetWindowPos(NULL, 
		                          rect.left, 
		                          rect.top, 
		                          rect.Width(), 
		                          rect.Height(), 
		                          SWP_NOACTIVATE | SWP_NOZORDER); 
	} 
}


怎样处理浏览器内的拖放

  有时可能有这样的需求,我们希望在资源管理器里托一个文件到浏览器而做出相应的处理,甚至是将文件拖到某一个网页元素上来做出相应的处理,而浏览器默认的处理拖放文件操作是将文件打开,但WebBrowser控件给了我们一个自己处理拖放的机会。 那就是在自定义的控制站点类中实现IDocHostUIHandler,在接口IDocHostUIHandler的GetDropTarget方法中调用 浏览器类的OnGetDropTarget虚函数。要处理网页内的拖放,必需在OnGetDropTarget函数中返回一个自己定义的IDropTarget接口指针, 所以我们自己写一个类CMyOleDropTarget从COleDropTarget类派生,并且在实现IDropTarget接口,此类的代码在这就不列出了,请下载演示 程序,参考文件MyOleDropTarget.h和MyOleDropTarget.cpp。我们看CLhpHtmlView中OnGetDropTarget的代码

HRESULT CLhpHtmlView::OnGetDropTarget(LPDROPTARGET pDropTarget, LPDROPTARGET* ppDropTarget )
{
	m_DropTarget.SetIEDropTarget(pDropTarget);

	LPDROPTARGET pMyDropTarget;
	pMyDropTarget = (LPDROPTARGET)m_DropTarget.GetInterface(&IID_IDropTarget);
	if(pMyDropTarget)
	{
		*ppDropTarget = pMyDropTarget;
		pMyDropTarget->AddRef();
		return S_OK;
	}

	return S_FALSE;
}

  m_DropTarget即为自定义的处理拖放的对象。这样就能通过在从CLhpHtmlView派生的类中重载OnDragEnter、OnDragOver、 OnDrop、OnDragLeave虚函数来处理拖放了。在这里顺带讲一下视图是怎样处理拖放的。 要使视图处理拖放,首先在视图里添加一个COleDropTarget(或派生类)成员变量,如CLhpHtmlView中的“CMyOleDropTarget m_DropTarget;”,再在 视图创建时调用COleDropTarget对象的Register,即把视图与COleDropTarget对象关联起来,如CLhpHtmlView中的“m_DropTarget.Register(this);”,再对拖放 触发的事件进行相应的处理, OnDragEnter 把某对象拖入到视图时触发,在此检测拖入的对象是不是视图想接受的对象,如是返回“DROPEFFECT_MOVE”表示接受此对象,如

if(pDataObject->IsDataAvailable(CF_HDROP))// 被拖对象是文件吗?
	return DROPEFFECT_MOVE;

  OnDragOver 被拖对象在视图上移动,同OnDragEnter一样检测拖入对象,如果要接受此对象返回“DROPEFFECT_MOVE”。 OnDrop 拖着被拖对象在视图上放开鼠标,在这里对拖入对象做出处理; OnDragLeave 拖着被拖对象离开视图。 C++的代码写好了,但事情还没完,还必须在网页里用脚本对拖放事件进行处理, 即页面里哪个元素要接受拖放对象哪个元素就要处理ondragenter、ondragover、ondrop,代码其实很简单,让事件的返回值为false即可,这样 C++的代码才有机会处理拖放事件,代码如下:

......
<td ondragenter="event.returnValue = false" ondragover="event.returnValue = false" 
ondrop="event.returnValue = false">
......

  如果要使整个视图都接受拖放,则在Body元素中处理此三个事件。 注意:别忘了让工程对OLE的支持即在初始化应用程序时调用AfxOleInit()。

怎样禁止网页元素的选取

  用网页做界面时多数情况下是不希望网页上的元素是能够被鼠标选中的, 要使网页元素不能被选中做法是:给浏览器的“宿主信息标记”加上DOCHOSTUIFLAG_DIALOG标记。

“宿主信息标记”用N个标记位来控制浏览器的许多性质,如:

  • 禁用浏览器的3D的边缘;
  • 禁止滚动条;
  • 禁用脚本;
  • 定义双击处理的方式;
  • 禁用浏览器的自动完成功能;

…… 更多详情请参考MSDN的DOCHOSTUIFLAG帮助。

怎样修改“宿主信息标记”?

在CDocHostSite中实现IDocHostUIHandler, 在GetHostInfo方法中调用浏览器的OnGetHostInfo虚函数,在虚函数OnGetHostInfo中便可指定“宿主信息标记”,如:

HRESULT CLhpHtmlView::OnGetHostInfo(DOCHOSTUIINFO * pInfo)
{
	pInfo->cbSize = sizeof(DOCHOSTUIINFO);
	pInfo->dwFlags = DOCHOSTUIFLAG_DIALOG | 
	                    DOCHOSTUIFLAG_THEME  | 
	                    DOCHOSTUIFLAG_NO3DBORDER | 
	                    DOCHOSTUIFLAG_SCROLL_NO;
	pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;

	return S_OK;
}

用脚本也可实现: 在Head中加入脚本:

document.onselectstart=new Function(''return false'');

或者

<body onselectstart="return false">。


其它

在CLhpHtmlView中还提供了几个函数, 修改网页元素的内容:

BOOL PutElementHtml(CString ElemID,CString Html);

取表单元素的值:

BOOL GetElementValue(CString ElemID,CString& Value);

设置表单元素的值:

BOOL PutElementValue(CString ElemID,CString Value);

给表单元素设置焦点:

void ElementSetFocus(CString EleName);

代码下载:reusingbrowser

修改浏览器信息 USER AGENT

测试页面http://www.kumouse.com/ietest.php

先来看看我的

GATEWAY_INTERFACE: CGI/1.1
HTTP_ACCEPT_LANGUAGE: zh-cn
HTTP_USER_AGENT: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
REMOTE_ADDR: 114.252.140.239
REMOTE_PORT: 1265

—————- User-Agents列表—————–

Internet Explorer 6
Internet Explorer 7
Internet Explorer 8
Firefox
Chrome
Safari
Netscape
Opera
Maxthon
360SE
OmniWeb

更多的User-Agents信息尽在useragents.xml

有了这个文件中的信息,什么样的浏览器和操作系系统都能伪装出来。

使用User-Agents的方法:

FireFox:[推荐使用User-Agent switcher 这个火狐扩展]
地址栏键入:about:config 回车
设置:general.useragent.override–>自定义的 UA 信息 。

Chrome
启动EXE图标属性中加上启动参数:–user-agent=”UA信息” 即可。

Safari
菜单栏->Edit->Preferences->Advanced->Show Develop menu in menu bar;
菜单栏->Develop->User-Agent->UA信息。

Maxthon
工具栏->工具->遨游设置中心->高级选项->自定义UA信息。

Opera
地址栏键入:opera:config 回车
设置:User-Agent->自定义ID 0—5
注: [0 Default 1 Opera 2 Mozilla, Opera detectable 3 Internet Explorer, Opera detectable 4 Mozilla, Opera hidden 5 Internet Explorer, Opera hidden ]

iPhone
替换 /System/Library/Frameworks/WebKit.framework/WebKit 这个文件的 Mozilla/5.0字符串 为自定义UA信息

IE浏览器的设置需要修改注册表

User-Agent在注册表的位置
[HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionInternet SettingsUser-AgentPost Platform] 下,新建字符串值,内容留空,名字为你想在 User-Agent 中加入的字符,这里以 “myok″为例。修改注册表对应的 REG 文件如下:
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionInternet SettingsUser-AgentPost Platform]
"myok"=”"

修改后重启IE浏览器即可。

IE的user-agent取值,下面是下列地址的组合+一些默认值
HKCUSoftwareMicrosoftWindowsCurrentVersionInternet SettingsUser-AgentPost Platform
HKLMSoftwareMicrosoftWindowsCurrentVersionInternet SettingsUser-AgentPost Platform
HKLMSoftwareMicrosoftWindowsCurrentVersionInternet Settings5.0User-AgentPost Platform

IE8的User-Agents:

IE8 on Windows Vista (兼容浏览)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0)

IE8 on Windows Vista
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)

IE8 on Windows 7
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)

64-bit IE on 64-bit Windows 7:
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Win64; x64; Trident/4.0)

32-bit IE on 64-bit Windows 7:
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0)

通过修改User-Agent,你还可以完美的伪装:操作系统、浏览器、蜘蛛、邮件客户端、链接检查、分析器、RSS 阅读器等客户端的信息。