有时候做程序,喜欢把程序的注册与用户QQ号码绑定,程序仅允许登录成功了指定QQ账号时才可使用,
为了实现这个目的,有人用API取QQ窗口、QQ托盘图标上的QQ账号,
但是这个方法写起来比较麻烦,如果用户有意玩玩的话,也可以自己在你软件获取之前先用API修改你要获取的目标信息!
还有一种方法就是内存读取,当然要找到一个QQ登录成功后存放QQ号码的地址,基址肯定是不存在的,再加之QQ更新频繁,
所以内存读取的办法也不太好,
为了实现这个目的,还有一种办法那就是利用QQ网页自身的功能,这种方法既方便又准确,下面就来说下原理:
打开: http://xui.ptlogin2.qq.com/cgi-bin/qlogin
我们会发现这也页面会自动获取我们的已登录的QQ信息,并可以实现快速登录,分析之:
关键的东西:
<script src="http://imgcache.qq.com/ptlogin/ac/v5/js/xui.js?v=1.2"></script>
通过分析这个js,我们找到如下2个重要的函数:
function ptui_qInit() 这个函数的作用是初始化SSOAxCtrlForPTLogin.SSOForPTLogin2对象,创建一个ActiveXObject对象
hummer_loaduin() 这个函数就是真正的通过 SSOAxCtrlForPTLogin这个COM来获取已登录QQ信息的,
到此,我们试着自己用程序来调用 SSOAxCtrlForPTLogin的com对象,但是发现失败,原因下面说,
既然不能自己调用 SSOAxCtrlForPTLogin对象,那先试试将页面保存为本地在打开看看什么情况,结果如下:
“快速登录失败,请您返回重试或切换到普通登录模式。”
很明显, SSOAxCtrlForPTLogin对象是要判断当前url的,如果url不是来自以下域名的都不能初始化成功:
var site=["qq.com","paipai.com","tencent.com","soso.com","taotao.com","tenpay.com","foxmail.com","wenwen.com","3366.com","imqq.com"];
好了,既然不能自己用本地页面,也不能自己写程序调用它的COM,那我们就直接来访问它获取它页面上已经获取好的信息吧:
同时附上已经格式好了的 xui.js代码,有兴趣的可以看看

function ptui_mapStr(B){
for (i = 0 ;i < B.length;i ++ )
{
var A = document.getElementById(B[i][ 1 ]);
if (A != null )
{
if ( " A " == A.nodeName || " U " == A.nodeName || " OPTION " == A.nodeName || " LABEL " == A.nodeName || " P " == A.nodeName)
{
if (A.innerHTML == "" )
{
A.innerHTML = ptui_str(B[i][ 0 ])
}
}
else
{
if ( " INPUT " == A.nodeName)
{
if (A.value == "" )
{
A.value = ptui_str(B[i][ 0 ])
}
}
else
{
if ( " IMG " == A.nodeName)
{
A.alt = ptui_str(B[i][ 0 ])
}
}
}
}
}
}
function ptui_str(A){
A -= 1 ;
if (A >= 0 && A < g_strArray.length)
{ return g_strArray[A]}
return ""
}
var g_labelMap = new Array([STR_QLOGIN, " loginbtn " ],[STR_QLOGIN_SELECT_TIP, " qlogin_select_tip " ]);
ptui_mapStr(g_labelMap);
function getArgs(){
var B = new Object();
var F = location.href.substring(location.href.indexOf( " /qlogin? " ) + 8 );
var E = F.split( " & " );
for ( var C = 0 ;C < E.length;C ++ )
{
var G = E[C].indexOf( " = " );
if (G ==- 1 )
{
continue
}
var A = E[C].substring( 0 ,G);
var D = E[C].substring(G + 1 );
D = decodeURIComponent(D);
B[A] = D
}
return B
}
var params = getArgs();
var g_qtarget = params.qtarget;
var g_domain = params.domain;
var g_jumpname = params.jumpname;
var g_param = params.param;
var site = [ " qq.com " , " paipai.com " , " tencent.com " , " soso.com " , " taotao.com " , " tenpay.com " , " foxmail.com " , " wenwen.com " , " 3366.com " , " imqq.com " ];
var flag = false ;
for ( var i = 0 ;i < site.length;i ++ )
{
if (site[i] == g_domain)
{flag = true }
}
if ( ! flag)
{
g_domain = " qq.com "
}
var q_bInit = false ;
var q_hummerQtrl = null ;
var g_vOptData = null ;
var q_aUinList = new Array();
function ptui_qInit(){
if (q_bInit)
{ return }
q_bInit = true ;
if ( ! window.ActiveXObject)
{ return }
try {
q_hummerQtrl = new ActiveXObject( " SSOAxCtrlForPTLogin.SSOForPTLogin2 " );
var A = q_hummerQtrl.CreateTXSSOData();
q_hummerQtrl.InitSSOFPTCtrl( 0 ,A);
g_vOptData = q_hummerQtrl.CreateTXSSOData();
hummer_loaduin();
if (q_aUinList.length <= 0 )
{
msg(ptui_str(STR_QLOGIN_NO_UIN));
return false
}
else
{
if (ptui_buildUinList)
{ptui_buildUinList(q_aUinList)}
}
document.cookie = " ptui_qstatus=2;domain=ptlogin2. " + g_domain
}
catch (B)
{
q_hummerQtrl = null ;
document.cookie = " ptui_qstatus=3;domain=ptlogin2. " + g_domain;
msg(ptui_str(STR_QLOGIN_OTHER_ERR));
ptui_reportAttr( 89217 , true )
}
}
function list(){
q_bInit = false ;
ptui_qInit();
xui_report()
}
function ptui_buildUinList(){
var G = "" ;
var E = document.getElementById( " list_uin " );
if ( null == E)
{ return }
var A = q_aUinList.length > 5 ? 5 :q_aUinList.length;
for ( var C = 0 ;C < A;C ++ )
{
var F = q_aUinList[C];
var B = "" ;
var D = "" ;
if (q_aUinList.length == 1 )
{D = ' style="display:none;" ' }
if (C == 0 )
{B = " checked='checked' " }
G += " <li><input type='radio' name='q_uin' id='uin_ " + F.uin + " ' " + B + D + " /><span> " + F.nick.replace( / & / g, " & " ).replace( / < / g, " < " ).replace( / > / g, " > " ) + " ( " + F.name + " )</span></li> "
}
E.innerHTML = G
}
function onQloginSelect(){
for ( var C = 0 ;C < q_aUinList.length;C ++ )
{
var D = q_aUinList[C];
var B = document.getElementById( " uin_ " + D.uin);
if (B != null )
{
if (B.checked)
{
hummer_loaduin();
var A = hummer_getUinObj(D.uin);
if (A == null )
{
msg(ptui_str(STR_QLOGIN_SELECT_OFFLINE), true );
return
}
document.getElementById( " qlogin_loading " ).innerHTML = ' <img src="http://imgcache.qq.com/ptlogin/v4/style/0/images/load.gif" align="absmiddle" /> ' + ptui_str(STR_QLOGINING);
document.getElementById( " loginbtn " ).className = " btn_gray " ;
document.getElementById( " loginbtn " ).style.color = " gray " ;
hummer_login(A,g_domain,g_jumpname,g_param)
}
}
}
}
function hummer_loaduin(){
q_aUinList.length = 0 ;
var P = q_hummerQtrl.DoOperation( 1 ,g_vOptData);
if ( null == P)
{ return }
try {
var M = P.GetArray( " PTALIST " );
var T = M.GetSize();
var O = "" ;
var F = document.getElementById( " list_uin " );
for ( var U = 0 ;U < T;U ++ )
{
var C = M.GetData(U);
var R = C.GetDWord( " dwSSO_Account_dwAccountUin " );
var G = "" ;
var J = C.GetByte( " cSSO_Account_cAccountType " );
var S = R;
if (J == 1 )
{
try {
G = C.GetArray( " SSO_Account_AccountValueList " );
S = G.GetStr( 0 )
}
catch (Q)
{}
}
var K = 0 ;
try {
K = C.GetWord( " wSSO_Account_wFaceIndex " )
}
catch (Q)
{K = 0 }
var L = "" ;
try {
L = C.GetStr( " strSSO_Account_strNickName " )
}
catch (Q)
{L = "" }
var D = C.GetBuf( " bufGTKey_PTLOGIN " );
var E = C.GetBuf( " bufST_PTLOGIN " );
var I = "" ;
var A = E.GetSize();
for ( var N = 0 ;N < A;N ++ )
{
var B = E.GetAt(N).toString( " 16 " );
if (B.length == 1 )
{B = " 0 " + B}
I += B
}
var H = {uin:R,name:S,type:J,face:K,nick:L,key:I};
q_aUinList[U] = H
}
switch (q_aUinList.length)
{
case 0 :ptui_reportAttr( 77430 , false ); break ;
case 1 :ptui_reportAttr( 77431 , false ); break ;
default :ptui_reportAttr( 77432 , false )
}
}
catch (Q){}
}
function hummer_getUinObj(B){
for ( var A = 0 ;A < q_aUinList.length;A ++ )
{
var C = q_aUinList[A];
if (C.uin == B)
{ return C}
}
return null
}
function unloadpage(){
document.domain = g_domain;
try {
parent.document.body.onbeforeunload = function (){};
parent.document.body.onunload = function (){};
for ( var A = 0 ;A < parent.parent.frames.length;A ++ )
{
parent.parent.frames[A].onunload = function (){};
parent.parent.frames[A].onbeforeunload = function (){}
}
if (parent.parent != top)
{
for ( var A = 0 ;A < parent.parent.parent.frames.length;A ++ )
{
parent.parent.parent.frames[A].onunload = function (){};
parent.parent.parent.frames[A].onbeforeunload = function (){}
}
}
}
catch (B){}
}
function hummer_login(E,D,A,F){
if (A == "" )
{A = " jump " }
var C = " http://ptlogin2. " + D + " / " + A + " ?clientuin= " + E.uin + " &clientkey= " + E.key + " &keyindex=9 " ;
if (F != null && F != "" )
{
var B = decodeURIComponent(F);
if (B.indexOf( " # " ) >- 1 )
{
B = B.replace( / # / g, " %23 " )
}
C += ( " & " + B)
}
switch (parseInt(g_qtarget))
{
case 0 :unloadpage();parent.location.href = C; break ;
case 1 :top.location.href = C; break ;
case 2 :unloadpage();parent.parent.location.href = C; break ;
default :top.location.href = C
}
}
function msg(A){
try {
var C = document.getElementById( " qlogin_loading " );
if ((C.style.display != " none " ) && (document.getElementById( " qlogin " ).style.display != " none " ))
{
C.innerHTML = ' <span style="color:#cc0000;"> ' + A + " </span> " ;C.style.display = ""
}
}
catch (B){}
}
function xui_report(B){
if (Math.random() > 0.001 )
{ return }
var A = new Date();
var C = location.hash.substr( 1 ,location.hash.length);
url = " http://isdspeed.qq.com/cgi-bin/r.cgi?flag1=6000&flag2=1&flag3=2&1= " + (A - C) + " &2= " + (A - time1);
imgTime = new Image();
imgTime.src = url
}
function ptui_reportAttr(C,B){
if ((B == false ) && (Math.random() > 0.001 ))
{ return }
url = " http://ui.ptlogin2. " + g_domain + " /cgi-bin/report?id= " + C;
var A = new Image();
A.src = url
}
list(); /* |xGv00|0d5165981e8a571e21b5c15cc81a9130 */
接着我们来直接实现程序吧,代码如下:

using System.Windows.Forms;
namespace CheckLoginedQQ
{
public class Checker
{
public Checker()
{
}
private HtmlDocument Document;
/// <summary>
/// 初始化
/// </summary>
/// <returns></returns>
public bool Initialize()
{
WebBrowser browser = new WebBrowser();
browser.Url = new Uri( " http://xui.ptlogin2.qq.com/cgi-bin/qlogin " );
while (browser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
if (browser.Document.Url.AbsoluteUri == " http://xui.ptlogin2.qq.com/cgi-bin/qlogin " )
{
Document = browser.Document;
return true ;
}
return false ;
}
/// <summary>
/// 检测登陆账号
/// </summary>
/// <param name="uin"></param>
/// <returns></returns>
public bool QQisLogined( string uin)
{
HtmlElementCollection elements = Document.GetElementsByTagName( " input " );
foreach (HtmlElement element in elements)
{
string type = element.GetAttribute( " type " );
if (type != " radio " )
continue ;
string name = element.GetAttribute( " name " );
if (name != " q_uin " )
continue ;
string id = element.GetAttribute( " id " );
if (id == " uin_ " + uin)
{
return true ;
}
}
return false ;
}
}
}
首先用Webbrowser控件访问,http://xui.ptlogin2.qq.com/cgi-bin/qlogin
通过ReadState属性判断页面是否加载完成
通过Document.url判断加载时候为http://xui.ptlogin2.qq.com/cgi-bin/qlogin页面
然后返回真或假
Initialize() == true 之后通过Webbrowser.Document来获取页面上的QQ信息,具体实现看代码!
好了,基本就是这样了,不过要注意的一点是Webbrowser是不能跨线程实例化的,因为他是一个基于COM的控件,所以必须使用 [STAThread] 管理线程
使用示例如下:
[STAThread]
static void Main(string[] args)
{
CheckLoginedQQ.Checker checker = new CheckLoginedQQ.Checker();
if (checker.Initialize())
{
if(checker.QQisLogined("110001"));
Console.Write("Logined");
else
Console.Write("Not Logined");
}
else
{
Console.Write("un Initialize");
}
}