蛙蛙推薦:蛙蛙牌firefox外掛
上次給大家演示了抓取IE核心瀏覽器的網頁,這次和大家一起寫一個抓取firefox頁面文字的例子。Firefox有自身的一套結構良好的外掛機制,但要深入開發Firefox外掛的話要了解javascript,XUL,XPCOM以及一些c++的基本常識,本貼算是給想開發Firefox外掛的朋友入門。
我們先來了解什麼是XPCOM,並開發一個
XPCOM(Cross Platform Component Object Model)是一種跨平臺元件模型,是Mozilla技術的核心。其原理與微軟的COM技術類似,但與COM不同的是,XPCOM是一種跨平臺技術,並支援多種語言繫結(Language Bindings)。也就是說,我們可以使用C++、JAVA、JavaScript、Python、Ruby、Perl等語言來編寫元件。而XPCOM的介面(Interface)是用一種叫做XPIDL的IDL(Interface Description Language)來定義的。
XPCOM 自身提供了一套核心的元件和類,用於諸如記憶體管理,執行緒,基本資料結構(strings, arrays, variants)等 。但是大部分的XPCOM元件並不是這個核心庫提供的,而是由很多第三方的平臺(例如Gecko或者Necko)提供,或者由一個應用,甚至一個擴充套件提供。
要想了解如何搭建MOZILLA開發環境,可以參考如下連結
Windows Build Prerequisites
我們下載MozillaBuild和Gecko SDK 1.8
先安裝MozillaBuild到D:/mozilla-build,後安裝Gecko SDK 1.8到D:/dev/gecko-sdk,雖然整個過程沒有用到MozillaBuild,但它是必須要安裝的,否則後面執行regxpcom會出好多莫名其妙的錯,(光解決這個問題就我就折騰了好幾個晚上,希望大家少走彎路,只下載wintools.zip和gecko sdk就是不行,就得裝MozillaBuild,這是先決條件。
建立XPCOM元件要先定義一個介面檔案,新建IWawaComp.idl,用記事本開啟,輸入如下內容
[scriptable, uuid(b7b04070-45fc-4635-b219-7a172f806bee)]
interface IWawaComp : nsISupports
{
void SendSysMessage(instring str);
};
這是一個很簡單的介面,只有一個方法SendSysMessage,接受一個string型別的引數,string是gecko的型別,不支援中文,要想用中文,可以用xpcom idl中的nsAString(在idl檔案裡要用AString表示,用nsAString會出錯), wstring等支援unicode的字串型別。因為我最終也沒辦法在js下把中文傳給XPCOM,所以這裡最終還是決定用string型別。b7b04070-45fc-4635-b219-7a172f806bee是一個GUID型別,可以用ultraedit或者vs.net自帶的功能生成一個。所有的介面必須繼承自nsISupports,因為要使用string型別,所以要包含以下nsISupports.idl檔案,好多基本型別都在這裡面定義。如果要用wstring或AString型別,要包含以下nsrootidl.idl檔案。其它的地方都是固定的,你可以自己加一些介面方法或者修改什麼的,這就是一個模板。
中文問題:
光解決中文的問題,我花了週末兩天和兩個晚上,最後才得到一個不太完美的方案,把過程分享一下。
第一種思路是用scriptableunicodeconverter的ConvertFromUnicode方法把中文轉換成unicode bytes,然後用window.btoa把unicode bytes轉換成base64字串,然後傳給XPCOM,這樣XPCOM的介面只用string型別就行,在XPCOM裡再進行base64解碼,再用MultiByteToWideChar來轉換成wchar_t等寬字元型別。可惜scriptableunicodeconverter轉換的bytes用window.btoa編碼後的base64根本就無法再原樣轉回去,弄了好久,死活不行,可能gecko的這個方法對中文支援就是不行。所以這種思路最後沒戲。
第一種思路的程式碼如下
{
try
{
const cc = Components.classes;
const ci = Components.interfaces;
const unicodeConverter = cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(ci.nsIScriptableUnicodeConverter);
unicodeConverter.charset = charset;
content = unicodeConverter.ConvertFromUnicode(content);
return content;
}
catch (err)
{
alert(err);
}
}
var data = unicodeToBytes("中文","utf-8");
alert(window.atob(window.btoa(data)));
彈出來的是亂碼,不知道咋解決,直接window.btoa("中文")也會出錯
第二種思路是把介面方法的引數定義成AString,然後在XPCOM的實現方法裡用NS_StringGetData和WideCharToMultiByte來轉換,結果NS_StringGetData在gecko 1.8下編譯連結時老提示無法解析的外部符號,在gecko1.7下倒是能編譯,可是無法註冊在firefox 2.0下,最後也實在沒辦法,不了了只。關於這種思路來源於以下連結http://rogerfd.cn/?p=165
相關討論如下
關於XPCOM開發的問題
然後就要根據介面定義檔案生成標頭檔案和型別庫檔案(.xpt),先把D:/mozilla-build/moztools-180compat/bin目錄下的libIDL-0.6.dll,glib-1.2.dll拷貝到D:/dev/gecko-sdk/bin目錄下,然後執行如下命令
D:/dev/gecko-sdk/bin/xpidl -m header -I ../idl IWawaComp.idl
D:/dev/gecko-sdk/bin/xpidl -m typelib -I ../idl IWawaComp.idl
第一行生成標頭檔案,第二行生成型別庫檔案。如果執行的時候出錯了,有可能是沒安裝mozillabuild,還不行的話就把geckosdk的那幾個bin,lib,include等目錄加入到系統環境變數PATH下。
開啟標頭檔案,大概如下
* DO NOT EDIT. THIS FILE IS GENERATED FROM IWawaComp.idl
*/
#ifndef __gen_IWawaComp_h__
#define __gen_IWawaComp_h__
#ifndef __gen_nsISupports_h__
#include "nsISupports.h"#endif/* For IDL files that don't want to include root IDL files. */
#ifndef NS_NO_VTABLE
#define NS_NO_VTABLE#endif/* starting interface: IWawaComp */#define IWAWACOMP_IID_STR "b7b04070-45fc-4635-b219-7a172f806bee"#define IWAWACOMP_IID /
{0xb7b04070, 0x45fc, 0x4635, /
{ 0xb2, 0x19, 0x7a, 0x17, 0x2f, 0x80, 0x6b, 0xee }}
class NS_NO_VTABLE IWawaComp : public nsISupports {
public:
NS_DEFINE_STATIC_IID_ACCESSOR(IWAWACOMP_IID)
/* void SendSysMessage (in string str); */
NS_IMETHOD SendSysMessage(constchar*str) =0;
};
/* Use this macro when declaring classes that implement this interface. */#define NS_DECL_IWAWACOMP /
NS_IMETHOD SendSysMessage(constchar*str);
/* Use this macro to declare functions that forward the behavior of this interface to another object. */#define NS_FORWARD_IWAWACOMP(_to) /
NS_IMETHOD SendSysMessage(constchar*str) { return _to SendSysMessage(str); }
/* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */#define NS_FORWARD_SAFE_IWAWACOMP(_to) /
NS_IMETHOD SendSysMessage(constchar*str) { return!_to ? NS_ERROR_NULL_POINTER : _to->SendSysMessage(str); }
#if 0/* Use the code below as a template for the implementation class for this interface. *//* Header file */class _MYCLASS_ : public IWawaComp
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IWAWACOMP
_MYCLASS_();
private:
~_MYCLASS_();
protected:
/* additional members */
};
/* Implementation file */
NS_IMPL_ISUPPORTS1(_MYCLASS_, IWawaComp)
_MYCLASS_::_MYCLASS_()
{
/* member initializers and constructor code */
}
_MYCLASS_::~_MYCLASS_()
{
/* destructor code */
}
/* void SendSysMessage (in string str); */
NS_IMETHODIMP _MYCLASS_::SendSysMessage(constchar*str)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* End of implementation class template. */#endif#endif /* __gen_IWawaComp_h__ */
下面被註釋掉的部分是告訴你如何寫介面的實現部分。
然後用vc2005新建一個c++類庫,不要MFC,ATL啥的,預編譯頭在開啟專案後也把勾去掉,再把沒用的std啥.h也都刪了。把剛生成的IWawaComp.h放在標頭檔案目錄下。然後要設定一下專案的配置,先開啟專案屬性,做如下配置
1、c++/常規/附加包含目錄,輸入如"D:/dev/gecko-sdk/include"
2、c++/前處理器/前處理器定義里加入WAWAXPCOM_EXPORTS和XPCOM_GLUE
3、c++/程式碼生成/執行時庫 改成”多執行緒DLL
4、連結器/附加庫目錄輸入”"D:/dev/gecko-sdk/lib"“
5、連結器/輸入/附加依賴項 輸入nspr4.lib、plc4.lib、plds4.lib、xpcomglue.lib
再新建一個WawaComp.h標頭檔案,內容如下
#ifndef _WAWA_COMPT_H_
#define _WAWA_COMPT_H_
#include "IWawaComp.h"#define WAWA_COMP_CONTRACTID "@fetionmm.com/XPCOM/WawaComp;1"#define WAWA_COMP_CLASSNAME "WawaComp"#define WAWA_COMP_CID {0xb7b04070, 0x45fc, 0x4635,{ 0xb2, 0x19, 0x7a, 0x17, 0x2f, 0x80, 0x6b, 0xee } }class WawaComp:public IWawaComp
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IWAWACOMP
};
#endif
前面是定義了一下元件的常量,WAWA_COMP_CONTRACTID是javascript建立XPCOM的時候用的字串,後面會看到,WAWA_COMP_CLASSNAME是元件的類名,WAWA_COMP_CID是元件的唯一ID,如果你不會從GUID變成這種格式,其實就是把剛才生成的標頭檔案裡的IWAWACOMP_IID巨集的值拷貝過來就行。剩下的部分基本都是固定格式,從生成的標頭檔案裡的註釋部分直接拷過來就行。
再新建一個WawaCompModule.cpp檔案,這個是一個XPCOM的模組檔案,是必須的。
#include "WawaComp.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(WawaComp)
static nsModuleComponentInfo components[] =
{
{
WAWA_COMP_CLASSNAME,
WAWA_COMP_CID,
WAWA_COMP_CONTRACTID,
WawaCompConstructor,
}
};
NS_IMPL_NSGETMODULE("WawaCompModule", components)
前兩個include是必須的,下面一句是生成本元件,引數WawaComp就是元件的實現類,再往下是生命一個nsModuleComponentInfo陣列,裡面前三個都是在WawaComp.h裡生命的幾個常量,最後一個WawaCompConstructor是固定格式,就是類名加上Constructor。再最後一句是註冊這個元件,第一個字串是這個模組的名字,第二個引數是前面宣告的陣列。都是固定格式,如果要自己開發XPCOM的話,按格式改改就行了。
再新建最後一個檔案WawaComp.cpp
程式碼如下
#include "WawaComp.h"
#include <windows.h>
#include <tchar.h>
NS_IMPL_ISUPPORTS1(WawaComp, IWawaComp)
NS_IMETHODIMP WawaComp::SendSysMessage(constchar*sText)
{
DWORD dwSize = MultiByteToWideChar (CP_ACP, 0, sText, -1, NULL, 0);
wchar_t *pwText;
pwText =new wchar_t[dwSize];
MultiByteToWideChar(CP_ACP, 0, sText, -1, pwText, dwSize);
HWND hWnd = FindWindow(NULL,_T("WawaApp"));
COPYDATASTRUCT cpd = {SW_NORMAL, lstrlen(pwText)*2+2, pwText};
SendMessage( hWnd, WM_COPYDATA, 0, (LPARAM) & cpd );
delete []pwText;
return NS_OK;
}
該檔案是介面的實現檔案,NS_IMPL_ISUPPORTS1一句是固定的,第一個引數是類名,第二個引數是介面名。下面就是SendSysMessage方法的實現了,該方法的簽名也在前面生成的標頭檔案的註釋裡複製過來,該方法返回型別是NS_IMETHODIMP型別,NS_OK表示成功。
實現的程式碼很簡單,主要是路是把多位元組字串用MultiByteToWideChar轉換成寬字元字串,然後找到標題為WawaApp的視窗,宣告一個COPYDATASTRUCT的結構,把寬字元字串放進去,最後用SendMessage方法向目標視窗傳送WM_COPYDATA訊息,完了再用delete把前面宣告的寬字元字串釋放記憶體。
MultiByteToWideChar的用法是固定的,第一次呼叫先得出目標字串的長度,第二次呼叫是填充字串,可以參考《windows核心程式設計》檢視詳細。COPYDATASTRUCT的第二個引數是lstrlen(pwText)*2+2,是因為一個寬字元站兩個位元組,所以要乘以2,完了字串是以兩個/0結尾,所以要加2。SendMessage是同步傳送訊息,這裡沒用SendMessageTimeout是怕超時後,目標程序還想讀取本程序的COPYDATASTRUCT,有可能引起本程序崩潰,沒有使用PostMessage是因為發了訊息後對方沒機會讀取COPYDATASTRUCT的資料了。
程序間通訊:
程序間通訊有好多種方式,如果只傳少量的字串的話,可以用GlobalFindAtom,GlobalGetAtomName,GlobalDeleteAtom,GlobalAddAtom幾個API來做到,不過我試驗了下,這幾個API傳遞的字串好像有長度限制,最後放棄了。還有就是可以用記憶體對映檔案,就是程式碼寫的有些多,也沒用;用命名管道或者socket也可以,但總感覺有些火箭穿蚊子,也沒用。WriteProcessMemory也可以,感覺不優雅,太暴力,最後才選定了用WM_COPYDATA。
編譯後生成WawaXPCOM.dll,到這一步XPCOM就算開發完了。關於XPCOM的部署,很麻煩,firefox3.0以下,可以用install.js來在.xpi包裡自動安裝,參考如下連結
http://blog.csdn.net/Xscarlet/archive/2007/07/23/1704450.aspx,但firefox3.0我測試了不行,貌似firefox3.0取消了對install.js的支援,那隻能把.DLL和.XPT複製到firefox安裝目錄的components子目錄下,然後刪除C:/Documents and Settings/Administrator/Application Data/Mozilla/Firefox/Profiles/brm8mamm.dev目錄下的xpti.dat和compreg.dat檔案,然後重啟firefox來重新自動註冊XPCOM了。關於firefox下自動註冊xpcom的正規方法,我費了半天勁也沒找到。
下面開始開發firefox外掛
先做準備工作,我用的是firefox2.0,firefox可以建立多個使用者配置檔案,用firefox -p執行,在對話方塊裡新建配置檔案dev,開啟後再位址列裡輸入about:config,修改如下選項
javascript.options.showInConsole = true //把 JavaScript 的出錯資訊顯示在錯誤控制檯
nglayout.debug.disable_xul_cache = true //禁用 XUL 快取,使得對視窗和對話方塊的修改不需要重新載入 XUL 檔案
browser.dom.window.dump.enabled = true //允許使用 dump() 語句向標準控制檯輸出資訊
javascript.options.strict = true //在錯誤控制檯中啟用嚴格的 JavaScript 警告資訊
然後給firefox安裝Extension Developer's Extension 外掛,這樣你修改了xpcom後執行選單 工具/Extension Developer/ Reload all Chrome不用重啟ff就可以測試外掛了。我們在C:/Documents and Settings/Administrator/Application Data/Mozilla/Firefox/Profiles/brm8mamm.dev/extensions(Administrator是當前使用者名稱,brm8mamm.dev可能和你的不一樣,但結尾是你剛新建的firefox配置檔名dev)目錄下新建一個[email protected]的檔案,用記事本開啟,輸入我們外掛的開發路徑,比如E:/huhao/project/WawaFF/xpcom,這樣firefox就會自動載入這個外掛了,每次改動了檔案只需Reload all Chrome就能測試。
關於firefox外掛開發環境搭建,參考如下連結
http://www.ibm.com/developerworks/cn/web/wa-lo-firefox-ext/
環境搭好了,先建立工作檔案,在E:/huhao/project/WawaFF/xpcom裡建立如下檔案
install.rdf
chrome.manifest
components
components/IWawaComp.xpt
components/WawaXPCOM.dll
chrome
chrome/skin
chrome/content
chrome/content/overlay.xul
chrome/content/style.css
chrome/content/wawa.js
關於firefox外掛的介面,參看如下連結
http://www.lewislv.org/mozilla-ext/mozilla-ext.html#ch3_1_1
其中skin檔案,本例沒有用到,我們先從overlay.xul開始吧,它是一個xul定義檔案,XUL是一個介面描述語言,是XML格式的,用javascript和xul也能開發一些跨平臺RIA應用。以下為引用
XUL 是一個Mozilla使用XML來描述使用者介面的一種技術,使用XUL你可以快速的創建出跨平臺,基於因特網的應用程式。基於XUL技術的應用程式可以很方便的使用好看的字型、圖形以及方便的介面佈局,而且也更容易部署和定製。如果程式設計師已經熟悉了Dynamic HTML (DHTML),那學習XUL將是更容易的事,也可以更快的開發基於XUL的應用程式.
來自https://developer.mozilla.org/cn/XUL
我們的overlay.xul,如下
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"><script type="application/x-javascript"
src="chrome://wawaxpcom/content/wawa.js"/><statusbar id="status-bar"><statusbarpanel id="statusbar_fs_sz" class="statusbarpanel-iconic" onclick="collection(true);" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAnElEQVQ4jcWTyw0DMQhEx24gJc2NItJBqtljOkgbiMZgD/Emq/VHjnzISMiSEWN4wgBww6pEZCMZAKaCZIjI9jEgGaoa7j4VqhrlQaTiEe5et/a8v8/Hq0rlnAEg5dXx/29QMzjm7qnw6DNoABvl2iO0TDrGfQbngkFXqYQ392CgM4P4qfJqBCCRhJlNF5kZSH4vlj/TRQeXqSXbATJtb4tQeT1wAAAAAElFTkSuQmCC" insertafter="statusbar-display" style="color:green" label="wawasoft" warning="true"/></statusbar></overlay>
就是引入了一個wawa.js檔案,然後再狀態列上家了一個圖片panel,src屬性指定圖片的路徑,不過我指了半天不會指,最後用base64來描述了一個小圖片,反正也不大,就似乎把圖片的bytes用base64字串寫在那裡,微軟的.mht格式也是把圖片轉成那樣的base64字串,還有郵件協議也是。要想讓statusbarpanel顯示圖片的話,必須設定class="statusbarpanel-iconic",最後onclick="collection(true);" 表示點選這個圖片出發collection函式,該函式在wawa.js裡定義,一會兒看。關於XUL節點定義,可以查些資料看。
再看下xul的樣式,就是普通的CSS,大家應該 很熟悉,
statusbar {
width: 100%;
border: 1px inset -moz-dialog;
margin: 4px;
padding: 0px 4px;}
#status {}
#status[warning] {
color: red;}
就定義了基本的一些邊框,外空白,內空白,警告顏色等資訊。然後主要的東西在wawa.js裡,如下
{
try{
var win = window.getBrowser().selectedBrowser.contentWindow;
var doc = window.getBrowser().selectedBrowser.contentDocument;
var sel = win.getSelection().QueryInterface(Components.interfaces.nsISelectionPrivate);
var contentHTMLSel = sel.toStringWithFormat("text/html", 0, 0);
var contentHTMLAll = doc.body.innerHTML;
var wawaxpcom = Components.classes["@fetionmm.com/XPCOM/WawaComp;1"].getService();
wawaxpcom = wawaxpcom.QueryInterface(Components.interfaces.IWawaComp);
var s = encodeURIComponent(contentHTMLSel);
if(Selection)
wawaxpcom.SendSysMessage(s);
else
wawaxpcom.SendSysMessage("<h1>KK</h1>");
}catch(err){alert(err)}
}
前面一些是獲取網頁上的html程式碼或者使用者選定的部分HTML程式碼,中間一節是建立我們上面建立的XPCOM,可以看到那裡用到了元件的類名,介面等資訊,都是固定格式。完了再下面就是把html編碼用encodeURIComponent進行編碼後呼叫元件的SendSysMessage方法發出去,進行SendSysMessage編碼的原因是考慮中文,中文直接傳到XPCOM裡有問題,上面說過了。
值得注意的firefox本身就有好多xpcom可以使用,有訪問網路的,unicode轉換的,讀寫檔案的,使用w32shell的等,很強大,在你動手寫你自己的xpcom之前,最好先了解一下,免得做無用功。
chrome.manifest定義了firefox外掛的檔案清單,內容如下
content wawaxpcom chrome/content/
overlay chrome://browser/content/browser.xul chrome://wawaxpcom/content/overlay.xul
第一行表示的是主要內容,第二行表示要用到的介面XUL,一般就這樣就行了,.css和.js不用列出來(不是太確認,有的也列出來了,不知道為啥,反正我的能用)。
install.rdf是一個部署宣告檔案,不是ie的頻道檔案,但是XML格式的,如下
xmlns:em="http://www.mozilla.org/2004/em-rdf#"><Description about="urn:mozilla:install-manifest"><em:id>[email protected]</em:id><em:name>wawaxpcom</em:name><em:version>0.1.0</em:version><em:description>WawaXPCOM</em:description><em:creator>wawa</em:creator><!-- Firefox --><em:targetApplication><Description><em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id><em:minVersion>1.5</em:minVersion><em:maxVersion>3.1.0.*</em:maxVersion></Description></em:targetApplication></Description></RDF>
其中ec8030f7-c20a-464f-9b0e-13a3a9e97384是固定的,不是你的XPCOM的ID,這個表示firefox,其它的很明顯,看節點名就行了,都是外掛ingcheng,版本,適應的firefox版本等資訊。
最後把整個E:/huhao/project/WawaFF/xpcom目錄壓縮成一個zip檔案,把字尾名改成.xpi格式,拖到firefox上就會提示安裝了,安裝後重啟ff,右下角就會有一個向下的小箭頭圖示了,說明外掛安裝成功了。
最後我們開發一個c#窗體程式來接受windows訊息。
新建一個c# windows程式,把預設窗體的Text屬性改成WawaApp,因為在XPCOM裡給WawaApp視窗發訊息,所以要用改成這樣,否則就得在XPCOM裡用broadcastsystemmessage進行訊息廣播。在窗體上放一個TextBox準備顯示收過來的資料。主要的程式碼如下
using System.Runtime.InteropServices;
using System.Web;
using System.Windows.Forms;
namespace ShowWeb
{
publicpartialclass Form1 : Form
{
privateconstint WM_COPYDATA =0x004A;
public Form1()
{
InitializeComponent();
}
privatevoid handlerMessage(string message)
{
if (InvokeRequired)
{
BeginInvoke(new handlerMessageDelegate(handlerMessage));
}
textBox1.Text = message;
}
protectedoverridevoid WndProc(ref Message m)
{
if (m.Msg == WM_COPYDATA)
{
COPYDATASTRUCT dat = (COPYDATASTRUCT) Marshal.PtrToStructure(
m.LParam, typeof (COPYDATASTRUCT));
handlerMessage(HttpUtility.UrlDecode(dat.lpData));
return;
}
base.WndProc(ref m);
}
#region Nested type: COPYDATASTRUCT
[StructLayout(LayoutKind.Sequential)]
publicstruct COPYDATASTRUCT
{
public IntPtr dwData;
publicint cbData;
[MarshalAs(UnmanagedType.LPWStr)] publicstring lpData;
}
#endregion#region Nested type: handlerMessageDelegateprivatedelegatevoid handlerMessageDelegate(string message);
#endregion
}
}
大概解釋一下,COPYDATASTRUCT用來接受WM_COPYDATA傳遞過來的資料,因為要傳遞寬字元,所以用UnmanagedType.LPWStr修飾,重寫WndProc方法可以有機會在第一事件捕獲本窗體收到的windows訊息並加以處理,我們在這裡截獲WM_COPYDATA訊息,用Marshal.PtrToStructure把收到的訊息轉換成c#的結構體,並把收到的資料進行url解碼,然後呼叫handlerMessage方法來處理,handlerMessage方法是一個簡化的winform多執行緒方案,如果InvokeRequired為true就呼叫BeginInvoke呼叫本身,否則執行給文字框直接賦值,這樣做是為了不讓介面執行緒把接受訊息的執行緒給hang住。
主要參考連結
其它參考連結
相關推薦
蛙蛙推薦:蛙蛙牌firefox外掛
上次給大家演示了抓取IE核心瀏覽器的網頁,這次和大家一起寫一個抓取firefox頁面文字的例子。Firefox有自身的一套結構良好的外掛機制,但要深入開發Firefox外掛的話要了解javascript,XUL,XPCOM以及一些c++的基本常識,本貼算是給想開發Firefox外掛的朋友入門。 我們先來了解
強烈推薦:240多個jQuery外掛
概述 jQuery 是繼 prototype 之後又一個優秀的 Javascript 框架。其宗旨是—寫更少的程式碼,做更多的事情。它是輕量級的 js 庫(壓縮後只有21k) ,這是其它的 js 庫所不及的,它相容 CSS3,還相容各種瀏覽器(IE 6.0+, FF 1
蛙蛙推薦:蛙蛙教你配置linux+mysql+.net環境
三、安裝配置mysql 有了apt,安裝mysql也很簡單,下面一個命令搞定 sudo apt-get install mysql-server mysql伺服器大約有100多M,不過咱們的源的速度快,每秒100多K,我洗了澡出來就裝好了,中間需要輸入mysql root賬戶的密碼,別的啥也不用管,因為
蛙蛙推薦:c#編寫網路電話
{ WaveFormat format =new WaveFormat(); //聲音的格式,通常使用WAVE_FORMAT_PCM來設定, //因為PCM是比較常用的聲音格式。 format.FormatTag = WaveFormat
蛙蛙推薦:蛙蛙教你文字聚類
{ //1、獲取文件輸入string[] docs = getInputDocs("input.txt"); if (docs.Length <1) { Console.WriteLine("沒有文件輸入"); Console.Read();
[BZOJ3036]綠豆蛙的歸宿:DAG上DP+期望DP
分析: 其實這道題是可以正著DP的(即不需要在反圖上拓撲排序)(為什麼好多人都說不能?) 我們令f[i]表示從起點1出發到點i的期望距離,如果一些點能到達點i,那麼這些點中的每個點對f[i]的貢獻為f[j]*po[j],po[j]表示通過j到達i的概率。實際上,我們令p[i]表示從起點1出發能到達i的概率,
甲小蛙戰記:PHP2Java 排雷指南
(馬蜂窩技術原創內容,申請轉載請在公眾後後臺留言,ID:mfwtech ) 大家好,我是來自馬蜂窩電商旅遊平臺的甲小蛙,從前是一名 PHP 工程師,現在可能是一名 PHJ 工程師,以後...... 前陣子,我從大道訊息聽說公司商品訂單技術棧要推 Java。我是一個喜歡走在時代前列線上的人,凡是要做到領先。
推薦firefox元件:廣告彈窗阻止外掛
推薦一個外掛,可以阻止廣告彈窗:https://addons.mozilla.org/en-us/firefox/addon/adblock-plus/ 煩人的廣告,把訪客當成了弱智一樣。雖然自己是一個站長,但是最反感去訪問某些網站,一進入,滑鼠不小心動了一下,就彈窗那種
分針網——每日推薦: 各路神仙關於閉包概念不同解讀
閉包 寫在前面: 閉包是被講爛的內容,但是當我不了解的情況下,看過很多教程,聽過很多道理,還是無法完全理解閉包這個東西。所以想要寫一篇比較詳細,前端小白也能夠真正理解閉包概念的幹貨文章,本文參考很多閉包資料,希望能真正把閉包這個東西講清楚,
分針網——每日推薦: 根據屏幕大小,加載不同大小的圖片
加載圖片 引言 今天要介紹的東西,很簡單,但是對於前端響應式的時候是個很重要的知識; 我們在用bootstrap這類前端框架時, 雖然頁面局部通過media query實現了,頁面始終無滾
強力推薦:Atom入坑必備插件
格式化 方便 tom 一個 rip 設置 等等 依靠 同步 強力推薦:Atom入坑必備插件 Atom作為Javascript/CSS/HTML等前端編輯器利器,其強大功能依靠各種插件,以下是筆者在入坑階段,精挑細選總結出的必不可少的插件,熟悉運用這些插件,一定成噸提高生產效
java書籍推薦:《Java SE 6 技術手冊》
rate rst tail right 5.1 important hid 新手 log Java SE 6 技術手冊 或 Java SE 6 技術手冊 Java SE 6 技術手冊 為什麼選擇用 Markdown?僅僅是單純把文件
好書推薦:OpenSuse
-- 之前 org href css ets ces shadow stop 現在正值暑期,有大把的空閑時間,覺得有必要好好琢磨琢磨,進一步學習認識網站相關的東東。 之前,出於某種考慮,購買了一年的搬瓦工虛擬主機(vps),每月有500G的流量。單純使用Shadow
(轉)Android開發書籍推薦:從入門到精通系列學習路線書籍介紹
成長 程序員 理論 targe base 官方 app als 自己的 Android開發書籍推薦:從入門到精通系列學習路線書籍介紹 轉自:http://blog.csdn.net/findsafety/article/details/52317506 很多時候我們都會
GitHub 萬星推薦:黑客成長技術清單
安全 probable security 滲透 target fig 自己 質量 理論 GitHub 萬星推薦:黑客成長技術清單 導語:如果你需要一些安全入門引導,“Awesome Hacking”無疑是最佳選擇之一。 最近兩天,在reddit安全板塊和Twi
21ic編輯推薦:從單片機開始的程序運行
決定 oda 單片機編程 使用率 映射 bios alloc 瓶頸 str 一直不清楚單片機中程序的執行過程,就是知道一個程序總是從一個main函數開始執行,然後把程序段存放在ROM裏面,動態數據存放在RAM裏面,而單片機的RAM資源又是及其的稀少,所以要省著用,但是到底怎
開源項目推薦:e-example / Springboot+bootstrap + ……
gateway 環境變量 source cal 持久層 tap utf-8 war 持久 前言: 我想要找一個 springboot + bootstrap 的例子介紹,然後搜索到了這個開源項目。 所有能跑起來的項目都有研究價值,看看這個項目的文檔。目前正好滿足我想
書籍推薦:《實戰Java虛擬機——JVM故障診斷與性能優化》下載
java實戰java虛擬機jvm本書詳細介紹Java虛擬機的基本原理和優化診斷方法。其中重點介紹Java虛擬機的體系結構、常用的虛擬機參數、Java虛擬機的垃圾回收原理、算法以及目前虛擬機所支持的各種垃圾回收器及其區別、特點和使用方法。在實踐和調優方面,重點介紹了Java的堆、棧分析方法,性能調優的一般思路、
育兒書籍推薦:《雙向養育》
溝通 適合 優秀 -s ges font pan ima left 《雙向養育》 原書名:《Breakthrough parenting》 作者:傑恩·梅傑(Jayne A. Major) 之所以推薦該本育兒書籍,最開始的時候對各種琳瑯滿目的育兒書籍,找本優秀的
淺談商品推薦:如何猜中用戶的心思?
維度 活動 不同 -i 鄰居 表示 找到 包括 一起 去商場前,你告訴自己今天只買T恤,出商場時,你還是拎了大包小包……導購員看你摸了摸連衣裙,讓你免費試穿,結果你這一穿就不願脫下了,而且一件接一件。導購猜測顧客喜歡什麽,推薦顧客試穿,滿足雙方各自的心理訴求來達成交易。電