2010年2月24日星期三

文章列表仅仅显示描述


private static Regex _Regex = new Regex(“<[^>]*>”, RegexOptions.Compiled);
  private static string StripHtml(string html)
  {
   if (string.IsNullOrEmpty(html))
    return string.Empty;
   return _Regex.Replace(html, string.Empty);
  }
 public string Body
  {
   get
   {
    string body = Post.Content;
    if (ShowExcerpt)
    {
     string link = ” <a href=\”" + Post.RelativeLink.ToString() + “\”>[" + (Page as BlogBasePage).Translate("more") + "]</a>”;
     if (!string.IsNullOrEmpty(Post.Description))
     {
      body = Post.Description + “.” + link;
     }
     else
     {
      body = StripHtml(Post.Content);
      if (body.Length > 300)
       body = body.Substring(0, 300) + “…” + link;
     }
    }
    ServingEventArgs arg = new ServingEventArgs(body, this.Location);
    Post.OnServing(Post, arg);
    if (arg.Cancel)
    {
     if (arg.Location == ServingLocation.SinglePost)
     {
      Response.Redirect(“~/error404.aspx”, true);
     }
     else
     {
      this.Visible = false;
     }
    }
    return arg.Body;
   }
  }
Share:

Visual C++数据之Win32中的数据类型

标准C或C++没有的数据类型
  
  数据类型  说明
  
  BOOL 布尔变量,实际上是UINT
  
  COLORREF 用作颜色索引的32位整数
  
  DWORD 32位的无符号整数
  
  HANDLE 32位的无符号整数,用作句柄
  
  HINSTANCE 32位的无符号整数,用作实例句柄
  
  HWND 32位的无符号整数,用作窗口句柄
  
  HDC 一个设备描述背景的句柄
  
  LONG 32位带符号整数
  
  LPARAM 32位整数,用作窗口函数或者其他回调函数的参数之一
  
  LPCSTR 指向一个字符串常量的32位指针
  
  LPSTR 定义一个线性的32位字符串指针
  
  LRESULT 32位整数,是窗口函数或者其他回调函数的返回值类型
  
  UINT 定义一个新的Win32数据类型,它会把一个参数强制转换成Windows3.x应用中的16位值 或Win32应用中的32位值
  
  WCHAR 声明一个16位的UNICODE字符,用来表示世界上所有已知的书写语言的符号
  
  WORD 16位的无符号整数
  
  WPARAM 32位整数,用作窗口函数或者其他回调函数的参数之一(在Windows3.x中为16位值)
  
  同时,为了统一变量的命名,在Windows中,不同类型的变量拥有各自的标准前缀,一般情况如表所示。
  不同数据类型的前缀
  前缀 数据类型
  
  c 字符(char)
  
  s 短整数(short)
  
  cb 用于定义对象(一般为一个结构)尺寸的整数
  
  n 整数(integer)
  
  sz 以''结尾的字符串
  
  b 字节
  
  f BOOL
  
  w 字(WORD,无符号短整数)
  
  l 长整数(LONG)
  
  h HANDLE(无符号整数)
  
  m_ 类成员变量
  
  fn 函数(function)
  
  dw 双字(DWORD,无符号长整数) 
 
Share:

C++标准库简介(转)

 C++标准库的所有头文件都没有扩展名。C++标准库的内容总共在50个标准头文件中定义,其中18个提供了C库的功能。 <cname>形式的标准头文件【 <complex>例外】其内容与ISO标准C包含的name.h头文件相同,但容纳了C++扩展的功能。在 <cname>形式标准的头文件中,与宏相关的名称在全局作用域中定义,其他名称在std命名空间中声明。在C++中还可以使用name.h形式的标准C库头文件名。

C++标准库的内容分为10类:

C1.语言支持 C2.输入/输出 C3.诊断 C4.一般工具 C5.字符串

C6.容器 C7.迭代器支持 C8.算法 C9.数值操作 C10.本地化


C1 标准库中与语言支持功能相关的头文件 头文件  描述 
<cstddef> 定义宏NULL和offsetof,以及其他标准类型size_t和ptrdiff_t。与对应的标准C头文件的区别是,NULL是C++空指针常量的补充定义,宏offsetof接受结构或者联合类型参数,只要他们没有成员指针类型的非静态成员即可。
<limits> 提供与基本数据类型相关的定义。例如,对于每个数值数据类型,它定义了可以表示出来的最大值和最小值以及二进制数字的位数。
<climits> 提供与基本整数数据类型相关的C样式定义。这些信息的C++样式定义在 <limits>中
<cfloat> 提供与基本浮点型数据类型相关的C样式定义。这些信息的C++样式定义在 <limits>中
<cstdlib> 提供支持程序启动和终止的宏和函数。这个头文件还声明了许多其他杂项函数,例如搜索和排序函数,从字符串转换为数值等函数。它与对应的标准C头文件stdlib.h不同,定义了abort(void)。abort()函数还有额外的功能,它不为静态或自动对象调用析构函数,也不调用传给atexit()函数的函数。它还定义了exit()函数的额外功能,可以释放静态对象,以注册的逆序调用用atexit()注册的函数。清除并关闭所有打开的C流,把控制权返回给主机环境。
<new> 支持动态内存分配
<typeinfo> 支持变量在运行期间的类型标识
<exception> 支持异常处理,这是处理程序中可能发生的错误的一种方式
<cstdarg> 支持接受数量可变的参数的函数。即在调用函数时,可以给函数传送数量不等的数据项。它定义了宏va_arg、va_end、va_start以及va_list类型
<csetjmp> 为C样式的非本地跳跃提供函数。这些函数在C++中不常用
<csignal> 为中断处理提供C样式支持

C2 支持流输入/输出的头文件 头文件  描述 
< iostream> 支持标准流cin、cout、cerr和clog的输入和输出,它还支持多字节字符标准流wcin、wcout、wcerr和wclog。
<iomanip> 提供操纵程序,允许改变流的状态,从而改变输出的格式。
<ios> 定义iostream的基类
<istream> 为管理输出流缓存区的输入定义模板类
<ostream> 为管理输出流缓存区的输出定义模板类
<sstream> 支持字符串的流输入输出
<fstream> 支持文件的流输入输出
<iosfwd> 为输入输出对象提供向前的声明
<streambuf> 支持流输入和输出的缓存
<cstdio> 为标准流提供C样式的输入和输出
<cwchar> 支持多字节字符的C样式输入输出

C3 与诊断功能相关的头文件 头文件 描述
<stdexcept> 定义标准异常。异常是处理错误的方式
<cassert> 定义断言宏,用于检查运行期间的情形
<cerrno> 支持C样式的错误信息

C4 定义工具函数的头文件 头文件 描述
<utility> 定义重载的关系运算符,简化关系运算符的写入,它还定义了pair类型,该类型是一种模板类型,可以存储一对值。这些功能在库的其他地方使用
<functional> 定义了许多函数对象类型和支持函数对象的功能,函数对象是支持operator()()函数调用运算符的任意对象
<memory> 给容器、管理内存的函数和auto_ptr模板类定义标准内存分配器
<ctime> 支持系统时钟函数

C5 支持字符串处理的头文件 头文件 描述
<string> 为字符串类型提供支持和定义,包括单字节字符串(由char组成)的string和多字节字符串(由wchar_t组成)
<cctype> 单字节字符类别
<cwctype> 多字节字符类别
<cstring> 为处理非空字节序列和内存块提供函数。这不同于对应的标准C库头文件,几个C样式字符串的一般C库函数被返回值为const和非const的函数对替代了
<cwchar> 为处理、执行I/O和转换多字节字符序列提供函数,这不同于对应的标准C库头文件,几个多字节C样式字符串操作的一般C库函数被返回值为const和非const的函数对替代了。
<cstdlib> 为把单字节字符串转换为数值、在多字节字符和多字节字符串之间转换提供函数

C6 定义容器类的模板的头文件 头文件 描述
<vector> 定义vector序列模板,这是一个大小可以重新设置的数组类型,比普通数组更安全、更灵活
<list> 定义list序列模板,这是一个序列的链表,常常在任意位置插入和删除元素
<deque> 定义deque序列模板,支持在开始和结尾的高效插入和删除操作
<queue> 为队列(先进先出)数据结构定义序列适配器queue和priority_queue
<stack> 为堆栈(后进先出)数据结构定义序列适配器stack
<map> map是一个关联容器类型,允许根据键值是唯一的,且按照升序存储。multimap类似于map,但键不是唯一的。
<set> set是一个关联容器类型,用于以升序方式存储唯一值。multiset类似于set,但是值不必是唯一的。
<bitset> 为固定长度的位序列定义bitset模板,它可以看作固定长度的紧凑型bool数组

C7 支持迭代器的头文件 头文件 描述
<iterator> 给迭代器提供定义和支持

C8 有关算法的头文件 头文件 描述
<algorithm> 提供一组基于算法的函数,包括置换、排序、合并和搜索
<cstdlib> 声明C标准库函数bsearch()和qsort(),进行搜索和排序
<ciso646> 允许在代码中使用and代替&&

C9 有关数值操作的头文件 头文件 描述
<complex> 支持复杂数值的定义和操作
<valarray> 支持数值矢量的操作
<numeric> 在数值序列上定义一组一般数学操作,例如accumulate和inner_product
<cmath> 这是C数学库,其中还附加了重载函数,以支持C++约定
<cstdlib> 提供的函数可以提取整数的绝对值,对整数进行取余数操作

C10 有关本地化的头文件 头文件 描述
<locale> 提供的本地化包括字符类别、排序序列以及货币和日期表示。
<clocale> 对本地化提供C样式支持
Share:

在 ASP.NET 网页中不经过回发而以编程方式实现客户端回调

客户端回调组件

创建以编程方式实现客户端回调的 ASP.NET 页与创建任何 ASP.NET 页的过程类似,但也存在下列区别。该页的服务器代码必须执行下列任务:

实现 ICallbackEventHandler 接口。可以向任何 ASP.NET 网页添加此接口声明。
提供 RaiseCallbackEvent 方法的实现。此方法将被调用以对服务器执行回调。
提供 GetCallbackResult 方法的实现。此方法会将回调结果返回给客户端。
此外,该页还必须包含执行以下操作的三个客户端脚本函数:

一个函数调用帮助器方法,该方法执行对服务器的实际请求。在此函数中,可以执行自定义逻辑,以最先准备事件实参。您可以将字符串作为形参发送给服务器端的回调事件处理程序。
另一个函数由处理回调事件的服务器代码的结果调用并接收该结果,同时接受表示该结果的字符串。此函数称为客户端回调函数。
第三个函数是执行实际服务器请求的 Helper 函数。当在服务器代码中使用 GetCallbackEventReference 方法生成对此函数的引用时,ASP.NET 将自动生成此函数。
客户端回调和回发都是针对原始页面的请求。因此,它们在 Web 服务器日志中被记录为页面请求。

在服务器代码中实现所需的接口
若要从客户端脚本中运行服务器代码而不执行回发,必须在服务器代码中实现一些接口。

声明 ICallbackEventHandler 接口
可以将 ICallbackEventHandler 接口作为页的类声明的一部分进行声明。如果创建代码隐藏页,则可以通过使用如下语法声明该接口。

      public partial class CallBack_DB_aspx :     System.Web.UI.Page, System.Web.UI.ICallbackEventHandle


如果正在处理单文件页或用户控件,则可以通过在页中使用 @ Implements 指令来添加声明,如下面的示例所示:


      <%@ Page Language="C#" %><%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>


注意:
接口名区分大小写。

创建服务器回调方法
在服务器代码中,必须创建一个实现 RaiseCallbackEvent 方法的方法和一个实现 GetCallbackResult 方法的方法。RaiseCallbackEvent 方法接受单个字符串参数,而不是通常用于事件处理程序的常见的两个参数。该方法的部分内容可能与下面的示例类似:

 
      public String RaiseCallbackEvent(String eventArgument){}


GetCallbackResult 不接受参数并返回一个字符串。该方法的部分内容可能与下面的示例类似:


      public string GetCallbackResult(){    return aStringValue;}


创建客户端脚本函数
必须向页中添加客户端脚本函数才能执行两个功能:向服务器页发送回调和接收结果。这两个客户端脚本函数都是用 ECMAScript (JavaScript) 编写。

发送回调
发送回调的函数在服务器代码中生成。实际回调由任何页都可使用的、实现 ICallbackEventHandler 接口的库函数执行。通过调用页的 GetCallbackEventReference 方法可以获取对库函数的引用,该方法可通过页 ClientScript 属性进行访问。然后动态生成一个客户端函数,该函数包含对来自 GetCallbackEventReference 方法的返回值的调用。应向该方法传递以下内容:一个对页的引用(在 C# 中为 this,在 Visual Basic 中为 Me)、传递数据时所使用的参数名称、将接收回调数据的客户端脚本函数的名称,以及传递所需的任何上下文的参数。

生成函数之后,通过调用 RegisterClientScriptBlock 方法将其插入到页中。

下面的示例演示如何以动态方式创建一个名为 CallServer 且调用回调的函数。

   void Page_Load(object sender, EventArgs e)
   { 

ClientScriptManager cm = Page.ClientScript;
   String cbReference = cm.GetCallbackEventReference(this, "arg",        "ReceiveServerData", "");
   String callbackScript = "function CallServer(arg, context) {" +        cbReference + "; }";
    cm.RegisterClientScriptBlock(this.GetType(),        "CallServer", callbackScript, true);
   }

由正在生成的函数接受的参数名称应与传递给 GetCallbackEventReference 方法的值的名称相匹配。

下面的示例演示了一些可用于调用回调并处理其返回值的标记:

      <input type="button" value="Callback"     onclick="CallServer(1, alert('Callback'))"/><br /><span id="Message"></span>

接收回调
可以编写在页中静态接收回调的客户端函数。该函数的名称必须与在 GetCallbackEventReference 方法调用中传递的名称相匹配。接收函数接受两个字符串值:一个用于返回值,另一个(可选)用于从服务器传回的上下文值。

该函数可能与下面的示例类似:

      <script type="text/javascript">function ReceiveServerData(arg, context){    Message.innerText = 'Processed callback.';}</script>
Share:

Sys.ArgumentOutOfRangeException Value must be an integer.

在Asp.net AJAX 中,使用FrameSet或Iframe时,frame或iframe的frameborder属性应使用1 或 0,如:
frameborder="0"
而不能使用yes 或 no,如:
frameborder="no"

否则AJAX 1.0会出现如下脚本错误:

Sys.ArgumentOutOfRangeException: Value must be an integer.
Parameter name: x
Actual value was NaN.

因为此错误可能会导致很多难以预料的问题。如RequiredFieldValidator无法工作等
Share:

.Net嵌入资源与读取

资源文档顾名思义就是存放资源的文档。资源文档在程式设计中有着自身独特的优势,他单独于源程式,这样资源文档就能够被多个程式使用。同时在程式设计的时候,有时出于安全或其他方面因素的考虑,把重要东西存放在资源文档中,也能够达到保密、安全的效果。      

在.NET中,有文本文件.resx文件和 .resources 文件三种资源文件。如果资源将只包含字符串数据,则文本文件是最简单的选择。如果资源将包含对象或字符串与对象的组合,则必须创建 .resx 文件或 .resources 文件。注意,只有 .resources 文件才应能嵌入在公共语言运行库程序集和附属程序集中。

对于.resx文件,可以通过在项目点击右键->属性->资源 方式创建。也可以通过在项目点击右键->添加->新建项->资源文件 此方式创建。.resx方式可以添加字符串、文件、以及图片等。VS.NET中创建的这种文件也是将其转成.resources 文件然后根据设置将其嵌入到Assembly中。

对于添加到.resx文件中的资源,可以使用ResourceManager对象获取。

程序集中的资源文件名称,可以通过一下方法获得。

Assembly asm = Assembly.GetExecutingAssembly();

foreach (string name in asm.GetManifestResourceNames())

         MessageBox.Show(name);

Assembly.GetExecutingAssembly()方法可以获得当前的程序集.

1.获取字符串的方式为:

Assembly asm = Assembly.GetExecutingAssembly();

ResourceManager LocRM = new ResourceManager("ResourceRead.Properties.Resources",asm);

String str = LocRM.GetString("String1");

ResourceManager构造函数中的第一个参数"ResourceRead.Properties.Resources"是嵌入到程序中的资源的名称。(ResourceRead.Propertie是命名空间,Resources为类名)

2.获取图片的方式为:

Assembly asm = Assembly.GetExecutingAssembly();

ResourceManager LocRM = new ResourceManager("ResourceRead.Properties.Resources",asm);

Bitmap bmp = LocRM.GetObject("Image1")as Bitmap;

3.获取其他类型文件 (如获取名称为so的一个mp3文件,该文件嵌入到资源文件中)

Assembly asm = Assembly.GetExecutingAssembly();

ResourceManager LocRM = new ResourceManager("ResourceRead.Properties.Resources",asm);

byte[] buffer = LocRM.GetObject("so")as byte[];

FileStream FS = new FileStream("so.mp3", FileMode.Create);

BinaryWriter BWriter = new BinaryWriter(FS);

BWriter.Write(buffer, 0, buffer.Length);

BWriter.Close();

FS.Close();

有一种特殊情况,那就是当你直接嵌入一资源时,也就是说,不通过一个资源文件(.resources)而直接将一资源(Object)嵌入到 Assembly 在这种情况下ResourceManager就没有用 了,因为它只能获取.resources资源文件(在或不在Assembly中)。

比如在项目中添加一个文件,在项目解决方案中,选中该文件点击右键->生成操作,选择‘嵌入的资源’。则该文件就嵌入到生成的程序集中。此时可以通过Assembly对象的GetManifestResourceStream方法获取该资源,相关操作如下:

1.     读取文本文件

   Assemblyasm = Assembly.GetExecutingAssembly();

Stream s =  asm.GetManifestResourceStream(asm.GetName().Name + ".文本文件.txt");

//使用Streamreader读取

//StreamReader sr = new StreamReader(s,Encoding.Default);

//string str =sr.ReadToEnd();

//MessageBox.Show(str);

//读入byte数组

   byte[]  m_fileContent = newbyte[s.Length];

   s.Read(m_fileContent, 0,m_fileContent.Length);

   stringstr2 = System.Text.Encoding.Default.GetString(m_fileContent);

MessageBox.Show(str2);

2.读取图片及其他文件
Assembly asm = Assembly.GetExecutingAssembly();

Stream s = asm.GetManifestResourceStream(asm.GetName().Name + ".PH20081204111844.jpg");

Bitmap bt = new Bitmap(s);

Assembly asm = Assembly.GetExecutingAssembly();

Stream s =  asm.GetManifestResourceStream(asm.GetName().Name + ".其他文件");

byte[]  m_fileContent = new byte[s.Length];

 s.Read(m_fileContent, 0,m_fileContent.Length);
Share:

Sql Sever大文件分段读取下载



Sql Sever大文件下载,如果直接使用Response.BinaryWrite(bytes);文件过大会出现错误:

[ArgumentOutOfRangeException: Specified argument was out of the range of valid values.

可以考虑分段读取大文件 

using (SqlConnection cn = new SqlConnection(“。。。”))

            {

                cn.Open();

                string strSql2 = "select ID,DocName,DocContent,ConType from Forum_MessageDoc where ID=" + Request.Params["AssID"];

                using(SqlCommand cmd = cn.CreateCommand())

                {

                    cmd.CommandText = strSql2;

                    SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.SequentialAccess);

                    const int buffersize = 100;

                    Byte[] bytes = new byte[buffersize];

                    long currentindex = 0;

                    int byteRead = 0;                    

                    while (rdr.Read())

                    {

                        byteRead = (int)rdr.GetBytes(2, currentindex, bytes, 0, buffersize);                 

                        while (byteRead != 0)

                        {

                             Response.BinaryWrite(bytes); 

                            currentindex += byteRead;

                            byteRead = (int)rdr.GetBytes(2, currentindex, bytes, 0, buffersize);

                        }

                    }

                    Response.End();

                }

            }

        }

    }



Share:

使用WebBrowser控件Post数据

使用 WebBrowser 控件可以在 Windows 窗体应用程序中承载网页以及支持浏览器的其他文档。例如,可以使用 WebBrowser 控件在应用程序中提供基于 HTML 的集成用户帮助或 Web 浏览功能。此外,还可以使用 WebBrowser 控件向 Windows 窗体客户端应用程序添加基于 Web 的现有控件。

    WebBrowser的Navigate函数提供了向网页Post数据的功能: 

    public void Navigate(string urlString,string targetFrameName,byte[] postData,string additionalHeaders)  将指定的统一资源定位符 (URL) 处的文档加载到 WebBrowser 控件中,使用指定 HTTP 数据请求该文档并替换具有指定名称的网页框架的内容。

    其中postData为HTTP POST 数据,例如窗体数据。additionalHeaders要添加到默认标头中的 HTTP标头。一般Http表头设为"Content-Type: application/x-www-form-urlencoded "即可.

    如果要正确地向网页Post数据,并须使用以上两个参数.登陆的 Post数据可以通过Fiddler监测获得,如下图:

    如变为字符则格式如下: "__VIEWSTATE=%2FwEPDwULLTExNTc2NTI3OTlkZFQGow3nzBFSYeWWyfo%2FOEXT8w5j&UserName=admin&Password=admin123&AuthenButton=%E7%94%A8%E6%88%B7%E7%99%BB%E5%BD%95&__EVENTVALIDATION=%2FwEWBALUudTOCwKvruq2CALSxeCRDwKGkaOOC8PpQ8Wk8rGROWhMJioW342WQkVO"

    则现在如果要WebBrowser打开一个登陆页面,但是向网页Post数据而自动登陆的话只需要步骤:

  String PostData="__VIEWSTATE=%2FwEPDwULLTExNTc2NTI3OTlkZFQGow3nzBFSYeWWyfo%2FOEXT8w5j&UserName=admin&Password=admin123&AuthenButton=%E7%94%A8%E6%88%B7%E7%99%BB%E5%BD%95&__EVENTVALIDATION=%2FwEWBALUudTOCwKvruq2CALSxeCRDwKGkaOOC8PpQ8Wk8rGROWhMJioW342WQkVO";

  byte[] b;

  b = System.Text.Encoding.UTF8.GetBytes(PostData);

  string header = "Content-Type: application/x-www-form-urlencoded ";

  WebBrowser.Navigate("http://****/login.aspx","",b,header);
Share:

VC++ 2005

20081212
-----------
  1.

    VC++2005可以生成两种类型的C++项目: 1. 本地C++项目 2.CLR C++项目 。本地C++项目包括(win32控制台项目、MFC项目),而CLR C++包括CLR控制台程序和窗体应用程序等。
 2.  语法: using std;using std::cout; using std::cin;
 
  3.头文件:
  #include<Iostream>
  如果使用默认应用程序向导生成win32控制台程序,则开始执行的主函数为_tmain(),而且包含头文件stdafx.h。
  stdafx.h中包含 #include "stdio.h"为用于标准的老式头文件功能同<iostream>, include "tchar.h" 是Microsoft特有的头文件,它定义文本函数。此处的想法是,stdafx.h应当为项目定义一组标准系统包含你文件,而且针对这个文件中需要的其他系统文件将添加#include指令。
  #include<iomanip> 操作符头文件。using std::setw(n) ,它输出的值在n个空格宽的字段中遵循右对齐,只对紧跟它的单个输出值有用。cout<<setw(6)<<num1<<setw(6)<<num2;
4.
 1个字节为8个字位。
 整形: int short long
 字符型: char letter = 'A'; char letter = 65  (-128~127); 宽字符型(16位):wchar_t letter = L'Z' ;
 布尔型: bool
  浮点型:double in_to_mm = 25.4 ;doubule占用8个字节个内存。 float pi = 3.14159f; float型占用4个字节。
5.
  typedef类型重定义: typedef long int BigOnes; BigOnes mynum=0L;
6.
  lvalue和rvalue
  lvalue是一种涉及内存地址的变量,所有产生lvalue的表达式都可以出现在赋值语句的左边。大多数变量都为lvalue,因为它们指定内存中的一个位置。但是有些被定义为常量的变量,不是lvalue,不能出现在表达式左边。
不是lvalue的表达式的结果称为rvalue

20081213
------------
  1.

   using 声明 using namespace std; using 指令 using std::cout;
  2.
  头文件:
  using <string>;
 3.
  c++/CLI扩展: safe_cast 运算符 int value = safe_cast<int>double_value;,特有的基本数据类型long long, unsigned long long;
  4.
   System命名空间定义值类型: bool System::Boolean ,int System::Int32
5.
  不同的枚举:
   enum Week { mon = 1, Tues,Wed....} thisWeek; thisWeek = Mon;
  enum Week nextWeek = Week nextWeek;

   enum Class Suit{Clubs,Diamonds,Hearts,Spades};
   Suit suit = Suit:Clubs;
6 .  L"This is string" L表示宽字符串。


20081214
-----------
  1.

字符串处理 :
  MFC中 CString类
  C++/CLI String类 System::String^ saying = L"Many hands make light work.";
  标准C++  char name[5] = "oooo";  cin.getline(char[],MAX,'
');
           #include<cstring> (c语言库)  wcsnlen(); count = std::strlen(buffer);
2.
  指向char类型的指针
  char* proverb = "A miss is as good as a mile."; 相当于const char类型的数字,并将字面值地址存储在指针proverb中。
  char类型指针数组
   char* pstr[] = { "x...","...",".."}; cou<< pstr[i];
3.
  new delete delete[]
  C++/CLI gcnew

  C++/CLI中的指针 句柄 数组 :引用型变量必须使用^
                     String ^s = L"This is a string";
                      array<int> samples = new array<int>;
                       array<String ^> ^names = { "Jack","Jane","Joe"..};
                      array<String^> names; names = gcnew array<String^>{...};
                          
4.
  声明和定义的区别: int a(声明); int a=0(声明和定义);
  5.
  头文件 #include"stdarg.h"
  va_list arg_ptr; (var_list类型是在stdarg.h头文件中定义的,该指针用来依次指向各个市参).
  var_start 宏用来初始化 arg_ptr,使其指向列表中第一个实参。
         #include<new>
  using std::bad_alloc
         #include <cstdlib> exit();
         #include <cctype>  isdigit();
Share:

SQL SERVER 2005 EXPRESS

1. SQL Server Express 与正式版本的区别

      SQL Server Express 使用与其他 SQL Server 2005 版本同样可靠的、高性能的数据库引擎,也使用相同的数据访问 API(如 ADO.NET、SQL Native Client 和 T-SQL)。事实上,它与其他 SQL Server 2005 版本的不同仅体现在以下方面:



















l 缺乏企业版功能支持
l 仅限一个 CPU
l 缓冲池内存限制为 1 GB
l 数据库最大为 4 GB

2. 安装SQL Server Express 


  •   如果要修改SQL Server 2005 Express的实例名称,应在填写注册信息那一步,取消“隐藏高级配置选项”。


 (在用户实例支持中,暗含着几个假设。假设实例名为 SQLEXPRESS 的 SQL Server Express 已安装在计算机上。还假设它是一个仅托管堆栈解决方案,您必须使用 .NET SQL Server 数据访问接口开发可部署的 XCopy 应用程序。也就是说,不能使用 SQL Native Client 或 MDAC 来开发可用于用户实例的应用程序。)


  •   默认情况下,SQL Server Express 只能访问本地计算机上的共享内存连接类型。SQL Server Express 不支持 VIA 协议和 HTTP 协议。因为默认情况下只能使用共享内存,所以除非打开网络,否则无法从远程计算机连接到 SQL Server Express。


             可以通过以下方式打开网络: 1.使用外围应用配置器工具启用网络启用远程连接,并启用和启动 SQLBROWSER 服务;2. 使用 SQL Server 配置管理器启用相关协议,并启动 SQL Browse

r

3.sql server express新功能"用户实例"

      用户实例是 SQL Server Express 中的新功能,可以像处理文件一样处理数据库。现在,本地数据库可以随应用程序一起移动、复制或通过电子邮件传送。在新的位置,不需要进行额外配置就可以使其正常运行。用于在 SQL Server Express 中启用应用程序用户实例支持的主要功能有三个:连接字符串中的 AttachDBFilename 选项、不需要指定逻辑数据库名称和“用户实例”选项。在用户实例支持中,暗含着几个假设。假设实例名为 SQLEXPRESS 的 SQL Server Express 已安装在计算机上。还假设它是一个仅托管堆栈解决方案,您必须使用 .NET SQL Server 数据访问接口开发可部署的 XCopy 应用程序。也就是说,不能使用 SQL Native Client 或 MDAC 来开发可用于用户实例的应用程序。

         通常,应用程序开发人员仅将用户数据库和日志文件随应用程序一起复制。但是,在 SQL Server 中,一个特定数据库(称为 master 数据库)中存在多个配置条目。依赖于 master 数据库中的条目的功能包括 SQL 身份验证(建议您尽可能使用 Windows 身份验证)。如果您的应用程序依赖于 master 数据库中的任何条目,则应用程序开发人员必须确保将这些配置条目复制到目标系统中。一个方法是:当将运行的应用程序安装到目标计算机上时,要随之包括一个 SMO、DMO 或 T-SQL 配置脚本。对于大多数使用 Windows 身份验证运行的应用程序,从 master 数据库复制信息并不难。
Share:

asp.net 连接 sql server数据库超时

错误信息:

    Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. 语句已终止。<BR>该错误为数据库超时。可参考以下几种方法解决.
 

asp.net 中:
 1. 延长SqlConnection.ConnectionTimeout 超时时间(秒)
 2. 延长SqlCommand.CommandTimeout 超时时间(秒)
 

如问题不能解决,数据库服务器(sql server)中设置:

 3 .企业管理器中的设置: 
A、在企业管理器中,选择菜单上的"工具",再选择"选项"; 
 B、在弹出的"SQL Server企业管理器属性"窗口中,点击"高级"选项卡; 
 C、在"连接设置"下的"登录超时(秒)"右边的框中输入一个比较大的数字,如 30。 
 
 4.查询分析器中的设置: 
 
 单击“工具”-&gt;"选项"-&gt;"连接"; 将登录超时设置为一个较大的数字,连接超时改为0。 

 5. 由于数据库设计问题造成SQL数据库新增数据时超时
 原因: 
 数据库设置时,[文件增长]按百分比来增长,当数据库文件很大时(1G以上),新增操作都会报超时,而这时候其实CPU、内存占用率都非常非常的低。 
 解决方法: 
 把上述的文件增长这里设置为一个更低的百分比或者直接指定增加多少兆字节。 同时可考虑收缩数据库日志.
Share:

asp.net Treeview SelectedNodeStyle无效果的原因

  The text of a node in the TreeView control can be in one of two modes:selection mode or navigation mode.When a node is in navigation mode, all selection events are disabled for that node. Clicking the node in navigation mode directs the user to the specified URL.

       You have used the NavigateUrl for all the nodes, so only OnSelectedNodeChanged is not getting fired.

        当一个节点是在导航模式下,所有的选择事件是停用的。点击节点的导航模式引导用户到指定的网址。您可以选择设定目标财产指定窗口或框架中显示链接的内容。因为选择事件被停用,所以导致选择节点后该节点的SelectedNodeStyle并没有产生效果。如果要产生效果,可以在OnSelectedNodeChanged 事件中设置。HoverNodeStyle是始终可以有效的。        


      The text of a node in the TreeView control can be in one of two modes: selection mode or navigation mode. By default, a node is in selection mode. To put a node into navigation mode, set the node's NavigateUrl property to a value other than an empty string (""). To put a node into selection mode, set the node's NavigateUrl property to an empty string.

      By default, clicking a node that is in selection mode posts the page back to the server and raises the SelectedNodeChanged event. You can optionally specify a different event to raise by setting the node's SelectAction property. For more information, see SelectAction. To determine which node was clicked in selection mode, use the SelectedNode property of the TreeView control.

      When a node is in navigation mode, all selection events are disabled for that node. Clicking the node in navigation mode directs the user to the specified URL. You can optionally set the Target property to specify the window or frame in which to display the linked content.
Share:

C# Collection

IEnumerator 是所有非泛型枚举数的基接口。IEnumerator 是所有非泛型枚举数的基接口。 枚举数可用于读取集合中的数据,但不能用于修改基础集合。最初,枚举数定位在集合中第一个元素前。Reset 方法还会将枚举数返回到此位置。在此位置,调用 Current 属性会引发异常。因此,在读取 Current 的值之前,必须调用 MoveNext 方法将枚举数提前到集合的第一个元素。在调用 MoveNext 或 Reset 之前,Current 返回同一对象。MoveNext 将 Current 设置为下一个元素。如果 MoveNext 越过集合的末尾,则枚举数将被放置在此集合中最后一个元素的后面,而且 MoveNext 返回 false。当枚举数位于此位置时,对 MoveNext 的后续调用也返回 false。如果最后一次调用 MoveNext 返回 false,则调用 Current 会引发异常。若要再次将 Current 设置为集合的第一个元素,可以调用 Reset,然后再调用 MoveNext只要集合保持不变,枚举数就保持有效。如果对集合进行了更改(如添加、修改或删除元素),则枚举数将失效且不可恢复,并且下一次对 MoveNext 或 Reset 的调用将引发 InvalidOperationException。如果在 MoveNext 和 Current 之间修改集合,那么即使枚举数已经无效,Current 也将返回它所设置成的元素。枚举数没有对集合的独占访问权;因此,枚举通过集合在本质上不是一个线程安全的过程。即使一个集合已进行同步,其他线程仍可以修改该集合,这将导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。

 

IEnumerable 接口 :公开枚举数,该枚举数支持在非泛型集合上进行简单迭代(foreach)

 

ICollection 接口 :定义所有非泛型集合的大小、枚举数和同步方法。ICollection 接口是 System.Collections 命名空间中类的基接口。ICollection 接口扩展 IEnumerableIDictionary 和 IList 则是扩展 ICollection 的更为专用的接口。IDictionary 实现是键/值对的集合,如 Hashtable 类。IList 实现是值的集合,其成员可通过索引访问,如 ArrayList 类。某些集合(如 Queue 类和 Stack 类)限制对其元素的访问,它们直接实现 ICollection 接口。如果 IDictionary 接口和 IList 接口都不能满足所需集合的要求,则从 ICollection 接口派生新集合类以提高灵活性。

 

IDictionary:ICollection  接口:表示键/值对的非通用集合。IDictionary 接口是键/值对的非通用集合的基接口。每个元素都是一个存储在 DictionaryEntry 对象中的键/值对。每一对都必须有唯一的键。实现在是否允许键为 nullNothingnullptrnull 引用(在 Visual Basic 中为 Nothing) 方面有所不同。此值可以为 nullNothingnullptrnull 引用(在 Visual Basic 中为 Nothing),并且不必是唯一的。IDictionary 接口允许对所包含的键和值进行枚举,但这并不意味着任何特定的排序顺序。IDictionary 实现有三种类别:只读、固定大小、可变大小。无法修改只读 IDictionary 对象。固定大小的 IDictionary 对象不允许添加或移除元素,但允许修改现有元素。可变大小的 IDictionary 对象允许添加、移除和修改元素。C# 语言中的 foreach 语句(在 Visual Basic 中为 for each)需要集合中每个元素的类型。由于 IDictionary 对象的每个元素都是一个键/值对,因此元素类型既不是键的类型,也不是值的类型。而是 DictionaryEntry 类型。例如:IList:ICollection  接口 :表示可按照索引单独访问的对象的非泛型集合。

 

IList(可通过索引访问) : ICollection 接口的子代,并且是所有非泛型列表的基接口。IList 实现有三种类别:只读、固定大小和可变大小。无法修改只读 IList。固定大小的 IList 不允许添加或移除元素,但允许修改现有元素。可变大小的 IList 允许添加、移除和修改元素。

 

IDictionaryEnumerator 接口 :枚举非泛型字典的元素。枚举数可用于读取集合中的数据,但不能用于修改基础集合。最初,枚举数定位在集合中第一个元素前。Reset 方法还会将枚举数返回到此位置。在此位置,调用 Current 属性会引发异常。因此,在读取 Current 的值之前,必须调用 MoveNext 方法将枚举数向前移动到集合的第一个元素。在调用 MoveNext 或 Reset 之前,Current 返回同一对象。MoveNext 将 Current 设置为下一个元素。如果 MoveNext 越过集合的结尾,则枚举数将被定位在此集合中最后一个元素的后面,且 MoveNext 返回 false。当枚举数位于此位置时,对 MoveNext 的后续调用也返回 false。如果最后一次调用 MoveNext 返回 false,则调用 Current 会引发异常。若要再次将 Current 设置为集合的第一个元素,可以调用 Reset,然后再调用 MoveNext只要集合保持不变,枚举数就保持有效。如果对集合进行了更改(如添加、修改或删除元素),则枚举数将失效且不可恢复,并且下一次对 MoveNext 或 Reset 的调用将引发 InvalidOperationException。如果在 MoveNext 和 Current 之间修改集合,那么即使枚举数已经无效,Current 也将返回它所设置成的元素。枚举数没有对集合的独占访问权;因此,枚举通过集合在本质上不是一个线程安全的过程。即使一个集合已进行同步,其他线程仍可以修改该集合,这将导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。

 

IComparer 接口 :公开一种比较两个对象的方法。

 

IEqualityComparer 接口 定义方法以支持对象的相等比较。使用此接口,可以实现集合的自定义相等比较。即,您可以创建自己的相等定义,并指定此定义与接受 IEqualityComparer 接口的集合类型一起使用。在 .NET Framework 中,Hashtable、NameValueCollection 和 OrderedDictionary 集合类型的构造函数接受此接口。此接口仅支持相等比较。IComparer 接口则允许自定义筛选和排序比较。

 

---------------------------

 

ArrayList  :使用大小可按需动态增加的数组实现 IList 接口。不保证会对 ArrayList 排序。 在执行需要对 ArrayList 排序的操作(如 BinarySearch)之前,必须对 ArrayList 进行排序。ArrayList 的容量是 ArrayList 可以保存的元素数。随着向 ArrayList 中添加元素,容量通过重新分配按需自动增加。可通过调用 TrimToSize 或通过显式设置 Capacity 属性减少容量。使用整数索引可以访问此集合中的元素。 此集合中的索引从零开始。ArrayList 接受 nullNothingnullptrnull 引用(在 Visual Basic 中为 Nothing) 作为有效值并且允许重复的元素

BitArray  :管理位值的压缩数组,该值表示为布尔值,其中 true 表示位是打开的 (1),false 表示位是关闭的 (0)。
Share:

javascript 获得页面高度宽度

在IE中:
document.body.clientWidth ==> BODY对象宽度
document.body.clientHeight ==> BODY对象高度
document.documentElement.clientWidth ==> 可见区域宽度
document.documentElement.clientHeight ==> 可见区域高度

在FireFox中:
document.body.clientWidth ==> BODY对象宽度
document.body.clientHeight ==> BODY对象高度
document.documentElement.clientWidth ==> 可见区域宽度
document.documentElement.clientHeight ==> 可见区域高度

而如果没有定义W3C的标准

在IE中:
document.documentElement.clientWidth ==> 0
document.documentElement.clientHeight ==> 0
FireFox为:
document.documentElement.clientWidth ==> 页面对象宽度(即BODY对象宽度加上Margin宽)

document.documentElement.clientHeight ==> 页面对象高度(即BODY对象高度加上Margin高)
Share:

ObjectDataSource 使用中的Null值问题

在使用数据控件Gridview/DetailView/FormView 绑定ObjectDataSource进行插入或者更新的时候。对于数据库中允许为空的列 往往会出现如下的几种错误:

异常详细信息: System.NullReferenceException: 未将对象引用设置到对象的实例。

异常详细信息: System.Data.SqlClient.SqlException: 过程 'usp_XXX' 需要参数 '@XXX,但未提供该参数。

...

产生这些错误的原因为数据控件在插入或者更新数据时自动把未输入的栏转换成null,然后传递给ObjectDataSource的参数引起的。

解决方法:

 在ObjectDataSource的Updating  Inserting 事件中判断输入参数是否为空,如果为空将其赋为默认值


protected void ObjectDataSource1_Inserting(object sender, ObjectDataSourceMethodEventArgs e)
    {
        if (e.InputParameters["XX"] == null)
        {
            e.InputParameters["ParentDeptNo"] = "";
        }
    }



Share:

BlogEngine 页面设计-- 动态使用母版页

ASP.NET 的母版页让你能够轻松地创建和维护Web网站上一致的外观。你可以用母版页来定义整个网站的显示,或者网站的一组子页面。母版页含有所有的顶级HTML元素。这包括html、页头、正文和HTML窗体元素,因此内容页面不应该含有这些元素。头元素必须带有runat=server属性,而且在运行期间其头文本要用内容页面的标题文本来替换。

母版页包括内容占位符控件,以接纳来自内容页面的输出。 在BlogEngine中,所有的站位符控件都使用ID “cphBody”
 <asp:ContentPlaceHolder ID="cphBody" runat="server" />

内容页面使用内容控件来指定要被插入母版页的内容区域。在BlogEngine的Default.aspx页面中,内容如下:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="default.aspx.cs" Inherits="_default" %>
<%@ Register Src="User controls/PostList.ascx" TagName="PostList" TagPrefix="uc1" %>


<asp:Content ID="Content1" ContentPlaceHolderID="cphBody" Runat="Server">
  <uc1:PostList ID="PostList1" runat="server" />
  <blog:PostCalendar runat="server" ID="calendar"
    EnableViewState="false"
    ShowPostTitles="true"
    BorderWidth="0"
    NextPrevStyle-CssClass="header"
    CssClass="calendar"
    WeekendDayStyle-CssClass="weekend"
    OtherMonthDayStyle-CssClass="other"
    UseAccessibleHeader="true"
    Visible="false"
    Width="100%" />   
</asp:Content>


在 <%@ Page Language="C#" > 中并没有通过设置MasterPageFile属性确定母版页。此时在vs设计器中打开页面时会显示错误



BlogEngine的default页继承自BlogEngine.Core.Web.Controls.BlogBasePage. 在页面的OnPreInit事件中,会通过程序设计的theme主题,自动选择当前页面的风格主题。

private string _Theme = BlogSettings.Instance.Theme;
  /// <summary>
  /// Assignes the selected theme to the pages.
  /// </summary>
  protected override void OnPreInit(EventArgs e)
  {
   if (Request.QueryString["theme"] != null)
    _Theme = Request.QueryString["theme"];


   MasterPageFile = Utils.RelativeWebRoot + "themes/" + _Theme + "/site.master";

   base.OnPreInit(e);

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

  }。

这也是我们创建可动态改变网站风格的一种方法。 使用母版页创建可更改主题风格的原理就是,在页面的PreInit事件中,设置当前的 this.MasterPageFile = "××××.master";

母板页的值可以防止在session中,或者数据中,或者cookie中,在需要时候获取
Share:

BlogEngine 配置

使用 ConfigurationSection 创建自定义配置节

1.创建自定义配置节处理程序

namespace BlogEngine.Core.Providers
{
  /// <summary>
  /// A configuration section for web.config.
  /// </summary>
  /// <remarks>
  /// In the config section you can specify the provider you
  /// want to use for BlogEngine.NET.
  /// </remarks>
  public class BlogProviderSection : ConfigurationSection
  {

    /// <summary>
    /// A collection of registered providers.
    /// </summary>
    [ConfigurationProperty("providers")]
    public ProviderSettingsCollection Providers
    {
      get { return (ProviderSettingsCollection)base["providers"]; }
    }

    /// <summary>
    /// The name of the default provider
    /// </summary>
    [StringValidator(MinLength = 1)]
    [ConfigurationProperty("defaultProvider", DefaultValue = "XmlBlogProvider")]
    public string DefaultProvider
    {
      get { return (string)base["defaultProvider"]; }
      set { base["defaultProvider"] = value; }
    }

  }

2.向 ASP.NET 配置文件添加自定义节处理程序

  (将 sectionGroup 元素和 section 元素添加到 Web.config 文件的 configSections 元素中。正是此声明将自定义节处理程序与节名关联。将 section 元素嵌套在 sectionGroup 中是可选的,但是建议这样做,以便更好地组织配置数据。)

  <configSections>
    <sectionGroup name="BlogEngine">
      <section name="blogProvider" requirePermission="false" type="BlogEngine.Core.Providers.BlogProviderSection, BlogEngine.Core" allowDefinition="MachineToApplication" restartOnExternalChanges="true"/>
    </sectionGroup>
  </configSections>

  <BlogEngine>
    <blogProvider defaultProvider="XmlBlogProvider">
      <providers>
        <add name="XmlBlogProvider" type="BlogEngine.Core.Providers.XmlBlogProvider, BlogEngine.Core"/>
        <add name="MSSQLBlogProvider" type="BlogEngine.Core.Providers.MSSQLBlogProvider, BlogEngine.Core"/>
      </providers>
    </blogProvider>
  </BlogEngine>
Share:

Issuevision 学习 6/n 序列化数据

Issuevision是个可离线运行的程序。对于离线运行的程序,它需要将一部分数据保存在本地。Issuevision登录后,首先会判断本地是否存在数据,有的话会将数据序列化后保存在本地。   如果处于连线状态的话,issuevision会从网络中获取新的数据,并将数据保存在本地。

将 SerializableAttribute 属性应用于一个类型可指示该类型的实例可以序列化。如果正在序列化的对象图中的任何类型未应用 属性,公共语言运行库则会引发 SerializationException。

即使该类也会实现 ISerializable 接口来控制序列化进程,仍要应用 SerializableAttribute 属性。

默认情况下,类型中由 SerializableAttribute 标记的所有公共和私有字段都会进行序列化,除非该类型实现 ISerializable 接口来重写序列化进程。默认的序列化进程会排除用 NonSerializedAttribute 属性标记的字段。如果可序列化类型的字段包含指针、句柄或其他某些针对于特定环境的数据结构,并且不能在不同的环境中以有意义的方式重建,则最好将 NonSerializedAttribute 属性应用于该字段。


using System;

using System.IO;

using System.Runtime.Serialization;

using System.Runtime.Serialization.Formatters.Soap;

//using System.Runtime.Serialization.Formatters.Binary;

public class Test {

   public static void Main()  {

      //Creates a new TestSimpleObject object.

      TestSimpleObject obj = new TestSimpleObject();

      Console.WriteLine("Before serialization the object contains: ");

      obj.Print();

      //Opens a file and serializes the object into it in binary format.

      Stream stream = File.Open("data.xml", FileMode.Create);

      SoapFormatter formatter = new SoapFormatter();

      //BinaryFormatter formatter = new BinaryFormatter();

      formatter.Serialize(stream, obj);

      stream.Close();

      //Empties obj.

      obj = null;

      //Opens file "data.xml" and deserializes the object from it.

      stream = File.Open("data.xml", FileMode.Open);

      formatter = new SoapFormatter();

      //formatter = new BinaryFormatter();

      obj = (TestSimpleObject)formatter.Deserialize(stream);

      stream.Close();

      Console.WriteLine("");

      Console.WriteLine("After deserialization the object contains: ");

      obj.Print();

   }

}

// A test object that needs to be serialized.

[Serializable()]

public class TestSimpleObject  {

    public int member1;

    public string member2;

    public string member3;

    public double member4;

    // A field that is not serialized.

    [NonSerialized()] public string member5; 

    public TestSimpleObject() {

        member1 = 11;

        member2 = "hello";

        member3 = "hello";

        member4 = 3.14159265;

        member5 = "hello world!";

    }

    public void Print() {

        Console.WriteLine("member1 = '{0}'", member1);

        Console.WriteLine("member2 = '{0}'", member2);

        Console.WriteLine("member3 = '{0}'", member3);

        Console.WriteLine("member4 = '{0}'", member4);

        Console.WriteLine("member5 = '{0}'", member5);

    }

}

Share:

IssueVision 学习 5/n 使用非托管代码

在Issuevison中,保存在本地的DataSet中的内容,使用了非托管代码DPAPI加密用户资料。这两个函数存在于Crypt32.dll库中,是CryptAPI的一部分。对于非托管的代码,.net中使用的方法如下:

  平台调用依赖于元数据在运行时查找导出的函数并封送其参数。下图显示了这一过程。对非托管 DLL 函数的“平台调用”调用过程



当“平台调用”调用非托管函数时,它将依次执行以下操作: 1. 查找包含该函数的 DLL。2. 将该 DLL 加载到内存中。3. 查找函数在内存中的地址并将其参数推到堆栈上,以封送所需的数据。4. 将控制权转移给非托管函数。平台调用会向托管调用方引发由非托管函数生成的异常。使用非托管DLL代码的步骤:1. 确定函数的名称和函数所在的DLL的名称。例如,如果指定 User32.dll 中的 MessageBox 函数,需要标识该函数 (MessageBox) 及其位置(User32.dll、User32 或 user32)。2. 创建用于容纳 DLL 函数的类。在托管代码中创建要使用的非托管函数的原型。 例如声明非托管MessageBox的函数原型如下:


public class Win32 {

     [DllImport("user32.dll", CharSet=CharSet.Auto)]    

     public static extern int MessageBox(int hWnd, String text,                       String caption, uint type);

 } 

3. 使用非托管代码

public class HelloWorld {

    public static void Main() {

        Win32.MessageBox(0, "Hello World", "Platform Invoke Sample", 0);   

 }


   Issuevison中使用非托管代码DPAPI,加密用户资料。该该接口一共有两个函数,他们提供了系统级的数据保护服务。这两个函数存在于Crypt32.dll库中,是CryptAPI的一部分,但使用起来要比其他的CryptAPI函数简单得多。DPAPI和系统帐户联系仍然十分紧密,他会自动使用当前用户的系统登录口令作为加解密的口令,这样一来同一台机器的所有程序都可以解密其他程序加密的数据。为了防止这一点,函数提供了pOptionalEntropy参数,使我们有机会使用自己的口令。如果提供了pOptionalEntropy参数,DPAPI将使用当前用户系统登录口令和我们提供的额外保护口令的组合进行加解密操作。如果你不想使用额外口令保护,则可设置该参数为NULL。因为DPAPI使用用户帐户联系的,所以一台机器上加密的数据,一般不能在另一台机器上解密

Share:

IssueVision学习 4/n 自定义WebService 的Soap 头部

SOAP 标头提供了一种方法,用于将数据传递到 XML Web services 方法或从 XML Web services 方法传递数据,条件是该数据不直接与 XML Web services 方法的主功能相关。例如,一个 XML Web services 可能包含若干个 XML Web services 方法,而每个方法都需要自定义的身份验证方案。您不用将参数添加到每个需要自定义身份验证方案的 XML Web services 方法,而可以将引用从 SoapHeader 派生的类的 SoapHeaderAttribute 应用于每个 XML Web services 方法。从 SoapHeader 派生的类的实现处理该自定义身份验证方案。按照此方式,XML Web services 方法使用 SOAP 标头来仅实现特定于它的功能并添加其他功能。

下面的列表概述接收和处理 SOAP 标头的基本步骤: 

1。创建一个从 SoapHeader 派生的类,表示传入 SOAP 标头的数据。

2。将一个成员添加到实现 XML Web services 的类或 XML Web services 客户端代理类(它们属于在第一步创建的类型)。

3。指定第二步中在 MemberName 属性中创建的成员,将 SoapHeaderAttribute 应用于 XML Web services 方法或代理类中的对应方法。

4。在 XML Web services 方法或 XML Web services 客户端代码中访问 MemberName 属性,以处理在 SOAP 标头中发送的数据。

下面的 MyWebService XML Web services 定义 MyHeader SOAP 标头,并要求它随同对 MyWebMethod XML Web services 方法的任意调用一起发送。另外,MyWebMethod 还接收 MyHeader SOAP 标头以外的任何 SOAP 标头

<%@ WebService Language="C#" Class="MyWebService"%>

using System.Web.Services;

using System.Web.Services.Protocols;

using System.Xml;

using System;

// Define a SOAP header by deriving from the SoapHeader base class.

public class MyHeader : SoapHeader {

    public string MyValue;

}

public class MyWebService {

    public MyHeader myHeader;

    // Receive all SOAP headers besides the MyHeader SOAP header.

    public SoapUnknownHeader[] unknownHeaders;

    [WebMethod]

    [SoapHeader("myHeader", Direction=SoapHeaderDirection.InOut)]

    //Receive any SOAP headers other than MyHeader.

    [SoapHeader("unknownHeaders",Required=false)]

    public string MyWebMethod() {

       foreach (SoapUnknownHeader header in unknownHeaders) {

           // Perform some processing on the header.

       if (header.Element.Name == "MyKnownHeader")

               header.DidUnderstand = true;

       else

                // For those headers that cannot be  

                // processed, set the DidUnderstand property to false.

                header.DidUnderstand = false;

       }

       return "Hello";

    }

}

 

 



Issuevision中web service的身份验证。

1.自定义SoapHeader CredentialSoapHeader包含用户名和密码属性

2.在web service中添加CredentialSoapHeader类型的成员。

        private CredentialSoapHeader m_credentials;
        //自定义SOAP头部,用来传递认证信息
        public CredentialSoapHeader Credentials
        {
            get
            {
                return m_credentials;
            }
            set
            {
                m_credentials = value;
            }
        }

3.为web service中的方法设置自定义soap头

        [SoapHeader("Credentials")]
        [WebMethod(Description="根据SOAP标头信息,验证用户")]
        public void Authenticate()
        {
            SecurityHelper.VerifyCredentials(this);
        }

   每次调用web service中的方法时,都会首先调用SecurityHelper.VerifyCredentials(this)方法验证soap头中传递的用户信息。

4.SecurityHelper.VerifyCredentials(this)中,会获取web sercice中的属性Credentials,从而得到用户名和密码,对密码进行加密后与数据库中的用户名密码比较。

如用户、密码验证不通过,则抛出SoapException,终止web sercice中方法的执行。

  public static void VerifyCredentials(IssueVision2Services service)
        {
            if (service.Credentials == null || service.Credentials.Username == null || service.Credentials.Password == null)
            {
                EventLogHelper.LogFailureAudit("缺少认证信息的登陆尝试.");
                throw new SoapException(string.Empty, SoapException.ClientFaultCode, "Security");
              
            }
            string password = Authenticate(service.Credentials);
        }

 private static string Authenticate(CredentialSoapHeader header)
        {
         .........
        }
Share:

IssueVision学习 3/n windows 事件日志

EventLog 使您可以访问或自定义 Windows 事件日志,事件日志记录关于重要的软件或硬件事件的信息。通过 EventLog,可以读取现有日志,向日志中写入项,创建或删除事件源,删除日志,以及响应日志项。也可在创建事件源时创建新日志。  若要从日志中读取,请为 EventLog 指定 Log 名称和 MachineName(服务器计算机名称)。不需要指定 Source,因为只有写入日志时才需要源。Entries 成员由事件日志的项列表自动填充。   如果写入事件日志,必须指定或创建一个 Source 事件。Source 在事件日志中将您的应用程序注册为有效的项源。使用 Source 一次只能写入一个日志。Source 可以为任何随机字符串,但其名称必须不同于计算机上的其他源。源通常是应用程序或另一个标识字符串的名称。创建重复的 Source 值的尝试将引发异常。但是,单个事件日志可以与多个源关联。   应用程序和服务应写入 Application 日志或自定义日志。设备驱动程序应写入 System 日志。如果不显式设置 Log 属性,事件日志会默认为 Application 日志。


using System;

using System.Diagnostics;

using System.Threading;

class MySample{

    public static void Main(){

        // Create the source, if it does not already exist.

        if(!EventLog.SourceExists("MySource")){

            EventLog.CreateEventSource("MySource", "MyNewLog");

            Console.WriteLine("CreatingEventSource");

        }

        // Create an EventLog instance and assign its source.

        EventLog myLog = new EventLog();

        myLog.Source = "MySource";

        // Write an informational entry to the event log.    

        myLog.WriteEntry("Writing to event log.");

    }

Share:

IssueVision学习 2/n

软件的三层架构分为数据访问层、业务逻辑层、表现层。在issuevision中,数据访问和业务逻辑层都部署在issuevision的web服务中。基础的数据访问都放在数据集IVDataSet中。使用vs2005可以更方便的创建数据集问题。  

Issvision中IVDataSet只包含数据表定义。具体的内部表和数据表的映射,以及数据操作都放到IVData.cs文件中。如果使用Vs2005重新改写的话,可以在IVDataSet数据集中直接添加TableAdapter,将数据映射和数据库操作都放到数据集中。在TableAdapter中可以方便的添加查询、创建、更新、删除的数据库操作。  

IVData.cs 中主要提供GetLookupTables()、SendReceiveIssues()、GetNewIssueList()函数供web服务调用,来获取基础的用户、事务(issue)类型数据,发送和接受更改的事务(issue)。对于SqlDataAdapter来说,如果指定了它的InsertCommand、SelectCommand、DeleteCommand、UpdateCommand。那么它就可根据DataSet中的行记录状态,通过SqlDataAdapter.Update(Dataset)方法自动的插入修改或者删除数据库中的数据。如果在更新Dataset时,其他的用户已经修改数据库中的内容,则会产生冲突。Issuevision中将冲突的行数据保存在IVDataset数据集的Conflicts表中。在vs2005中添加的IVDataSet数据集的内容如下图:



IVData.cs类结构如下

其中_daIssueHistory,_daIssues,_daIssueTypes,_daStaffer都是IVDataSet数据集中TableAdatper类型的对象 IVDataSetTableAdapters.daStaffers _daStaffer如果更新时产生冲突,则放入m_conflicts表中

if (changedIssues != null)           

    {

                    try{      

                       _daIssues.Update(changedIssues);   

                    }            

                  catch (DBConcurrencyException ex)  {                  

                     m_conflicts.Rows.Add(ex.Row[0]);        

                  }              

 }
Share:

2010年2月23日星期二

允许将显式值插入表的标识列中 SET IDENTITY_INSERT (Transact-SQL)

任何时候,会话中只有一个表的 IDENTITY_INSERT 属性可以设置为 ON。如果某个表已将此属性设置为 ON,并且为另一个表发出了 SET IDENTITY_INSERT ON 语句,则 Microsoft® SQL Server™ 返回一个错误信息,指出 SET IDENTITY_INSERT 已设置为 ON 并报告此属性已设置为 ON 的表。

如果插入值大于表的当前标识值,则 SQL Server 自动将新插入值作为当前标识值使用。

SET IDENTITY_INSERT 的设置是在执行或运行时设置,而不是在分析时设置。
权限

执行权限默认授予 sysadmin 固定服务器角色和 db_ownerdb_ddladmin 固定数据库角色以及对象所有者。
以下示例将创建一个包含标识列的表,并显示如何使用 SET IDENTITY_INSERT 设置来填充由 DELETE 语句导致的标识值中的空隙。


USE AdventureWorks;
GO
-- Create tool table.
CREATE TABLE dbo.Tool(
   ID INT IDENTITY NOT NULL PRIMARY KEY,
   Name VARCHAR(40) NOT NULL
)
GO
-- Inserting values into products table.
INSERT INTO dbo.Tool(Name) VALUES ('Screwdriver')
INSERT INTO dbo.Tool(Name) VALUES ('Hammer')
INSERT INTO dbo.Tool(Name) VALUES ('Saw')
INSERT INTO dbo.Tool(Name) VALUES ('Shovel')
GO

-- Create a gap in the identity values.
DELETE dbo.Tool
WHERE Name = 'Saw'
GO

SELECT *
FROM dbo.Tool
GO

-- Try to insert an explicit ID value of 3;
-- should return a warning.
INSERT INTO dbo.Tool (ID, Name) VALUES (3, 'Garden shovel') --必须使用列名添加
GO
-- SET IDENTITY_INSERT to ON.
SET IDENTITY_INSERT dbo.Tool ON
GO

-- Try to insert an explicit ID value of 3.
INSERT INTO dbo.Tool (ID, Name) VALUES (3, 'Garden shovel')
GO

SELECT *
FROM dbo.Tool
GO
-- Drop products table.
DROP TABLE dbo.Tool
GO



Share:

2010年2月14日星期日

C#中获取程序的当前路径的方法


C#如何获得当前程序所在的目录

Application.StartupPath;Environment.SpecialFolder.ApplicationData
Environment.SpecialFolder命名空间可以获取很多特定的路径目录。
    Environment.SpecialFolder.
      ApplicationData
      CommonApplicationData
      CommonProgramFiles
      Cookies
      DesktopDirectory
      Favorites
      History
      InternetCache
      LocalApplicationData
      Personal
      ProgramFiles
      Programs
      Recent
      SendTo
      StartMenu
      Startup
      Templates
      System
System.Web.HttpContext.Request.{获取服务器绝对路径和虚拟目录路径}

System.AppDomain.CurrentDomain.BaseDirectory;获取应用程序的当前工作目录。

string   path   =   System.IO.Directory.GetCurrentDirectory();Environment.CurrentDirectory
获取应用程序的当前工作目录。
System.IO.Directory.GetCurrentDirectory()
AppDomain.CurrentDomain.BaseDirectory
这两个只能在WindowForm中使用;
Application.StartupPath
Application.ExecutablePath
--获取和设置当前目录(即该进程从中启动的目录)的完全限定路径。
Environment.CurrentDirectory    
--获取启动了应用程序的可执行文件的路径,包括可执行文件的名称。
Application.ExecutablePath

我在项目文件夹里放了一个mdb数据库,供程序使用,发布时我如何获得该路径?vb里是app.path,就是应用程序的路径,不知在c#里怎么写?

答案一

application.path()

答案二

localpath
  具体记不清楚了
答案三 c#的写法 (梓赫)
string path = “”;
           if (system.environment.currentdirectory == appdomain.currentdomain.basedirectory)//windows应用程序则相等
           …{
               path = appdomain.currentdomain.basedirectory;
           }
           else
           …{
               path = appdomain.currentdomain.basedirectory + “bin\”;
           }
C#获取项目程序路径的方法 1.asp.net webform用“Request.PhysicalApplicationPath获取站点所在虚拟目录的物理路径,最后包含“\”;
2.c# winform用
A:“Application.StartupPath”:获取当前应用程序所在目录的路径,最后不包含“\”;
B:“Application.ExecutablePath ”:获取当前应用程序文件的路径,包含文件的名称;
C:“AppDomain.CurrentDomain.BaseDirectory”:获取当前应用程序所在目录的路径,最后包含“\”;
D:“System.Threading.Thread.GetDomain().BaseDirectory”:获取当前应用程序所在目录的路径,最后包含“\”;
E:“Environment.CurrentDirectory”:获取当前应用程序的路径,最后不包含“\”;
F:“System.IO.Directory.GetCurrentDirectory”:获取当前应用程序的路径,最后不包含“\”;
3.c# windows service用“AppDomain.CurrentDomain.BaseDirectory”或“System.Threading.Thread.GetDomain().BaseDirectory”;
用“Environment.CurrentDirectory”和“System.IO.Directory.GetCurrentDirectory”将得到“ system32”目录的路径;
如果要使用“Application.StartupPath”或“Application.ExecutablePath ”,需要手动添加对“System.Windows.Forms.dll ”的引用,并在程序开头用“using    System.Windows.Forms”声明该引用;

 C#获取程序当前路径的方法
C#获取程序当前路径的方法

//获取新的 Process 组件并将其与当前活动的进程关联的主模块的完整路径,包含文件名(进程名)。
string str = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
result: X:\xxx\xxx\xxx.exe (.exe文件所在的目录+.exe文件名)
//获取和设置当前目录(即该进程从中启动的目录)的完全限定路径。
string str = System.Environment.CurrentDirectory;
result: X:\xxx\xxx (.exe文件所在的目录)

//获取当前 Thread 的当前应用程序域的基目录,它由程序集冲突解决程序用来探测程序集。
string str = System.AppDomain.CurrentDomain.BaseDirectory;
result: X:\xxx\xxx\ (.exe文件所在的目录+”\”)

//获取和设置包含该应用程序的目录的名称。
string str = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
result: X:\xxx\xxx\ (.exe文件所在的目录+”\”)

//获取启动了应用程序的可执行文件的路径,不包括可执行文件的名称。
string str = System.Windows.Forms.Application.StartupPath;
result: X:\xxx\xxx (.exe文件所在的目录)

//获取启动了应用程序的可执行文件的路径,包括可执行文件的名称。
string str = System.Windows.Forms.Application.ExecutablePath;
result: X:\xxx\xxx\xxx.exe (.exe文件所在的目录+.exe文件名)

//获取应用程序的当前工作目录。
string str = System.IO.Directory.GetCurrentDirectory();
result: X:\xxx\xxx (.exe文件所在的目录)

经常要在应用程序里读取与其在同一个路径下的配置文件或其它文件等,但在Windows平台与WinCE平台下,获取当前路径的方法却不一样,现把本人的经验写在下面,下面的“CurrentPath”属性可以获取应用程序的当前路径,经测试,在WinXP与WinCE下均正常运行。

PS:可以写成静态方法,编译到动态库里,到处都可以用了。

public class Configs
{
  private string m_CurrentPath;

 private string Platform
{

get

{
return Environment.OSVersion.Platform.ToString();
}


public string CurrentPath



get

}

if(Platform.Equals(“WinCE”))

{

m_CurrentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);

}

else if(Platform.Equals(“Win32NT”))

{

m_CurrentPath = Directory.GetCurrentDirectory();
}

 return m_CurrentPath;

}

}

public Configs()

{

}

}

C#中获取程序当前路径的集中方法

string str1 =Process.GetCurrentProcess().MainModule.FileName;//可获得当前执行的exe的文件名。
string str2=Environment.CurrentDirectory;//获取和设置当前目录(即该进程从中启动的目录)的完全限定路径。
//备注 按照定义,如果该进程在本地或网络驱动器的根目录中启动,则此属性的值为驱动器名称后跟一个尾部反斜杠(如“C:\”)。如果该进程在子目录中启动,则此属性的值为不带尾部反斜杠的驱动器和子目录路径(如“C:\mySubDirectory”)。
string str3=Directory.GetCurrentDirectory();//获取应用程序的当前工作目录。
string str4=AppDomain.CurrentDomain.BaseDirectory;//获取基目录,它由程序集冲突解决程序用来探测程序集。
string str5=Application.StartupPath;//获取启动了应用程序的可执行文件的路径,不包括可执行文件的名称。
string str6=Application.ExecutablePath;//获取启动了应用程序的可执行文件的路径,包括可执行文件的名称。
string str7=AppDomain.CurrentDomain.SetupInformation.ApplicationBase;//获取或设置包含该应用程序的目录的名称。

1.   System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName
     获取模块的完整路径。
2.   System.Environment.CurrentDirectory
     获取和设置当前目录(该进程从中启动的目录)的完全限定目录。
3.   System.IO.Directory.GetCurrentDirectory()
     获取应用程序的当前工作目录。这个不一定是程序从中启动的目录啊,有可能程序放在C:\www里,这个函数有可能返回C:\Documents and Settings\ZYB\,或者C:\Program Files\Adobe\,有时不一定返回什么东东,我也搞不懂了。
4.  System.AppDomain.CurrentDomain.BaseDirectory
     获取程序的基目录。
5.  System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase
     获取和设置包括该应用程序的目录的名称。
6.  System.Windows.Forms.Application.StartupPath
     获取启动了应用程序的可执行文件的路径。效果和2、5一样。只是5返回的字符串后面多了一个”\”而已
7.  System.Windows.Forms.Application.ExecutablePath
     获取启动了应用程序的可执行文件的路径及文件名,效果和1一样。
Share:

2010年2月10日星期三

基于.Net平台应用程序唯一运行实例实现


发布日期: 2006-06-30 | 更新日期: 2006-07-04
作者:郑佐
适用于: Windows 操作系统
.NET Framework 1.x,2.0运行时环境
.NET Windows开发
摘要:本文阐述了在基于.NET平台的应用程序开发中如何实现唯一应用程序运行实例,对几种实现方式进行分析测试比较,从而寻找一种合适的处理方式。单击此处才查看本文的示例代码。
概述
在开发一些应用系统的时候,由于程序内在的一些特征,系统的某些组成子程序只允许运行一个应用程序实例,以保证业务和数据处理安全。本文将从实际应用角度来分析其实现原理,对三种实现方式进行测试比较,从而确定一种合适的实现方法。文章的例子使用C#语言进行描述。
进程匹配
对于每一个应用程序运行实例都会包含该实例的一个或多个进程,而且在程序运行过程中可能会动态的创建或销毁进程,或者访问其他现有进程进行通信。不难发现,在程序最先初始化的那一刻只有一个进程运行,而且应用程序进程生命周期最大进程名称集合是不变的。因此,在应用程序初始化的时候,可以根据进程关键信息检查系统进程列表是否存在同当前初始化进程匹配的进程来确定是否已经运行进程实例。
逻辑处理步骤如下,
1.初始化应用程序,启动程序初始化进程;
2.访问系统进程列表,根据初始化进程关键信息进行匹配查找;
3.没有找到匹配进程(这一步是不会发生的,因为当前初始化进程也在列表中,不过还要看获取进程列表的实现代码怎么写),继续初始化进程,程序初始化完成运行。
4.找到第一个匹配进程,判断找到的进程ID是否同初始化进程ID相同;
5.如果第一个匹配进程ID同初始化进程ID相同,则为当前初始化进程,继续查找;
6.没有找到第二个匹配进程,表明当前运行的是首个实例,继续初始化进程,程序初始化完成运行。
7.找到第二个,表明已有一个实例在运行,停止当前程序初始化,提示已有应用程序运行。
8.如果找到第一个匹配进程ID不同,表明已有一个实例在运行,停止当前程序初始化,提示已有应用程序运行。
可见上面的逻辑实现中用于进程匹配的信息是关键,选择不当功能就无法实现。在这个实例中笔者使用了应用程序完全文件名称作为关键信息。
在代码中首先需要引用下面命名空间,以调用WinAPI函数。
using System.Runtime.InteropServices;
把实现唯一运行实例功能的类名取为SingleInstance,在类前面加static关键字为C# 2.0新增的语言特征。
public static class SingleInstance {}
使用GetRunningInstance静态方法获取应用程序进程实例,如果没有匹配进程,返回Null值,
public static Process GetRunningInstance()
{
        Process currentProcess = Process.GetCurrentProcess(); //获取当前进程
        //获取当前运行程序完全限定名
        string currentFileName = currentProcess.MainModule.FileName;
        //获取进程名为ProcessName的Process数组。
        Process[] processes = Process.GetProcessesByName(currentProcess.ProcessName);
        //遍历有相同进程名称正在运行的进程
        foreach (Process process in processes)
        {
                if (process.MainModule.FileName == currentFileName)
                {
                        if (process.Id != currentProcess.Id) //根据进程ID排除当前进程
                                return process;//返回已运行的进程实例
                }
        }
        return null;
}
接下来调用两个WinAPI,其功能将在包装方法中描述,
[DllImport("User32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
定义类成员辅助变量,
private const int WS_SHOWNORMAL = 1;
以上的方法声明为私有,对其进一步包装,HandleRunningInstance静态方法为获取应用程序句柄,设置应用程序为前台运行,并返回bool值。
public static bool HandleRunningInstance(Process instance)
{
        //确保窗口没有被最小化或最大化
        ShowWindowAsync(instance.MainWindowHandle, WS_SHOWNORMAL);
        //设置为foreground window
        return SetForegroundWindow(instance.MainWindowHandle);
}
对上面的方法创建一个重载版本,使调用代码更加简洁,
public static bool HandleRunningInstance()
{
        Process p = GetRunningInstance();
        if (p != null)
        {
                HandleRunningInstance(p);
                return true;
        }
        return false;
}
上面的方法实现获取已经运行的进程实例的句柄,并获取其焦点显示到前台,这个很有用,在其他实现方式中也可以用到。
在Main函数中调用下面代码实现单一应用程序实例,
Process p = SingleInstance.GetRunningInstance();
if (p != null) //已经有应用程序副本执行
{
        SingleInstance.HandleRunningInstance(p);
}
else //启动第一个应用程序
{
        Application.Run(new MainForm());
}
简洁的调用为,
if (SingleInstance.HandleRunningInstance()== false)
{
        Application.Run(new MainForm());
}
可见,在上面的实现过程中,由于关键信息采用应用程序的完整文件名,因此在文件名称或路径名称修改后,以上实现就会失效。
进程互斥
在这个实现方式中需要定义一个进程同步基元,可以理解为临界资源,该资源只允许一个进程使用。根据这一点实现应用程序唯一运行实例就比较简单了。
实现步骤如下,
1.应用程序初始化访问该同步基元;
2.可以访问,说明该同步基元未被使用,也就是说没有应用程序实例运行,使用同步基元,可以继续初始化成为第一个运行实例。
3.不可以访问,说明该同步基元已被使用,也就是说已有应用程序实例运行,停止当前程序初始化,提示已有应用程序运行。
4.应用程序实例退出释放同步基元占用。
在代码中笔者使用System.Threading.Mutex类实现同步基元,实现应用程序实例之间互斥功能。Mutex默认名字取Assembly.GetEntryAssembly().FullName。
在类成员中声明同步基元,
private static Mutex mutex = null;
CreateMutex静态方法创建应用程序进程Mutex,返回创建结果为true表示创建成功,false失败。
public static bool CreateMutex()
{
        return CreateMutex(Assembly.GetEntryAssembly().FullName);
}
实现其重载方法,让用户可以自定义Mutex名字,
public static bool CreateMutex(string name)
{
        bool result = false;
        mutex = new Mutex(true, name, out result);
        return result;
}
对应的释放Mutex资源方法为,
public static void ReleaseMutex()
{
        if (mutex != null)
        {
                mutex.Close();
        }
}
在Main函数中调用下面代码实现单一应用程序实例,
if (SingleInstance.CreateMutex())
{
        Application.Run(new MainForm());
        SingleInstance.ReleaseMutex();
}
else
{
        MessageBox.Show(“程序已经运行!”);
}
可见,在上面的实现过程中,Mutex名字是同步基元的唯一标识,如果刚好有不同的应用程序使用了相同名称的Mutex,那不同的应用程序实例也会出现互斥现象。
运行标志
使用应用程序运行标志简单来讲就是在程序初始化的时候设置一个标志表示程序已运行,在程序运行结束的时候删除该标志。
基本步骤如下,
1.应用程序初始化检查运行标志是否已经设置;
2.发现已经设置,说明已有应用程序实例运行,停止当前程序初始化,提示已有应用程序运行。 3.发现没有设置,说明没有应用程序实例运行,继续当前程序初始化。
4.退出应用程序时删除该运行标志。
对于标志存储载体可以使用注册表、数据库或外部文件等,这里的代码使用外部文件实现。对存放标志的文件目录选择C:\Documents and Settings\All Users\Application Data,也可以是C:\Program Files\Common Files。
声明类成员标志文件名称变量,
private static string runFlagFullname = null;
初始化程序运行标志,如果设置成功,返回true,已经设置返回false,设置失败将抛出异常,
public static bool InitRunFlag()
{
        if (File.Exists(RunFlag))
        {
                return false;
        }
        using (FileStream fs = new FileStream(RunFlag, FileMode.Create))
        {
        }
        return true;
}
释放初始化程序运行标志,如果释放失败将抛出异常,
public static void DisposeRunFlag()
{
        if (File.Exists(RunFlag))
        {
                File.Delete(RunFlag);
        }
}
获取或设置程序运行标志,必须符合Windows文件命名规范,
public static string RunFlag
{
        get
        {
                if(runFlagFullname == null)
                {
                        string assemblyFullName = Assembly.GetEntryAssembly().FullName;
                        string path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
                        runFlagFullname = Path.Combine(path, assemblyFullName);
                }
                return runFlagFullname;
        }
        set
        {
                runFlagFullname = value;
        }
}
在Main函数中调用下面代码实现单一应用程序实例,
if (SingleInstance.InitRunFlag())
{
        Application.Run(new MainForm());
        SingleInstance.DisposeRunFlag();
}
else
{
        MessageBox.Show(“程序已经运行!”);
}
可见,在上面的实现过程中,需要访问文件IO,因此有可能会出现异常,对异常需要进行具体处理。如果不同应用程序使用了相同的运行标志,也会出现进程互斥实现中存在的问题。由于运行标志存在外部载体中,如果笔者把启动的应用程序进程实例直接在Windows管理器进程列表中结束或使其产生异常,那设置的运行标志就不会销毁,应用程序就没法再次运行。
功能测试
这一节对上面的三个功能进行测试,以分析之间的区别。功能测试类别包括下面五类,
1.本地系统同一应用程序目录;
2.本地系统同一应用程序修改运行文件名称使两次运行名称不同;
3.本地系统两次运行程序目录不同,不修改文件名称;
4.本地系统不同会话用户登录启动应用程序;
5.远程计算机程序访问启动应用程序(一个程序在远程另一个在本地)。
根据代码实现细节不同,对测试的结果可能会有所不同,这里的测试结果以笔者上面几节中实现的代码为准。为了测试简单化,通过给应用程序传入测试参数,决定使用哪种方式,入口函数调用代码为,
[STAThread]
static void Main(string[] args)
{
        if (args.Length == 0) //没有传送参数
        {
                Process p = SingleInstance.GetRunningInstance();
                if (p != null) //已经有应用程序副本执行
                        SingleInstance.HandleRunningInstance(p);
                else //启动第一个应用程序
                        Application.Run(new MainForm());
        }
        else //有多个参数
        {
                switch (args[0].ToLower())
                {
                        case “-api”:
                                if (SingleInstance.HandleRunningInstance() == false)
                                        Application.Run(new MainForm());
                                break;
                        case “-mutex”:
                                if (args.Length >= 2) //参数中传入互斥体名称
                                {
                                        if ( SingleInstance.CreateMutex(args[1]) )
                                        {
                                                Application.Run(new MainForm());
                                                SingleInstance.ReleaseMutex();
                                        }
                                        else
                                                //调用SingleInstance.HandleRunningInstance()方法显示到前台。
                                                MessageBox.Show(“程序已经运行!”);
                                }
                                else
                                {
                                        if (SingleInstance.CreateMutex())
                                        {
                                                Application.Run(new MainForm());
                                                SingleInstance.ReleaseMutex();
                                        }
                                        else
                                                //调用SingleInstance.HandleRunningInstance()方法显示到前台。
                                                MessageBox.Show(“程序已经运行!”);
                                }
                                break;
                        case “-flag”://使用该方式需要在程序退出时调用
                                if (args.Length >= 2) //参数中传入运行标志文件名称
                                        SingleInstance.RunFlag = args[1];
                                try
                                {
                                        if (SingleInstance.InitRunFlag())
                                        {
                                                Application.Run(new MainForm());
                                                SingleInstance.DisposeRunFlag();
                                        }
                                        else
                                                //调用SingleInstance.HandleRunningInstance()方法显示到前台。
                                                MessageBox.Show(“程序已经运行!”);
                                }
                                catch (Exception ex)
                                {
                                        MessageBox.Show(ex.ToString());
                                }
                                break;
                        default:
                                MessageBox.Show(“应用程序参数设置失败。”);
                                break;
                }
        }
}
运行CMD命令行,
第一种调用方式:
 WindowsApplication1.exe –api 或 WindowsApplication1.exe
第二种调用方式:
 WindowsApplication1.exe –mutex 或WindowsApplication1.exe –mutex {F140AE26-626C-42f8-BD49-45025742205E}
第三种调用方式:
 WindowsApplication1.exe –flag 或WindowsApplication1.exe –flag c:\blog.csdn.net.zhzuo
测试结果:
匹配/互斥/标志1同一目录2修改名称3不同目录4不同用户5远程访问
1同一目录O/O/O
2修改名称X/O/O
3不同目录X/O/O
4不同用户#/X/O
5远程访问X/O/O
备注:O – 表示成功,X – 表示失败,# – 程序第二个运行没有反应
针对远程访问的测试,需要在系统管理工具的.NET Framework 2.0 Configuration中进行设置授权该局域网路径允许访问,否则会抛出System.Security.SecurityException异常。根据测试结果可见三种实现方式适用范围不同,理想的实现是结合他们的优点进行多点判断。
Share: