[转载]flex 联机游戏开发四国军棋游戏:(二)棋盘棋子

mikel阅读(1152)

[转载]flex 联机游戏开发 – 四国军棋游戏:(二)棋盘棋子 – 博弈居 – 开放的第三方应用游戏开发测试平台 – 博客园.

玩了太多年的四国游戏,现在,我打算做个四国游戏的flex版,下面的文章与代码是边做边写的,所以,当我贴出来的时候,说不定我已经将代码进行重构了,但是,如果你也是一名开发者,我想,设计思路总是对你有参考意义的。

想知道我爱四国的多深,看看引子里的那个文章就知道了。你也可以点击这儿查看这些文章的全部。中途岛之战 深圳mm.活着viva&&冷血雅雅

老规矩,先画棋盘,一般的军棋游戏棋盘都利用的图片做背景,然后判断鼠标的点击来定位棋子,现在,我决定不用图片,直接用flex绘制棋盘,源于两 个方面的原因,一是adobe是做美术出身的,所以,用flex绘制的棋盘基本上在ui上比java,C#绘制的要好看得多,同时,你可以很方便地对棋 盘,棋子使用各种滤镜效果。二是绘制出来的棋盘,本身会减小相当多的开发工作,因为你点击的如果是棋子,他本身就可以实现事件触发。将细节处理放到了元部 件中。

一,单人棋盘 Layout

public function createBoard():void
{

for (var row:int = 0; row <=xsize; row++) {
var tempArray:Array=new Array(ysize);
gameControl.boardArray[row] =tempArray;
}
//y 轴的线条
for (var i:int=0;i<=ysize;i++)
{
var line:Line=new Line();
line.xFrom=0+padding;
line.xTo=board.width-padding;
line.y=i*(board.height-2*padding)/ysize+padding;
line.stroke=DEFAULT_STROKE;
board.addElement(line);
}
//x 轴的线条
for (var j:int=0;j<=xsize;j++)
{
var line2:Line=new Line();
line2.yFrom=0+padding;
line2.yTo=board.height-padding;
line2.x=j*(board.width-2*padding)/xsize+padding;
line2.stroke=DEFAULT_STROKE;
board.addElement(line2);
}
//营里的线条
for (var x1:int=0;x1<=xsize;x1++)
{
for (var y1:int=0;y1<=ysize;y1++)
{
if ((x1==1&&y1==1)||(x1==3&&y1==3)||(x1==2&&y1==2)||(x1==1&&y1==3)||(x1==3&&y1==1))
{
//画四个方向的线
board.addElement(drawLine(x1*(board.width-2*padding)/xsize+padding,(x1+1)*(board.width-2*padding)/xsize+padding,y1*(board.height-2*padding)/ysize+padding,(y1+1)*(board.height-2*padding)/ysize+padding));
board.addElement(drawLine(x1*(board.width-2*padding)/xsize+padding,(x1+1)*(board.width-2*padding)/xsize+padding,y1*(board.height-2*padding)/ysize+padding,(y1-1)*(board.height-2*padding)/ysize+padding));
board.addElement(drawLine(x1*(board.width-2*padding)/xsize+padding,(x1-1)*(board.width-2*padding)/xsize+padding,y1*(board.height-2*padding)/ysize+padding,(y1+1)*(board.height-2*padding)/ysize+padding));
board.addElement(drawLine(x1*(board.width-2*padding)/xsize+padding,(x1-1)*(board.width-2*padding)/xsize+padding,y1*(board.height-2*padding)/ysize+padding,(y1-1)*(board.height-2*padding)/ysize+padding));
}
}
}
//默认的棋盘填充,
for (var x:int=0;x<=xsize;x++)
{
for (var y:int=0;y<=ysize;y++)
{
if ((x==1&&y==5)||(x==3&&y==5))
//画大本营
board.addElement(drawDaBenYing(x,y));
else if ((x==1&&y==1)||(x==3&&y==3)||(x==2&&y==2)||(x==1&&y==3)||(x==3&&y==1))
//画营
board.addElement(drawYing(x,y));
else
//其它
board.addElement(drawQizhi(x,y));
}
}

}

现在我对决定在每个地方加一个默认的棋子,默认是不可见的,但在棋盘的实际情况应该是这样的。在测试的时候加入一种颜色,实际情况中应该是选择alpha为0,这样,棋子是不可见的,同样,这个函数也需要传入用户的位置,这样,每个玩家就可以选择不同颜色的棋子。

//默认的棋盘填充,
for (var x:int=0;x<=xsize;x++)
{
for (var y:int=0;y<=ysize;y++)
{
if ((x==1&&y==5)||(x==3&&y==5))
//画大本营
board.addElement(drawDaBenYing(x,y));
else if ((x==1&&y==1)||(x==3&&y==3)||(x==2&&y==2)||(x==1&&y==3)||(x==3&&y==1))
//画营
board.addElement(drawYing(x,y));
else
//其它
board.addElement(drawRect(x,y));

//放一个默认的棋子
board.addElement(drawQizhi(x,y));
}
}

//默认的棋盘填充,
for (var x:int=0;x<=xsize;x++)
{
for (var y:int=0;y<=ysize;y++)
{
if ((x==1&&y==5)||(x==3&&y==5))
//画大本营
board.addElement(drawDaBenYing(x,y));
else if ((x==1&&y==1)||(x==3&&y==3)||(x==2&&y==2)||(x==1&&y==3)||(x==3&&y==1))
//画营
board.addElement(drawYing(x,y));
else
//其它
board.addElement(drawRect(x,y));

//放一个默认的棋子
board.addElement(drawQizhi(x,y));
}
}

对不同颜色的棋子,我们再加入一个默认的数组来填充不同用户的颜色,同样,我们可以设置这个数组是可变的,当你的上家说,要吃红子时,实际上红子对不同位置的玩家是完全不一样的,有一定的迷惑作用,当然,他可以说,人吃上家或下家,总归是道高一,魔高一章啊。

//玩家填充
private const TURNARRAY:Array=[new SolidColor(0xff0000,1),new SolidColor(0x00ff00,1),new SolidColor(0x0000ff,1),new SolidColor(0xffcc00,1),new SolidColor(0xffffff,0)];

哦,差点忘了,在游戏中还有另外一种状态,那就是棋子是覆盖着的,不可见的。我们也得把这个加上。

二 四人棋盘 Board

现在,每个人的棋盘已经画好,我们现在需要四个棋盘+一个中间部分来完成整个棋盘的初始化工作,flex对效果的支持现在开始发威,我们只要把这个棋盘放上四份,一份为原始,一份旋转90,一份旋转-90,一份旋转180度,就可以完成4个棋盘的工作,

<s:Group width=”660″ height=”660″ left=”20″ top=”20″>
<ns1:Layout x=”425″ y=”220″ rotation=”180″>
</ns1:Layout>
<ns1:Layout x=”220″ y=”235″ rotation=”90″>
</ns1:Layout>
<ns1:Layout x=”235″ y=”440″>
</ns1:Layout>
<ns1:Layout x=”440″ y=”425″ rotation=”-90″>
</ns1:Layout>
<ns1:CenterGroup x=”220″ y=”220″>
</ns1:CenterGroup>
</s:Group>

下面的工作就是画中间那个部分了。

protected function init(event:FlexEvent):void
{
// TODO Auto-generated method stub
// TODO Auto-generated method stub
//y 轴的线条

BOARD_IMG.source=BoardChess;
BOARD_IMG.fillMode =  BitmapFillMode.REPEAT;

for (var i:int=0;i<=2;i++)
{
var line:Line=new Line();
line.xFrom=0;
line.xTo=center.width;
line.y=i*(center.height-2*padding)/2+padding;
line.stroke=DEFAULT_TEDAO_STROKE;
center.addElement(line);
}
//x 轴的线条
for (var j:int=0;j<=2;j++)
{
var line2:Line=new Line();
line2.yFrom=0;
line2.yTo=center.height;
line2.x=j*(center.width-2*padding)/2+padding;
line2.stroke=DEFAULT_TEDAO_STROKE;
center.addElement(line2);

}

//边角的弧
center.addElement(drawArc(-padding,-padding));
center.addElement(drawArc(-padding,2*(center.height-2*padding)/2+padding));
center.addElement(drawArc(2*(center.height-2*padding)/2+padding,-padding));
center.addElement(drawArc(2*(center.height-2*padding)/2+padding,2*(center.height-2*padding)/2+padding));

//填充,
for (var x:int=0;x<=2;x++)
{
for (var y:int=0;y<=2;y++)
{

var rect:Rect=new Rect();
rect.width=15;
rect.height=15;
rect.x=x*(center.width-2*padding)/2-15/2+padding;
rect.y=y*(center.height-2*padding)/2-15/2+padding;
rect.radiusX=3;
rect.radiusY=3;
rect.stroke=DEFAULT_STROKE;
rect.fill=BOARD_IMG;
center.addElement(rect);
}
}
}

这样,所有的部分已经完成,我们现在稍微改变一下逻辑,将底纹填充放到主类,在layout类不进行填充。生成的棋局应该就是这样子的。

三,最终布局

最后,我们将用户的信息传入棋盘,并加入一些控制键,最终形成的棋盘应该如下

<s:BorderContainer id=”board” width=”880″ height=”670″  top=”20″ left=”150″ horizontalCenter=”0″>
<ns1:CenterGroup x=”320″ y=”198″>
</ns1:CenterGroup>
<ns1:Layout x=”530″ y=”219″ rotation=”180″>
</ns1:Layout>
<ns1:Layout x=”341″ y=”218″ rotation=”90″>
</ns1:Layout>
<ns1:Layout x=”340″ y=”407″>
</ns1:Layout>
<ns1:Layout x=”529″ y=”408″ rotation=”-90″>
</ns1:Layout>
<ns1:UserInfo x=”221″ y=”17″>
</ns1:UserInfo>
<ns1:UserInfo x=”550″ y=”433″>
</ns1:UserInfo>
<ns1:UserInfo x=”10″ y=”228″>
</ns1:UserInfo>
<mx:Button id=”btnlose” label=”投降” enabled=”false”   x=”669″ y=”465″ width=”70″ height=”21″/>
<mx:Button id=”btnstart” label=”开始” enabled=”true”   x=”669″ y=”435″ width=”70″ height=”21″/>
<mx:Button id=”btnsave” label=”调入布局” enabled=”true”   x=”669″ y=”525″ width=”70″ height=”21″/>
<mx:Button id=”btnopen” label=”保存布局” enabled=”true”   x=”669″ y=”495″ width=”70″ height=”21″/>
<ns1:UserInfo x=”763″ y=”228″>
</ns1:UserInfo>

</s:BorderContainer>

[转载]爆破内存中的SWF文件

mikel阅读(1078)

[转载]爆破内存中的SWF文件 – zcsor ~~~ VB业余爱好者的窝 – 博客园.

因为最近有一点需要,所以想提取打开的网页里面的SWF文件出来,其实以前也做过,用个游戏修改器,搜索一下FWS然后挨个检查一下结果,手工复制 一下内存数据。这次的却比较多,挨个弄比较麻烦还容易出错。于是写了一小段程序。只是需要注意的是,很多浏览器并不是那个有启动窗口的进程是我们要的…… 哎……不提这个,只是提一下这个实现。其实嘿嘿,前面那个从OFFICE中提取的,也可以考虑一下用这个代替?~咕~~(╯﹏╰)b~速度,速度……慢点 点而已了

1、枚举进程,列表以供选择

2、枚举所选进程内存块,搜索FWS字样……貌似叫暴力……(其实只需稍加修改源程序就可以连同CWS一起搜索)

3、筛选,依据被定为这样几个:

A、版本(第四字节)为,7,8,9,10的

B、文件大小大于0(5-8字节)

C、文件结尾为0X40,0X00,0X00,0X00的

第一部分,枚举进程其实没什么好说的,Process类就可以了。

第二部分,其实就是核心内容了:内存检索,一般来说,应该先枚举进程所用的内存块及其属性,这样可以通过属性进行筛选,但实际在实现时,我是用ReadProcessMemory函数是否出错来决定是否继续搜索本块内存的……真是懒人啊~~~~

代码

Public Structure MEMORY_BASIC_INFORMATION
Dim BaseAddress As Integer
Dim AllocationBase As Integer
Dim AllocationProtect As Integer
Dim RegionSize As Integer
Dim State As Integer
Dim Protect As Integer
Dim lType As Integer
End Structure
‘出处http://www.cnblogs.com/zcsor/
Private Declare Function VirtualQueryEx Lib kernel32 (ByVal hProcess As Int32, ByVal lpAddress As IntPtr, ByRef lpBuffer As MEMORY_BASIC_INFORMATION, ByVal dwLength As Int32) As Int32
Private Declare Function ReadProcessMemory Lib kernel32 (ByVal hProcess As Integer, ByVal lpBaseAddress As Integer, ByVal lpBuffer() As Byte, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Integer
Private Shared m_phandle As Integer

这样就声明了所用的API函数,接下来是枚举过程:

代码

Private Shared Function GetMemoryInfo(ByVal pHandle As Integer) As ArrayList
Dim Infs As New ArrayList
Dim pAddr As Integer, dwTotalCommit As Integer, ret As Integer, miLen As Integer
Dim mi As New MEMORY_BASIC_INFORMATION
miLen
= Len(mi)
dwTotalCommit
= 0 这是结果
pAddr = 0 这个时查询起始地址,设为0,即进程虚拟地址开始处。
ret = VirtualQueryEx(pHandle, pAddr, mi, miLen) 从起始地址开始查询
Infs.Add(mi)
Do While (ret = miLen)
dwTotalCommit
= dwTotalCommit + mi.RegionSize
pAddr
= mi.BaseAddress + mi.RegionSize 跳过已经查询过的内存块,到未被查询的内存地址起始处
ret = VirtualQueryEx(pHandle, pAddr, mi, miLen) 再次查询,直到查询失败(所有可查询地址都已经查过了)
If mi.State = &H1000 Then Infs.Add(mi)
Loop
Return Infs
End Function

很简单,相信大家一看就懂~~~~~~~~~~接下来,就是读内存数据了,这里需要考虑这样一个问题:有些内存块大的……可怜的VB溢出了咋 办……前几天绘制一个几十万像素宽的图像就……咕~~(╯﹏╰)b,其实解决办法很简单的,分块即可,这里读写内存我们就分1024字节吧~~当然有一定 原因了~~分4096也许你喜欢?O(∩_∩)O~其实编码起来都差不多,只要你知道~~~原因

出处:http://www.cnblogs.com/zcsor/

代码


Public Shared Function Scan(ByVal pHandle As Integer) As ArrayList
m_phandle
= pHandle
If Not ToKen.ToKenPrivileges() Then MsgBox(提升权限失败,扫描结果可能有遗漏)
Dim Ret As New ArrayList
Dim int As ArrayList = GetMemoryInfo(pHandle)
For Each mi As MEMORY_BASIC_INFORMATION In int
Dim bLen As Integer = mi.RegionSize
Dim rLen As Integer
Dim mBaseAddr As Integer = mi.BaseAddress
Dim mStep As Integer = 1024
Dim test(3) As Byte
Do While bLen > 0
If bLen > mStep Then rLen = mStep Else rLen = bLen
bLen
-= mStep
Dim Bytes(rLen 1) As Byte
Dim read As Integer = ReadProcessMemory(pHandle, mBaseAddr, Bytes, rLen, 0)
If read = 0 Then Exit Do
For mIndex = 0 To mStep 3 Step 4
If Bytes(mIndex) = &H46 AndAlso Bytes(mIndex + 1) = &H57 AndAlso Bytes(mIndex + 2) = &H53 Then just FWS
Dim f As New Flash
f.addr
= mIndex + mBaseAddr
f.ver
= Bytes(mIndex + 3)
f.size
= System.BitConverter.ToInt32(Bytes, 4)
If f.size > 0 AndAlso f.ver > 6 AndAlso f.ver < 11 Then
read
= ReadProcessMemory(pHandle, mBaseAddr + mIndex + f.size 4, test, 4, 0)
If read = 0 Then
Exit Do
Else
If test(0) = &H40 AndAlso test(1) = 0 AndAlso test(2) = 0 AndAlso test(3) = 0 Then Ret.Add(f)
End If
End If
End If
Next
mBaseAddr
+= mStep
Loop
Next
Return Ret
End Function

Structure Flash
Public addr As Integer
Public ver As Integer
Public size As Integer
Public Overrides Function ToString() As String
Return addr: & Hex(addr) & vbCrLf & _
ver: & ver & vbCrLf & _
size: & Format(size, #,0.) & vbCrLf
End Function
End Structure

这样,大功告成了啊~得到了可读的内存空间内的全部貌似SWF文件的信息,接下来……保存它们吧!

代码

Shared Function Save2File(ByVal Pid As Integer, ByVal addr As Integer, ByVal size As Integer) As Boolean
Dim bs(size 1) As Byte
Dim read As Integer = ReadProcessMemory(m_phandle, addr, bs, size, 0)
If read = 0 Then
Return False
Else
My.Computer.FileSystem.WriteAllBytes(
c:\ & Hex(Pid) & _ & Hex(addr) & .swf, bs, False)
End If
End Function

呃,发现我确实很懒了吧……其实也检测了一下,当读内存失败的时候不保存(也没法保存嘛~),这是防止FWS字样出现的位置后面的字节数不够SIZE(超出内存段)时的错误,换句话说,就是如果代码没写错,那这个FWS一定不是一个FLASH文件~

’出处http://www.cnblogs.com/zcsor/

最后附上,成品~呃,其实你自己写一下修改修改我的代码,才真的是成品,这个里面不识别压缩的~~其实压缩的和这个一样的~~~~如果你愿意解压,那就解压吧~几句代码的事……不过我懒

/Files/zcsor/FLASH提取工具.7z

[转载]迅雷网站设计浅析

mikel阅读(1020)

[转载]迅雷网站设计浅析 – 莫文的博客 – 博客园.

很久之前就想写篇关于迅雷网站的设计分析文章了,迅雷看看一直是我在线看电影的网站,也是我比较喜欢的网站,所以我也比较乐于分析一下它的设计,先从结构上分析,也就是导航、搜索、详细信息展示、以及播放页面的顺序。

1、导航

全局导航

迅雷首页导航是非常清晰的,一目了然,如图:

根据人的视觉路线来看,除了阿拉伯等少数语系之外,大部分人都是从左到右,从上到下,也就是图中的红色箭头所示。这也是它能做到一目了然的原因。再来细看导航:

迅雷看看Logo是可点击的链接,用于返回首页,其实,Logo能不能点击问题不是很大,不可点击,也不会给用户带来太大的困扰,但对于一些专家用户来说,是一个返回首页的捷径,增加此处设计能增加用户体验度。

可用性导航很醒目很简洁,放在了Logo的旁边,减少了用户的记忆负担,当用户想登录的时候,他能马上把鼠标移到Logo旁边。同时也给右边的广告腾出了空的地方。

主导航放的是频道列表,内容频道的划分非常合理,上来迅雷看看的用户有一些时间是比较空闲的,可能比较喜欢看电视剧,也有一些用户时间不是很富裕的,像我就没有太多时间,我的空余时间不够我看电视连续剧,只好看电影,所以我一上来就直奔电影频道。

副导航在这里就显得不大显眼了一点,副导航采用的是专题形式,专题有个好处就是能推荐很多内容,且能抓住当前大部分用户关注的话题来推荐。

说完了首页导航再来看,频道内导航:

从各个频道来看,一致的布局,一样的颜色,每个频道最顶上的全局导航颜色要比下边 的当前主频道要浅很多,让用户的视觉聚焦到频道主导航上来。每个频道的主导航旁边都有一个检索框,方便用户进行直接查找,放在右边还有一个好处,就是尽量 不影响主导航,毕竟有具体查找目标的用户相对于根分类来查找自己喜欢的内容的用户来说还是少数。

分类导航

频道主导航右上边的热门分类,可以帮用户迅速定位到当前热门上。

从上边频道截图还可以看到,频道主导航都有一个“分类”的标签,从这个小细节能看得出来,迅雷看看比较重视使用分类来导航的。以电影频道的“分类浏览”来看,点击“分类浏览”,页面如图:

选择了一个分类,之后页面如图:


到了这个页面之后,左边仍然显示分类等筛选条件,每切换一个条件,右边的检索结果则会根据选择条件显示相应的结果。整个流程很清晰,不用用户思考,让人感觉到不到导航的存在就直奔自己想看的电影去了。

关联导航

在关联导航上,迅雷看看做得非常,在这个方面,它从没有让我失望过。在首页有一处很有意思的地方:

“猜你会喜欢”,虽然迅雷看看在这个推荐信息不一定能做到百分百猜得对,但是迅雷为每个用户都不同的推荐信息,给了用户一种被重视的感觉,而且标题标签的右边还允许用户关闭,还可以设置下次不显示,减少对用户的打扰。

2、搜索

搜索有两种情况会用到,第一种用户有明确具体的搜索关键词,第二种情况是在当前导航找不到自己想要找的资源最后不得不求助于搜索。所以从这个观点来说,搜索不用放在很重要的位置,只要我想要使用它,我能立刻知道它在哪就成。

迅雷看看首页的搜索条放在了主导航的底下:

相对于页面的主导航来说,搜索条的颜色浅很多,视觉焦点在主导航上,但检索条的位置遵守了大多数网站默认设计,放在主导航和内容区之间,所以当用户想要用搜索条的时候,找起来还是很方便的。

频道首页的搜索条,放在了频道主导航的右边:

这里也遵循占用地方不大,位置很显然的原则。

3、详细信息页设计

其实在迅雷看看,详细信息页面的地位不是很受重视,很多页面上的链接是直接链接到在线播放页面去的,只有在搜索结果页面,有详细信息页面的链接:

详细信息页面,设计也是比较清晰简洁:

标题很明显,重点链接很突出,没有太多的空白,也没有拥挤。

给用户提供推荐信息:

4、在线播放页面

在线播放页面是最终的服务页面,这个页面是整个系统里边至关重要的页面,也是决定用户体验的终极页,系统其它地方设计得再好,也不能弥补这个页面的不好。迅雷看看的在线播放页面的在线播放也和详细信息页一样,很简洁:

[转载].Net 实现游戏修改器

mikel阅读(908)

[转载].Net 实现游戏修改器 – cyclone – 博客园.

前不久玩植物大战僵尸,不停地玩啊玩,也通关了,准备开始享受一下IMBA的感觉。“玩玩小游戏”模式中有关“谁笑到最后”,一来就有5000的阳光,随你布置,布置完后开始攻击,过关挺容易。但是毕竟5000的阳光可布置的植物有限,总觉得不过瘾,于是找来《金山游侠》改阳光数量。好好享受了几次imba的感觉。

不用说,我当然不甘心用别人的工具,我要自己来。我选择.NET Framework 3.5作为该程序的实现平台。

整个过程总结如下:

一.获取具有窗体的进程集合

二.在所选进程的私有地址空间内查找数据

三.跟踪所选进程的数据修改情况,获得所要修改的数据的唯一地址

四.修改该地址中的数据内容

这就好办了,思路有了,我们就根据思路来搜集和整理相关知识:

进程

进程只是个被动容器,其中包含了很多资源。

System.Diagnostics命名空间中的 Process类表示进程。Process类中的 GetProcesses() 方法可以获取系统中所有进程。判断MainWindowHandle 是否为空可以确定进程是否包含主窗体。

我当前的宿主OSXP,在32位的 Windwos NT/2000/XP 中,进程地址空间有4GB,但却只能访问其地址空间底部的2GB,另外2GB留给内核模式相关的一些东西用。在这部分可访问的2GB空间中,最低和最高的64KB不能访问,于是可访问的地址范围是:0x00010000 0x7ffeffff

我们还需要读和写进程的内存,System.Diagnostics.Process 提供的方法不能做到。还好,Windowskernel32库中的进程相关API可以帮到:ReadProcessMemory WriteProcessMemory,可以将他们表达为如下C#语句:

[DllImport(“kernel32.dll”, SetLastError = true)]

static extern bool ReadProcessMemory(

IntPtr hProcess,

IntPtr lpBaseAddress,

[Out] byte[] lpBuffer,

int dwSize,

out int lpNumberOfBytesRead

);

[DllImport(“kernel32.dll”, SetLastError = true)]

static extern bool WriteProcessMemory(

IntPtr hProcess,

IntPtr lpBaseAddress,

byte[] lpBuffer,

uint nSize,

out int lpNumberOfBytesWritten);

名字很直观:读/写进程内存。

两个方法签名基本相同,我这里简单解释一下:

hProcess :进程句柄

lpBaseAddress:基地址,也就是起始地址(起始位置)

lpBuffer:从基地址起读取或要写入的内存值

nSize:读取或写入的数量,单位是字节

lpNumberOfBytesRead lpNumberOfBytesWritten:用作传出,表示实际读取或写入的数量

好了!开始实战吧!

创建一个C#Windows forms项目

在窗体上我这样布局:

为类添加如下几个成员:

List<Process> _windowedProcesses = new List<Process>();//存放有窗体的进程集合

private List<IntPtr> _addrList = new List<IntPtr>();//存放作为结果的地址列表

bool isFirstSearch = true;//是否是第一次搜索

Process _selectedProcess;//所选进程

还要获取有窗体的进程并列出来,让使用者选择需要的进程

private void RefreshProcessList()

{

listBox1.Items.Clear();

_windowedProcesses.Clear();

textBox2.Enabled = false;//在没得到唯一的地址前不能写入

foreach (var p in System.Diagnostics.Process.GetProcesses())

{

if (p.MainWindowHandle != IntPtr.Zero)//进程有窗口

{

if (!string.IsNullOrEmpty(p.MainWindowTitle))//窗体名不为空。因为有些时候会有一些进程如iexplorer.exe ,它有窗口,但窗口没名称且没显示。所以应该排除一下

{

listBox1.Items.Add(p.MainWindowTitle);

_windowedProcesses.Add(p);

}

}

}

}

于是可以在我们的窗体装载和单击刷新按钮时调用该方法

private void Form1_Load(object sender, EventArgs e)

{

RefreshProcessList();

}

private void btnRefreshPList_Click(object sender, EventArgs e)

{

RefreshProcessList();

}

为什么要区别是否是第一次搜索?因为第一次搜索是在整个进程可访问内存范围内查找,而之后的查找是基于第一次找到的地址。这样做不是唯一的,但是最好的方法。

下面是搜索按钮单击的事件处理代码:

private void button1_Click(object sender, EventArgs e)

{

if (_selectedProcess == null) return;

if (isFirstSearch)

{

uint baseAddr = 0x00010000;

uint endAddr = 0x7ffeffff;

for (uint i = baseAddr; i < endAddr; i += (4 * 1024))

{

var addrs = CreateAddrList(new IntPtr(i), int.Parse(textBox1.Text));

if (addrs !=null )

_addrList.AddRange( addrs);

}

isFirstSearch = false;

}

else

{

RefreshAddrList(int.Parse(textBox1.Text));

}

label2.Text = 找到结果+ _addrList.Count.ToString() + ;

if (_addrList.Count == 1)

textBox2.Enabled = true;

}

很明显CreateAddrList是第一次查找掉用的方法,RefreshAddrList是之后查找调用的方法。在第一次查找中,我们以4KB作一次跳跃。为什么查找的地址范围如此本文开始已作说明,这里就不再赘述。

好了,现在来看看CreateAddrList方法:

private List<IntPtr> CreateAddrList(IntPtr baseAddr, int value)

{

int bytesRead;

byte[] buffer = new byte[4096];

bool ok;

List<IntPtr> result = new List<IntPtr>();

ok = ReadProcessMemory(_selectedProcess.Handle, baseAddr, buffer, 4096, out bytesRead);

if (!ok)

return null ;

int currentVal;

for (int i = 0; i < 4096 – 3; i++)

{

currentVal = BitConverter.ToInt32(buffer, i);

if (currentVal == value)

{

IntPtr addr = new IntPtr(baseAddr.ToInt32() + i);

result.Add(addr);

i += 3;

}

}

return result;

}

该方法用以创建地址列表。它接受2个参数,分别是基地址和要查找的值。

我们用ReadProcessMemory 一次读取4KB的值,并把它存放在buffer中。由于buffer byte[] ,所以需要用BitConverter.ToInt32()buffer中的一部分值转成Int32以和要查找的值进行比对。

如果值匹配,则把对应地址添加到该方法的 result中以供方法返回。

接下来是RefreshAddrList方法:

. private void RefreshAddrList(int value)

{

var la = _addrList.ToList();

_addrList.Clear();

byte[] buffer = new byte[4];

int bytesRead;

foreach (var i in la)

{

ReadProcessMemory(_selectedProcess.Handle, i, buffer, 4, out bytesRead);

if (BitConverter.ToInt32(buffer, 0) == value)

_addrList.Add(i);

}

}

因为要根据第一次查找的地址结果进行查找并要更新主地址列表,所以要用addrList.ToList()得到一份主地址列表的拷贝。接下来再在作为第一次搜索结果的地址表中查找新的值。如果等于之前的值的地址中的数据现在还等于新的值,那么就添加到地址列表。

回看查找按钮的事件处理代码可以发现:反复多次,直到地址列表中只有一个地址时,就可以确定这就是我们要的地址,此时,我们就可以修改它了。

private void button2_Click(object sender, EventArgs e)

{

int value;

if (!int.TryParse(textBox2.Text, out value))

{

MessageBox.Show(输入值太大!小心溢出!请重新输入!);

return;

}

var buffer=BitConverter.GetBytes(value);

int bytesWritten;

WriteProcessMemory(_selectedProcess.Handle, _addrList[0], buffer, 4,out bytesWritten);

}

哈哈!这样就完成了。按下F5我又IMBA了一回

代码下载:ProgramMemoryEditor

该程序搜索值的数据类型是Int32 ,若我们要修改的程序的某个数据是以其他数据类型存储的,则需要小修改下我们的修改器。

笔者水平有限,若有疑问或更好的建议,务必不吝赐教。

[转载]Silverlight MMORPG网页游戏开发课程第十四课:完结篇

mikel阅读(836)

[转载]Silverlight MMORPG网页游戏开发课程[一期] 第十四课:完结篇 – 深蓝色右手 – 博客园.

引言

英雄的故事将要谢幕,似乎每段传奇都该有个华丽的结局。于是我打算用全新的魔法、炫酷的特效和再一次的重构为这期教程画上句号;虽然依旧伴随着一些客观因素导致的瑕疵,然而更让我欣慰的是这款快被敲烂了的Silverlight MMORPG客户端引擎正逐步迈向成熟。或许Silverlight 5 的新特性能给这款引擎带来质的飞跃,又或者我们更加期待Web 3D 游戏时代的来临,那望眼欲穿的公元2011年春

14.1完结篇[一期] (交叉参考:HLSL自定义渲染特效之完美攻略() HLSL自定义渲染特效之完美攻略() HLSL自定义渲染特效之完美攻略() )

上一节的魔法效果是否已足够打动你的心?或许你会感叹原来Silverlight开发的RPG可以这么美的。其实更奇妙的风景还在下面,为了演绎这场华丽的结局,全新编写的4个魔法旨在换取您的惊叹,一切源于Silverlight,因此您无须复杂的代码照样可以实现绝非简单的游戏特效。

圆月斩,附带HLSL编写的空间扭曲动画效果,技能施放时游戏场景会出现短暂的3D波动效果(空间扭曲动画),这是HLSL给我们所有Silverlight游戏开发者带来的惊喜,2KB成就超绚特效。虽然目前Silverlight 4还无法利用硬件加速进行HLSL渲染,不过令人兴奋的是Moonlight近期已实现对HLSL编写的渲染进行GPU硬件加速,相信同样的效果不久将出现在Silverlight新版本中,相当期待:

落焰斩,多次伤害附加Storyboard实现的屏幕震动模仿地震效果。其中素材由两部分动画拼接而成;一部分是落下的火焰,另一部分是着地后产生的熔岩喷发。为了轻松的处理多对象间的Z轴层次顺序,可借助切分/分离某些图片/动画部件以达到更加逼真的2.5D游戏场景效果:

碧月斩,直线穿梭类型技能,伤害范围为一个多边形,游戏中的激光等魔法同样可以类似处理,另外与圆月斩类似,附带弹开效果:

地裂斩,直线波浪发散逐环伤害,附带Storyboard实现的击飞效果,如能配上很酷的素材可呈现更加壮观的战场画面。

基于不同类型魔法都拥有各异特性,编写方式也就迥然不同。作为一款能够拓展到商业级别的Silverlight RPG游戏Demo,其动画/魔法的配置文件必须在游戏初期/初始化时被动态下载/加载(同一类的所有Info.xml归到一个文件中)。另外,基于结构的考虑,我重构了魔法控件这一大块,不再有Magic类,所有的技能/魔法依旧由Animation创建,而至今所制作的7类技能/魔法均重新继承自名为MagicBase的抽象类:

using System.Collections.Generic;
using System.Windows;
using System.Reflection;

namespace Controls.Base {

#region

public class MagicArgs {

/// <summary>
/// 获取或设置代号
/// </summary>
public int Code { get; set; }

/// <summary>
/// 获取或设置出现坐标
/// </summary>
public Point Coordinate { get; set; }

/// <summary>
/// 获取或设置目标
/// </summary>
public Point Target { get; set; }

/// <summary>
/// 获取或设置Z层次
/// </summary>
public int Z { get; set; }

/// <summary>
/// 获取或设置等级
/// </summary>
public int Level { get; set; }

/// <summary>
/// 获取或设置最小伤害
/// </summary>
public int DamageMin { get; set; }

/// <summary>
/// 获取或设置最大伤害
/// </summary>
public int DamageMax { get; set; }

/// <summary>
/// 获取或设置范围半径
/// </summary>
public int Radius { get; set; }

/// <summary>
/// 获取或设置魔法类型
/// </summary>
public MagicTypes MagicType { get; set; }

/// <summary>
/// 获取或设置装饰特效
/// </summary>
public SpecialEffects SpecialEffect { get; set; }

/// <summary>
/// 获取或设置附加效果
/// </summary>
public AdditionalEffects AdditionalEffect { get; set; }

/// <summary>
/// 获取或设置数量
/// </summary>
public int Number { get; set; }
}

#endregion

#region 枚举

/// <summary>
/// 魔法类型
/// </summary>
public enum MagicTypes {
/// <summary>
/// 圆形
/// </summary>
CircleMagic = 0,
/// <summary>
/// 扇形
/// </summary>
SectorMagic = 1,
/// <summary>
/// 十字
/// </summary>
CrossMagic = 2,
/// <summary>
/// 圆发散波浪
/// </summary>
CircleWaveMagic = 3,
/// <summary>
/// 圆多段
/// </summary>
MultipleHitCircleMagic = 4,
/// <summary>
/// 矩形穿梭
/// </summary>
LinearShuttleMagic = 5,
/// <summary>
/// 矩形波浪
/// </summary>
LinearWaveMagic = 6,
}

/// <summary>
/// 特殊效果
/// </summary>
public enum SpecialEffects {
/// <summary>
///
/// </summary>
None = 1,
/// <summary>
/// 麻痹
/// </summary>
Mine = 7,
/// <summary>
/// 风切
/// </summary>
Wind = 8,
/// <summary>
/// 灼烧
/// </summary>
Fire = 9,
/// <summary>
/// 冰冻
/// </summary>
Ice = 10,
/// <summary>
/// 爆裂
/// </summary>
Explosion = 12,
/// <summary>
/// 爆裂1
/// </summary>
Explosion1 = 20,
}

/// <summary>
/// 附加效果
/// </summary>
public enum AdditionalEffects {
/// <summary>
/// 击伤
/// </summary>
Injure = 0,
/// <summary>
/// 弹开
/// </summary>
Bounce = 1,
/// <summary>
/// 击飞
/// </summary>
BeKnock = 2,
}

#endregion

/// <summary>
/// 魔法类
/// </summary>
public abstract class MagicBase {

#region 属性

/// <summary>
/// 获取或设置参数
/// </summary>
public MagicArgs Args { get; set; }

/// <summary>
/// 获取或设置目标精灵集合
/// </summary>
public List<Sprite> Targets { get; set; }

#endregion

#region 构造

public MagicBase() {
Targets
= new List<Sprite>();
}

#endregion

#region 方法

/// <summary>
/// 触发/运行
/// </summary>
/// <param name=”caster”>施法者</param>
/// <param name=”scene”>所在场景</param>
/// <param name=”args”>魔法参数</param>
public abstract void Run(Sprite caster, Scene scene, MagicArgs args);

#endregion

}
}

剩下的仅仅是当技能/魔法被释放时,通过反射来动态加载并创建对应类型的魔法实例,短短几行代码即轻松搞定任意类型技能/魔法的运行:

//开始技能/魔法攻击
void sprite_DoCasting(object sender, DoCastingEventArgs e) {
Sprite caster
= sender as Sprite;
//通过反射来加载并实例化各类型魔法
Assembly assembly = Assembly.Load(Controls,Version=1.0.0.0);
MagicBase magic
= assembly.CreateInstance(string.Format(Controls.Magic.{0},e.MagicArgs.MagicType.ToString())) as MagicBase;
magic.Run(caster, scene, e.MagicArgs);
}

到此,整个Controls控件项目库的结构已脉络清晰,关系图如下:

技能/魔法在逻辑处理上变化很大,需要对配置参数更细的划分方能适应,比如类型、子对象个数、伤害特效、爆破时特效、是否持续伤害及间隔等等,某些特殊的魔法甚至作为单独的类存在,当然好处也是相对的,游戏的趣味性与众不同,整体可玩性将大幅度提升。

快告一段落了,至今朋友们会发现第一期几乎囊括了MMORPG单机部分的所有功能除了任务系统外。没错,我一直的主张是如果一切都透彻了那就真的失去了继续探索的价值,毕竟当前Silverlight网游开发依旧处于摸索阶段,我和朋友们长期以来都在不断的专研中前行,思索着如何改进与提升。以下也列出了一些个人意见,若想在本课程示例代码上进行更深度的修正以达到较完美境界,还需要补充及完善的内容大致如下:

1、改进及优化动态障碍物系统(包括其中的障碍物碰撞检测、以及精灵AI)。

2、重新封装游戏对象的3个坐标属性以满足各种情况需求:窗口坐标系精确值,游戏坐标系精确值,游戏坐标系整值。

3、编写更多的游戏开发辅助工具以提升效率,比如游戏资源管理器等。

4PNG图片格式在未来软件应用中显得尤为重要,包括8位和24位,甚至它的动画格式MNG,因为无论是2D还是3D;无论是SilverlightFlash还是HTML5;无论是在网页上还是客户端亦或是WP7PNG格式将在未来的2D图像呈现方面长期处于统治地位。

5、更多技巧需要相互的取长补短以习得,做游戏就得从玩游戏开始一点没错。强烈推荐开发者去参考目前已运营的商业游戏,利用浏览器缓存查看它们的资源布局、配置存储、压缩加密以及动画的处理手法等等,高手均诞生于普通人之中,只是看得多了,做的多了就成了师傅,尔耳。

[一期]结束语:一 款成功的商业游戏必须具备天时、地利、人和。优秀的美工团队意味着游戏成功了一半,这绝非危言耸听;而像我们这类做程序的则常挂嘴边最多的词不外呼“性 能”二字,回归原点,性能的关键取决于细节。完美的画面加上优秀的游戏架构和引人入胜的体验将缔造成功参数中的“人和”,假以时日在那即将载入史册的场 景,旷世之巨作莅临终将显得毫无悬念。

牢记这么一句话:成功的秘诀不在别人嘴里,而在你自己手里。

Silverlight网游开发真诚期待您的加入,行动吧!那就从今天开始。

()

本课源码[14.1]

参考资料:中游在线[WOWO世界] Silverlight C# 游戏开发:游戏开发技术

友情招聘:中游在线[北京]招聘Silverlight游戏开发者,盛情期待您的加入!

具体要求:

理解面向对象的编程思想、设计模式;

熟悉使用基于C#语言基础及常用库的开发;

熟练下述任一开发环境,Visual StudioMicrosoft Expression Studio

Silverlight开发非常有兴趣;

有责任心,学习、沟通和团队合作必不可少,能适应较强的工作压力;

简历附有作品或项目介绍,提供演示程序或截图;

待遇面议,用您的潜力见证您的价值!

联系方式:http://nxria.com/contact.html

教程Demo在线演示地址http://silverfuture.cn

[转载]使用ASP.NET MVC MvcHandler设计自定义系统权限

mikel阅读(969)

[转载]使用MvcHandler设计自定义系统权限 – RyanDing – 博客园.

本文是续上篇 使用MvcHandler设计自定义系统权限<上> 的下篇。通过本篇,我将在MVC2.0中提出一个系统权限的解决方案,如存在不足的地方,希望您能够指出。谢谢!

一、回顾上篇中的内容:


重点回顾一下上篇的3.2中的MyHandler类,该类继承MVC2.0的MvcHandler类。代码如下:

01 protected override System.IAsyncResult BeginProcessRequest(System.Web.HttpContext httpContext, System.AsyncCallback callback, object state)
02 {
03 if (1 == 1) //系统权限的入口
04 {
05 //当条件满足时可以跳转到一个自定义的httphandler,比如(Error.aspx)等。
06 httpContext.Response.Redirect("http://www.cnblogs.com/ryanding");
07 return null;
08 }
09 return base.BeginProcessRequest(httpContext, callback, state);
10 }

贴出以上代码,着重想讲解的就是我们的权限判断应该注入在第11行,这个if语句内。将权限注入在第11行,个人认为这个权限是建立在MVC整个框 架外的。效率应该会更高,而且随着未来ASP.NET MVC版本的升级换代。这种权限结构在一定程度上是不受MVC框架的制约的,总比继承AuthorizeAttribute特性来控制权限高明一些吧?否 则那些“反reflection派”又开始出来用效率说事。

二、切入正题,将权限功能注入系统


先看以下代码:

01 protected override System.IAsyncResult BeginProcessRequest(System.Web.HttpContext httpContext, System.AsyncCallback callback, object state)
02 {
03
04 //使用系统System.Security.Principal保存登录后的用户名
05 //示例代码此处userName赋值不够严谨,请注意对象空引用
06 string userName = httpContext.User.Identity.Name;
07
08 if (string.Compare(userName, "admin", true) == 0)
09 {
10 //当条件满足时可以跳转到一个自定义的httphandler,比如(Error.aspx)等。
11 httpContext.Response.Redirect("http://www.cnblogs.com/ryanding");
12 return null;
13 }
14 return base.BeginProcessRequest(httpContext, callback, state);
15 }

这里我们还是使用.NET经典的System.Security.Principal类库。从httpContext.User.Identity 获取到登录的用户名符串(从Name属性获取值)。在Login这个View内保存登录成功的用户信息,Login View 这个登录页面不需要对权限进行处理,everyone 都可以打开。后台代码如下:

1 FormsAuthenticationService auth = new FormsAuthenticationService();
2 auth.SignIn("admin", false);

假设admin就是当前用户,这里的admin硬编码了,应该从UI获取。

三、具体的实现思路


在MVC2.0中如果通过Action进行判断的话,难免要考虑到Area机制。因为在不同的Area中,可能会出现相同的Controller和 Action。所以在权限判断时还需要将Area加入到逻辑中一起判断。这样就可以精确的控制所有的MVC的URL。比如我们通过 AreaName + ControllerName + ActionName 这样判断就可以对系统具体的URL进行唯一标识,同时也方便处理权限了。我们将BeginProcessRequest方法重新改造如下:

01 protected override System.IAsyncResult BeginProcessRequest(System.Web.HttpContext httpContext, System.AsyncCallback callback, object state)
02 {
03
04 //使用系统System.Security.Principal保存登录后的用户名
05 //示例代码此处userName赋值不够严谨,请注意对象空引用
06 string userName = httpContext.User.Identity.Name;
07
08 //"admin"
09 string strAreaName = base.RequestContext.RouteData.DataTokens["area"] == null ?
10 "" : base.RequestContext.RouteData.DataTokens["area"].ToString();
11
12 //"TestMvc.Areas.admin.Controllers"
13 string strNameSpaces = base.RequestContext.RouteData.DataTokens["namespaces"] == null ?
14 "" : base.RequestContext.RouteData.DataTokens["namespaces"].ToString();
15 //"index"
16 string strAction = base.RequestContext.RouteData.Values["action"].ToString();
17 //"home1"
18 string strController = base.RequestContext.RouteData.Values["controller"].ToString();
19
20 //TODO:将area + action + controller 参与到权限判断中来
21 if (string.Compare(userName, "admin", true) == 0)
22 {
23 //当条件满足时可以跳转到一个自定义的httphandler,比如(Error.aspx)等。
24 httpContext.Response.Redirect("http://www.cnblogs.com/ryanding");
25 return null;
26 }
27
28 return base.BeginProcessRequest(httpContext, callback, state);
29 }

从MVC RouteData 的 DataTokens得到area相关信息;Values得到action与controller相关信息,最后将获取的值再参与具体的权限判断。


四、注意事项


在整个调试的过程中,发现只有一个地方让我调试了半天:MyHandler不能控制area的内部的Action。后来查阅相关资料,原来是在AreaRegistration(MVC2.0新增Area后自动生成的类)中设置问题。贴出代码,供参考:

1 public override void RegisterArea(AreaRegistrationContext context)
2 {
3 context.Routes.Add(new Route("{area}/{controller}/{action}/{id}"
4 , new RouteValueDictionary(new { area = "admin", controller = "Home1", action = "Index", id = "" })
5 , new RouteValueDictionary(new { area = "admin" })
6 , new RouteValueDictionary(new {area = "admin",  Namespaces = "TestMvc.Areas.admin.Controllers" })
7 , new HelloWorldHandler()));
8 }

新增的area结构如下:

至此,您可以在MVC2.0中加入权限机制了。我相信通过这种方式注入自定义的权限结构到MVC程序中,总比用HttpModule来的强一些,因为客户端请求CSS/JS时,不需要再过滤这类的URL。代码看起会更舒服点,判断也就更简单点。

希望本篇文章可以给您带来帮助,如有不足之处欢迎指出,谢谢!

[转载]乐易拍电子商务系统(asp.net+ extjs)

mikel阅读(1165)

[转载]乐易拍电子商务系统(asp.net+ extjs) – huhai – 博客园.

乐易拍信息平台主要为企业实现电子商务提供一个快捷的解决方案.通过本方案系统,可以加快开发速度,降低开发成本.搭建高效、稳定、可拓展的系统。
本系统运用ASP.NET 2.0开发,数据库使用SQL2000(可以放在SQL2005上),包括前台和后台两个部分。
前台主要运用JQuery+CSS 构建的展示订购平台。
后台是运用CSS+EXTJS构建的一个完整的权限管理+销售订单管理的系统。主要功能:
地区信息设置 、用户信息管理 、系统角色管理 、系统仓库配置 、物流公司配置 、数据库备份 、
系统操作日志、 品牌管理 、类型管理 、商品添加 、商品管理 、销售客户管理 、销售订单管理 。

(给初学者叁考)项目下载 http://code.google.com/p/leyipai/
后台默认帐户密码: huhai/111111

效果图

[转载]关于QQ一些功能的实现

mikel阅读(1167)

[转载]关于QQ一些功能的实现 – Create Chen – 博客园.

在网上搜了很久关于腾讯的接口, 但资料很有限, 绝大部分都是重复的, 由于想实现一些比较好玩的应用, 我只好根

据手头能搜到的资料进行实现了. 现在可以实现QQ登陆, 发消息, 接受消息, 加好友, 查询好友资料, 更改QQ状态, 查询

已添加的好友(只能按QQ号排序, 获取到前120名名单, 原因不明), 查看某QQ用户个人资料等功能, 可以给有兴趣的同学

们参考一下. 也请有这方面经验的前辈们补充补充你们知道的其它功能:)

原理是向腾讯http://tqq.tencent.com:8000 进行POST一个UTF8编码的Byte[], 这个数组是根据各个功能的协

议进行编码的, 成功后返回一个UTF8编码的Byte[]数组, 因此我把这个方法抽取出来:

private void UploadData() { try { pageData = _client.UploadData("http://tqq.tencent.com:8000", "POST", byteArray); } catch { } }

类中定义的变量:

public string num; //构造函数的QQ号码 private string pwd; //构造函数的QQ密码 public string[] online_Face={""}; //在线的头像号码 public string[] online_Station={""}; //在线的状态 public string[] online_Number={""}; //在线的号码 public string[] online_NameK={""}; //在线的昵称 private WebClient _client = new WebClient(); //用来给服务器发送消息的 private string postValues; //发送给服务器的字符串 private byte[] byteArray; //把要发送的字符串变成字符数组 private byte[] pageData; //接受服务器返回的字符数组 private string s; //把返回的字符数组变成字符串 public string[] MT; //储存信息类型 public string[] UN; //储存信息来源号码 public string[] MG; //储存信息内容 public bool is_RightLogin; //判断当前用户是否正确登录

构造一个QQ类实例的构造函数:

/// <summary> /// QQ类的构造函数 /// </summary> /// <param name="QQ_Num">QQ号码</param> /// <param name="QQ_Pwd">QQ密码</param> public QQ (string QQ_Num, string QQ_Pwd) { this.num = QQ_Num; this.pwd = QQ_Pwd; }

实现QQ登陆的功能:

协议: VER=1.1&CMD=Login&SEQ=&UIN=&PS=&M5=1&LC=9326B87B234E7235

解释: VER是用来说明QQ协议的版本,CMD是说明协议的命令,Login就是指QQ的登录了, SEQ是他的为了防止

重复发送而设定的一个标记,可以取时间得毫秒值, 一个随机数也可以, UIN是说明你当前要登录的用户QQ号, PS是M

D5加密过后的密码的值.

/// <summary> /// 登陆QQ /// </summary> /// <returns>登陆成功就返回True</returns> public bool QQ_Login() { postValues = "VER=1.1&CMD=Login&SEQ=" + DateTime.Now.Ticks.ToString().Substring(7, 7) + "&UIN=" + num + "&PS=" + MD5(pwd) + " &M5=1&LC=9326B87B234E7235"; byteArray = System.Text.Encoding.UTF8.GetBytes(postValues); //向服务器POST数据 UploadData(); if (Encoding.UTF8.GetString(pageData).Contains("RES=0&RS=0")) { is_RightLogin = true; return true; } else return false; }

QQ的MD5加密方法:

public static string MD5(string toCryString) { MD5CryptoServiceProvider hashmd5; //using System.Security.Cryptography安全.密码系统 hashmd5 = new MD5CryptoServiceProvider(); return BitConverter.ToString(hashmd5.ComputeHash(Encoding.UTF8.GetBytes(toCryString))).Replace("-", "").ToLower(); }

QQ登陆的返回协议: VER=1.1&CMD=Login&SEQ=&UIN=&RES=0&RS=0&HI=60&LI=300(这是登陆成功的一个例子)

解释: RES为0表示成功返回,RS为0表示登录成功, VER=1.1&CMD=Login&SEQ=&UIN=&RES=0&RS=1&RA=登录失败

RS为1表示登录失败,那么就会出现提示信息RA说明原因.

获取QQ好友列表:

协议: VER=1.1&CMD=List&SEQ=&UIN=&TN=160&UN=0

/// <summary> /// 获取QQ好友列表 /// </summary> /// <returns>返回一个字符串数组,数组最后一个元素是空格</returns> public string[] QQ_List() { postValues = "VER=1.1&CMD=List&SEQ=" + DateTime.Now.Ticks.ToString().Substring(7, 7) + "&UIN=" + num + "&TN=160&UN=0"; byteArray = System.Text.Encoding.UTF8.GetBytes(postValues); //向服务器POST数据 UploadData(); s = Encoding.UTF8.GetString(pageData); if (!s.Contains("&RES=0")) is_RightLogin = false; string s2 = s.Remove(0, s.IndexOf("&UN=")+4); string[] QQ_Friend_List = s.Split(','); return QQ_Friend_List; }

返回协议: VER=1.1&CMD=LIST&SEQ=&UIN=&RES=0&FN=1&SN=&UN=

解释: UN后面则是您好友的QQ号码,每个号码都由,进行分开, 我用string.Split(‘,’)把值放入字符串数组中返回了.

更新目前在线online_四个字符串数组中好友信息的值:

协议: VER=1.1&CMD=Query_Stat&SEQ=&UIN=&TN=50&UN=0

解释: 获得QQ好友在线名单跟获得好友名单差不多,不同的是用的命令不同用的是Query_Stat

/// <summary> /// 更新QQ类中目前在线online_四个字符串数组的值 /// </summary> public void QQ_Query_Stat() { postValues = "VER=1.1&CMD=Query_Stat&SEQ=" + DateTime.Now.Ticks.ToString().Substring(7, 7) + "&UIN=" + num + "&TN=50&UN=0"; byteArray = System.Text.Encoding.UTF8.GetBytes(postValues); //向服务器POST数据 UploadData(); s = Encoding.UTF8.GetString(pageData); if (!s.Contains("&RES=0")) is_RightLogin = false; StringBuilder sb = new StringBuilder(s); sb.Remove(s.IndexOf("&FN="), s.Length - s.IndexOf("&FN=")); sb.Remove(0,s.IndexOf("&FC=")+4); online_Face = sb.ToString().Split(','); sb = new StringBuilder(s); sb.Remove(s.IndexOf("&UN="), s.Length - s.IndexOf("&UN=")); sb.Remove(0, s.IndexOf("&ST=") + 4); online_Station = sb.ToString().Split(','); sb = new StringBuilder(s); sb.Remove(s.IndexOf("&NK="), s.Length - s.IndexOf("&NK=")); sb.Remove(0, s.IndexOf("&UN=") + 4); online_Number = sb.ToString().Split(','); string ss = s.Remove(0, s.IndexOf("&NK=") + 4); online_NameK = ss.Split(','); }

返回协议: VER=1.1&CMD=QUERY_STAT&SEQ=9118265&UIN=634882287&RES=0&FC=12,&FN=1&SN=1

&ST=10,&UN=635478153,&NK=,

解释: FC为QQ头像的的ID,如的头像ID为270,那么其头使用的图片为91.bmp,其算法为ID/3+1, ST为QQ用户的状态,

10为上线,20为离线,30为忙碌, UN为在线用户的QQ号,NK为在线用户的QQ昵称.ST,UN,NK,每个逗号隔开的数据相互对应.

输入一个QQ号,查询这个QQ号用户的信息:

协议: VER=1.1&CMD=GetInfo&SEQ=&UIN=&LV=2&UN=

解释: UN为要查看用户信息的QQ号.

/// <summary> /// 输入一个QQ号,查询这个QQ号用户的信息 /// </summary> /// <param name="search_num">输入一个QQ号,查询该QQ信息</param> /// <returns>字符串数组(联系地址,用户年龄,用户邮箱,头像,个人网站,职业,邮箱,联系电话,简介,省份,真实姓名,毕业院校,性别,QQ号,昵称)</returns> public string[] QQ_GetInfo(string search_num) { postValues = "VER=1.1&CMD=GetInfo&SEQ=" + DateTime.Now.Ticks.ToString().Substring(7, 7) + "&UIN=" + num + "&LV=2&UN=" + search_num; byteArray = System.Text.Encoding.UTF8.GetBytes(postValues); //向服务器POST数据 UploadData(); s = Encoding.UTF8.GetString(pageData); if (!s.Contains("&RES=0")) is_RightLogin = false; MatchCollection matches = Regex.Matches(s, "&([^=][^=])=([^&]*)"); List<string> Info = new List<string>(); for (int i = 0; i < matches.Count; i++) Info.Add(matches[i].Groups[2].ToString()); Info.RemoveAt(6); //去除LV=多少, 这表示查询方式,默然就是普通查询 if (Info[12].ToString() == "0") Info[12] = ""; else Info[12] = ""; string[] Inf = Info.ToArray(); return Inf; }

返回协议: VER=1.1&CMD=GETINFO&SEQ=41707&UIN=&RES=0&AD=&AG=&EM=&FC=&HP=&JB=&LV=&PC=&PH=&

PR=PV=&RN=&SC=&SX=&UN=&NK=

解释: AD用户的联系地址, AG为用户年龄, EM为用户MAIL, FC为用户头像, HP为用户网站, JB为用户职业, PC为用户邮编, PH为用户

联系电话, PR为用户简介, PV为用户所以的省, RN为用户真实名称, SC为用户毕业院校, SX为用户性别, UN为用户QQ号, NK为用户QQ昵称

添加好友功能:

协议: VER=1.1&CMD=AddToList&SEQ=&UIN=&UN=

解释: UN为我们要增加用户的QQ号

/// <summary> /// 添加好友功能 /// </summary> /// <param name="fir_num">输入一个QQ号,请求加为好友</param> /// <returns>0表示已经加为好友,1表示需要验证请求,2表示拒绝</returns> public string AddToList(string fir_num) { postValues = "VER=1.1&CMD=AddToList&SEQ=" + DateTime.Now.Ticks.ToString().Substring(7, 7) + "&UIN=" + num + "&UN=" + fir_num; byteArray = System.Text.Encoding.UTF8.GetBytes(postValues); //向服务器POST数据 UploadData(); s = Encoding.UTF8.GetString(pageData); if (!s.Contains("&RES=0")) is_RightLogin = false; MatchCollection matchs = Regex.Matches(s,"&CD=(.)"); return matchs[0].Groups[1].ToString(); }

返回协议: VER=1.1&CMD=AddToList&SEQ=&UIN=&RES=0&CD=0&UN=

解释: CD为被加QQ的身份验证状态,CD为0表示”允许任何人把我列为好友”
CD为1表示”需要身份证认才能把我列为好友”,CD为2表示”不允许任何人把我列为好友”
如果CD为0那么信息回馈后,用户就直接加为好友了,如果CD为1,那么还要发送一次回应加为好友的响应

回应添加好友的请求:

协议: VER=1.1&CMD=Ack_AddToList&SEQ=&UIN=&UN=&CD=&RS=

解释: CD为响应状态, CD为0表示”通过验证”, CD为1表示”拒决加为对方为好友”

/// <summary> /// 回应加为好友的响应 /// </summary> /// <param name="fri_Num">请求的QQ号码</param> /// <param name="agree_Type">0表示通过验证,1表示拒绝对方,2表示请求加对方为好友</param> public void Ack_AddToList(string fri_Num,string agree_Type) { //WebClient _client = new WebClient(); postValues = "VER=1.1&CMD=Ack_AddToList&SEQ=" + DateTime.Now.Ticks.ToString().Substring(7, 7) + "&UIN=" + num + "&UN=" + fri_Num + "&CD="+agree_Type+"&RS="; byteArray = System.Text.Encoding.UTF8.GetBytes(postValues); //向服务器POST数据 UploadData(); s=Encoding.UTF8.GetString(pageData); if (!s.Contains("&RES=0")) is_RightLogin = false; }

成功操作后返回协议: VER=1.1&CMD=Ack_AddToList&SEQ=&UIN=&RES=0&

删除好友:

协议: VER=1.1&CMD=DelFromList&SEQ=&UIN=&UN=

解释: UN为你要删除的QQ号

/// <summary> /// 删除好友,成功返回True /// </summary> /// <param name="del_num">输入一个QQ号,删除这个QQ好友</param> /// <returns></returns> public bool DelFromList(string del_num) { postValues = "VER=1.1&CMD=DelFromList&SEQ=" + DateTime.Now.Ticks.ToString().Substring(7, 7) + "&UIN=" + num + "&UN=" + del_num; byteArray = System.Text.Encoding.UTF8.GetBytes(postValues); //向服务器POST数据 UploadData(); s = Encoding.UTF8.GetString(pageData); if (s.Contains("&RES=0")) return true; else return false; }

操作成功返回协议: VER=1.1&CMD=DelFromList&SEQ=&UIN=&RES=0&

改变QQ当前状态(在线,离线,忙碌):

协议: VER=1.1&CMD=Change_Stat&SEQ=&UIN=&ST=

解释: ST为要改变的状态,10为上线,20为离线,30为忙碌.

public bool Change_Stat(string stat) { postValues = "VER=1.1&CMD=Change_Stat&SEQ=" + DateTime.Now.Ticks.ToString().Substring(7, 7) + "&UIN=" + num + "&ST=" + stat; byteArray = System.Text.Encoding.UTF8.GetBytes(postValues); //向服务器POST数据 UploadData(); s = Encoding.UTF8.GetString(pageData); if(s.Contains("&RES=0")) return true; else return false; }

成功操作后返回协议: VER=1.1&CMD=Change_Stat&SEQ=&UIN=&RES=0&

给QQ好友发送消息:

协议: VER=1.1&CMD=CLTMSG&SEQ=&UIN=&UN=&MG=

解释: UN是你的QQ好友, MG就是消息内容

/// <summary> /// 向一个QQ号码发送消息 /// </summary> /// <param name="msgTo">输入一个QQ号,向他发送消息</param> /// <param name="msg">输入消息内容</param> /// <returns>成功返回True</returns> public bool QQ_SendMsg(string msgTo, string msg) { postValues = "VER=1.2&CMD=CLTMSG&SEQ=" + DateTime.Now.Ticks.ToString().Substring(7, 7) + "&UIN=" + num + "&UN=" + msgTo + "&MG=" + msg; byteArray = System.Text.Encoding.UTF8.GetBytes(postValues); //向服务器POST数据 UploadData(); s = Encoding.UTF8.GetString(pageData); if (s.Contains("&RES=20")) { is_RightLogin = false; return false; } if (s.Contains("&RES=0")) return true; else return false; }

返回协议: VER=1.1&CMD=CLTMSG&SEQ=标记&UIN=QQ号&RES=0&(成功发送,对方不一定能收到哦)
VER=1.1&CMD=CLTMSG&SEQ=标记&UIN=QQ号&RES=3(发送过快)
VER=1.1&CMD=CLTMSG&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)
解释: 1. 当你发消息时,以下情形对方可能看不到(其实是收到了,QQ不提示)你发送的消息:
你俩互为陌生人,且对方没有和你说过话
你在他的陌生人列表里,并且他没有和你说过话(没有验证)

2. 当你过快发送消息时,系统会给你一个惩罚,RES=3,相应时间20s

接收QQ消息:

协议: VER=1.1&CMD=GetMsgEx&SEQ=&UIN=

解释: 这个不需要解释了, 看懂前面的协议, 这个肯定能看懂的, 呵呵

public void GetMsgEx() { postValues = "VER=1.1&CMD=GetMsgEx&SEQ=" + DateTime.Now.Ticks.ToString().Substring(7, 7) + "&UIN=" + num; byteArray = System.Text.Encoding.UTF8.GetBytes(postValues); //向服务器POST数据 UploadData(); s = Encoding.UTF8.GetString(pageData); if (s.Contains("\r")) s = s.Replace("\r", "\n"); if (s.Contains("&RES=0")) { is_RightLogin = true; MatchCollection matches = Regex.Matches(s, "&MN=([^&]*)"); if (matches[0].Groups[1].ToString() != "0") //判断返回的信息数量是否为0条 { matches = Regex.Matches(s, "&MT=([^&]*)&UN=([^&]*)&MG=([^&]*)"); MT = matches[0].Groups[1].ToString().Split(','); //信息类型 UN = matches[0].Groups[2].ToString().Split(','); //信息来源号码 s = s.Remove(0, s.IndexOf("&MG=") + 4); MG = s.Split(','); //信息内容 //将消息内容进行转码 for(int i = 0; i<MG.Length-1;i++) { MG[i] = MG[i].Replace("%25", "%"); MG[i] = MG[i].Replace("%26", "&"); MG[i] = MG[i].Replace("%2c", ","); } } else { MT = null; UN = null; MG = null; is_RightLogin = false; } }

返回协议: VER=1.1&CMD=GETMSGEX&SEQ=&UIN=&RES=0&MN=&MT=,&UN=,&MG=,(正确返回的)

VER=1.1&CMD=GETMSGEX&SEQ=标记&UIN=QQ号&RES=0&MN=0&MT=&UN=&MG=(表示没有信息)

VER=1.1&CMD=GETMSGEX&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)

解释: 关于MT:
9为用户消息,99为系统消息,2为请求信息,3为通过验证,4为拒绝被加好友

当MT=2时,MG为对方请求你验证的信息
当MT=3时,表示对方通过你的验证
当MT=4时,MG为对方拒绝你理由
关于MG:
当MT=9时,MG为用户发送的消息内容
当MT=99时,
MG=10(QQ_STATUS_ONLINE)表示对方上线
MG=20(QQ_STATUS_OFFLINE)表示对方下线
MG=30(QQ_STATUS_BUSY)表示对方进入忙碌状态

退出QQ:

协议: VER=1.1&CMD=Logout&SEQ=&UIN=

解释: 这个也不解释, 非常简单

/// <summary> /// QQ退出登陆,并改变is_RightLogin为False /// </summary> public void QQ_Logout() { postValues = "VER=1.1&CMD=Logout&SEQ=" + DateTime.Now.Ticks.ToString().Substring(7, 7) + "&UIN=" + num; byteArray = System.Text.Encoding.UTF8.GetBytes(postValues); //向服务器POST数据 UploadData(); s = Encoding.UTF8.GetString(pageData); if (s.Contains("&RES=0")) is_RightLogin = false; }

成功返回协议: VER=1.1&CMD=LOGOUT&SEQ=&UIN=&RES=0

结尾

这些功能研究了两三天, 代码写的不好的地方请各位大牛们指点指点. 本篇最主要的目的还是抛砖引玉, 希望关于QQ其

它一些有意思的功能, 自己可以实现的, 欢迎各位高手前辈补充补充:)

点击下载 : 点击

下载地址: http://cid-07452800dc0167da.office.live.com/browse.aspx/.Public/Contact?uc=1

转载请注明: http://www.cnblogs.com/technology/ Create Chen

[转载]VS2010快捷键

mikel阅读(977)

[转载]VS2010快捷键 – 天行健@中国元素 – 博客园.

有人发了VS2010的快捷键,不够全,那才哪到哪呀,我来发个完整的 吧,后附VS2008的快捷键

Ctrl+E,D —-格式化全部代码

Ctrl+E,F —-格式化选中的代码

CTRL + SHIFT + B生成解决方案

CTRL + F7 生成编译

CTRL + O 打开文件

CTRL + SHIFT + O打开项目

CTRL + SHIFT + C显示类视图窗口

F4 显示属性窗口

SHIFT + F4显示项目属性窗口

CTRL + SHIFT + E显示资源视图

F12 转到定义

CTRL + F12转到声明

CTRL + ALT + J对象浏览

CTRL + ALT + F1帮助目录

CTRL + F1 动态帮助

F1 帮助

SHIFT + F1当前窗口帮助

CTRL + ALT + F3帮助-搜索

SHIFT + ALT + ENTER全屏显示

CTRL + -向后定位

CTRL + SHIFT + -向前定位

CTRL + F4关闭文档窗口

CTRL + PAGE DOWN光标定位到窗口上方

CTRL + PAGE UP光标定位到窗口下方

CTRL + F6

CTRL + TAB下一个文档窗口

CTRL + SHIFT + F6

CTRL + SHIFT + TAB上一个文档窗口

ALT + F6下一个面板窗口

CTRL + K, CTRL + L取消remark

CTRL + K, CTRL + C注释选择的代码

CTRL + K, CTRL + U取消对选择代码的注释

CTRL + M, CTRL + O折叠代码定义

CTRL + M, CTRL + L展开代码定义

CTRL + DELETE删除至词尾

CTRL + BACKSPACE删除至词头

SHIFT + TAB取消制表符

CTRL + U转小写

CTRL + SHIFT + U转大写

CTRL + SHIFT + END选择至文档末尾

CTRL + SHIFT + HOME选择至文档末尾开始

SHIFT + END选择至行尾

SHIFT + HOME选择至行开始处

SHIFT + ALT + END垂直选择到最后尾

SHIFT + ALT + HOME垂直选择到最前面

CTRL + SHIFT + PAGE UP选择至本页前面

CTRL + SHIFT + PAGE DOWN选择至本页后面

CTRL + END文档定位到最后

CTRL + HOME文档定位到最前

CTRL + A全选

CTRL + W选择当前单词

CTRL + G转到…

CTRL + K, CTRL + P上一个标签

CTRL + K, CTRL + N下一个标签

ALT + F10调试-ApplyCodeChanges

CTRL + ALT+ Break停止调试

CTRL + SHIFT + F9 取消所有断点

CTRL + F9允许中断

CTRL + SHIFT + F5调试-重新开始

F5运行调试

CTRL + F5运行不调试

F10跨过程序执行

F11单步逐句执行

CTRL + J列出成员

CTRL + PAGE DOWN下一个视图

CTRL + B格式-粗体

CTRL + SHIFT + T格式-文字缩进

调试快捷键

F6: 生成解决方案

Ctrl+F6: 生成当前项目

F7: 查看代码

Shift+F7: 查看窗体设计器

F5: 启动调试

Ctrl+F5: 开始执行(不调试)

Shift+F5: 停止调试

Ctrl+Shift+F5: 重启调试

F9: 切换断点

Ctrl+F9: 启用/停止断点

Ctrl+Shift+F9: 删除全部断点

F10: 逐过程

Ctrl+F10: 运行到光标处

F11: 逐语句

编辑快捷键

Shift+Alt+Enter: 切换全屏编辑

Ctrl+B,T / Ctrl+K,K: 切换书签开关

Ctrl+B,N / Ctrl+K,N: 移动到下一书签

Ctrl+B,P: 移动到上一书签

Ctrl+B,C: 清除全部标签

Ctrl+I: 渐进式搜索

Ctrl+Shift+I: 反向渐进式搜索

Ctrl+F: 查找

Ctrl+Shift+F: 在文件中查找

F3: 查找下一个

Shift+F3: 查找上一个

Ctrl+H: 替换

Ctrl+Shift+H: 在文件中替换

Alt+F12: 查找符号(列出所有查找结果)

Ctrl+Shift+V: 剪贴板循环

Ctrl+左右箭头键: 一次可以移动一个单词

Ctrl+上下箭头键: 滚动代码屏幕,但不移动光标位置。

Ctrl+Shift+L: 删除当前行

Ctrl+M,M: 隐藏或展开当前嵌套的折叠状态

Ctrl+M,L: 将所有过程设置为相同的隐藏或展开状态

Ctrl+M,P: 停止大纲显示

Ctrl+E,S: 查看空白

Ctrl+E,W: 自动换行

Ctrl+G: 转到指定行

Shift+Alt+箭头键: 选择矩形文本

Alt+鼠标左按钮: 选择矩形文本

Ctrl+Shift+U: 全部变为大写

Ctrl+U: 全部变为小写

代码快捷键

Ctrl+J / Ctrl+K,L: 列出成员

Ctrl+Shift+空格键 / Ctrl+K,P: 参数信息

Ctrl+K,I: 快速信息

Ctrl+E,C / Ctrl+K,C: 注释选定内容

Ctrl+E,U / Ctrl+K,U: 取消选定注释内容

Ctrl+K,M: 生成方法存根

Ctrl+K,X: 插入代码段

Ctrl+K,S: 插入外侧代码

F12: 转到所调用过程或变量的定义

窗口快捷键

Ctrl+W,W: 浏览器窗口

Ctrl+W,S: 解决方案管理器

Ctrl+W,C: 类视图

Ctrl+W,E: 错误列表

Ctrl+W,O: 输出视图

Ctrl+W,P: 属性窗口

Ctrl+W,T: 任务列表

Ctrl+W,X: 工具箱

Ctrl+W,B: 书签窗口

Ctrl+W,U: 文档大纲

Ctrl+D,B: 断点窗口

Ctrl+D,I: 即时窗口

Ctrl+Tab: 活动窗体切换

Ctrl+Shift+N: 新建项目

Ctrl+Shift+O: 打开项目

Ctrl+Shift+S: 全部保存

Shift+Alt+C: 新建类

Ctrl+Shift+A: 新建项

VS2005的隐藏快捷键

这里我将会把一些无意中发现的VS2005中没有明确指出的快捷键共享出来,并不是所有的快捷键,或者常见的一些快捷键。

1、Ctrl+Space直接完成类或函数(本来这个并不算隐藏的快捷键,但是因为中文输入法抢占这个快捷键,所以。。。,替代的快捷键是Alt+Right)

2、Shift+Delete整行删除,并且将这一行放到剪贴板(这时候不能选中一段内容)

3、Shift+Insert粘贴,有点匪夷所思,Ctrl+V就可以了,大概是为了和Shift+Delete对应吧

4、Ctrl+Up,Ctrl+Down滚动编辑器,但尽量不移动光标,光标保证在可见范围内

5、Ctrl+BackSpace,Ctrl+Delete整词删除,有的时候很有用

6、Ctrl+Left,Ctrl+Right按整词移动光标(不算隐藏,和前面几条加起来就是Ctrl光标控制套件了)

7、Alt+Shift+F10打开执行改名,实现接口和抽象类的小窗口(还可以用Ctrl+.,不过有的中文输入法用到这个)

8、Shift+F9调试是打开QuickWatch,内容是当前光标所在处的内容

9、F12转跳到定义,很有用的快捷键

10、Shift+F12查找所有引用

—————————————VS2008快捷键大全—————————-

Ctrl+m+Crtr+o折叠所有大纲

Ctrl+M+Crtr+P: 停止大纲显示

Ctrl+K+Crtr+C: 注释选定内容

Ctrl+K+Crtr+U: 取消选定注释内容

Ctrl+J : 列出成员智能感知

Shift+Alt+Enter: 切换全屏编辑

Ctrl+B,T / Ctrl+K,K: 切换书签开关

Ctrl+B,N / Ctrl+K,N: 移动到下一书签

Ctrl+B,P: 移动到上一书签

Ctrl+B,C: 清除全部标签

Ctrl+I: 渐进式搜索

Ctrl+Shift+I: 反向渐进式搜索

Ctrl+F: 查找

Ctrl+Shift+F: 在文件中查找

F3: 查找下一个

Shift+F3: 查找上一个

Ctrl+H: 替换

Ctrl+Shift+H: 在文件中替换

Alt+F12: 查找符号(列出所有查找结果)

Ctrl+Shift+V: 剪贴板循环

Ctrl+左右箭头键: 一次可以移动一个单词

Ctrl+上下箭头键: 滚动代码屏幕,但不移动光标位置。

Ctrl+Shift+L: 删除当前行

Ctrl+M,M: 隐藏或展开当前嵌套的折叠状态

Ctrl+M,L: 将所有过程设置为相同的隐藏或展开状态

Ctrl+E,S: 查看空白

Ctrl+E,W: 自动换行

Ctrl+G: 转到指定行

Shift+Alt+箭头键: 选择矩形文本

Alt+鼠标左按钮: 选择矩形文本

Ctrl+Shift+U: 全部变为大写

Ctrl+U: 全部变为小写

代码快捷键

Ctrl+Shift+空格键 / Ctrl+K,P: 参数信息

Ctrl+K,I: 快速信息

Ctrl+E,U / Ctrl+K,U: 取消选定注释内容

Ctrl+K,M: 生成方法存根

Ctrl+K,X: 插入代码段

Ctrl+K,S: 插入外侧代码

F12: 转到所调用过程或变量的定义

窗口快捷键

Ctrl+W,W: 浏览器窗口

Ctrl+W,S: 解决方案管理器

Ctrl+W,C: 类视图

Ctrl+W,E: 错误列表

Ctrl+W,O: 输出视图

trl+W,P: 属性窗口

Ctrl+W,T: 任务列表

Ctrl+W,X: 工具箱

Ctrl+W,B: 书签窗口

Ctrl+W,U: 文档大纲

Ctrl+D,B: 断点窗口

Ctrl+D,I: 即时窗口

Ctrl+Tab: 活动窗体切换

Ctrl+Shift+N: 新建项目

Ctrl+Shift+O: 打开项目

Ctrl+Shift+S: 全部保存

Shift+Alt+C: 新建类

Ctrl+Shift+A: 新建项

Shift+Alt+Enter: 切换全屏编辑

Ctrl+B,T / Ctrl+K,K: 切换书签开关

Ctrl+B,N / Ctrl+K,N: 移动到下一书签

Ctrl+B,P: 移动到上一书签

Ctrl+B,C: 清除全部标签

Ctrl+I: 渐进式搜索

Ctrl+Shift+I: 反向渐进式搜索

Ctrl+F: 查找

Ctrl+Shift+F: 在文件中查找

F3: 查找下一个

Shift+F3: 查找上一个

Ctrl+H: 替换

Ctrl+Shift+H: 在文件中替换

Alt+F12: 查找符号(列出所有查找结果)

Ctrl+Shift+V: 剪贴板循环

Ctrl+左右箭头键: 一次可以移动一个单词

Ctrl+上下箭头键: 滚动代码屏幕,但不移动光标位置。

Ctrl+Shift+L: 删除当前行

Ctrl+M,M: 隐藏或展开当前嵌套的折叠状态

Ctrl+M,L: 将所有过程设置为相同的隐藏或展开状态

Ctrl+M,P: 停止大纲显示

Ctrl+E,S: 查看空白

Ctrl+E,W: 自动换行

Ctrl+G: 转到指定行

Shift+Alt+箭头键: 选择矩形文本

Alt+鼠标左按钮: 选择矩形文本

Ctrl+Shift+U: 全部变为大写

Ctrl+U: 全部变为小写