[转载]一步一步打造WebIM(4)——Comet的特殊之处

mikel阅读(911)

[转载]一步一步打造WebIM(4)——Comet的特殊之处 – 卢春城专栏 – 博客园.

WebIM系列文章

一步一步打 造WebIM(1)一文中已经使用Comet实现了一个简单的WebIM,那么,Comet究竟和一般的打开网页有何区别,本文将通过编写一个简 单的HTTP服务器来说明两者的区别。

所谓网站,其实可以理解为服务器上的一个应用程序,该应用程序创建了一个Socket并在80端 口(一般是80端口)上监听,并接受和处理浏览器发送过来的HTTP请求。

当你打开网页时,浏览器会发送一个HTTP请求到服务器,之 后浏览器将一直等待知道服务器发送完HTTP回应。当服务器接受到这个http请求后,就会解析HTTP请求的头部,根据报文中的信息向浏览器发送数据 (网页,图片,数据等),当服务器发送完数据后,浏览器就结束等待,显示出网页。关于浏览器和服务器之前的交互,可以阅读这篇博文:

HTTP协议及其POST与GET操作差异 & C#中如何使用POST、GET等

根 据以上叙述的交互过程,可以编写一个简单的http服务器,该服务器在8000端口监听,并且只提供两个网页

http://localhost:8000/index.htm

http://localhost:8000/comet.htm

这两个网页的区别在于,index.htm服务器在接收到HTTP请求后立刻发送网页并结束本次链接,而Comet.htm则不是如此,是将链 接先挂起,并启动一个线程,新的线程将延迟5秒再发送网页。服务器源代码如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace http
{
    class Program
    {
        static String ResponeFormat =
        "HTTP/1.1 200 OK\r\n" +
        "Content-Length: {0}\r\n" +
        "Connection: close\r\n" +
        "Content-Type: text/html\r\n" +
        "\r\n" +
        "{1}\r\n";

        static void Main(string[] args)
        {
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //在8000端口监听
            EndPoint ep = new IPEndPoint(new IPAddress(new byte[] { 127, 0, 0, 1 }), 8000);
            server.Bind(ep);
            server.Listen(5);

            while (true)
            {
                Socket client = server.Accept();

                //读取HTTP报文头部
                String header = String.Empty;
                while (header.IndexOf("\r\n\r\n") < 0)
                {
                    byte[] buffer = new byte[1024];
                    int len = client.Receive(buffer);
                    header += Encoding.ASCII.GetString(buffer, 0, len);
                }

                Console.WriteLine("================================================================================");
                Console.Write(header);

                Regex reg = new Regex(@"GET\s+([^\s\r\n]+)\s+HTTP/1.1\s*\r\n", RegexOptions.IgnoreCase);
                Match match = reg.Match(header);
                if (match.Success)
                {
                    String fname = Path.GetFileName(match.Groups[1].Value).ToLower();

                    if (fname == "index.htm")
                    {
                        String html = String.Format("<span style='color:red; fonst-size:24px;'>{0}</span>", DateTime.Now);
                        String response = String.Format(ResponeFormat, html.Length, html);

                        //发送HTTP回应,本次HTTP请求结束,浏览器将立刻接受到网页
                        client.Send(Encoding.UTF8.GetBytes(response));
                        client.Close();
                    }
                    else if (fname == "comet.htm")
                    {
                        //假设此时数据未准备好,先挂起链接,网页将在另一个线程中发送到浏览器
                        //在线程没有发送HTTP回应前,浏览器将一直等待,直到启动的线程发送回应
                        Thread thread = new Thread(new ParameterizedThreadStart(SendThreadEntry));
                        thread.Start(client);

                    }
                    else
                    {
                        client.Close();
                    }
                }
            }
        }

        static void SendThreadEntry(object data)
        {
            //等待5s
            Thread.Sleep(5000);
            Socket client = data as Socket;
            String html = String.Format("<span style='color:red; fonst-size:24px;'>{0}</span>", DateTime.Now);
            String response = String.Format(ResponeFormat, html.Length, html);

            Console.WriteLine("Send Response");

            //发送HTTP回应,本次HTTP请求结束,浏览器此时才接收到网页,在此之前浏览器一直等待
            client.Send(Encoding.UTF8.GetBytes(response));
            client.Close();
        }
    }
}

当然,发送Comet.htm的数据不一定启动一个新的线程,可以先将client保存起来(例如保存到全局变量中),等数据准备好了,调用 client的Send方法发送数据。

源代码下载

[转载]在构造JSON串时需要注意的一点

mikel阅读(1079)

[转载]在构造JSON串时需要注意的一点 – 编程之美 – 博客园.

随着web技术的发展,现在Ajax已经不再是新生事物了,基本上已经成为了我们开发的标准。而JSON凭借着自身的优势基本上成为我们在使用 异步请求时首选的数据传输格式,尤其是使用它在传输数据的时候可以大大减小数据量,从而提高网络通讯的效率,但是在使用JSON的时候有一个地方需要我们 注意一下,那就是JSON中的一些关键字。看看下面的代码

1 public class json : IHttpHandler
2 {
3
4 public void ProcessRequest(HttpContext context)
5 {
6 context.Response.Write({\title\:\hello\nworld\});
7 }
8
9 public bool IsReusable
10 {
11 get
12 {
13 return false;
14 }
15 }
16 }
1 $(document).ready(function() {
2
3 $.ajax({
4
5 url: json.ashx?stamp= + new Date().getMilliseconds(),
6 dataType: json,
7 success: function(json) {
8 alert(json.title);
9 },
10 error: function(e,r) {
11 alert(r);
12 }
13 });
14 });

如果我们只是简单的看代码,可能会认为这里会弹出hello world这个字符串,可是当我们实际运行这个程序的时候,会意外的出现解析错误,这个理解起来可能不是很直观。我们注意到hello world中间的那\n,正是这个东西导致的解析错误,原因是为什么呢?

其实也不是很深奥,JSON串虽然称作串,但它的全程叫做JavaScript Object Notation—JavaScript对象表示法,这个字符串是作为js中的对象来解析的,更直白点说它是语法级别的,它们是被当作JS代码的,是要给 JS解释器来看的,正如刚才那个JSON串,他的正确表示方法应该是这样

{title:hello world}

而在我们的程序中实际输出的却是这种情况:

{title:”hello
world”}

上面这种表示法,在JS解释器中会被认为是语法错误,因为字符串没有在行内闭合,因为我们多输出了一个\n,所以产生了语法错误,所以我们平时 在写JS的时候一定要注意这种情况,类似的字符还有 ” ‘ [ ] {} 等,这些也都有可能引起JSON串的语法错误。

[转载]Delphi 组件开发教程指南(8)定制特色Button

mikel阅读(1016)

[转载]Delphi 组件开发教程指南(8)定制特色Button – 得闲笔记 – 博客园.

不知不觉,本系列的文章已经到了第8篇了,不知对大部分初学者是否有帮助。虽然说,本人写这些东西的仅仅是个人的兴趣所致,但是我还是希望他们能够确确实 实的帮助各个入门者,让他们少走弯路。到目前为止,还有属性编辑器没有讲解道,其他的,基本上应该都涉及到了,所以,这系列基本上也差不多接近尾声了。当 然这个没讲到的还是有很多的,比如各种各样的Windows消息,这个东西,太多,莫说我讲不全,因为很多消息,我都没真实的去理解到,Windows程 序员参考大全中就有一本是专门讲解Windows消息的作用的,书名叫《Microsoft Win32程序员参考大全(五)—-消息、结构和宏.pdf》,那个书是一定要备用的。建议各位开发者将本系列全部弄全,一共5本。所以这个消息,我 也就能捡一部分常见的说了,其他的N多N多消息,就需要咱们在实际开发中去查找资料与摸索了。

这次,我思来想去,就只有想到了这个模拟Windows系统的Button组件来讲解一部分消息,虽然针对Windows的系统消息还是九牛一毛的,但是 基本上方式都差不多的,你理解了系统的各个消息的触发时间和触发条件,那么你就可以很容易的来拦截这些消息来进行自己的处理。这次这个定制的 Button,我从TCustomControl继承下来往下面来实现,首先,我们还是先分析一下操作Button的时候的一些条件以及触发的事件。这是 显而易见的,首先,鼠标要按下弹起,就触发一次Click事件,而Button的最重要的也就是单击操作,这里有两个效果,鼠标按下的时候,一个效果,鼠 标弹起的时候一个效果,另外,当鼠标按下了之后会获得焦点,所以还有一个焦点的是否效果存在哈,这都是可操作的情况,除此之外,还会有按钮不可用的状况, 也就是说Enabled := False的状况,此时的按钮状态又要是另一个效果。通过这些简单的分析,现在我们锁定了按钮的几种效果分别是:按下效果,平常状态的效果,焦点效果,不 可用效果这4种情况。这里我是和Windows的Button比较来说,其实说起来,应该还有一个鼠标滑过效果的,这次先不讲。然后我们看看这里涉及到的 几个消息,鼠标按下弹起当然就是WM_LButtonDown,WM_LButtonUp了,然后就是看不可用变化,这个消息是经过Delphi包装之后 发送出来的消息,是CM_ENableCHANGED,用来标记变化效果,这些消息就是用来控制变化效果的。还有一个情况,上面忘记了说,就是按钮标题文 字变化时候也会触发一个消息,这个是CM_TEXTCHANGED。焦点变化的时候的焦点效果,这里有两个消息WM_KillFocus失去焦点的时候触 发,除此之外,WM_SetFocus是获得焦点的时候触发。拦截这两个消息的目的都是用来刷新绘制焦点框的。现在分析完毕,那么剩下的,就是来代码的编 写,注意,Windows的系统按钮是不可设置颜色的,我现在扩充为可设置颜色。

前面说了,要拦截鼠标按下和抬起消息,这个我们直接继承MouseDown和MouseUp消息就OK了,鼠标按下的时候,我们就需要刷新一次,鼠标弹起 MouseUP的时候刷新一次,然后还有一个事件,就是判断鼠标是否在上面,如果在按钮上面就触发Click,来触发单击事件。这里,需要说明一下这个单 击事件,不晓得我在前面有没有说过ControlStyle这个属性,这个用来指定一些组件的样式等,里面有一个csClickEvents,我们这里需 要将这个样式移除。然后再实现我们自己的Click,至于为何移除,暂留,大家思考一下原因。下面就给出一个效果,然后看看代码:

这 个asdf就是实现的一个模拟的Button控件了,现在目前是一个非常挫的效果,不过框架已经出来了,要什么效果,以后都能自己扩充绘制。现在就给出代 码,代码非常简单,里面也就仅仅是简单的实现了一下,大家自己思考思考,将Button的一些其他功能属性补全,下一期,我将介绍将本Button扩充为 QQ效果的按钮

unit DxButton; interface uses Windows,Messages,Classes,SysUtils,Controls,Graphics; type TDxButton = class(TCustomControl) private FIsDown:Boolean; FInButtonArea: Boolean; FOnClick: TNotifyEvent; protected procedure Paint;override; procedure CMTextChanged(var msg: TMessage);message CM_TEXTCHANGED; procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED; procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER; procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE; procedure MouseDown(Button: TMouseButton; Shift: TShiftState;X, Y: Integer); override; procedure MouseUp(Button: TMouseButton; Shift: TShiftState;X, Y: Integer); override; procedure WMEnable(var Message: TMessage); message WM_ENABLE; procedure WMKillFocus(var msg: TWMKillFocus);message WM_KILLFOCUS; procedure WMS(var msg: TWMSetFocus);message WM_SETFOCUS; public constructor Create(AOwner: TComponent);override; procedure Click; override; published property Color; property Enabled; property Caption; property OnClick: TNotifyEvent read FOnClick write FOnClick; end; implementation procedure Frame3D(Canvas: TCanvas; var Rect: TRect; TopColor, BottomColor: TColor; Width: Integer); procedure DoRect; var TopRight, BottomLeft: TPoint; begin with Canvas, Rect do begin TopRight.X := Right; TopRight.Y := Top; BottomLeft.X := Left; BottomLeft.Y := Bottom; Pen.Color := TopColor; PolyLine([BottomLeft, TopLeft, TopRight]); Pen.Color := BottomColor; Dec(BottomLeft.X); PolyLine([TopRight, BottomRight, BottomLeft]); end; end; begin Canvas.Pen.Width := 1; Dec(Rect.Bottom); Dec(Rect.Right); while Width > 0 do begin Dec(Width); DoRect; InflateRect(Rect, -1, -1); end; Inc(Rect.Bottom); Inc(Rect.Right); end; function GetNearColor(const Color: TColor;OffsetValue: integer): TColor; var R, G, B, dR, dG, dB: Byte; begin if (OffsetValue > 127) or (OffsetValue < -127) then raise Exception.Create('偏移值为-127-127之间') else if OffsetValue = 0 then Result := Color else begin Result := ($80 + OffsetValue) shl 24 or (ColorToRGB(Color)); R := Byte(Result shr 0); G := Byte(Result shr 8); B := Byte(Result shr 16); if OffsetValue > 0 then begin Inc(OffsetValue); dR := not R; dG := not G; dB := not B; end else begin dR := R; dG := G; dB := B; end; R := R + (dR * OffsetValue) shr 7; G := G + (dG * OffsetValue) shr 7; B := B + (dB * OffsetValue) shr 7; Result := RGB(R,G,B) end; end; { TDxButton } procedure TDxButton.Click; begin if Visible and Enabled then begin if Assigned(FOnClick) then FOnClick(Self); end; end; procedure TDxButton.CMEnabledChanged(var Message: TMessage); begin inherited; if Parent <> nil then Invalidate; end; procedure TDxButton.CMMouseEnter(var Message: TMessage); begin FInButtonArea:=True; inherited; end; procedure TDxButton.CMMouseLeave(var Message: TMessage); begin FInButtonArea:=False; inherited; end; procedure TDxButton.CMTextChanged(var msg: TMessage); begin Invalidate; end; constructor TDxButton.Create(AOwner: TComponent); begin inherited; ControlStyle := [csSetCaption, csCaptureMouse]; Width := 40; Height := 20; end; procedure TDxButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin inherited; if Enabled then begin SetFocus; FIsDown:=True; Invalidate; end; end; procedure TDxButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var IsClick: Boolean; begin inherited; IsClick := FIsDown; FIsDown := False; Invalidate; if IsClick and FInButtonArea then begin Click; FIsDown:=False; end; end; procedure TDxButton.Paint; var r: TRect; begin r := ClientRect; if not FIsDown then Frame3D(Canvas,r,GetNearColor(Color,80),GetNearColor(Color,-80),1) else Frame3D(Canvas,r,GetNearColor(Color,-80),GetNearColor(Color,80),1); //然后绘制文字 if Focused then begin Canvas.Brush.Color := not Color; InflateRect(r,-1,-1); DrawFocusRect(Canvas.Handle,r) end; Canvas.Brush.Style := bsClear; Canvas.Font.Assign(Font); if not Enabled then begin OffsetRect(r, 1, 1); Canvas.Font.Color := clWhite; DrawText(Canvas.Handle, PChar(Caption), Length(Caption), r, DT_CENTER or DT_VCENTER or DT_SINGLELINE); Canvas.Font.Color := clGray; OffsetRect(r, -1, -1); end; DrawText(Canvas.Handle, PChar(Caption), Length(Caption), r, DT_CENTER or DT_VCENTER or DT_SINGLELINE); end; procedure TDxButton.WMEnable(var Message: TMessage); begin SetEnabled(Message.WParam <> 0); end; procedure TDxButton.WMKillFocus(var msg: TWMKillFocus); begin inherited; Invalidate; end; procedure TDxButton.WMS(var msg: TWMSetFocus); begin inherited; Invalidate; end; end.

Delphi 组件开发教程指南目录

作者:不得闲
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必 须保留此段声明,且在文章页面明显位置给出原
文连接,否则保留追究法律责任的权利。

[转载]C#扩展方法详解

mikel阅读(1104)

[转载]扩展方法详解 – 你听海是不是在笑 – 博客园.

一、基础

扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。扩展方法是一种特殊的静态方法,调用扩展方 法与调用在类型中实际定义的方法之间没有明显的差异。扩展方法是可以通过使用实例方法语法调用的静态方法。效果上,使得附加的方法扩展已存在类型和构造类 型成为可能。他可以对现有类功能进行扩充,从而使该类型的实例具有更多的方法。扩展方法有助于把今天动态语言中流行的对快速录入支持的灵活性,与强类型语 言之性能和编译时验证融合起来。这里先举一个msdn中的例子:
下面的示例演示为 System.String 类定义的一个扩展方法。假设我要分析一个字符串,希望得到字符串中单词的个数,一般情况下我们可能使用一个统计的函数来解决这个问题

代码

但这样用起来感觉上可能会很别扭,这个WordCount方法如果就是 字符串类中的一个方法多好,像所有字符串的实例方法一样用。这里我们引出扩展方法来解决这个问题。

代码

注意,上面的静态方法在第一个类型是string的参数变量前有个 “this”关键词,这告诉编译器,这个特定的扩展方法应该添加到类型为“string”的对象中去。
使用 using 指令将 WordCount 扩展方法放入范围中:
using ExtensionMethods;

string s = Hello Extension Methods;
int i = s.WordCount();

二、应用场景

1、扩展类库中已经存在的类

比方说我们是无法改变系统已经存在的类的,就像上例中我们无法在string类中添加WordCount方法,但我又希望在使用时直接使用

这篇文章c#扩展方法奇思妙用中也有很多例子,大家可以参考一下

2、多个类实现接口时有些方法实现方式相同,所以不希望每个实现类中重新实现一遍

假如系统中用户接口需要两个类实现,希望IUser接口声明的实体都有检测Email的方法IsUniqueEmail,而实际上这个方法在所有实 现类中的实现逻辑都是一样的,而我们如果把这个方法添加到IUser中,那么两个类就要都实现一遍IsUniqueEmail的逻辑。中间加一层继承关系 也可以解决问题,这种方式会增加维护成本,这时我们可以使用扩展方法

如下图:

代码模拟如下:

代码

interface IUser { string Email { get; set; } string Name { get; set; } } class UserLinqToOracle : IUser { #region IUser 成员 private string email; public string Email { get { return email; } set { email=value; } } private string name; public string Name { get { return name; } set { name = value; } } #endregion } class UserLinqToSql : IUser { #region IUser 成员 private string email; public string Email { get { return email; } set { email=value; } } private string name; public string Name { get { return name; } set { name = value; } } #endregion }
代码

static class UserExtension { public static bool IsUniqueEmail(this IUser forUser, string email) { return CheckEmail(email); } private static bool CheckEmail(string email) { return true; } }
static void Main(string[] args) { IUser user = new UserLinqToOracle(); Console.WriteLine(user.IsUniqueEmail("")); }

三、扩展方法要点
1、扩展方法的本质为将实例方法调用在编译 期改变为静态类中的静态方法调用。事实上,它确实拥有静态方法所具有的所有功能。
2、通常,您更多时候是调用扩展方法而不是实现您自己的扩展方 法。由于扩展方法是使用实例方法语法调用的,因此不需要任何特殊知识即可从客户端代码中使用它们。若要为特定类型启用扩展方法,只需为在其中定义这些方法 的命名空间添加 using 指令。
3、扩展方法的优先级:现有实例方法优先级最高,其次为最近的namespace下的静态类的静态方法,最后 为较远的namespace下的静态类的静态方法。(与接口或类方法具有相同名称和签名的扩展方法永远不会被调用 )

代码

class Program { static void Main(string[] args) { User user = new User() { FirstName = "l", LastName = "fm" }; Console.WriteLine(user.FullName()); } } public class User { public string FirstName { get; set; } public string LastName { get; set; } public string FullName() { return "来源于实例方法:" + FirstName + LastName; } } public static class MyExtensions { public static string FullName(this User user) { return "来源于扩展方法:"+user.FirstName+user.LastName; } }

结果为“来源于实例方法:lfm”

代码

namespace ConsoleApplication7
{
using MyNamespace;
class Program
{
static void Main(string[] args)
{
User user
= new User() { FirstName = "l", LastName = "fm" };
Console.WriteLine(user.FullName());
}

}
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public static class MyExtensions
{
public static string FullName(this User user)
{
return "来源于较近的扩展方法:"+user.FirstName+user.LastName;
}
}
}
namespace MyNamespace
{
using ConsoleApplication7;
public static class MyExtensions
{
public static string FullName(

[转载]扩展PowerDbg自动化调试过程

mikel阅读(1117)

[转载]扩展PowerDbg自动化调试过程 – Killmyday – 博客园.

在前面的文章使用PowerDbg自动化Windbg调 试过程里,简单介绍了如何使用PowerDbg自动化一些Windbg的命令。但问题是,PowerDbg自身提供的命令太少了,幸 好PowerDbg提供了源代码,可以让我们了解它是如何工作的,因此我 也有机会自己扩展PowerDbg

上次在用Windbg调试一个问题的时候,需要查看一个Dictionary对象里面所有的值,用来确认有些特殊的值是否被正确的加入到Dictionary对象里。本来是用Visual Studio调试这个问题的,但是后面发现Visual Studio实在是太慢了—其实我调试的程序就是Visual Studio本身,只不过用另外一个Visual Studio调试它而已。在没有符号文件和源代码的情况下,在Visual Studio里面设置一个托管代码的函数断点的速度的确很慢。不得已,只好切换到Windbg,但是同时就没有了Visual Studio强大的变量显示的功能(Visualizer)。

Windbg里面,如果要查看Dictionary对象的值,一般是通过下面几个命令实现的:

#  1. 查看Dictionary对象本身的值,找到保存元素的槽(bucket)。

0:000> !do 018e2e0c

Name: System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib]]

MethodTable: 002b201c

EEClass: 62e00e18

Size: 52(0x34) bytes

(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)

Fields:

MT    Field   Offset                 Type VT     Attr    Value Name

6304aa5c 40009af        4       System.Int32[] 0 instance 018e30e0 buckets

00000000 40009b0        8              SZARRAY 0 instance 018e30f8 entries

6304ab0c 40009b1       20         System.Int32 1 instance        2 count

6304ab0c 40009b2       24         System.Int32 1 instance        2 version

6304ab0c 40009b3       28         System.Int32 1 instance       -1 freeList

6304ab0c 40009b4       2c         System.Int32 1 instance        0 freeCount

00000000 40009b5        c                       0 instance 018e2e7c comparer

00000000 40009b6       10                      0 instance 00000000 keys

00000000 40009b7       14                       0 instance 00000000 values

630484dc 40009b8       18        System.Object 0 instance 00000000 _syncRoot

6302f3d0 40009b9       1c …SerializationInfo 0 instance 00000000 m_siInfo

#  2. 使用!DumpArray打印数组并且显示每个元素的详细信息。

0:000> !da -details 018e30f8

Name: System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib]][]

MethodTable: 002b23e8

EEClass: 002b2368

Size: 60(0x3c) bytes

Array: Rank 1, Number of elements 3, Type VALUETYPE

Element Methodtable: 002b2318

# 每个元素的详细信息

[0] 018e3100

Name: System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib]]

MethodTable 002b2318

EEClass: 62e00f5c

Size: 24(0x18) bytes

(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)

Fields:

MT    Field   Offset                 Type VT     Attr    Value Name

6304ab0c 40009ba        8         System.Int32 1 instance 1497890914 hashCode

6304ab0c 40009bb        c         System.Int32 1 instance       -1 next

# 找到键值对以及他们的地址

63048530 40009bc        0       System.__Canon 0 instance 018e2c08 key

63048530 40009bd        4       System.__Canon 0 instance 018e2e88 value

[1] 018e3110

Name: System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib]]

MethodTable 002b2318

EEClass: 62e00f5c

Size: 24(0x18) bytes

(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)

Fields:

MT    Field   Offset                 Type VT     Attr    Value Name

6304ab0c 40009ba        8         System.Int32 1 instance 1306722402 hashCode

6304ab0c 40009bb        c         System.Int32 1 instance       -1 next

63048530 40009bc        0       System.__Canon 0 instance 018e2dbc key

63048530 40009bd        4       System.__Canon 0 instance 018e3134 value

[2] 018e3120

Name: System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib]]

MethodTable 002b2318

EEClass: 62e00f5c

Size: 24(0x18) bytes

(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)

Fields:

MT    Field   Offset                 Type VT     Attr    Value Name

6304ab0c 40009ba        8         System.Int32 1 instance        0 hashCode

6304ab0c 40009bb        c         System.Int32 1 instance        0 next

63048530 40009bc        0       System.__Canon 0 instance 00000000 key

63048530 40009bd        4       System.__Canon 0 instance 00000000 value

#  3. 查看键值对 的详细信息,这一步可以通过!DumpObject命令完成。

0:000> !do 018e2c08

Name: System.String

MethodTable: 630488c0

EEClass: 62e0a498

Size: 26(0x1a) bytes

(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)

String: key1

Fields:

MT    Field   Offset                 Type VT     Attr    Value Name

6304ab0c 4000096        4         System.Int32 1 instance        5 m_arrayLength

6304ab0c 4000097        8         System.Int32 1 instance        4 m_stringLength

630495a0 4000098        c          System.Char 1 instance       6b m_firstChar

630488c0 4000099       10        System.String 0   shared   static Empty

>> Domain:Value 003829d0:018e1198 <<

630494f0 400009a       14        System.Char[] 0   shared   static WhitespaceChars

>> Domain:Value 003829d0:018e1924 <<

#  4. 针对Dictionary对象的每一个元素,重复第二步和第三步。

从上表里面可以看 出,在Windbg里面查看一个Dictionary对象的确不是一件轻松的事情,特别是在Dictionary对象的Value参 数也是一个 Dictionary对象的时候,那就更痛苦了。

我当时调试那个问 题的时候,就是使用!DumpArray!DumpObject以及DU(用来显 示字符串)命令手工遍历一个40个元素的Dictionary对象。分析完那一个问题以后,我决定再也不做类似的事情了!

因此我用PowerShell结合PowerDbg已有的命令写了下面一个脚本,用来自动递归打印Dictionary对象里面所有的元素(里面有必要的注释),如果要使用这个脚本,只需要把下面的代码合并到PowerDbg的源代码里面就好了。如果你连合并都懒得做,没关系,下面的链接是已经合并好的代码:

/Files/killmyday/ParseDumpDic.zip

#

# 这个函数就是入口函数,你需要提供一个Dictionary对象的地址, 这个地址你需要自己去查看Windbg+SOS

# 的输出才能找到,它只支持x86平台,如果需要支持x64平台,那你需 要自己修改一下下面的脚本

#

function Parse-PowerDbgDUMPDIC([string] $address = $(throw “Error! You must provide the address of Dictionaryo object.”))

{

set-psDebug -strict

$ErrorActionPreference = “stop”

trap {“Error message: $_”}

# 根据Dictionary对象的地址组 合一个SOS命令(!DumpArray),因为Dictionary对象保存键 值对数组

# 的属性距离Dictionary对象的地址有8个字节的位置 (如果是64位系统,就 是16个字节)。

#

# poi命令是Windbg用来获取一个 指针指向的内存的内容。这是因为Dictionary对象的地址加上8个字节

# 的偏移量,只是获取了键值对数组的属性的地址,而不是键 值对数组的地址。

$cmd = “!da -details poi(0x{0:x8}+8)” -f $address

# 保存最后格式化的结果,创建的是一个.NETStringBuilder对象

$builder = New-Object System.Text.StringBuilder

$builder.AppendLine(“key,value”)

Invoke-WindbgDUMPARR $cmd $builder 0

return $builder.ToString()

}

function Invoke-WindbgDUMPARR([string] $cmd = $(throw “Error! You must provide windbg !DumpArray command to invoke.”),

[System.Text.StringBuilder] $builder = $(throw “Error! You must provide buffer for result.”),

[int] $level)

{

# 将格式化好的Windbg命令发送到windbg远程调试服务 器中执行

Invoke-WinDbgCommand $cmd

# 执行完毕以后,$global:g_commandOutput全局变量保 存了Windbg的输出

# 注意,Invoke-WinDbgCommand只会将上一 次命令的输出保存在这个变量里面

# 至于它是如何做到的,你可以阅读Invoke-WinDbgCommand的源代码

$stringReader = [System.IO.StringReader] $global:g_commandOutput

# 一行行处理Windbg输出,使用正 则表达式提取出我们需要的信息,例如

# 变量类型,变量地址甚至是变量的值

while(($line = $stringReader.ReadLine()) -ne $null)

{

# 递归处理键值对数组里面的每一个键值对

if ($line -match “^\s*\[\d+\]\s+(?<addr>[0-9a-fA-F]+)$”)

{

# 获取键值对的 地址,后面我们可以用!DumpObject命令来处理 它

$addr = $matches[“addr”]

$line = $stringReader.ReadLine();

if ( $line -eq $null )

{

throw “Errors! There is error in Windbg output. Expect more output for dictionary entry.”

}

# 获取键值对的类型,因为我期望这个程序可以处理尽量多的 从Dictionary<,>派生出来

# 的类型。

if ( $line -match “Name:\s+(?<type>(.+))” )

{

Parse-PowerDbgDictionary $addr $matches[“type”] $builder $level

}

}

}

}

# 这个命令模板用来打印键值对里面的键(Key)的值,注意,它只处理字符串类型

# 如果指定的键值对地址是一个空值(NULL),则什么都不做。因为Dictionary对象采取

# ArrayList相似的动态扩展内存的逻辑,键值对数组并不一定都是满的

$global:g_WindbgViewKeyCmd = “j poi(0x{0:8})=0 ;du poi(0x{0:8})+c”

# 这个命令模板用来打印键值对里面的值(Value)的值,注 意,它只处理字符串类型

$global:g_WindbgViewValueCmd = “j poi(0x{0:8})=0 ;du poi(0x{0:8}+4)+c”

# 如果Dictionary对象的值类型不是字符串(String)类型的话, 而是另外一个Dictionary对象的话

# 下面这个命令模板用来递归处理这个Dictionary对象

$global:g_WindbgDumpArrCmd = “j poi(0x{0:8})=0 ;!da -details poi(poi(0x{0:8}+4)+8)”

function Parse-PowerDbgDictionary(

[string] $objAddr = $(throw “Error! You must provide the address of Dictionaryo object.”),

[string] $typeName = $(throw “Error! you must provide full type name of Dictionary entry.”),

[System.Text.StringBuilder] $builder = $(throw “Error! You must provide buffer for result.”),

[int] $level)

{

# 根据键值对的类型获取键(Key)的类型和值(Value)的类型

$result = Parse-PowerDbgDictionaryEntry $typeName

$keyType = $result[0]

$valueType = $result[1]

# 只处理键(Key)类型为字符串的情况

if ( [String]::Compare($keyType, 0, “System.String”, 0, “System.String”.Length) -eq 0 )

{

$cmd = $global:g_WindbgViewKeyCmd -f $objAddr

Invoke-WinDbgCommand $cmd

if ( $global:g_commandOutput -match “[0-9A-Za-z]{8}\s+””(?<text>.+)””\s*$” )

{

$builder.AppendLine(“”)

for ( [int]$i = 0; $i -lt $level; $i++ )

{

$builder.Append(” ,”)

}

$builder.Append($matches[“text”])

}

}

# 如果值(Value)类型为字符串的话,打印出它的值

if ( [String]::Compare($valueType, 0, “System.String”, 0, “System.String”.Length) -eq 0 )

{

$cmd = $global:g_WindbgViewValueCmd -f $objAddr

Invoke-WinDbgCommand $cmd

if ( $global:g_commandOutput -match “[0-9A-Za-z]{8}\s+””(?<text>.+)””\s*$” )

{

$builder.Append(” = “)

$builder.Append($matches[“text”])

}

}

# 看看值(Value)类型是不是 另外一个Dictionary对象

elseif ([String]::Compare($valueType, 0, “System.Collections.Generic.Dictionary”, 0, “System.Collections.Generic.Dictionary”.Length) -eq 0)

{

$cmd = $global:g_WindbgDumpArrCmd -f $objAddr

$level = $level + 1

# 是的话,递归 处理这个Dictionary对象,然后 再打印下一个键值对

Invoke-WindbgDUMPARR $cmd $builder $level

}

else

{

throw “Value type {0} is not supported.” -f $valueType

}

}

$global:g_WindbgGenericDicName = “System.Collections.Generic.Dictionary”

function Parse-PowerDbgDictionaryEntry(

[string] $typeName = $(throw “Error! you must provide full type name of Dictionary entry.”))

{

if([String]::Compare($typeName, 0, $global:g_WindbgGenericDicName, 0, $global:g_WindbgGenericDicName.Length) -ne 0)

{

throw “Error! Just Dictionary or generic Dictionary are supported.”

}

$typeName = $typeName -replace “\[\]”, “”

if ( $typeName -match “^[^\[\]]*(((?’Open’\[)(?<key>[^\[\]]*))+((?’Close-Open’\])[^\[\]]*)+)*$” )

{

$keyType = $matches[“key”]

$valueType = $matches[“Close”]

# skip [$keyType], and get the result

$valueType = $valueType.SubString($keyType.Length + 3)

$valueType = $valueType.SubString(1, $valueType.Length – 2)

# output the type of DictionaryEntry.Key

$keyType

# output the type of DictionaryEntry.Value

$valueType

}

else

{

throw “Error! Parenthese in input DictionaryEntry’s type name are not balanced.”

}

}

# 将里面的函数导出,这样可以在PowerShell里面使用下面 这几个函数

Export-ModuleMember -Function Parse-PowerDbgDUMPDIC

Export-ModuleMember -Function Parse-PowerDbgDictionaryEntry

Export-ModuleMember -Function Invoke-WindbgDUMPARR

Export-ModuleMember -Function Parse-PowerDbgDictionary

下面是一个输出的 例子 脚本里面有一个Bug, 不知道为什么那个Capacity等东西被PowerShell打印出来了,但是不影响我的使用,就放在那里没理它了):

> $result = Parse-PowerDbgDUMPDIC “018e2e0c”

> $result

Capacity                      MaxCapacity                           Length

——–                      ———–                           ——

512                       2147483647                              275

key,value

key1

,subkey1 = value1

,subkey2 = value2

,subkey3 = value3

,subkey4 = value4

,subkey5 = value5

,subkey6 = value6

key2

,subkey-1 = value-1

,subkey-2 = value-2

,subkey-3 = value-3

,subkey-4 = value-4

,subkey-5 = value-5

,subkey-6 = value-6

[转载]精进不休 .NET 4.0 (3) - asp.net 4.0 新特性之动态数据(Dynamic Data)增强, AJAX增强; IDE之Visual Studio 2010增强

mikel阅读(755)

[转载]精进不休 .NET 4.0 (3) – asp.net 4.0 新特性之动态数据(Dynamic Data)增强, AJAX增强; IDE之Visual Studio 2010增强 – webabcd – 专注于asp.net, Silverlight – 博客园.

[源码下载]

精进不休 .NET 4.0 (3) – ASP.NET 4.0 新特性之动态数据(Dynamic Data)增强, AJAX增强; IDE之Visual Studio 2010增强

作者:webabcd

介绍
ASP.NET 4.0 的新增功能

  • EnableDynamicData – 启用 Dynamic Data 的功能
  • DynamicHyperLink – 用于方便地生成在 Dynamic Data 站点中导航的超级链接
  • Entity Template – 实体模板是一个新增的用于自定义数据显示的模板,其基于 FormView 控件做数据呈现
  • DisplayAttribute – 新增的一个 Attribute(可以设置字段的Name和Order)
  • 其它新特性

AJAX 增强
Visual Studio 2010 增强

示例
1、Dynamic Data 之 EnableDynamicData
Demo/EnableDynamicData.aspx

代码

<%@ Page Language=C# AutoEventWireup=true CodeBehind=EnableDynamicData.aspx.cs
Inherits
=DynamicData.Demo.EnableDynamicData %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title></title>
</head>
<body>
<form id=”form1″ runat=”server”>
<div>
<!–收集并显示由 Dynamic Data 所做的数据验证的结果–>
<asp:ValidationSummary ID=”ValidationSummary1″ runat=”server” EnableClientScript=”true” HeaderText=”验证错误的列表” />

<asp:DetailsView ID=”DetailsView1″ runat=”server” AllowPaging=”True” DataKeyNames=”ProductID”
DataSourceID
=”EntityDataSource1″>
<Fields>
<asp:CommandField ShowDeleteButton=”True” ShowEditButton=”True” ShowInsertButton=”True” />
</Fields>
</asp:DetailsView>
<asp:EntityDataSource ID=”EntityDataSource1″ runat=”server” ConnectionString=”name=AdventureWorksEntities”
DefaultContainerName
=”AdventureWorksEntities” EnableDelete=”True” EnableInsert=”True”
EnableUpdate
=”True” EntitySetName=”Products” EnableFlattening=”False”>
</asp:EntityDataSource>
</div>
</form>
</body>
</html>

Demo/EnableDynamicData.aspx.cs

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace DynamicData.Demo
{
public partial class EnableDynamicData : System.Web.UI.Page
{
protected void Page_Init()
{
/*
* EnableDynamicData – 启用 Dynamic Data 的功能(包括字段模板,数据验证在内的等 等 Dynamic Data 的功能都会被启用)
*     第一个参数:需要启用 Dynamic Data 功能的实体类型
*     第二个参数:当控件启用插入模板的时候,为字段指定默认值
*/
DetailsView1.EnableDynamicData(
typeof(Product), new { Name = 默认名称 });
}

protected void Page_Load(object sender, EventArgs e)
{

}
}
}

2、Dynamic Data 之 DynamicHyperLink
Demo/DynamicHyperLinkDemo.aspx

代码

<%@ Page Language=C# AutoEventWireup=true CodeBehind=DynamicHyperLinkDemo.aspx.cs
Inherits
=DynamicData.Demo.DynamicHyperLinkDemo %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title></title>
</head>
<body>
<form id=”form1″ runat=”server”>
<div>
<!–
DynamicHyperLink – 用于方便地生成在 Dynamic Data 站点中导航的超级链接
Action – 指定 Action(可选值有 List|Details|Edit|Insert)
TableName – 需要链接到的目标表名
ContextTypeName – 上下文的类全名
–>
<asp:DynamicHyperLink ID=”ListHyperLink” runat=”server” Text=”全部产 品” Action=”List”
TableName
=”Products” ContextTypeName=”DynamicData.AdventureWorksEntities”>
</asp:DynamicHyperLink>

<!–
生成的 HTML 代码如下:
<a id=”ListHyperLink” href=”/Products/List.aspx”>全部产品& lt;/a>
–>
</div>
</form>
</body>
</html>

3、Dynamic Data 之 Entity Template
DynamicData/EntityTemplates/Products.ascx

代码

<%@ Control Language=C# AutoEventWireup=true CodeBehind=Products.ascx.cs Inherits=DynamicData.DynamicData.EntityTemplates.Products %>

<!–
Entity Template – 实体模板是一个新增的用于自定义数据显示的模板,其是基于 FormView 控件做数据呈现的
以下演示如何实现自定义的 Entity Template ,进入产品详情页可查看此 Demo 的效果
以下内容会被替换 到 PageTemplates/Details.aspx <asp:DynamicEntity runat=”server” /& gt; 中去
–>
<tr>
<td>
产品ID
</td>
<td>
<asp:DynamicControl ID=”DynamicControl1″ runat=”server” DataField=”ProductID” />
</td>
</tr>
<tr>
<td>
产品名称
</td>
<td>
<asp:DynamicControl ID=”DynamicControl2″ runat=”server” DataField=”Name” />
</td>
</tr>

DynamicData/EntityTemplates/Products.ascx.cs

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace DynamicData.DynamicData.EntityTemplates
{
// 自定义的 EntityTemplate 的基类 是 System.Web.DynamicData.EntityTemplateUserControl
public partial class Products : System.Web.DynamicData.EntityTemplateUserControl
{

}
}

4、Dynamic Data 之 DisplayAttribute
Demo/Metadata.cs

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.ComponentModel.DataAnnotations;

namespace DynamicData
{
[MetadataType(
typeof(Product_Metadata))]
public partial class Product
{

}

public class Product_Metadata
{
// DisplayAttribute – 新增的一个 Attribute
// Name – 指定字段的名称(参 考 Demo/EnableDynamicData.aspx ,其“ProductID”字段显示为“产品ID”)
// Order – 指定字段在所有字段中的排序
[Display(Name=产品ID, Order=0)]
public object ProductID { get; set; }

[Display(Name = 产品名称, Order = 1)]
public object Name { get; set; }

[Range(1, 100, ErrorMessage={0}的范围必须是{1}-{2})]
[Display(Name
= 单价, Order = 2)]
public object ListPrice;
}
}

5、Dynamic Data 之 其它新特性
Demo/Others.aspx

代码

<%@ Page Language=C# AutoEventWireup=true CodeBehind=Others.aspx.cs Inherits=DynamicData.Demo.Others %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title></title>
<style>
body
{
font-size
: 12px;
}
textarea
{
width
: 99%;
}
</style>
</head>
<body>
<form id=”form1″ runat=”server”>
<div>
<p>
1、DynamicDataManager 控件新增了 DataControls 属性,在其内可以声明多 个 DataControlReference,用于指定哪些控件需要启用 Dynamic Data 的功能
<textarea>
<asp:DynamicDataManager ID=”DynamicDataManager1″ runat=”server” AutoLoadForeignKeys=”true”>
<DataControls>
<asp:DataControlReference ControlID=”GridView1″ />
</DataControls>
</asp:DynamicDataManager>
<asp:GridView id=”GridView1″ runat=”server” />
</textarea>
</p>
<p>
2、新增了一个 EnumDataTypeAttribute,可以声明某字段为枚举类型。对应的字段模板里也新增 了 Enumeration.ascx 和 Enumeration_Edit.ascx
</p>
<p>
3、新增了两个数据类型
<ul>
<li>[DataType(DataType.EmailAddress)] 对 应的字段模板里也新增了 EmailAddress.ascx</li>
<li>[DataType(DataType.Url)] 对 应的字段模板里也新增了 Url.ascx</li>
</ul>
</p>
<p>
4、支持多对多的关系(限 Entity Framework 模型)。对应的字段模板里也新增 了 ManyToMany.ascx 和 ManyToMany_Edit.ascx
</p>
<p>
5、原 Dynamic Data 会自动地将类型为 Boolean, Enumeration, ForeignKey 的 字段作为查询条件输出到页面上。现在新增了 Filter Template(查询模板),可以对这些查询条件的样式和逻辑等做自定义修改(详 见 DynamicData/Filters 下的文件)
</p>
</div>
</form>
</body>
</html>

6、AJAX 增强
List.html

代码

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title></title>
<style>
body
{
font-size
: 12px;
}
textarea
{
width
: 99%;
}
</style>
</head>
<body>
<p>
1、Visual Studio 2010 内包含了 JQuery
</p>
<p>
2、JavaScript 框架的 CDN
<ul>
<li>JQuery – &lt;script src=”http://ajax.Microsoft.com/ajax/JQuery-1.3.2.js” type=”text/JavaScript&gt;&lt;/script&gt;</li>
<li>asp.net ajax – &lt;asp:ScriptManager ID=”ScriptManager1” EnableCdn=”true” runat=”server” /&gt;</li>
</ul>
</p>
<p>
3、asp.net ajax 的按需加载脚本,如下所示
<textarea rows=”10″>
<asp:ScriptManager ID=”ScriptManager1″ AjaxFrameworkMode=”Explicit” runat=”server”>
<Scripts>
<asp:ScriptReference Name=MicrosoftAjaxCore.js />
<asp:ScriptReference Name=MicrosoftAjaxComponentModel.js />
<asp:ScriptReference Name=MicrosoftAjaxSerialization.js />
<asp:ScriptReference Name=MicrosoftAjaxNetwork.js />
</Scripts>
</asp:ScriptManager>
</textarea>
</p>
</body>
</html>

7、Visual Studio 2010 增强

代码

<%@ Page Title=“” Language=C# MasterPageFile=~/Site.Master AutoEventWireup=true
CodeBehind
=List.aspx.cs Inherits=VisualStudio.List %>

<asp:Content ID=”Content1″ ContentPlaceHolderID=”head” runat=”server”>
<style>
body
{
font-size
: 12px;
}
textarea
{
width
: 99%;
}
</style>
</asp:Content>
<asp:Content ID=”Content2″ ContentPlaceHolderID=”ContentPlaceHolder1″ runat=”server”>
<p>
1、HTML 和 JavaScript 支持 Code Snippets(代码片段)
<ul>
<li>组合键 ctrl+k ctrl+x 用 于插入代码片段</li>
<li>组合键 ctrl+k ctrl+b 用 于调出代码片段管理器</li>
<li>
比如要在 HTML 的代码片段中增加一个 fieldset 的代码片段,可参考如下写法,将其保存 为 snippet 格式的文件保存到“我的文档\Visual Studio 2010\Code Snippets \Visual Web Developer\My HTML Snippets”
<textarea rows=”6″>
<CodeSnippet Format=”1.1.0″ xmlns=”http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet”>
<Header>
<Title>fieldset</Title>
<Author>webabcd</Author>
<Shortcut>fieldset</Shortcut>
<Description>Markup snippet for a fieldset</Description>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>legend</ID>
<ToolTip>legend</ToolTip>
<Default>标题</Default>
</Literal>
<Literal>
<ID>content</ID>
<ToolTip>content</ToolTip>
<Default>content</Default>
</Literal>
</Declarations>
<Code Language=”html”>
<![CDATA[<fieldset>
<legend>$legend$</legend>
<div>
$content$
</div>
</fieldset>$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</textarea>
</li>
</ul>
</p>
<p>
2、根据不同配置生成不同版本的 Web.config 文件
<ul>
<li>在 Web.config 文件点右键, 选择 “Add Config Transforms”,可生成不同配置的 Web.config 文件(生成包的时候,按哪种配置生成则生成哪种配置 的 Web.config 文件)</li>
<li>默认有两种配置, 即 Debug 和 Release ,可以在 Build -> Configuration Manager 中做 新增/删除/修改/更 新 配置的操作</li>
</ul>
</p>
<p>
3、智能提示的改进。比如在对象后面“点”属性的时候,它将根据已输入的字母做筛选。例:如果在 TextBox 对象后键 入 text, 则智能提示只会显示 Text, TextChanged, TextMode(以前会把对象的所有属性、方法、事件都列出来)
</p>
<p>
4、将光标放到某一个标识上的时候,则该页所有使用了此标识的地方都会被 IDE 突出显示出来
</p>
<p>
5、新增了一个开发环境配置 – Code Optimized ,在此模式下只有 html 编辑视图,而没有设计视图。在第一次启 动 Visual Studio 2010 的时候,可以选择开发环境配置。以后如果需要修改开发环境配置,可以在此处操 作:Tools > Import & Export Settings > Reset all settings option
</p>
<p>
6、在项目属性中新增了两个选项卡,Package/Publish Web 和 Package/Publish SQL,分别用于生 成 Web 包和 SQL 包
</p>
</asp:Content>

OK
[源码下载]

[转载]Android2.2 SDK正式提供下载

mikel阅读(929)

[转载]Android2.2 SDK正式提供下载 — Windows Live.

就在5月20日的谷歌I/O大会基调演讲中,Android2.2(代号Froyo)正式公布。紧接着没多久,Android2.2 SDK就开始正式提供下载。联想到之前几个版本的发布过程,我们有理由相信Android2.2的更新极有可能在六月上中旬发布!

下载:Android2.2 SDK

此外,Adobe的Flash Player 10.1 for Android也开始正式提供beta下载,但需要使用2.2版本的Android访问,看来只有少量使用2.2的内部人员才能参与测试吧。 

下载:Adobe的 Flash Player 10.1 for Android

[转载]基于Mapabc API的周边查询应用

mikel阅读(1077)

[转载]基于Mapabc API的周边查询应用 – 小狼的世界 – 博客园.

现在,越来越多的 Location Based 应用,或者Geolocation的应用出现在网络、手机等各种各样的终端上,为人们的日常生活、出行和工作都提供了不少的便利。最常见的就是出门前,利 用地图工具,看看合适的公共交通路线,寻找一个自己最满意的方案,既方便有低碳环保。有时候,对周边环境不熟悉,想要找到周边的医院、银行、学校等一些单 位的时候,地图同样可以帮上我们的大忙。

今天,我就做了一个基于Mapabc地图API的周边查询工具,利用Mapabc详尽的基础地 物信息,为我查找周边的便民信息提供了有利的帮助。

开始之前,我大概需要知道最后的结果是什么样子的,可以借助传统的纸和笔,也可以用 现代的原型工具,当然PS这样的重量级工具就不太需要了。下面是效果图:

1131

有了效果图,接下来需要稍微规划一下代码。 Mapabc的API在页面加载完成后,需要初始化地图,所以初始化的操作放在一个函数 pageInit() 中。周边查询的操作,希望通过用户输入一个关键词,然后通过点选地图设置中心点的方式实现。那么就需要一个函数来监听鼠标在地图上的单击事件,然后一个负 责查询的函数和一个负责显示结果的函数。程序的结构大致如此,接下来就进入实质的编码过程。

编码的重点函数有以下几个:

mapObj.addEventListener(mapObj, MOUSE_CLICK, searchAround);

对地图添加监听事件。

mls.poiSearchByCenterXY(new MLngLat(cordx, cordy),keyword,citycode,mlsp);

利用中心点坐标查询查找周边信息

利用这两个函数,加上参考Mapabc官方的示例,我们就可以完成这个应 用。

1132

通过修改区号和关键字,可以在不同城市之间切 换,怎么样,简单吧。

[转载] DedeCMS采集规则-过滤-替换-技巧

mikel阅读(882)

[转载]DedeCMS采集规则-过滤-替换-技巧 – jim.ma (农民的天空) – 域名博客中心 – Powered by X-Space.

过滤替换

CODE:

1.采集去除链 接
[Copy to clipboard]CODE:
{dede:trim}]*)>([^<]*){/dede:trim}
--------------------------------

让field:title 标题突破30这个长度,修改代码的方法
找到./include/inc_arcpart_view.php
行291 :
if($titlelen=="") $titlelen = 30;
修改为
if($titlelen=="") $titlelen = 60;
就可以了,然后, 你可以这样调用了
{dede:channelArtlist typeid='0' col=1 tablewidth='100%'}
{dede:arclist row="10"}
[field:title function="cn_substr('@me',38)" /]

{/dede:arclist}
{/dede:channelArtlist}

把 这个延伸一下:关于inc_arcpart_view.php
function GetArcList($typeid=0,$row=10,$col=1,$titlelen=30,$infolen=160,
$imgwidth=120,$imgheight=90,$listtype="all",$orderby="default",$keyword="",
$innertext="",$tablewidth="100",$arcid=0,$idlist="")
这 里的参数都可以更改你实际需要的模板元素尺寸大小.

2. 采集过虑中去掉链接保留文字的方法!

柏老大的 方法是{dede:trim}<a ([^>]*)>([^<]*)</a>{/dede:trim}
这样 做会去掉<a hf.......>与</a>之间的字符!这样整个文章就少了部分字符,不完整了!

后来我多次 测试,总算找到了正确的使用方法!如下:
{dede:trim}<a([^>]*)>{/dede:trim}
{dede:trim}</a>{/dede:trim}

做 成两条采集规则就可以了!
在实际使用中好像([^<]*)([^>]*)两条一起使用才行!

3. 过滤div
{dede:trim}]*)>{/dede:trim}
{dede:trim}
{/dede:trim}
过 滤js
{dede:trim}]*)>([^<]*){/dede:trim}

过滤未知变量字符
固 定(.*)固定

4.dede万能过滤代码
以下是常用的正则表达式标签
{dede:trim}<tbody(.*)>{/dede:trim}
{dede:trim}</tbody>{/dede:trim}
{dede:trim}<table(.*)>{/dede:trim}
{dede:trim}</table>{/dede:trim}
{dede:trim}<tr(.*)>{/dede:trim}
{dede:trim}</tr>{/dede:trim}
{dede:trim}<td(.*)>{/dede:trim}
{dede:trim}</td>{/dede:trim}
{dede:trim}<font(.*)>{/dede:trim}
{dede:trim}</font>{/dede:trim}
{dede:trim}<a(.*)>{/dede:trim}
{dede:trim}</a>{/dede:trim}
{dede:trim}<param(.*)>{/dede:trim}
{dede:trim}<embed(.*)</embed>{/dede:trim}
{dede:trim}<object(.*)</object>{/dede:trim}
{dede:trim}<iframe(.*)</iframe>{/dede:trim}
{dede:trim}<form(.*)</form>{/dede:trim}
{dede:trim}<input(.*)>{/dede:trim}
{dede:trim}<scrīpt(.*)</scrīpt>{/dede:trim}
{dede:trim}<style(.*)</style>{/dede:trim}
{dede:trim}<!--(.*)-->{/dede:trim}

以 下为不常用的正则表达式标签
{dede:trim}<div(.*)>{/dede:trim}
{dede:trim}</div>{/dede:trim}
{dede:trim}<center(.*)>{/dede:trim}
{dede:trim}</center>{/dede:trim}
{dede:trim}<p(.*)>{/dede:trim}
{dede:trim}</p>{/dede:trim}
{dede:trim}<span(.*)>{dede:trim}
{dede:trim}</span>{dede:trim}
{dede:trim}<img(.*)>{/dede:trim}

/////////////////////////////////////

5. 织梦标题不全,鼠标指向显示全部的代码:
{dede:arclist titlelen='100'}

[field:title function=' ( strlen("@me")>40 ? cn_substr("@me",40): "@me" ) '/]
{/dede:arclist}

6.dede/inc /inc_archives_functions.php第100行(flash频道远程flash本地化的BUG)
$cfg_uploaddir = $GLOBALS['media_dir'];
修改成
$cfg_uploaddir = $GLOBALS['cfg_other_medias'];

6.发布时间,来源,作者可以通过@me函数实现,如:在自定义处理接口: 处输入  @me = "Azure·【博】" 就表示来源为“Azure·【博】”
7.内容的替换:在所采集的文章内容中有多媒体,使用的是相 对路径,采集的时候又不想下载,最好的办法就是将地址替换成媒体的实际地址。可以这样实现,在文章内容规则部分的自定义处理接口:处输入 @me=str_replace('src="str1','src="str2',@me);

dedecms 带超连接关键字 如何去掉
全部去
{dede:trim}^<a*'>*</a>${/dede:trim}
dede 自带采集器的高阶技巧

CODE:


dede 系统自带的采集器其实相当强大,特别是整个dede系统是完全开源的,即使有些采集上的特殊需求,只要对PHP的基本语言有掌握的话,也可以轻意实现。诛 仙外挂
要实现特殊的采集要求,内容规则里有一个自定义处理接口非常关键。
比如说,你要在每篇采集文章正文前加一个前言,前言的内容是正文的前100个字,应该怎么做呢?
首先,需要定义好文章内容的采集规则诛仙外挂,保证最后得到的只是文章的正文(这属于最基本的东西,不再说明)
然后,就需要在自定义处理接口里写一段程序:
@me='前言:'.substr(@me, 0, 200).'<br><br>'.@me
其中,因为汉字是双字节的,所以200个字节表示100个汉字,另外,如果正文里混有其它html代码的话,还需要调整一下字节数。以上代码中'前言:' 和'<br><br>'只是一个最基本的美工修饰,让前言和文章主体分离。
通过以上一段代码,所有采集的文章都会自动加上一个前言,这是一个最简单的例子了。
现在举一个复杂些的例子,需要修改源代码。
假设,你现在需要在文章正文处贴上文章来源的网址,比如说你从http://aa.com/43456.html采集到一篇文章,现在你需要在文章正文显 示这个网址,怎么做呢?
这个需求可能大家遇上的不多,但举一反正,诛仙外挂其它类似的需求也就有解决的思路了。
我们注意到,在自定义处理 接口的右边有@body,@litpic,@me三个预定变量,分别表示原始网页,缩略图、当前标记值和最终结果。那么我们现在要再增加一个变量 @url,表示当
前文章的网址。要做到这个功能,就必须改源代码。
只需改一个文件就可以了,即:include目录下的pub_collection.php(注意,这里使用的是DEDE4.0版本)
在文件最 后有一个函数:
//---------------------------------
//用扩展函数处理采集到的原始数据
//-------------------------------
function RunPHP($fvalue,$phpcode)
{
$DedeMeValue = $fvalue;
$phpcode = preg_replace("/'@me'|\"@me\"|@me/isU",'$DedeMeValue',$phpcode);
if(eregi('@body',$phpcode)){
$DedeBodyValue = $this->tmpHtml;
$phpcode = preg_replace("/'@body'|\"@body\"|@body/isU",'$DedeBodyValue',$phpcode);
}
if(eregi('@litpic',$phpcode)){
$DedeLitPicValue = $this->breImage;
$phpcode = preg_replace("/'@litpic'|\"@litpic\"|@litpic/isU",'$DedeLitPicValue',$phpcode);
}
@eval($phpcode.";");
return $DedeMeValue;
}
这里定义了默认的几个预定变量
现在我们要增加一个@url
因此上一段就要改成:
function RunPHP($fvalue,$phpcode, $dourl=false)
{
$DedeMeValue = $fvalue;
$phpcode = preg_replace("/'@me'|\"@me\"|@me/isU",'$DedeMeValue',$phpcode);
if(eregi('@body',$phpcode)){
$DedeBodyValue = $this->tmpHtml;
$phpcode = preg_replace("/'@body'|\"@body\"|@body/isU",'$DedeBodyValue',$phpcode);
}
if(eregi('@litpic',$phpcode)){
$DedeLitPicValue = $this->breImage;
$phpcode = preg_replace("/'@litpic'|\"@litpic\"|@litpic/isU",'$DedeLitPicValue',$phpcode);
}
if(eregi('@url',$phpcode)){
$DedeUrlValue = $dourl;
$phpcode = preg_replace("/'@url'|\"@url\"|@url/isU",'$DedeUrlValue',$phpcode);
}
@eval($phpcode.";");
return $DedeMeValue;
}
注意,为了给@url传递值,我们在函数的参数里增加了一个参数$dourl=false,因此还 需要在调用这个函数的地方改一下($dourl=false表示该参数并不是必须的,一般调用时可以不用此参数)
找到如下代码:
//用户 自行对内容进行处理的接口
if($sarr["function"]!=""){
if(!eregi('@litpic',$sarr["function"])){
$v = $this->RunPHP($v,$sarr["function"]);
$artitem .= "{dede:field name='$k'}$v{/dede:field}\r\n";
}else{
$tmpLtKeys[$k]['v'] = $v;
$tmpLtKeys[$k]['f'] = $sarr["function"];
}
把 其中的
$v = $this->RunPHP($v,$sarr["function"]);
改成:
$v = $this->RunPHP($v,$sarr["function"], $dourl);
就可以了,至此源代码全部修改完毕。
要 使用这个新的变量@url很简单
比如说,在文章内容的自定义处理接口里写下:
@me=@me.'<br><br& gt;文章来源:'.@url
就可以了
转自 bbs.dedecms.com