2023年2月

腾讯向大众提供了申请QQ的界面,方便很多潜在用户加入QQ大军中,注册页面是http://reg.qq.com ,为了限制用户的过度使用,设置了验证码、IP限制等手段,一般用户默认一天只能申请几个QQ号码,号码是随机生成的,当然好的号码是不会有的,这些是腾讯的资源,需要付费才能获取到。

本文主要介绍如何利用C#来实现QQ号码的快速、批量申请操作,以及考虑如何加入宽带拨号的方式实现IP的限制,尽可能的申请到更多的QQ号码,以供他用。

先看看演示程序的界面效果:


=====》

上面是我使用C#实现QQ号码申请的演示程序,是应用的雏形。其中为了方便,把用户需要填写的信息封装起来,用户开通QQ号码后,再自行修改即可,只需要输入验证码即可快速申请到QQ。

1、首先需要获取验证码,然后供用户输入并提交申请,这里我使用了后台线程进行处理,提高用户界面的响应效果。


public

partial

class
Form1 : Form

{

private
CookieContainer cookieReg
=

new
CookieContainer();

private
BackgroundWorker worker
=

new
BackgroundWorker();

private
WebProxy proxy;



public
Form1()
{
InitializeComponent();

worker.DoWork

+=
new
DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted

+=

new
RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);


//
proxy = new WebProxy("120.203.214.184", 80);


}


private

void
GetVerifyImage()
{
worker.RunWorkerAsync(

"
GetImage
"
);
}


void
worker_DoWork(
object
sender, DoWorkEventArgs e)
{

string
newverifyUrl
=

"
http://captcha.qq.com/getimage?aid=1007901&
"

+


new
Random().NextDouble()
+

new
Random().Next(
10
)
+

new
Random().Next(
10
);

cookieReg

=

new
CookieContainer();
HttpHelper httpHelper

=

new
HttpHelper();

string
regurl
=

"
http://reg.qq.com
"
;
httpHelper.GetHtml(regurl, cookieReg, regurl, proxy);


using
(Stream s
=
httpHelper.GetStream(newverifyUrl, cookieReg, regurl, proxy))
{

if
(s
==

null
)
{
MessageUtil.ShowWarning(

"
获取登陆码错误,请检查您的网络!
"
);

return
;
}
e.Result

=
Image.FromStream(s);
}
}


void
worker_RunWorkerCompleted(
object
sender, RunWorkerCompletedEventArgs e)
{
Image image

=
e.Result
as
Image;

if
(image
!=

null
)
{

this
.pictureBox1.Image
=
image;
}
}

2、 处理申请操作结果显示。申请QQ的处理操作相对比较繁琐,我把它封装在一个处理函数中,这样我们在事件处理的时候,先宏观处理结果,然后在进一步细化具体的操作,如下所示:


private

void
btnApplyNumber_Click(
object
sender, EventArgs e)
{

if
(
this
.txtVerifyCode.Text.Length
<

4
)
{
SetTips(

"
请输入验证码
"
);

this
.txtVerifyCode.Focus();
}


string
qqnumber
=
ApplyQQ();

if
(
!
string
.IsNullOrEmpty(qqnumber))
{
SetTips(

string
.Format(
"
恭喜您,申请到QQ {0}
"
, qqnumber));
LogTextHelper.WriteLine(

string
.Format(
"
{0}----{1}
"
, qqnumber,
"
123abc
"
));
}

else

{

if
(getnumHtml.Contains(
"
此IP申请的操作过于频繁
"
))
{
SetTips(

"
IP操作过于频繁
"
);
}

else

{
SetTips(

"
申请失败,请重试
"
);
}
}
GetVerifyImage();
}

以上先对申请的操作进行处理,并记录最后申请结果页面,如果页面有一些特殊的如IP操作频繁的信息,则提示用户IP操作的问题,方便采取如“手工拨号”等方式进行处理,实现IP的变化,从而可以申请更多的QQ号码。如果一切正常,返回QQ号码,那么记录到相关的媒介并显示即可。

3、 详细QQ申请操作处理逻辑。申请QQ的处理,不是一般的麻烦,为了不给用户识别相应的提交内容,腾讯把申请的资料进行乱码化处理,并把顺序打乱,把相应的名称处理成0f2b4766321c2d6a4c7a61515324这样不可识别的字符串,然后统一提交,一般人是识别不了这些字符串的,字符串不是加密串,是随机按照规则组合而成,很变态。这里我提供大致的思路,就是先获取Cookie的相关信息,把里面的变量作为基础,对数据进行处理,然后作为相应的字段名称,混合提交的内容,一起POST到服务器,如果成功,则返回QQ号码,否则提示相应的错误信息。


private

string
getnumHtml
=

""
;
//
最后申请的网页内容



private

string
ApplyQQ()
{

string
html
=

""
;
//
"g_dataArray=new Array(new Array(\"0f2b4766321c2d6a4c7a61515324\",\"4621587a340437624e71315a5a2b\",\"23755a6a29192c7b506d73474536\",\"4f21596e22043464537b774b493a\",\"2d2d5b6a01163078567072577102\",\"4121587a340437624e7130525223\",\"4f2d486122002679117b66666665728\",\"047548612200267910736d5d5f30\",\"306d4a6024193779584b55656718\",\"2574486122002679136b75454738\",\"33344a66250e703f1429370b0970\",\"252348683412256c49766a585c2d\",\"161059673e192645547262564206\"),new Array(9796,9806,9807,9803,9797,9795,9793,9798,9802,9801,9800,9799,9792),\"/cgi-bin/getnum\");";



string
regurl
=

"
http://reg.qq.com/
"
;
HttpHelper helper

=

new
HttpHelper();


int
BaseNum
=

0
;
BaseNum

=
GetBaseNumByCookieSkey(cookieReg);


string
nick
=

"
test
"
;

string
pass
=

"
123abc
"
;

string
repass
=
pass;

string
year
=

"
2009
"
;

string
month
=

"
12
"
;

string
day
=

"
13
"
;

string
sex
=

"
1
"
;

string
verifycode
=

this
.txtVerifyCode.Text;

string
province
=

"
11
"
;

string
city
=

"
1
"
;
List

<
string
>
ElementsArrName
=
GetElementArray(nick, pass, repass, year, month, day, sex, verifycode, province, city);
List

<
short
>
DataArrayShort
=

new
List
<
short
>
();
List

<
string
>
DataArrayLong
=

new
List
<
string
>
();

Thread.Sleep(

100
);

string
regurl2
=

"
http://reg.qq.com/cgi-bin/checkconn?seed
"

+

new
Random().NextDouble()
+

new
Random().Next(
10
);

html

=
helper.GetHtml(regurl2, cookieReg, regurl, proxy);

//
html = "g_dataArray=new Array(new Array(\"0f2b4766321c2d6a4c7a61515324\",\"4621587a340437624e71315a5a2b\",\"23755a6a29192c7b506d73474536\",\"4f21596e22043464537b774b493a\",\"2d2d5b6a01163078567072577102\",\"4121587a340437624e7130525223\",\"4f2d486122002679117b66666665728\",\"047548612200267910736d5d5f30\",\"306d4a6024193779584b55656718\",\"2574486122002679136b75454738\",\"33344a66250e703f1429370b0970\",\"252348683412256c49766a585c2d\",\"161059673e192645547262564206\"),new Array(9796,9806,9807,9803,9797,9795,9793,9798,9802,9801,9800,9799,9792),\"/cgi-bin/getnum\");";


GetDataArray(html,
ref
DataArrayShort,
ref
DataArrayLong);


string
postData
=
GetPostData(ElementsArrName, BaseNum, DataArrayShort, DataArrayLong);

//
MessageBox.Show(postData);



Thread.Sleep(

100
);

string
regurl3
=

"
http://reg.qq.com/cgi-bin/getnum
"
;
html

=
helper.GetHtml(regurl3, cookieReg, postData,
true
, regurl, proxy);
getnumHtml

=
html;
//
记录最后分析的内容,以便进一步分析操作



Regex re

=

new
Regex(
"
var\\s*xyz=\
"
(.
*?
)\
"
;
"
, RegexOptions.IgnoreCase
|
RegexOptions.Singleline
|
RegexOptions.IgnorePatternWhitespace);
Match mc

=
re.Match(html);

if
(
!
mc.Success)
{

return

""
;
}

string
qqnumber
=
mc.Groups[
1
].Value;

return
qqnumber;
}

上面注释的部分,就是获取到的相关信息,乱码一片,不过既然是本地处理,也是有规律可循的,本文只是介绍相关的规则,详细实现不在一一呈现,主要的处理逻辑就是获取本地Cookie的某项内容,并把内容进行截断,作为一个BaseNum,然后把获取到的数组进行乱码化,作为提交字段的名称,然后一一放置相关的内容,提交到服务处理。

上面只是一个简单的Demo,一般情况下,申请3~10左右的号码可能就会因为IP的限制,而不能继续,需要暂停几个小时才能继续可以申请一个左右,第二天才可以继续申请更多的号码。

为了更好的申请更多的QQ号码,一般需要加入拨号这种方式来处理IP的限制,如我的QQ搜通天中应用来批量申请QQ号码的界面如下所示:

如果需要了解详细,可以下载该软件来玩玩,熟悉下QQ申请的操作模式。

在之前的文章中,介绍了两篇关于GMap.NET这样的开源地图控件,介绍了其一些基本信息以及如何进行初步的应用。

Winform下的地图开发控件(GMap.NET)使用心得



Winform下的地图开发控件(GMap.NET)使用心得之二



这是一个基于Winform的地图控件。该地图控件的定义如下:

GMap.NET是一个强大、免费、跨平台、开源的.NET控件,它在Windows Forms 和WPF环境中能够通过Google, Yahoo!, Bing, OpenStreetMap, ArcGIS, Pergo, SigPac等实现寻找路径、地理编码以及地图展示功能,并支持缓存和运行在Mobile环境中。


GMap.NET是一个开源的GEO地图定位和跟踪程序。就像谷歌地图、雅虎地图一样,可以自动计算两地的距离,定位经纬度,与Google地图不同的是,该项目是建立在C#语言WinForm基础上的。可以对地图放大缩小,进行城市标记等。

本文主要介绍应用该控件来批量解析地址经纬度坐标。由于项目需要,我需要把数据库里面的4千多条地址信息解析出经纬度坐标,以供其他用途。经纬度坐标不要求非常精确,大体位置准确即可。如果人工通过Googlemap或者其他途径标准,工作量非常大,而且也很枯燥。由于之前接触过该控件,知道好像比较容易能够解析获取地址的经纬度坐标,那么我们把数据库信息逐一解析即可,这样可以节省人力,更重要的是,可以避免做枯燥的工作。我们先看看程序的运行效果,然后分析如何具体编码实现。

开始一段时间的效果图:

基本完成的效果图:


代码实现如下所示,主要利用了背景工作线程BackgroundWorker来提高界面的友好响应。



private
BackgroundWorker work
=

new
BackgroundWorker();


public
FrmMainMap()
{
InitializeComponent();

work.WorkerReportsProgress

=

true
;
work.DoWork

+=

new
DoWorkEventHandler(work_DoWork);
work.RunWorkerCompleted

+=

new
RunWorkerCompletedEventHandler(work_RunWorkerCompleted);
work.ProgressChanged

+=

new
ProgressChangedEventHandler(work_ProgressChanged);
}


void
work_ProgressChanged(
object
sender, ProgressChangedEventArgs e)
{

this
.progressBar.Value
=
e.ProgressPercentage;

this
.tsslTips.Text
=
e.UserState.ToString();
}


private

void
FrmMainMap_FormClosing(
object
sender, FormClosingEventArgs e)
{
work.DoWork

-=

new
DoWorkEventHandler(work_DoWork);
work.RunWorkerCompleted

-=

new
RunWorkerCompletedEventHandler(work_RunWorkerCompleted);
}


void
work_RunWorkerCompleted(
object
sender, RunWorkerCompletedEventArgs e)
{

string
tips
=

string
.Format(
"
更新完成, 共成功更新数据 {0} 个
"
, e.Result);

this
.tsslTips.Text
=
tips;

this
.progressBar.Value
=

100
;
MessageUtil.ShowTips(tips);
}


void
work_DoWork(
object
sender, DoWorkEventArgs e)
{

string
sql
=

"
select t.ID, t.company_addr from tbpark_enterprise t where t.company_addr is not null and t.latitude is null
"
;
Database db

=
DatabaseFactory.CreateDatabase();
DbCommand command

=
db.GetSqlStringCommand(sql);
Dictionary

<
int
,
string
>
addrList
=

new
Dictionary
<
int
,
string
>
();

using
(IDataReader dr
=
db.ExecuteReader(command))
{
SmartDataReader reader

=

new
SmartDataReader(dr);

while
(reader.Read())
{

int
id
=
reader.GetInt32(
"
ID
"
);

string
addr
=
reader.GetString(
"
company_addr
"
);
work.ReportProgress(

10
,
string
.Format(
"
正在处理地址[ {0} ]
"
, addr));


if
(
!
string
.IsNullOrEmpty(addr))
{

int
sindex
=
addr.IndexOfAny(
new

char
[] {
'

'
,
'
(
'
});

if
(sindex
>

0
)
{
addr

=
addr.Substring(
0
, sindex);
}


if
(
!
addrList.ContainsKey(id))
{
addrList.Add(id, addr);
}
}
}
}
work.ReportProgress(

20
,
string
.Format(
"
获取列表地址信息结束,正在获取Google地图坐标...
"
));


int
totoal
=
addrList.Count;

double
step
=
Convert.ToDouble(
100
)
/
totoal;

int
i
=

1
;

int
successCount
=

0
;

foreach
(
int
id
in
addrList.Keys)
{

int
percent
=
Convert.ToInt32(step
*
i);
work.ReportProgress(percent,

string
.Format(
"
正在获取Google地图坐标...,已成功获取 {0} 个
"
, successCount));


try

{

string
addr
=
addrList[id];
GeoCoderStatusCode unknow

=
GeoCoderStatusCode.Unknow;
PointLatLng

?
latLngFromGeocoder
=
Singleton
<
GMaps
>
.Instance.GetLatLngFromGeocoder(addr,
out
unknow);

if
(latLngFromGeocoder.HasValue
&&
(unknow
==
GeoCoderStatusCode.G_GEO_SUCCESS))
{
PointLatLng latLng

=
latLngFromGeocoder.Value;

sql

=

string
.Format(
"
Update tbpark_enterprise t set t.LATITUDE={0}, t.LONGITUDE={1} where t.ID={2}
"
,
latLng.Lat, latLng.Lng, id);
command

=
db.GetSqlStringCommand(sql);

bool
success
=
db.ExecuteNonQuery(command)
>

0
;

if
(success)
{
successCount

++
;
}
}

else

{
LogHelper.Info(unknow.ToString());
}
}

catch
(Exception ex)
{
LogHelper.Error(ex);
}

i

++
;
}
e.Result

=
successCount;
}


上面最为关键的部分代码就是如何解析地址为坐标的操作,判断如果解析成功,则获取相应的地理坐标即可,是下面这段:


string
addr
=
addrList[id];
GeoCoderStatusCode unknow

=
GeoCoderStatusCode.Unknow;
PointLatLng

?
latLngFromGeocoder
=
Singleton
<
GMaps
>
.Instance.GetLatLngFromGeocoder(addr,
out
unknow);

if
(latLngFromGeocoder.HasValue
&&
(unknow
==
GeoCoderStatusCode.G_GEO_SUCCESS))
{
PointLatLng latLng

=
latLngFromGeocoder.Value;

sql

=

string
.Format(
"
Update tbpark_enterprise t set t.LATITUDE={0}, t.LONGITUDE={1} where t.ID={2}
"
,
latLng.Lat, latLng.Lng, id);
command

=
db.GetSqlStringCommand(sql);

bool
success
=
db.ExecuteNonQuery(command)
>

0
;

if
(success)
{
successCount

++
;
}
}

很多情况下,我们可以基于GoogleMap做GIS方面的应用,只要涉及到地图的相关操作,基本上都可以派生出很多应用,如可以用在配电线路、水管布线、地标附加等操作。本文主要介绍基于图标叠加的应用,通过三种不同的GoogleMap操作,来分析各种操作的优劣,寻找出最符合实际、最高效简洁的操作方式,指导我们今后对GoogleMap的应用操作。

方式一:使用Javascript和Html混合方式处理。

在页面前台给Javascript赋相关的变量,在后台把相关属性内容(包含InfoWindows显示的内容,图标坐标等)赋值给脚本对象, 由脚本分拆对象数据,然后添加图标。如下面代码所示:



代码


<
script
type
="text/javascript"
charset
="utf-8"
>


var
map
=

null
;

var
xmapx
=

"
<%=map_x%>
"
;

var
ymapy
=

"
<%=map_y%>
"
;

var
title
=

"
<%=title%>
"
;

var
device_id
=

"
<%=deviceid%>
"
;

var
map_no
=

"
<%=mapno%>
"
;

var
center
=

"
<%=centers%>
"
;

var
districtname
=

"
<%=districtname%>
"
;

var
newIcon;


function
initialize() {

var
mapdivw
=
screen.width
-

250
;

var
mapdivh
=
screen.height
-

340
;

var
mapdiv
=
document.getElementById(
"
map_canvas
"
);
mapdiv.style.width

=
mapdivw;
mapdiv.style.height

=
mapdivh;


if
(GBrowserIsCompatible()) {
map

=

new
GMap2(document.getElementById(
"
map_canvas
"
));
//
新建一个地图


map.setCenter(
new
GLatLng(
23.136216352329377
,
113.32762241363525
),
15
);
//
定义地图中心坐标


map.addControl(
new
GLargeMapControl());

showmarker();
}

else
{
alert(

'
你使用的浏览器不支持 Google Map!
'
);
}
}


function
showmarker() {

var
mapxx
=
xmapx.split(
"
|
"
);

var
mapyy
=
ymapy.split(
"
|
"
);

var
title_
=
title.split(
"
|
"
);

var
devices_id
=
device_id.split(
"
|
"
);

var
mapnos
=
map_no.split(
"
|
"
);

var
firstPoint
=

null
;
//
获得位置,移动到此处




for
(
var
i
=

0
; i
<
mapxx.length
-

1
; i
++
) {

var
_title
=
title_[i].replace(
/
<br>
/
g,
'
\n
'
);

newIcon

=

new
GIcon(G_DEFAULT_ICON);

//
newIcon.iconSize=new GSize(48, 48);


newIcon.image
=

"
http://gmaps-samples.googlecode.com/svn/trunk/markers/blue/marker
"

+
(i
+

1
)
+

"
.png
"


//
newIcon = MapIconMaker.createMarkerIcon({ width: 48, height: 48, primaryColor: "#00ff00" });


point
=

new
GLatLng(mapxx[i], mapyy[i]);
//
经纬度



if
(firstPoint
==

null
) {
firstPoint

=
point;
}

markers(point, _title, title_[i], devices_id[i]);
}
map.panTo(firstPoint);

//
移动到第一个点


}


function
markers(point, _title, title_br, _device_id) {

var
marker
=

new
GMarker(point, { draggable:
false
, icon: newIcon, title: _title });
//
markerOptions); //新建一个标记


map.addOverlay(marker)
//
叠加一个层



GEvent.addListener(marker,

"
click
"
,
function
() {
marker.openInfoWindowHtml(

"
<table border=0><tr><td>
"

+
title_br
+

"
</td></tr></table>
"
);
});
}

最终HTML生成会有很多字符的变量,如下面所示:



代码


<
script
type
="text/javascript"
charset
="utf-8"
>



var
map
=

null
;


var
xmapx
=

"
23.1403303909433650|23.1391267764026970|23.1348449780739640|23.13282242210374|23.1326448303667040|23.1391761050798230|23.13819939389483|23.1358414456525060|23.1322699137051440|23.1325363019671920|23.1306123748529370|
"
;


var
ymapy
=

"
113.32088470458984|113.32077741622925|113.32098126411438|113.32255840301513|113.32709670066833|113.32873821258545|113.32922101020813|113.32914590835571|113.32933902740478|113.33328723907470|113.32048773765564|
"
;


var
title
=

"
停车场名称:中石化大厦停车场<br>停车场地址:中石化大厦停车场<br>停车场类型:商业<br>总车位数:600<br>当前空车位:521<br>收费类型:政府指导价<br>收费标准:<br>|停车场名称:城建大厦停车场<br>停车场地址:<br>停车场类型:<br>总车位数:240<br>当前空车位:196<br>收费类型:<br>收费标准:<br>|停车场名称:维多利亚广场停车场<br>停车场地址:<br>停车场类型:<br>总车位数:338<br>当前空车位:304<br>收费类型:<br>收费标准:<br>|停车场名称:天河城停车场<br>停车场地址:广州市天河区天河路208号天河城广场地下车库经营室<br>停车场类型:<br>总车位数:800<br>当前空车位:573<br>收费类型:<br>收费标准:<br>|停车场名称:正佳广场停车场<br>停车场地址:<br>停车场类型:商业<br>总车位数:1500<br>当前空车位:925<br>收费类型:<br>收费标准:<br>|停车场名称:南方证券大厦停车场<br>停车场地址:<br>停车场类型:商业<br>总车位数:134<br>当前空车位:106<br>收费类型:<br>收费标准:<br>|停车场名称:金利来大厦停车场<br>停车场地址:<br>停车场类型:商业<br>总车位数:120<br>当前空车位:47<br>收费类型:<br>收费标准:<br>|停车场名称:财富大厦停车场<br>停车场地址:<br>停车场类型:商业<br>总车位数:157<br>当前空车位:0<br>收费类型:<br>收费标准:<br>|停车场名称:创展中心停车场<br>停车场地址:<br>停车场类型:商业<br>总车位数:340<br>当前空车位:325<br>收费类型:<br>收费标准:<br>|停车场名称:丰兴大厦停车场<br>停车场地址:<br>停车场类型:商业<br>总车位数:530<br>当前空车位:433<br>收费类型:<br>收费标准:<br>|停车场名称:东洲大厦停车场<br>停车场地址:<br>停车场类型:商业<br>总车位数:100<br>当前空车位:74<br>收费类型:<br>收费标准:<br>|
"
;


var
device_id
=

"
P-0001|P-0002|P-0003|P-0007|P-0008|P-0006|P-0005|P-0004|P-0009|P-0010|P-0011|
"
;

.....................

这样方式处理,虽然较容易理解,不过由于是代码混合,操作及扩展还是相对比较麻烦,而且代码量也很多,多数是Javascript脚本,操作控制也不是很方便。 Asp.net后台代码还需要给这些变量赋值,每个项目之间通过|分号来分隔,这样对于少量的图标操作,也倒是响应很快。但是对于超过几十个乃至上百个,图标操作处理就变得非常缓慢,因此这种方式处理,是极为不好的。

方式二:使用GGeoXml的Javascript对象。

通过动态的URL对象,根据不同的参数,返回不同的XML对象流给Javascript脚本处理,这种方式简洁明了,而且速度也非常快,属于不错的处理方式。主要的代码如下所示:


代码


<
script
type
="text/javascript"
>


var
map;

var
geoXml;

var
center
=

"
<%=centers%>
"
;

function
initialize() {

if
(GBrowserIsCompatible()) {

//
地址必须是公网能访问的地址,否则不会显示图标


map
=

new
GMap2(document.getElementById(
"
map_canvas
"
));


center_set();
map.addControl(

new
GLargeMapControl());
geoXml

=

new
GGeoXml(
"
http://219.136.**.**/Pages/Expert/
GetParkKML.aspx?area=<%=Server.UrlEncode(Request.QueryString["area"])%>");

map.addOverlay(geoXml);


}

else
{
alert(

'
你使用的浏览器不支持 Google Map!
'
);
}
}

..................

这样把主动权交给GetParkXML.aspx来组装数据了,这个页面主要负责构造相应的KML文件(就是一个XML文件)给Javascript对象,这个页面的代码大致如下所示:



代码

public partial class GetParkKML : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string area = Request.QueryString["area"];
string areaCountry = area;//行政区
DataTable table = BLLFactory

<
Enterprise
>
.Instance.FindByStreetParkAreaName("", areaCountry);

StringBuilder str = new StringBuilder();
str.Append("

<?
xml version=\"1.0\" encoding=\"UTF-8\"
?>
\r\n");
str.Append("

<
kml
xmlns
=\"http://www.opengis.net/kml/2.2\"
>
\r\n");
str.Append("

<
Document
>
\r\n\r\n");
//必须用公网地址:http://gmaps-samples.googlecode.com/svn/trunk/markers/blue/blank.png
str.Append("\t

<
Style
id
=\"myStationStyles\"
>
<IconStyle><scale>1</scale><Icon><href>http://219.136.*.*/Themes/Default/parking.png</href></Icon></IconStyle>
</
Style
>
");

StringBuilder sbTitle = new StringBuilder();
foreach (DataRow row in table.Rows)
{
str.Append("

<
Placemark
>
\r\n");
str.Append(string.Format("\t

<
name
>
<!
[CDATA[{0}]]
>
</
name
>
\r\n", GetParkName(row)));
str.Append(string.Format("\t

<
description
>
<!
[CDATA[{0}]]
>
</
description
>
\r\n", GetParkDescription(row)));
str.Append("\t

<
styleUrl
>
#myStationStyles
</
styleUrl
>
\r\n");
string longLat = string.Format("{0},{1},0", row["LONGITUDE"], row["LATITUDE"]);
str.Append(string.Format("\t

<
Point
><
coordinates
>
{0}
</
coordinates
></
Point
>
\r\n",  longLat));
str.Append("

</
Placemark
>
\r\n");
}
str.Append("

</
Document
>
\r\n");
str.Append("

</
kml
>
");

Response.Clear();
Response.ContentType = "application/vnd.google-earth.kml+xml";
Response.Write(str.ToString());
Response.Flush();
Response.End();
}
.............

这种方式是比较通用的处理方式,很多Googmap使用者应该都是基于此类型操作较多,详细还可以参考一篇文章介绍《
基于google map api开发web和google earth的KML地标插件
》,作者写的思路比较清晰,值得参考。

该方式处理的问题,就是图标数量比较多的时候(如500个以上),速度比较慢,甚至有时候就不显示图标了,不知何故,不过对于小于这个数量级的,一般还是不错的。

3、 方式三:基于方式二做进一步的改进。

由于方式二,还是每次都会请求生成图标的KML文件,生成的过程如果涉及操作逻辑比较复杂,数量比较多的时候,就会出现比较缓慢,或者甚至显示不了图标的情况。那么我们如果认为图标内容在一段的时间内不会发生太大的变化的话,其实可以预先生成好XML文件,然后直接给Javascript前台操作显示,这样处理肯定快很多,而且也可以避免方式二有时候出不来图标的情况。

处理操作,其实很简单,就是把方式二的代码

geoXml = new GGeoXml("http://219.136.**.**/Pages/Expert/
GetParkKML.aspx?area=<%=Server.UrlEncode(Request.QueryString["area"])%>");

修改为下面代码即可。

geoXml = new GGeoXml("http://219.136.*.*/Pages/Expert/
<%
=
fileName
%>
.kml");

然后在页面后台中,我们根据不同的条件,指定不同的XML文件给它就可以了。


public

string
centers
=

"
23.140330390943365,113.32088470458984|
"
;

public

string
fileName
=

""
;

public

string
googleMapKey
=

""
;

protected

void
Page_Load(
object
sender, EventArgs e)
{
googleMapKey

=
Helper.GetGoogleMapKey(Request.Url.AbsoluteUri);


string
area
=
Request.QueryString[
"
area
"
];

string
areaCountry
=
area;
//
行政区



if
(
string
.IsNullOrEmpty(area))
{
centers

=

"
23.136216352329377, 113.32762241363525
"
;

return
;
}


string
centerLatlng
=
BLLFactory
<
Enterprise
>
.Instance.FindAreaCenterLatLng(area);



if
(
!
string
.IsNullOrEmpty(centerLatlng))
{
centers

=
centerLatlng;
}

fileName

=
area.GetHashCode().ToString();
//
不能用中文,故出此策



string
filePath
=
Server.MapPath(
"
.
"
)
+

"
/
"

+
fileName
+

"
.kml
"
;

if
(
!
File.Exists(filePath))
{
CreateMapData();
}

else

{

//
可指定更新的日期自动对比


DateTime dtLastTime
=
GetLastWriteTime(filePath);

if
(DateTime.Now
>
dtLastTime.AddMonths(
1
))
{
CreateMapData();
}
}
}


private

void
CreateMapData()
{

string
area
=
Request.QueryString[
"
area
"
];

if
(
!
string
.IsNullOrEmpty(area))
{

string
httpUrl
=
Request.Url.AbsoluteUri;

int
eindx
=
httpUrl.LastIndexOf(
'
/
'
);

string
dataurl
=
httpUrl.Substring(
0
, eindx)
+

string
.Format(
"
/GetParkKML.aspx?area={0}
"
, Server.UrlEncode(area));
WebClient client

=

new
WebClient();
client.Encoding

=
Encoding.UTF8;

string
content
=
client.DownloadString(dataurl);

fileName

=
area.GetHashCode().ToString();
//
不能用中文,故出此策



string
filePath
=
Server.MapPath(
"
.
"
)
+

"
/
"

+
fileName
+

"
.kml
"
;
File.WriteAllText(filePath, content, Encoding.UTF8);
}
}


///

<summary>


///
取文件最后存储时间

///

</summary>


///

<param name="fullpath"></param>


///

<returns></returns>



public

static
DateTime GetLastWriteTime(
string
fullpath)
{
FileInfo fi

=

new
FileInfo(fullpath);

return
fi.LastWriteTime;
}
}

最后,还应该提供一个按钮给用户,实现数据的实时更新,而不是每次由系统根据文件的情况更新,这样可以给用户更好的选择,保证数据的及时性。


protected

void
btnRefreshData_Click(
object
sender, ImageClickEventArgs e)
{

string
area
=
Request.QueryString[
"
area
"
];
CreateMapData();


string
url
=

string
.Format(
"
ParkGisSearch.aspx?area={0}
"
, Server.UrlEncode(area));
Response.Redirect(url);
}

实际效果,这样在操作近千个图标的时候,速度非常快,比前面两种都快得多,而且也不会出现有时候方式二呈现不了图标的情况,基本上解决效果还是比较满意的。。实际项目的效果截图如下所示。

广州爱奇迪营销助手


QQ搜通天企业版

采集网易圈子信息









文件大小

< 28M

更新时间

2013-12-07

下载地址

系统截图浏览:




产品说明如下:
★软件功能
广州爱奇迪营销助手,是一款网络营销应用软件,通过采集个人群成员QQ或电子邮件,网易圈子电子邮件等目标群体的QQ号码或者邮件内容,能够迅速定位目标用户群体,并可通过群发邮件等方式进行网络营销,迅速推广您的产品和公司业务信息,本软件能够为您提供最有好的服务。

该软件能快速采集特定目标群体的邮件信息并可导出使用,集成邮件群发功能,采用不用发件人信息可发送邮件的邮件直投技术和普通发送方式结合,采用多线程发送邮件,有效利用您的机器资源,发送过程不影响您的其他软件操作。

★系统需求

广州爱奇迪营销助手,使用C#语言开发 适运行在 Microsoft WindowsNT/2000/XP/2003 等平台,但必须安装有.Net4.0平台.
如果您的计算机不能运行本程序,强烈建议您下载 .NET 4.0 运行库。

随着微软.NET战略的推进,必将有越来越多的程序使用.NET进行开发,而此架框是必须具备组件。.NET Framework 4.0 官方下载地址:
http://www.microsoft.com/zh-cn/download/details.aspx?id=17718

★软件注册

广州爱奇迪营销助手
是一款共享软件,您可以自由发布和传播软件,未注册版本会在使用30天后过期。在带给您方便的同时, 希望您能考虑注册这款软件,注册软件的时候请提供软件的机器码。如果您在电脑报刊发表了介绍这个软件的文章,将刊物名称和期号告诉我,我们将免费为您提供注册码。或者您有好的建议/意见/程序错误,请写信告诉我们,如果我们采纳,也将免费为您提供注册码。

该软件版权归伍华聪所有,作者保留该软件所有权利,未注册用户不得将该软件用于商业用途。


转帐或银行汇款注意事项

1. 请汇款或者转帐后,及时联系我们。可以使用邮件wuhuacong@163.com, 6966254@qq.com进行联系。
2. 银行汇款请先联系我们确认具体的汇款地址,避免耽误您的时间。
3. 建议使用支付宝进行支付,安全快捷且免手续费。





顶层
帐 号:9558 8236 0200 8692 135
户 名:伍华聪
地 址:广东省广州市

顶层
帐 号:wuhuacong@hotmail.com
户 名:伍华聪
地 址:广东省广州市



在前段时间一篇随笔《
利用优秀的.NET界面控件,打造新潮的界面效果
》中介绍过Dotnetbar的界面效果,虽然引发不少关于该控件效果的争议,不过话说回来,使用该控件也不失为一个界面的解决方案,本文继续探寻该控件的使用,在QQ搜通天企业版软件中使用该控件做了一次完整的改造,碰到并解决了一些问题,本文主要总结介绍如何利用Dotnetbar控件来实现多文档界面的效果。首先我们先来看看软件的主体界面效果,如下图所示。

QQ搜通天企业版

本界面主要利用Bar控件来实现多文档的界面效果,每个子窗体皆为一个用户控件,当然也包括容纳各种窗体的容器CtrlMdiBar类,也是一个用户控件,在主界面Ribbon控件的RibbonClientPanel区域放置容器,然后每次打开窗体,就动态创建或者激活一个窗体页,这样就实现了多文档界面的效果了。

bar
=

new
CtrlMdiBar();
bar.DockTabClosed

=

new
DockTabClosedDelegate(OnDockItemClosed);
SetDetailPanel(bar);



private
CtrlMdiBar bar;

private
Dictionary
<
string
, DockContainerItem
>
MdiDict
=

new
Dictionary
<
string
, DockContainerItem
>
();


public

void
SetDetailPanel(UserControl uc)
{

if
(uc
==

null
)
{

throw

new
ArgumentNullException(
"
uc
"
,
@"
用户控件uc不能为空
"
);
}

uc.Dock

=
DockStyle.Fill;
ribbonDetailPanel.Controls.Clear();
ribbonDetailPanel.Controls.Add(uc);
}


public

void
SetMdiForm(UserControl uc,
string
itemText)
{
DockContainerItem item

=

null
;

string
type
=
itemText;
//
uc.GetType().Name;



if
(MdiDict.ContainsKey(type))
{
item

=
MdiDict[type];
}

else

{
PanelDockContainer panel

=

new
PanelDockContainer();
item

=

new
DockContainerItem();
item.Control

=
panel;
item.Text

=
itemText;
uc.Dock

=
DockStyle.Fill;
panel.Controls.Add(uc);

MdiDict.Add(type, item);
bar.bar1.Items.Add(item);
}
bar.bar1.SelectedDockContainerItem

=
item;

this
.Refresh();
}


private

void
OnDockItemClosed(DockContainerItem item)
{

string
type
=
item.Text;

if
(MdiDict.ContainsKey(type))
{
MdiDict.Remove(type);
}
}

然后每次按钮打开一个窗体页的时候,只需要简单的调用函数即可,如下面几个窗体页的打开操作一样


private

void
btnMyQunUser_Click(
object
sender, EventArgs e)

{
SetMdiForm(

new
CtrlGroupUser(), MDIForm.查询个人群成员.ToString());
}


private

void
btnMyQQUser_Click(
object
sender, EventArgs e)
{
SetMdiForm(

new
CtrlQQContact(), MDIForm.查询个人QQ好友.ToString());
}


private

void
btnXiaoyou_Click(
object
sender, EventArgs e)
{
SetMdiForm(

new
CtrlXiaoyou(), MDIForm.查询QQ校友.ToString());
}

其中CtrlMDIBar容器,主要是一个用户控件放置一个Dotnetbar的Bar控件,然后在该控件里面调用委托处理控件关闭的事件,主要代码如下所示。


public

delegate

void
DockTabClosedDelegate(DockContainerItem item);

public

partial

class
CtrlMdiBar : UserControl
{

public
DockTabClosedDelegate DockTabClosed;


public
CtrlMdiBar()
{
InitializeComponent();
}


private

void
bar1_DockTabClosed(
object
sender, DevComponents.DotNetBar.DockTabClosingEventArgs e)
{

//
MessageExUtil.ShowTips("DockTabClosed");



if
(DockTabClosed
!=

null
)
{
DockTabClosed(e.DockContainerItem);
}
}

...............

这样,我们后面如果要增加一个窗体页放到容器里面,只需要再定义一个用户控件,并设计好界面等处理方式即可,动态增加一个界面窗体页将非常简单。