2011年7月24日
这里的"动态内存"包含以下两个方面的内容:
1.内存。这里的"内存"指的是进程的虚拟内存空间。在Win32环境下,每一个进程拥有独立的,大小为4G(0x0000 0000 ~ 0xFFFF FFFF)的虚拟内存空间。
2.动态。这里的"动态"指的是进程虚拟内存空间中的动态内存区域。在一个进程的虚拟内存空间中,只有动态内存可以在运行是被应用程序自由的分配/使用/释放。
在Win32环境下,我们可以使用多种方式来分配/使用/释放动态内存,这些方式包括:
1.Win32 API. 这些API包括VirtualXXX(),HeapXXX(),LocalAlloc(),GlobalAlloc()。
2.C Run-Time Library.这些函数包括malloc(),free()。
3.C++提供的关键词new和关键词delete。
有这么多的内存分配方式,我们在学习和实际项目中编码过程中常常会为使用那种方式而感到迷惑。他们的内部实现是否相同?他们之间有什么本质的区别?他们各自的使用场合又是怎样的? 本文试图通过深入探究他们的本质,为正确理解和使用他们提供一些依据。
首先,我们最好从全局的高度把握他们之间的关系。这里有一张图很好的描述了他们之间的层次关系:
这张图给了我们一个全景,仅从这张图我们就可以清楚地看到他们之间的层次关系:
第一层:Win32 API作为系统的接口,提供了一组操作虚拟内存的接口;
第二层:Heap作为虚拟内存的一部分,Win32 API又提供了一组操作Heap内存的接口,但是这些接口是建立在操作虚拟内存的接口的基础上。
第三层:Windows平台下的C Run-Time Library 又利用Heap API来实现malloc和free。
由此我们可以看出,这些动态内存操作方式之间存有单一的层次关系,位于这个层次的最低层的是Virtual Memory API,可以说这些方式都是建立在Virtual Memory API的基础上。下面就从Virtual Memory API开始,逐层分析他们之间的区别:
一.Virtual Memory API
作为Windows系统提供的最"核心"的对虚拟内存操作的接口,也作为其他几种方式的基础,Virtual Memory API应该在几种方式中是最通用,也是功能最强大的一种方式。如果想对Virtual Memory API的使用深入的了解,可以参阅《Programming Application for Windows》(By Jeffrey Richter)
二.Heap Memory API
我们在学习进程内存空间"映象"的时候,也提到了"Heap"这个概念,那个时候"Heap"指的是一段由应用程序在运行时动态分配的内存段(Segment),和其他的内存段(代码段,数据段,栈段等)构成了进程的内存空间。而这里的"Heap"指的是进程拥有的一种对象(Windows中有很多对象,例如WINDOW,ICON,BRUSH),当我们创建一个Heap对象的时候,我们就可以获得这个对象的Handle,然后我们就可以使用这个handle来使用动态内存,最后销毁这个对象。
三.LocalAlloc/GlobalAlloc
这两个函数是Win16 API中遗留下来的两个函数,Win32 API为了保持兼容性才包含了这两个函数。这两个函数内部是通过Heap Memory API来操作一个"特殊"的Heap对象:进程的默认堆对象。每一个进程在初始化的时候,都会创建一个默认的Heap对象,在进程结束的时候销毁这个默认的Heap对象。LocalAlloc和GblobalAlloc的区别仅表现在Win16环境下,在Win16环境下,内存的地址是通过段:段内偏移量来获取的,LocalAlloc()只能在同一段内分配内存,而GlobalAlloc可以跨越段边界访问内存。 在Win32环境下内存访问不存在这样的限制,所以他们表现出相同的功能。由于Heap Memory API完全可以实现他们两个的功能,所以在Win32下不推荐使用这两个函数。
四.malloc/free
这两个函数是使用频率最高的两个函数,由于他们是标准C库中的一部分,所以具有极高的移植性。这里的"移植性"指的是使用他们的代码可以在不同的平台下编译通过,而不同的平台下的C Run-Time Library的具体实现是平台相关的,在Windows平台的C Run-Time Library中的malloc()和free()是通过调用Heap Memory API来实现的。值得注意的是C Run-Time Library拥有独立的Heap对象,我们知道,当一个应用程序初始化的时候,首先被初始化的是C Run-Time Library,然后才是应用程序的入口函数,而Heap对象就是在C Run-Time Library被初始化的时候被创建的。对于动态链接的C Run-Time Library,运行库只被初始化一次,而对于静态连接的运行库,每链接一次就初始化一次,所以对于每个静态链接的运行库都拥有彼此不同的Heap 对象。这样在某种情况下就会出问题,导致程序崩溃,例如一个应用程序调用了多个DLL,除了一个DLL外,其他的DLL,包括应用程序本身动态连接运行库,这样他们就使用同一个Heap对象。而有一个DLL使用静态连接的运行库,它就拥有一个和其他DLL不同的Heap 对象,当在其他DLL中分配的内存在这个DLL中释放时,问题就出现了。
五.关键词new/关键词delete
这两个词是C++内置的关键词(keyword)。当C++编译器看到关键词new的时候,例如:
CMyObject* pObj = new CMyObject;
编译器会执行以下两个任务:
1。在堆上动态分配必要的内存。这个任务是由编译器提供的一个全局函数void* ::operator new(size_t)来完成的。值得注意的是任何一个类都可以重载这个全局函数。如果类重载了这个函数的化,被类重载的那个会被调用。
2。调用CMyClass的构造函数来初始化刚刚生成的对象。当然如果分配的对象是C++中的基本数据类型则不会有构造函数调用。
如果要深入全局函数void* ::operator new(size_t)的话,我们会发现,它的具体实现是通过调用malloc来分配内存的。
有了这样的分析,我们对这些动态内存分配方式有了一个更高一级的认识,在我们的代码中就可以正确使用他们。
2009年7月21日
摘要: 一、只复制一个表结构,不复制数据selecttop0*into[t1]from[t2]二、获取数据库中某个对象的创建脚本1、先用下面的脚本创建一个函数ifexists(select1fromsysobjectswhereid=object_id('fgetscript')andobjectproperty(id,'IsInlineFunction')=0)dropfunctionfgetscrip...
阅读全文
2009年7月2日
1 通过隐藏的button或者textbox来传值,js激发这些控件的事件来执行后台代码
document。getelementbyid()。click();
2 通过AJAX 原始的回传与回调机制 或者包装的AJAX方法
3 js调用webservice来执行后台方法,不刷新页面
private static string CDecode(string data)
{
if (data.IndexOf("=?") > -1)
{
int index = data.IndexOf("=?");
string[] parts = data.Substring(index + 2).Split(new char[] { '?' });
string encoding = parts[0];
string type = parts[1];
string datax = parts[2];
if (encoding.ToLower().StartsWith("gb2312") && encoding.Length > 5)
encoding = "gb2312";
if (encoding.ToLower().StartsWith("iso-2022-jp") && encoding.Length > 6)
encoding = "iso-2022-jp";
if (encoding.ToLower().StartsWith("utf8") && encoding.Length >3)
encoding = "utf-8";
System.Text.Encoding enc = null;
enc = System.Text.Encoding.GetEncoding(encoding);
if (type.ToUpper() == "Q")
{
return QPUnEncryCode(datax);
}
if (type.ToUpper() == "B")
{
return enc.GetString(Convert.FromBase64String(datax));
}
}
return data;
}
private static string QPUnEncryCode(string source)
{
source=source.Replace ("=\r\n","");
int len=source.Length ;
string dest=string.Empty ;
int i=0;
while(i<len)
{
string temp=source.Substring (i,1);
if(temp=="=")
{
int code=Convert.ToInt32 (source.Substring (i+1,2),16);
if(Convert.ToInt32 (code.ToString (),10)<127)
{
dest+=((char)code).ToString ();
i=i+3;
}
else
{
dest+=System.Text.Encoding.Default.GetString(new byte []{Convert.ToByte (source.Substring (i+1,2),16),Convert.ToByte (source.Substring (i+4,2),16)}) ;
i=i+6;
}
}
else
{
dest+=temp;
i++;
}
}
return dest;
}
<form id="form1" runat="server">
<div>
<asp:Timer ID="Timer1" runat="server" Interval="600" ontick="Timer1_Tick">
</asp:Timer>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="MeetingRoomCode"
CellPadding="4" ForeColor="#333333" GridLines="None">
<Columns>
<asp:BoundField DataField="MeetingRoomCode" HeaderText="MeetingRoomCode" InsertVisible="False" ReadOnly="True"
SortExpression="MeetingRoomCode" />
<asp:BoundField DataField="MeetingTel" HeaderText="MeetingTel" SortExpression="MeetingTel" />
</Columns>
<FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
<RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
<EditRowStyle BackColor="#999999" />
<SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
<PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />
<HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
<AlternatingRowStyle BackColor="White" ForeColor="#284775" />
</asp:GridView>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />
</Triggers>
</asp:UpdatePanel>
</div>
</form>
SqlConnection con = new SqlConnection( ConfigurationManager.AppSettings["connection"]);
protected void Page_Load(object sender, EventArgs e)
{
con.Open();
string s = "select * from test";
SqlCommand sqlcom = new SqlCommand(s, con);
SqlDataAdapter sqlda = new SqlDataAdapter(sqlcom);
DataTable dt = new DataTable();
sqlda.Fill(dt);
GridView1.DataSource = dt;
GridView1.DataBind();
con.Close();
}
protected void Timer1_Tick(object sender, EventArgs e)
{
GridView1.DataBind();
}
2009年6月10日
最近做OCS与短信平台整合过程中,要对收到的邮件进行解析,发现现在的解析器都有这几个问题:
1 当邮件是传真邮件,语音邮件时 解析器不能识别Header部分的某一些字段(除了FROM.TO.SUBJECT.DATE...等),所以header部分应该是Hashtable
2 在对头部或者邮件正文进行解析是编码不能正确转换。比如邮件头中的subject有简体或者繁体就乱码
发现最好的解析器应该属于LumiSoft.Net.Mime了,但是这个解析器存在的问题是不能识别Header部分的某一些字段(除了FROM.TO.SUBJECT.DATE...等),
因此本人将它改进,对原文件进行分析后发现:如果将该源文件的 ParseHeaderField(headerfileds,headers)方法公开后还是能得到一些其他字段的Mime 。ParseHeaderField,这样柑橘不好的事你必须首先传输你要得到的字段,
string st = mimeparser.ParseHeaderField("Content-Class:", mimeparser.Headers).ToString().Trim();
为添加正则匹配header字段的到header的hashtable
internal static Hashtable parseHeaders(string partData)
{
Hashtable headerFields = new Hashtable();
string[] parts = Regex.Split(partData, DOUBLE_NEWLINE);
string headerString = parts[0] + DOUBLE_NEWLINE;
MatchCollection headerKeyCollectionMatch = Regex.Matches(headerString, @"^(?<key>\S*):", RegexOptions.Multiline);
string headerKey = null;
foreach (Match headerKeyMatch in headerKeyCollectionMatch)
{
headerKey = headerKeyMatch.Result("${key}");
Match valueMatch = Regex.Match(headerString, headerKey + @":(?<value>.*?)\r\n[\S\r]", RegexOptions.Singleline);
if (valueMatch.Success)
{
string headerValue = valueMatch.Result("${value}").Trim();
headerValue = Regex.Replace(headerValue, "\r\n", "");
headerValue = Regex.Replace(headerValue, @"\s+", " ");
// TODO: Duplicate headers (like Received) will be overwritten by the 'last' value.
headerFields[headerKey] =CDecode(headerValue);
}
}
return headerFields;
}
这样真个DLL编译为:
常用的一些字段可以使用 MimeParser mimeparser = new MimeParser(Encoding.Default.GetBytes(message.Data));
mimeparser.From/To/Subject/Date/MessageID/BCC/Cc...等得到 mimeparser.Headers 是头文件的string.
还可以使用Hashtable ht = mimeparser.headers; ht["Subject"] ht["Content-Class"]等来得到某一些字段
注意ht["To"]字段格式为 :XXX 《XXX@XXXX.COM》等,所以取地址的话进行字符串处理
int length1 = message.Headers["To"].ToString().IndexOf("<");
int length2 = message.Headers["To"].ToString().IndexOf(">");
string Toaddress = message.Headers["To"].ToString().Substring(length1 + 1, length2 - length1 - 1);
字段乱码的问题已解决了。
附上DLL和源文件:
大家有什么需要修改的欢迎给我反馈 。QQ476074295
2009年5月27日
Transfer--->拨打XXXXX号码--->触发EndPointLister线程的EnterRequest事件处理,该处理把拨入的号码转入一个特定会议。
对于会议操作者Agent1 ,此时想要实现的是该事件把当前拨入的号码转入一个比如说conference.name="A"的conference会议。
Agent1与conference.name="A"的conference会议没有任何关联,我的意思是指此时conference只是一个标识了conferenceName的某一个conference实例。
对于会议操作者Agent2 ,此时想要实现的该事件把当前拨入的号码转入一个conference.name="B"的conference会议。
Agent2与conference.name="B"的conference会议也没有任何关联。即Agent与会议对象没有任何关联。
现在的问题是当Agent1和Agent2同时登录,如果Transfer--->拨打XXXXX号码--->触发了EnterRequest事件处理,此时可能
1 把当前拨入的号码转入一个conference.name="A"的conference会议
2 把当前拨入的号码转入一个conference.name="B"的conference会议。
两个结果都可能被执行。也就是
两个实例不传EventArgs参数,同时触发Event,希望得到不同的处理的方法(实例不与处理方法中的某个参数或者对象关联)
请问此时我该怎么去控制呢?各位有什么比较好的方法没有。
2009年5月25日
摘要: 本片文章的议题是有关于传递参数到线程的几种方法。首先我们要知道什么是线程,什么时候要用到线程,如何去使用线程,如何更好的利用线程来完成工作。线程是程序可执行片段的最小单元,是组成运行时程序的基本单元,一个进程有至少一个线程组成。一般在并行处理等待事件的时候要用到线程,如等待网络响应,等待I/O通讯,后台事务处理等情况。使用线程其实很简单,在.net框架下面你首先要定义一个函数来完成一些工作,然后实...
阅读全文
摘要: 看到这里很多问如何实现单点登录,本人根据自己的经验,提供一个实现单点登录的类。支持web和winfrom,测试成功。利用哈希表,作为保存登录用户的队列 private static Hashtable m_userList;设置用户在线超时的时限(我设置的是30分钟,可以根据自己的需求自行修改) private static TimeSpan m_tsSub = new TimeSpan(0, 3...
阅读全文
2009年5月21日
摘要: #region ReceiveData //从网络读取指定长度的数据 //从网络读取指定长度的数据 ,存放在buff中 public static byte[] ReceiveData(NetworkStream stream, int BufferSize) { List<byte> list = new List<byte>(); byte[] buffer = ...
阅读全文