ASP.NET Forms Authentication所生成Cookie的安全性

最初我想,.net的验证应该是比较安全的吧,生成的cookie也应该与这台电脑的独特的参数相关,拿到另一台电脑上就应该无效了。那么是不是一个用户名对应一个cookie值呢?是否能够通过伪造cookie值来骗过表单验证呢?做一番试验。
web.c
<authentication mode="forms">
<forms name="mylab" loginurl="/login.aspx">
<credentials passwordformat="clear">
<user name="fancyray" password="fancyray"/>
</credentials>
</forms>
</authenticati

<authorizati
<deny users="?" />
</authorizati

login.aspx只有一个用户名输入框txtusername、一个密码输入框txtpassword和一个提交按钮,click事件如下:
if (formsauthentication.authenticate(this.txtusername.text, this.txtpassword.text))
{
formsauthentication.redirectfromloginpage(this.txtusername.text, true);
}
else
{
response.write("login denied");
}
借助iehttpheaders(http://www.blunck.info/)可以看到,通过验证后增加了一个类似这样的cookie:
mylab=3ff83247c29eb5d14d61f389d453eee0586b94e27609c321b017be7b88d1a94d249996428a7a18f5c2d69f3c4dd2b88c00172cafb0b4b4ed8784db62d1d61bcc0c786b4ea7868fc6
看来这就是加密以后的cookie了。下面要换一台电脑,直接将这个值设置为cookie,看看是否需要forms验证。
在login.aspx页面中加上这样一句话:
<script language=javascript>
document.cookie="mylab=3ff83247c29eb5d14d61f389d453eee0586b94e27609c321b017be7b88d1a94d249996428a7a18f5c2d69f3c4dd2b88c00172cafb0b4b4ed8784db62d1d61bcc0c786b4ea7868fc6";
</script>
这样只要一打开login.aspx页面就会自动加入这个cookie。
另一台电脑:输入同一个webapplication下的另外一个页面(应该会自动跳转到login.aspx页面)http://10.0.0.7/upload.aspx,这时成功跳转到了http://10.0.0.7/login.aspx?returnurl=%2fupload.aspx,正常。这时cookie的值应该已经生效了。那么我们再输入刚才那个页面的网址http://10.0.0.7/upload.aspx!
按照我的猜想,肯定还会跳到login.aspx页面的,因为那个cookie是在另一台电脑上生成的。实际呢,没有跳转!完整地显示出了upload.aspx的内容!而我们根本没有在这台电脑上登录,甚至我们连用户名都不知道!
我回到10.0.0.7这台电脑在upload.aspx页面的page_load()第一行添了一句:response.write (user.identity.name);,在另一台电脑上刷新显示出来了的upload.aspx,结果也出现了fancyray,正是我的用户名。
这说明,cookie的加密不依赖于登录的电脑。也就是说,一旦你的cookie被别人获得,他就有可能获得你在这台服务器上的权限。
那么cookie的这个值是怎么来的呢?黑客是否可能不通过穷举就得到这个值呢?

我们先来看看cookie中到底存储了一些什么,以及怎样进行的加密。reflactor(http://www.aisto.com/roeder/dotnet)上场!
public static void setauthcookie(string username, bool createpersistentcookie, string strcookiepath)
{
formsauthentication.initialize();
httpcontext.current.response.cookies.add(formsauthentication.getauthcookie(username, createpersistentcookie, strcookiepath));
}

public static httpcookie getauthcookie(string username, bool createpersistentcookie, string strcookiepath)
{
formsauthentication.initialize();
if (username == null)
{
username = "";
}
if ((strcookiepath == null) || (strcookiepath.length < 1))
{
strcookiepath = formsauthentication.formscookiepath;
}
formsauthenticati ticket1 = new formsauthenticati username, datetime.now, createpersistentcookie ? datetime.now.addyears(50) : datetime.now.addminutes((double) formsauthentication._timeout), createpersistentcookie, "", strcookiepath);
string text1 = formsauthentication.encrypt(ticket1);
formsauthentication.trace("ticket is " + text1);
if ((text1 == null) || (text1.length < 1))
{
throw new httpexception(httpruntime.formatresourcestring("unable_to_encrypt_cookie_ticket"));
}
httpcookie cookie1 = new httpcookie(formsauthentication.formscookiename, text1);
cookie1.path = strcookiepath;
cookie1.secure = formsauthentication._requiressl;
if (ticket1.ispersistent)
{
cookie1.expires = ticket1.expirati
}
return cookie1;
}
cookie中存储的值就是里面的text1,text1是由string text1 = formsauthentication.encrypt(ticket1);生成的,所以text1里面的信息就是ticket1了。 formsauthenticati
public formsauthenticati versi string name, datetime issuedate, datetime expirati bool ispersistent, string userdata, string cookiepath)
里面有用户名、生成ticket1的时间和过期时间。
看到这里我不禁打了一个冷颤。ticket1实际上只用到了用户名一个关键信息,连密码都没有用!这样岂不是任何一个用户的ticket1都可以轻易的制造出来吗?只要通过formsauthentication.encrypt(ticket1)就得到了cookie的值,来伪装成任何一个用户?太可怕了。现在只能寄希望于encrypt这个函数了。看看它的实现:
public static string encrypt(formsauthenticati ticket)
{
if (ticket == null)
{
throw new argumentnullexcepti
}
formsauthentication.initialize();
byte[] buffer1 = formsauthentication.maketicketintobinaryblob(ticket);
if (buffer1 == null)
{
return null;
}
if (formsauthentication._protection == formsprotectionenum.n
{
return machinekey.bytearraytohexstring(buffer1, 0);
}
if ((formsauthentication._protection == formsprotectionenum.all) || (formsauthentication._protection == formsprotectionenum.validati
{
byte[] buffer2 = machinekey.hashdata(buffer1, null, 0, buffer1.length);
if (buffer2 == null)
{
return null;
}
formsauthentication.trace("encrypt: mac length is: " + buffer2.length);
byte[] buffer3 = new byte[buffer2.length + buffer1.length];
buffer.blockcopy(buffer1, 0, buffer3, 0, buffer1.length);
buffer.blockcopy(buffer2, 0, buffer3, buffer1.length, buffer2.length);
if (formsauthentication._protection == formsprotectionenum.validati
{
return machinekey.bytearraytohexstring(buffer3, 0);
}
buffer1 = buffer3;
}
buffer1 = machinekey.encryptordecryptdata(true, buffer1, null, 0, buffer1.length);
return machinekey.bytearraytohexstring(buffer1, buffer1.length);
}

看到了machinekey这个词,终于松了一口气。看来加解密过程是与服务器的参数有关系。也就是说,服务器上有自己的密钥,只有用这个密钥才能进行cookie的加解密。如果不知道这个密钥,别人是无法伪造cookie的。

看来cookie还是安全的,在你的电脑没有被入侵的前提下。与其他任何信息一样,所需注意的仅仅是网络传输中的安全性了。
cookie的这个值和sessi

还有一个问题不容忽视。虽然cookie的值的生成与具体的时间有关,也就是我注销后再次登陆所生成的cookie是不一样的,但是一个合法的 cookie值是永久有效的,不受是否改变密码及时间的影响。也就是说,我上一次生成的cookie在我注销以后拿到另一台电脑上仍然可以用,只要服务器的machinekey不变。的确是个安全隐患。我们也只能说:“cookie的值很长,要穷举到一个有效的cookie在有生之年是办不到的”来找一些安慰。密码可以通过频繁的更改来进一步减小穷举到的可能性,但合法的cookie却无法更改。密码是唯一的,但合法的cookie值却不是唯一的。这一切总让人觉得不太放心。
也许担心是多余的,因为电子签名、证书都是建立在“穷举要付出很大代价”的基础上的,要是考虑“碰巧被穷举到”的话,安全就不复存在了。相信在一般的安全领域,forms生成的cookie的安全级别还是足够的。

在asp.net中长内容自动分页的实现

在asp.net中长内容自动分页的实现
在一篇文章过长时,可以自动的写个小程序对其进行分页.
具体代码:

public class t3 : system.web.ui.page
{
private string str;//字符
private int strl;//字符总长度
private int pagesize;//每页显示的字符数
private void page_load(object sender, system.eventargs e)
{
// 在此处放置用户代码以初始化页面
str="1234567891234567898522555";
pagesize=3;
strl=str.length;
response.write(strl);
substr();
}

private void substr()
{
int ct=int32.parse(request.querystring["page"]);
if(strl==(strl/pagesize)*pagesize)//看看页面的总记录是否能被每页的记录数整除
{
for(int i=1;i<=strl/pagesize;i++)
{
response.write("页:<a href='#'  +">"+(i)+"</"+"a>");
}
string s=str.substring(pagesize*ct-pagesize,pagesize);
response.write(s);
}
else if(ct*pagesize>strl)//在不被整除的情况下,最后一页的设置,如字符长13,每页3,则处理最后那一页的显示
{
for(int i=1;i<=(strl/pagesize)+1;i++)
{
response.write("页:<a href='#' /"+"a>");
}
string s=str.substring((ct-1)*pagesize,strl-(ct-1)*pagesize);
response.write(s);

}
else //在不被整除的情况下其他页面的显示设置
{
for(int i=1;i<=strl/pagesize+1;i++)
{
response.write("页:<a href='#'  +">"+(i)+"</"+"a>");
}
string s=str.substring(pagesize*ct-pagesize,pagesize);
response.write(s);
}

}
}

非数据库数据源分页的实现

主要代码:

把arraylist(或数组)的数据源绑定到repeater,并进行分页
+++demo++++
aspx
__________________________________________
<body>
<form id="form1" method="post" runat="server">
<font face="宋体">
<p><asp:repeater id="rtest" runat="server" datamember="1">
<itemtemplate>
<f color="#ff3366">
<%#container.dataitem%>
<br>
</f
</itemtemplate>
</asp:repeater></p>
<p>当前页:&nbsp;
<asp:label id="dq" runat="server">1</asp:label></p>
<p>
<asp:label id="link" runat="server">label</asp:label></p>
</font>
</form>
</body>
____________________________________________________

.cs
____________________________________________________________
public class t1 : system.web.ui.page
{
protected system.web.ui.webcontrols.label dq;
protected system.web.ui.webcontrols.label link;
protected system.web.ui.webcontrols.label d;
protected system.web.ui.webcontrols.repeater rtestt;
protected system.web.ui.webcontrols.repeater rtest;
private int pagesize;//每页显示的记录数目

public void page_load(object sender, system.eventargs e)
{
// 在此处放置用户代码以初始化页面
if(!page.ispostback)
pagesize=40; //每页显示的记录数目
this.rtest.datasource=datasource();//直接绑定到数据源
this.rtest.databind();
}

//设置arraylist,供arraylist datasource()调用
public arraylist data()
{
//++++给repeater构造数据源,长度变量为j+++++++++++++++++++++++++++++++++++++++++++++++++++++
arraylist s=new arraylist();
for(int j=1;j<100;j++)
s.add("aa"+j.tostring());
return s;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
#regi
private arraylist datasource()
{

arraylist s=data();
{
//再次构造一个arraylist,作为每个页面显示的数据
arraylist subs=new arraylist();
int rek=int32.parse(request.querystring["page"]);
dq.text=(rek+1).tostring();

//如果是能被整除,如总记录数为20,每页显示4条记录,所以就整除,要是每页显示3条记录,则在最后页为2条记录
if(s.count==pagesize*(s.count/pagesize))
{
for(int link=0;link<s.count/pagesize;link++)
{
response.write("页:<a href='#' /"+"a>";

}
}
//不能被整除的处理
if(s.count!=pagesize*(s.count/pagesize))
{
for(int link=0;link<(s.count/pagesize)+1;link++)
{
response.write("页:<a href='#' ) +">"+(link+1)+"</"+"a>";

}
}

//判断是否能记录是否能被页数整除
if(s.count==pagesize*(s.count/pagesize))
{
int k=rek;
if((k*pagesize)<s.count)
{
for(int i=(pagesize*k);i<(pagesize*k+pagesize);i++)
{
subs.add(s[i]);
}
}
}

//不能整除,对最后页的设置.如总记录数为20,每页显示3条记录,以下就设置就只显示最后也的那2条记录
if(rek==s.count/pagesize)
{
int k=rek;
for(int i=(pagesize*k);i<(s.count);i++)
{

subs.add(s[i]);
}

}

else
{
int k=rek;

{
for(int i=(pagesize*k);i<(pagesize*k+pagesize);i++)
{

subs.add(s[i]);
}
}

}
if(s.count==pagesize*(s.count/pagesize))
{
response.write("共"+s.count/pagesize+"页";
}
else
{
response.write("共"+(1+s.count/pagesize)+"页";
}
return subs;
}

}
#endregion

#region web 窗体设计器生成的代码
override protected void oninit(eventargs e)
{
//
// codegen: 该调用是 asp.net web 窗体设计器所必需的。
//
initializecomp
base.oninit(e);
}

/// <summary>
/// 设计器支持所需的方法 – 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void initializecomp
{
this.load += new system.eventhandler(this.page_load);

}
#endregion
}

思路:
建立个arraylist data(),储存所有的数据.
而arraylist datasource()则为从总的数据源中取要显示的某部分.
也就是你看到的当前页的数据.

实现方法:
首先找到当前页的开始位置i,(即s[i]中的i是几)和当前页结束时的i
并把他们添加到arraylist subs中
其中arraylist subs只储存当前页的记录.

主要变量声明:
pagesize 每页显示的记录数目
j 数据源的总记录数值(我设置的为99,因为1=j<100)

用途:
这样只要你把数据库中的记录查询后,把他添加到arraylist s里
就可以直接
pagesize=40; //每页显示的记录数目
this.rtest.datasource=datasource();//直接绑定到数据源
this.rtest.databind();
绑定,并分页.(也可以封装为一个组件使用)

说明:对datalist和datagrid的我还没研究.此方法是我偶想到的,具体的用法我会进步"开发".暂时首也需要带参数page.

解决ASP.NET上传文件大小限制

解决asp.net上传文件大小限制
对于asp.net,默认只允许上传2m文件,增加如下配置,一般可以自定义最大文件大小.

<httpruntime

executi

maxrequestlength="40960"

usefullyqualifiedredirecturl="false"/>

如果还不行,可以使用思归提供的方案:

我们在上传大文件时都遇到过这样或那样的问题。设置很大的maxrequestlength值并不能完全解决问题,因为asp.net会block直到把整个文件载入内存后,再加以处理。实际上,如果文件很大的话,我们经常会见到internet explorer显示 "the page cannot be displayed – cannot find server or dns error",好像是怎么也catch不了这个错误。为什么?因为这是个client side错误,server side端的applicati

handling server error when upload file too large

解决的方法是利用隐含的httpworkerrequest,用它的getpreloadedentitybody 和 readentitybody方法从iis为asp.net建立的pipe里分块读取数据

iserviceprovider provider = (iserviceprovider) httpcontext.current;
httpworkerrequest wr = (httpworkerrequest) provider.getservice(typeof(httpworkerrequest));
byte[] bs = wr.getpreloadedentitybody();
….
if (!wr.isentireentitybodyispreloaded())
{
int n = 1024;
byte[] bs2 = new byte[n];
while (wr.readentitybody(bs2,n) >0)
{
…..
}
}

ASP.NET程序员应用程序域须知

asp.net程序员应用程序域须知
本文将讨论.net的应用程序域,并且它们是如何对asp.net的执行和调度产生影响的。

  当在windows中启动notepad程序时,众所周知程序会执行一个包含在容器内的进程。可以启动多个notepad的实例,并且每个实例都会在一个专注的进行程运行。使用任务管理器,可以看到在系统中当前运行的所有进程的清单。

  一个进程包括可执行从操作系统中保留的在内存中的代码和程序数据。在进程之内只少有一个包含在进程之内的正在执行指令的线程,并且在多数情况下有多个线程。如果程序打开了任何文件或者资源,这些资源将属于这个进程。

  一个进程也有一个分界线。包含在进程之内的错误代码不能在当前进程之外的地区腐化。在一个进程之内很容易通讯,但是专业技术要求一个进程对另一个进程通讯是必需的。每一个进程也在一个特殊的上下文安全系统中运行,这个安全系统规定在机器和网络中进程做什么。

  一个进程是一个在windows 操作系统中独立运行的最小单位。这会给在一个单一服务器上对一大堆应用程序的isp提出一个问题。isp将会分离每一个在同一个服务器上的与另一个公司的应用程序干扰的asp.net应用程序。但是相关的发射和执行一个对成百上千的应用程序的过程成本禁止的。

  介绍应用程序域

  .net介绍一个应用程序域的概念,或者appdomain像一个过程,appdomain是既是容器又是边界线。.net运行时间使用 appdomain作为代码和数据的容器,就像操作系统一个过程作为代码和数据的容器一样。当操作系统使用一个过程来分离不整齐的代码时,.net运行时间使用一个appdomain来分离在一个安全边线内的代码。

  一个appdomain仅仅属于一个单过程,但是单个过程能够保持多重的appdomain。一个appdomain创建起来相对容易(与一个过程比较起来),并且与一个过程比较起来具有少的维护费用。由于这些原因,一个appdomain是isp(提供成千上万的应用程序)的很好的解决方案。每一个应用程序可以生存在一个独立的appdomain之内,并且许多这样的appdomain可以生存于一个单一的过程(节省费用)之内。

  appdomain

  在同服务器上创建了两个asp.net应用程序,并且没有任何特殊配置。会发生什么事情呢?

  一个单一的asp.net手工进程使asp.net应用程序变成两方面的主要程序。在windows xp和windows 2000中,这一程序被命名为aspnet_wp.exe,并且这一程序运行在本地的aspnet计数器的前后安全关系中。在windows 2003手工程序拥有w3wp.exe并且默认运行在networ service中。

  一个对旬可以进住在一个appdomain中。每一个asp.net应用程序将具有它自己的一套全局变量:cache, application进住进同一进程,.net appdomain是一个独立的单元。如果存有共享的或静态成员的类,并且那些类存在于两种应用程序之内,每一个appdomain拥有它自己的静态字段的备份—数据并不共享。每一个应用程序的数据和代码安全独立存在并且在一边界之内由appdomain提供。

  为了在appdomain之间通讯或者在appdomain之间交换对象,需要查看在.net中穿过边界的通讯技术,例如.net细微的或web 服务。

  对将appdomain作为边界思想的警告之一是asp.net应用程序在默认情况下会带着充分的信任运行。充分信任的代码可以执行本地代码,并且本地代码可以本质地在进程之内的任何内容。需要运行带着部分信任执行应用程序来约束存取不完整的代码并且对安全的appdomain验证所有代码。

  隐藏备份并且重新启动

  一旦一个集合加载到一个appdomain,没有办法从appdomain集合的办法。不过,从一个进程中移除一个appdomain是有可能的。

  如果将一个已更新的dll复制到一个应用程序的子目录中,从asp.net的运行时间知道有新代码要执行。既然asp.net不能将dll复制到已存在的appdomain中,它就会起动一个新appdomain。旧的应用程序域是“排水已停止”,那就是,存在的需要被允许完成执行并且一旦它们执行完成appdomain可以卸载。带有新代码的新的appdomain就会开始并且开始所有的新请求。

  典型地说,当一个dll加载进一个进程时,进程对dll加锁并且不能对磁盘的上的文件进行覆盖。不过,appdomain有一个众所周知的特点:隐藏复制那所有的允许保留在磁盘上的那些未被加锁的可替换的集合。

  运行时间对二进制子目录的带有shadow copy的asp.net进行初始化。appdomain将任何的加锁之前的dll从二进制子目录中拷贝到一个临时位置并且再将这些dll加载到内存。 shadow copy允许没有将网页在线的情况下对所有在二进制子目录中的任何dll进行重写。

  熟练掌握domain

  应用程序域替换os进程将为单独的.net结点单元。一个可理解的应用程序域将会给你一个在asp.net应用程序后的手工发生的概念。使用 appdomain类的currentdomain属性,可以检查关于代码正在运行的appdomain的属性,包括我们在此文章中讨论的shadow copy。

datagridcolumnstyle重写,实现插入不同控件列体验

最近谈论较多的就是datagrid,特别新手最是郁闷为何没有更好的控件,来满足自已的需求。
其实通过重写可以达到很多不同的功能体验,在这里我们仅仅讨论关于datagridcolumnstyle重写的问题
==========================================

1。重写textbox:

public class xp_textbox
inherits system.windows.forms.textbox

#region " windows "

public sub new()
mybase.new()

initializecomp

end sub

protected overloads overrides sub dispose(byval disposing as boolean)
if disposing then
if not (comp is nothing) then
components.dispose()
end if
end if
mybase.dispose(disposing)
end sub

'windows
private comp as system.componentmodel.ic

<system.diagnostics.debuggerstepthrough()> private sub initializecomp
'
'textbox
'
me.enablec = true
me.enablepaste = true
me.name = "textbox"

end sub

#end region

#region " variables "

private m_enpaste as boolean = true
private m_enc as boolean = true

#end region

#region " property "

property enablepaste() as boolean
get
return m_enpaste
end get
set(byval value as boolean)
m_enpaste = value
me.invalidate()
end set
end property

property enablec as boolean
get
return m_enc
end get
set(byval value as boolean)
m_enc = value
me.invalidate()
end set
end property

#end region

protected overrides sub wndproc(byref m as system.windows.forms.message)
select case m.msg
case &h302 'paste
raiseevent pasteevent()
if not m_enpaste then return
case &h7b 'c
if not m_enc then return
end select
mybase.wndproc(m)
end sub

public event pasteevent()

end class

2。重写datagridcolumnstyle(重点介绍内容):

imports system.drawing
imports system.windows.forms

public notinheritable class datagridtextboxcolumnstyle
inherits system.windows.forms.datagridcolumnstyle

#region "declare property"

private withevents m_textbox as new landlord.component.xp_textbox
private isediting as boolean
private editingrow as integer = -1
private m_oldvalue as string

#end region

#region " windows "

sub new()
me.m_textbox.visible = false
end sub

public sub new(byval c as system.componentmodel.ic
myclass.new()

container.add(me)
end sub

protected overloads overrides sub dispose(byval disposing as boolean)
if disposing then
if not (comp is nothing) then
components.dispose()
end if
end if
mybase.dispose(disposing)
end sub

private comp as system.componentmodel.ic

<system.diagnostics.debuggerstepthrough()> private sub initializecomp
comp = new system.componentmodel.c
end sub

#end region

#region "get functi

protected overrides function getminimumheight() as integer
return m_textbox.preferredheight + 2
end function

protected overrides function getpreferredheight(byval g as system.drawing.graphics, byval value as object) as integer
return m_textbox.preferredheight + 2
end function

protected overrides function getpreferredsize(byval g as system.drawing.graphics, byval value as object) as system.drawing.size
return new size(50, m_textbox.preferredheight + 2)
end function

#end region

#region "paint"

protected overloads overrides sub paint(byval g as system.drawing.graphics, byval bounds as system.drawing.rectangle, byval [source] as system.windows.forms.currencymanager, byval rownum as integer)
paint(g, bounds, [source], rownum, false)
end sub

protected overloads overrides sub paint(byval g as system.drawing.graphics, byval bounds as system.drawing.rectangle, byval [source] as system.windows.forms.currencymanager, byval rownum as integer, byval aligntoright as boolean)
dim brush_for as brush = new solidbrush(me.datagridtablestyle.forecolor)
dim brush_bak as brush = new solidbrush(me.datagridtablestyle.backcolor)
paint(g, bounds, [source], rownum, brush_bak, brush_for, aligntoright)
brush_for.dispose()
brush_bak.dispose()
end sub

protected overloads overrides sub paint(byval g as system.drawing.graphics, byval bounds as system.drawing.rectangle, byval [source] as system.windows.forms.currencymanager, byval rownum as integer, byval backbrush as brush, byval forebrush as brush, byval aligntoright as boolean)
dim str as string
if isdbnull(getcolumnvalueatrow([source], rownum)) then
str = me.nulltext
else
str = ctype(getcolumnvalueatrow([source], rownum), string)
end if
dim brush as brush = backbrush
dim rect as system.drawing.rectangle = bounds
g.fillrectangle(brush, rect)
if me.isediting and editingrow = rownum then
brush = new solidbrush(color.white)
g.fillrectangle(brush, bounds)
end if
rect.offset(0, 2)
rect.height -= 2
brush = new solidbrush(me.datagridtablestyle.forecolor)
if me.datagridtablestyle.datagrid.isselected(rownum) then
brush = new solidbrush(me.datagridtablestyle.selecti
dim rectf as rectanglef = new rectanglef(bounds.x, bounds.y, bounds.width, bounds.height)
g.fillrectangle(brush, rectf)
brush = new solidbrush(me.datagridtablestyle.selecti
end if
if me.alignment = horizontalalignment.center then
dim w as integer = g.measurestring(str, me.datagridtablestyle.datagrid.f new sizef(bounds.width, bounds.height)).width
rect.x = rect.x + (bounds.width – w) / 2
elseif me.alignment = horizontalalignment.right then
dim w as integer = g.measurestring(str, me.datagridtablestyle.datagrid.f new sizef(bounds.width, bounds.height)).width
rect.x = bounds.right – w
end if
g.drawstring(str, me.datagridtablestyle.datagrid.f brush, rect.x, rect.y)
brush.dispose()
end sub

#end region

#region "overrides method"

protected overrides function commit(byval datasource as system.windows.forms.currencymanager, byval rownum as integer) as boolean
me.m_textbox.bounds = rectangle.empty
if not me.isediting then
return true
end if
editingrow = -1
isediting = false
try
dim value as object
value = m_textbox.text
if nulltext.equals(value) then
value = system.dbnull.value
end if
setcolumnvalueatrow(datasource, rownum, value)
catch ex as exception
abort(rownum)
return false
end try
invalidate()
return true
end function

protected overrides sub abort(byval rownum as integer)
me.m_textbox.text = m_oldvalue

editingrow = -1

if m_textbox.focused then
me.datagridtablestyle.datagrid.focus()
end if
me.m_textbox.visible = false

me.isediting = false
me.invalidate()
end sub

protected overloads overrides sub edit(byval source as system.windows.forms.currencymanager, byval rownum as integer, byval bounds as system.drawing.rectangle, byval [readonly] as boolean, byval instanttext as string, byval cellisvisible as boolean)
editingrow = rownum
isediting = true
dim value as string
if isdbnull(getcolumnvalueatrow(source, rownum)) then
value = me.nulltext
else
value = ctype(getcolumnvalueatrow(source, rownum), string)
end if
m_oldvalue = value
if cellisvisible then
if not me.readonly then
me.m_textbox.bounds = new rectangle(bounds.x + 1, bounds.y + 1, bounds.width – 2, bounds.height – 2)
me.m_textbox.text = value
me.m_textbox.select()
me.m_textbox.focus()
me.m_textbox.selectall()
me.m_textbox.visible = true
me.m_textbox.flat = true
end if
else
me.m_textbox.text = value
'滚动时会丢失焦点
'me.m_textbox.visible = false
end if
if me.m_textbox.visible then
datagridtablestyle.datagrid.invalidate(bounds)
end if
end sub

protected overloads overrides sub setdatagridincolumn(byval value as system.windows.forms.datagrid)
mybase.setdatagridincolumn(value)
if not m_textbox.parent is nothing then
m_textbox.parent.controls.remove(me.m_textbox)
end if
if not value is nothing then
value.controls.add(me.m_textbox)
end if
end sub

protected overrides sub c
editingrow = -1
'否则先点到新增行,再回选非新行格时该列最后一行的值变为(null)
isediting = false
me.m_textbox.visible = false
invalidate()
end sub

protected overrides sub enternullvalue()
me.m_textbox.text = me.nulltext
end sub

private sub m_textbox_keypress(byval sender as object, byval e as system.windows.forms.keypresseventargs) handles m_textbox.keypress
if not char.iscontrol(e.keychar) then
me.isediting = true
mybase.columnstartedediting(m_textbox)
end if
end sub

private sub m_textbox_pasteevent() handles m_textbox.pasteevent
me.isediting = true
me.columnstartedediting(m_textbox)
end sub

#end region

end class

3。使用:
新建一个空窗体,拖入datagrid,窗体load事件中代码如下
private idtb_temp as new datatable

private sub form1_load(byval sender as system.object, byval e as system.eventargs) handles mybase.load

idtb_temp = new datatable("nametable")
idtb_temp.columns.add(new datacolumn("normal"))
idtb_temp.columns.add(new datacolumn("textbox1"))
idtb_temp.columns.add(new datacolumn("combobox1"))
dim datecolumns as datacolumn
datecolumns = new datacolumn("datetime1", type.gettype("system.datetime"))
idtb_temp.columns.add(datecolumns)
idtb_temp.columns.add(new datacolumn("checkbox1", type.gettype("system.boolean")))

dim idrw_row as datarow
dim i as integer
for i = 0 to 20
idrw_row = idtb_temp.newrow
idrw_row.item("normal") = "names"
idrw_row.item("textbox1") = "nick"
idrw_row.item("combobox1") = i.tostring
idrw_row.item("datetime1") = "2004-06-04"
idrw_row.item("checkbox1") = true
idtb_temp.rows.add(idrw_row)
next

me.datagrid1.datasource = idtb_temp

dim mygridstyle as windows.forms.datagridtablestyle = new windows.forms.datagridtablestyle
mygridstyle.mappingname = "nametable"
mygridstyle.preferredrowheight = 30
mygridstyle.selecti = color.blue
mygridstyle.backcolor = color.yellow

dim c1 as windows.forms.datagridtextboxcolumn = new windows.forms.datagridtextboxcolumn
with c1
.mappingname = "normal"
.width = 100
.headertext = "normal"
.alignment = horizontalalignment.center
end with
mygridstyle.gridcolumnstyles.add(c1)

dim c2 as landlord.component.draggrid.datagridtextboxcolumnstyle = new landlord.component.draggrid.datagridtextboxcolumnstyle
with c2
.mappingname = "textbox1"
.width = 100
.headertext = "textbox1"
end with
mygridstyle.gridcolumnstyles.add(c2)

dim c3 as landlord.component.draggrid.datagridcomboboxcolumnstyle = new landlord.component.draggrid.datagridcomboboxcolumnstyle
with c3
.mappingname = "combobox1"
.headertext = "combobox1"
.width = 100
.additem("111")
.additem("222")
.alignment = horizontalalignment.center
end with
mygridstyle.gridcolumnstyles.add(c3)

dim c4 as landlord.component.draggrid.datagriddatetimepickercolumnstyle = new landlord.component.draggrid.datagriddatetimepickercolumnstyle
with c4
.mappingname = "datetime1"
.headertext = "datetime1"
.width = "100"
.alignment = horizontalalignment.center
end with
mygridstyle.gridcolumnstyles.add(c4)

dim c5 as landlord.component.draggrid.datagridcheckboxcolumnstyle = new landlord.component.draggrid.datagridcheckboxcolumnstyle
with c5
.mappingname = "checkbox1"
.headertext = "checkbox1"
.width = 100
.truecolor = color.red
end with
mygridstyle.gridcolumnstyles.add(c5)

me.datagrid1.tablestyles.clear()
me.datagrid1.tablestyles.add(mygridstyle)

end sub

4。说明:
其他控件的加入原理基本一样,这里就不重复了

switch语句的“不准遍历”

switch语句的“不准遍历”
switch语句的“不准遍历”
来源:网络收集 加入时间:10月28日 09:22
c和c++语言允许switch语句中case标签后不出现break语句从而可以继续执行下面case后的语句。但是这往往由于程序员忘记写break语句而成为一个难以发现的漏洞,所以c#不允许这样,它要求每个标签项后使用break语句或跳转语句goto,即不允许从一个case自动遍历到其他case,否则编译时报错,即使是default语句后也不例外。

这个特定决定了三个特点:

a)我们可以任意排列switch语句中的case项而不会影响switch语句的功能。

b)如果要像c/c++那样在执行完后继续遍历其他的语句,只要在case语句后加上以下两条跳转语句中的一条即可:goto case label;goto default。

3)除了goto外,throw和return也可以。

c#switch语句与c/c++另一个不同点是c#同vb一样把字符串常量当作常量表达式来使用,但c/c++却不支持。所以在c#中,case “ok”:这样的形式也是支持的。

C#中的代理(delegate)

c#中的代理(delegate)
有些书上也称“代表”或“委托”。 c#中取消了指针的概念。对指针恋恋不舍的程序员可以有两种解决方法:声明“非安全”(unsafe)代码段然后在其中使用指针或者使用c#的一个引用类型——“代理”(delegate)。“代理”相当与c中的函数指针原型,区别是c#是类型安全的。
“代理”(delegate)(代表、委托):“代理”是类型安全的并且完全面向对象的。
(1)在c#中,所有的代理都是从system.delegate类派生的(delegate是system.delegate的别名)。
(2)代理隐含具有sealed属性,即不能用来派生新的类型。
(3)代理最大的作用就是为类的事件绑定事件处理程序。
(4)在通过代理调用函数前,必须先检查代理是否为空(null),若非空,才能调用函数。(5)在代理实例中可以封装静态的方法也可以封装实例方法。
(6)在创建代理实例时,需要传递将要映射的方法或其他代理实例以指明代理将要封装的函数原型(.net中称为方法签名:signature)。注意,如果映射的是静态方法,传递的参数应该是类名.方法名,如果映射的是实例方法,传递的参数应该是实例名.方法名。
(7)只有当两个代理实例所映射的方法以及该方法所属的对象都相同时,才认为它们是想等的(从函数地址考虑)。
(8)多个代理实例可以形成一个代理链,system.delegate中定义了用来维护代理链的静态方法combi
(9)代理的定义必须放在任何类的外面,如delegate int mydelegate();而在类的方法中调用mydelegate d = new mydelegate(myclass.mymethod);来实例化自定义代理的实例。
(10)代理三步曲:
a.生成自定义代理类:delegate int mydelegate();
b.然后实例化代理类:mydelegate d = new mydelegate(myclass.mymethod);
c.最后通过实例对象调用方法:int ret = d();

C#中的“装箱”(boxing)与“拆箱”(unboxing)

c#中的“装箱”(boxing)与“拆箱”(unboxing)
装箱和拆箱:任何值类型、引用类型可以和object(对象)类型之间进行转换。装箱转换是指将一个值类型隐式或显式地转换成一个object类型,或者把这个值类型转换成一个被该值类型应用的接口类型(interface-type)。把一个值类型的值装箱,就是创建一个object实例并将这个值复制给这个object,装箱后的object对象中的数据位于堆中,堆中的地址在栈中。被装箱的类型的值是作为一个拷贝赋给对象的。如:

int i = 10;

object obj = i; //隐式装箱

object obj = object(i); //显式装箱

if(obj is int) //int

console.writeline(“ok”);

console.writeline(obj.gettype()); //system.int32

有两种方式来查看包装以后的引用对象中包装的原始数据的类型。要判断原始类型是否是某个给定的原子类型,用is;如果要返回一个字符串,可以用object类的gettype方法。

拆箱转换是指将一个对象类型显式地转换成一个值类型,或是将一个接口类型显式地转换成一个执行该接口地值类型。注意装箱操作可以隐式进行但拆箱操作必须是显式的。拆箱过程分成两步:首先,检查这个对象实例,看它是否为给定的值类型的装箱值。然后,把这个实例的值拷贝给值类型的变量。比如:

int i = 10;

object obj = i;

int j = (int)obj;

有两种方式来查看包装以后的引用对象中包装的原始数据的类型。要判断原始类型是否是某个给定的原子类型,用is;如果要返回一个字符串,可以用object类的gettype方法。

拆箱转换是指将一个对象类型显式地转换成一个值类型,或是将一个接口类型显式地转换成一个执行该接口地值类型。注意装箱操作可以隐式进行但拆箱操作必须是显式的。拆箱过程分成两步:首先,检查这个对象实例,看它是否为给定的值类型的装箱值。然后,把这个实例的值拷贝给值类型的变量。比如:

int i = 10;

object obj = i;

int j = (int)obj;

在C#中利用DirectX实现声音播放

在c#中利用directx实现声音播放
我感觉声音的播放比较简单。我们从播放声音开始。为什么我这么觉得?我也不知道。 这里是展示最简单的directx播放声音的例子,我尽量省略了无关的代码。最后的代码只有19行,够简单了吧?

  准备工作:

  1.安装了directx sdk(有9个dll文件)。这里我们只用到microsoft.directx.dll 和 microsoft.directx.directsound.dll

  2.一个wav文件。(这样的文件比较好找,在qq的目录里就不少啊。这里就不多说了。)名字叫snd.wav,放在最后目标程序的同个目录下面

  开始写程序啦。随便用个ultraedit就好了。

  1.引入directx 的dll文件的名字空间:

using microsoft.directx;
using microsoft.directx.directsound;

  2.建立设备。在我们导入的microsoft.directx.directsound空间中,有个device的类。这个是表示系统中的声音设备。

device dv=new device();

  3.设置cooperativelevel。因为windows是多任务的系统,设备不是独占的,所以在使用设备前要为这个设备设置cooperativelevel。调用device的setcooperativelevel方法:其中,第一个参数是一个c

  在这个程序中,c

dv.setcooperativelevel((new uf()),cooperativelevel.priority);

  4.开辟缓冲区。对于上面的声音设备,他有个自己的缓冲区,叫主缓冲区。系统中,一个设备有唯一的主缓冲区。由于windows是多任务(又是这个!),所以可以有几个程序同时利用一个设备播放声音,所以每个程序都自己开辟一个二级缓冲区,放自己的声音。

  系统根据各个程序的优先级别,按照相应的顺序分别去各个二级缓冲区中读取内容到主缓冲区中播放。这里,我们为snd.wav开辟一个缓冲区。

  其中,第一个参数表示文件名(傻瓜都看出来了!),第二个就是需要使用的设备。

sec buf=new secondarybuffer(@"snd.wav",dv);

  5.接下来就可以播放啦。第一个参数表示优先级别,0是最低的。第2个参数是播放方式,这里是循环播放。

buf.play(0,bufferplayflags.looping);

  6.由于命令行程序没有消息循环,执行完代码就退出了,所以,我们需要暂停程序。

console.read();

  7.关键的部分已经完了,这里只是交代一下刚才的那个倒霉的new uf() 是什么东西。这个完全是为了应付setcooperativelevel的参数要求。我不知道这样做有什么附作用(各位如果因此把声卡烧了…………)

class uf:form{}

  8.代码写完啦~~~。下面可以编译了,这里编译比较复杂点。

csc /r:directxmicrosoft.directx.dll;directxmicrosoft.directx.directsound.dll dxsnd.cs

  这里,我把2个dll文件放在当前目录的directx目录下(这个是我自己建的,你只需要指出这2个文件的位置就可以了。)

  顺便把我的目录结构说明一下:

–dxsnd.cs
–snd.wav
–<directx>

–microsoft.directx.dll
–microsoft.directx.dll

  下面是完整代码:

//dxsnd.cs
using system;
using microsoft.directx;
using microsoft.directx.directsound;
using system.windows.forms;
namespace test1
{
 class test
 {
  public static void main(string [] args)
  {
   device dv=new device();
   dv.setcooperativelevel((new uf()),cooperativelevel.priority);
   sec buf=new secondarybuffer(@"snd.wav",dv);
   buf.play(0,bufferplayflags.looping);
   console.readline();
  }
  class uf:form{}
 }
}