找与证书存储区中的特定 X.509 证书关联的私钥文件的位置和名称可能很困难。 使用 FindPrivateKey.exe 工具可以更快地完成此过程。
X.509 证书是由计算机管理员或计算机上的任何用户安装的。 但是,该证书可以由使用其他帐户(例如 Windows XP 上的 ASPNET 或 Windows Server 2003 上的 NETWORK SERVICE 帐户)运行的服务来访问。
此帐户可能没有访问私钥文件的权限,因为证书原来并不是由它安装的。 FindPrivateKey 工具可以为您提供给定 X.509 证书的私钥文件的位置。 您可以在知道特定 X.509 证书的私钥文件的位置后立即添加或移除对此文件的权限。
使用证书确保安全性的示例在 Setup.bat 文件中使用 FindPrivateKey 工具。 找到私钥文件后,您可以使用其他工具(如 Cacls.exe)设置该文件的适当访问权限。
使用用户帐户(如自承载可执行文件)运行 Windows Communication Foundation (WCF) 服务时,请确保该用户帐户具有该文件的只读访问权限。 使用 Internet 信息服务 (IIS) 运行 WCF 服务时,该服务使用的默认帐户是 Windows XP 上的 ASPNET 或 Windows Server 2003 上的 NETWORK SERVICE,该帐户默认情况下没有私钥文件的访问权限。
示例
访问进程没有读取特权的证书时,您将看到类似如下示例的异常消息。
| |
复制代码 |
System.ArgumentException was unhandled
Message="The certificate 'CN=localhost' must have a private key that is capable of key exchange. The process must have access rights for the private key."
Source="System.ServiceModel"
|
发生这种情况时,可以使用 FindPrivateKey 工具查找私钥文件,然后为服务正在其中运行的进程设置访问权限。 例如,可以使用下面示例中所示的 Cacls.exe 工具完成此操作。
| |
复制代码 |
cacls.exe "C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys\8aeda5eb81555f14f8f9960745b5a40d_38f7de48-5ee9-452d-8a5a-92789d7110b1" /E /G "NETWORK SERVICE":R
|
生成 FindPrivateKey 项目
-
打开 Windows 资源管理器,然后定位到安装示例的目录位置下的语言特定的子目录。
-
双击 .sln 文件图标,在 Visual Studio 中打开该文件。
-
在“生成”菜单中选择“重新生成解决方案”。 客户端程序文件在 client\bin 中生成,服务程序文件在 service\bin 中生成。
-
生成解决方案将生成如下文件:FindPrivateKey.exe。
约定 - 命令行条目
“[选项]”代表一组可选参数。
“{选项}”代表一组强制参数。
“选项1 | 选项2”代表一组选项中的一个选项。
“<值>”代表要输入的参数值。
用法
| |
复制代码 |
FindPrivateKey <storeName> <storeLocation> [{ {-n <subjectName>} | {-t <thumbprint>} } [-f | -d | -a]]
|
其中:
| |
复制代码 |
<subjectName> The subject name of the certificate
<thumbprint> The thumbprint of the certificate (You can use the Certmgr.exe tool to find this)
-f output file name only
-d output directory only
-a output absolute file name
|
如果没在命令提示符中指定任何参数,将显示此帮助文本。
示例
此示例在 Current User.FindPrivateKey My CurrentUser -n "CN=localhost" 的“个人”存储区中查找主题名称为 "CN=localhost" 的证书的文件名。
此示例在 Current 的“个人”存储区中查找主题名称为 "CN=localhost" 的证书的文件名,并输出完整的目录路径。
| |
复制代码 |
User.FindPrivateKey My CurrentUser -n "CN=localhost" -a
|
此示例在“本地计算机”的“个人”存储区中查找指纹为 "03 33 98 63 d0 47 e7 48 71 33 62 64 76 5c 4c 9d 42 1d 6b 52" 的证书的文件名。
| |
复制代码 |
FindPrivateKey My LocalMachine -t "03 33 98 63 d0 47 e7 48 71 33 62 64 76 5c 4c 9d 42 1d 6b 52" –c
|
public partial class Controls_SectionIndex : System.Web.UI.UserControl
{
private NBearLite.Database Db = NBearLite.Database.Default;
protected void Page_Load(object sender, EventArgs e)
{
int sectionType = Convert.ToInt32(Request.QueryString["sectiontype"]);
rep.DataSource = ListIndex(sectionType, 0);
rep.DataBind();
}
public IEnumerable<GHSection> ListIndex(int sectionType, int parentID)
{
List<SqlGHSection> list = Db.Select(Tables.GHSection)
.Where(Tables.GHSection.SectionType == sectionType && Tables.GHSection.ParentID == parentID)
.OrderBy(Tables.GHSection.ID.Asc)
.ToList<SqlGHSection>();
foreach (GHSection node in list)
{
yield return node;
foreach (GHSection node3 in ListIndex(sectionType, node.ID))
{
yield return node3;
}
}
}
}
本页内容
问题总会有解决办法。您刚刚部署的应用程序在开发计算机上一切都好,但在生产中却出了差错。问题可能会马上显现,也可能会随着时间的推移慢慢显露。现在怎么办呢?
开发人员会匆忙推出代码的调试版本并重新部署,这种情形太常见了。加载这一特别版本时,通常会向磁盘上的某个文件中随机写入信息,以便开发人员以后进行研究,以弄清究竟发生了什么问题。除此之外一定还有更好的办法。
解决方案就在 System.Diagnostics 命名空间中,该命名空间提供了用于与事件日志、性能计数器和系统进程进行交互的类。您可以在生产应用程序中保持对此监控代码的启用,并在发生问题时查看相关信息。下面让我们看一下如何在应用程序中使用这些类。
事件日志
无数开发人员都会通过向外部文件中写入信息来调试其应用程序。除了会在硬盘上留下少量垃圾外,此方法通常需要花费很长的时间,无法进行分类或筛选,并且极少能够提供丰富数据。那么为什么还有如此多的人使用这种方法呢?原因只是他们没有意识到使用事件日志有多么轻松。
让我们从耗费时间的写入事件日志的方法入手,说明相关的概念,然后研究快速而容易的方法。首先,向您的代码中添加一个导入语句来引用 System.Diagnostics 命名空间,然后在您的错误处理例程中插入以下代码:111
‘ 如果源不存在,则创建该
‘ 源。
If Not EventLog.SourceExists("MSDNDemo") Then
EventLog.CreateEventSource("MSDNDemo", _
"CustomLog")
End If
‘ 创建一个 EventLog 实例并指定其
‘ 源。
Dim myLog As New EventLog()
myLog.Source = "MSDNDemo"
‘ 向事件日志中写入一个信息性条目。
myLog.WriteEntry("Hello from the event log.")
上面部分的代码会检查您是否已经创建了事件的源。大多数开发人员都会使用其应用程序的名称,从而轻松快速地从事件日志的众多条目中找出该应用程序的事件。
如果源不存在,则会创建源,并将其与一个事件日志相关联。您可以使用该事件日志的现有部分,比如 Application 和 System 部分,也可以通过指定名称来创建您自己的部分(就像本演示这样)。
创建源之后,向事件日志中添加事件就像创建新的 EventLog 对象并将其与源相关联一样简单。本示例中仅写入了“Hello from the event log”,但您可以向 WriteEntry 方法传递详细的信息(比如堆栈跟踪),并可将任何信息放在事件日志中。
图 1 用户在 Custom 部分中生成的事件日志条目
让此代码执行数次,您的事件日志将会显示图 1 所示的活动。您可以在 Windows® 事件查看器 (%windir%\system32\eventvwr.exe) 中进行查看,如图 1 所示。您也可以从服务器资源管理器中的 Visual Studio® 内部查看事件日志,如图 2 所示。如果您双击这些条目之一,您即可以查看详细信息和您的自定义错误消息(请参阅图 3)。
图 2 Visual Studio 内的事件日志
EventLog.WriteEntry 方法具有 10 个重载,为您提供了极大的灵活性和控制能力。其中最有用的一个重载允许您指定您要记录的事件的类型。在图 1 中请注意,每个项目都为“Information”(信息)类型。对于演示目的,这就已经足够了,但在实际情况下,您可能会有大量不同的事件。WriteEntry 方法允许您提供事件类型,比如错误、警告、信息、成功审核或失败审核:
myLog.WriteEntry("Your error here.", _
EventLogEntryType.Error)
图 3 用户生成的事件日志
下面让我们来试试使用快速而容易的方法。Visual Basic® 2005 引入了 My 对象作为快速进入框架的手段。您可以使用 My 向事件日志中快速添加事件:
My.Application.Log.WriteException( _
"Written from My", TraceEventType.Warning, _
"Additional Information")
您也可以通过 WriteEntry 方法使用 My:My.Application.Log.WriteEntry("Written from My", TraceEventType.Error)
但如果您调用此方法并查看事件日志,则可能会看不到消息,因为 My 对象允许您根据配置文件中的设置,将您的消息路由到不同的侦听程序。有关这一极其强大的功能的详细论述不属于本专栏文章的范围,但您可以在“My.Application.Log 对象”找到更多信息。
使用事件日志时应当审慎,没有比应用程序在事件日志中罗里罗嗦地写满毫无价值的消息更令人讨厌的了。如果有错误、重要警告或信息,则应记录。对条目指定类型将具有极大的帮助,因为 Windows 的事件查看器可以对大多数列进行筛选。
性能计数器
事件日志是写入消息的理想场所,但如果想要知道应用程序运行时内部发生了什么,应该怎么办呢?此时理所当然应当选择性能计数器。
许多开发人员都精通于使用 Windows 性能监视器来查看 CPU 和内存的使用情况或硬盘信息(请参阅图 4)。您知道可以使用 PerformanceCounter 类添加您自己的计数器和数据点吗?当 PerformanceCounter 和 Windows 性能监视器结合使用时,就像随时可以将 EKG 计算机与您的应用程序挂钩一样。
图 4 标准 Windows 性能工具
在讨论添加自定义计数器之前,让我们研究一下已经存在的计数器。在您的一台计算机上,打开 Visual Studio 中的服务器资源管理器,并导航到“Performance Counters”(性能计数器)。您将看到一个巨大的计数器类别列表,展开后可以显示每个类别的众多计数器(请参阅图 5)。
图 5 性能计数器
您可以在您的应用程序中使用这些系统托管的计数器。接着展开“Memory”(内存)类别,然后展开“Available Bytes”(可用字节)计数器。将该“Available Bytes”(可用字节)计数器拖动到您的应用程序中的新 Windows 窗体上。
在该窗体上,放置一个标签和一个新计时器对象,使其 Enabled 属性设置为 true。添加以下代码:
Private Sub Timer1_Tick(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Timer1.Tick
Label1.Text = Me.PerformanceCounter1.NextValue
End Sub
这是一个非常简单的示例,在实际的应用程序中您不会这样做,但它向您显示了如何向应用程序中添加基本的监控或检索系统计数器。在实际情况下,您可以会根据某个值的状态采取行动、记录此信息或执行许多操作中的任意一项操作。
很可能您打算编写一些自定义的计数器,以便监控您的应用程序在任何特定的时间的运行状况和性能。若要添加新的计数器,请在服务器资源管理器中右键单击“Performance Counters”(性能计数器)项目,然后选择“Create New Category”(创建新类别)。
在本示例中,让我们创建一个名为 TestCategory 的类别,并添加一个名为 TestCounter 的新计数器。这一次,您将通过代码访问该计数器。
首先将一个新计时器和按钮拖放到窗体上,然后向窗体中添加一个名为 counter 的私有变量:
Private counter As PerformanceCounter
在按钮单击事件中,添加以下代码:
‘初始化计数器对象
counter = New PerformanceCounter("TestCategory", "TestCounter", False)
‘ 将其值设置为 0
counter.RawValue = 0
‘ 启动时间以更新该计数器
Timer2.Enabled = True
在计时器事件中,添加以下代码:
Counter.IncrementBy(New Random().Next(-5, 10))
这会导致该计数器按照 -5 到 10 之间的某个随机数递增。本示例使用了随机数,但这个数字还可以是已处理的销售或订单数、登录的用户数、记录的错误数或在您的应用程序中进行跟踪的任何有意义的事物。
在启动该示例应用程序之前,请从管理工具下加载性能监视器。这一次,请单击 + 号来添加一个新计数器。在“Performance Object”(性能对象)下,您现在会看到您新创建的 TestCategory。您可以从下拉列表中选择该对象,并从计数器列表中选择 TestCounter。
在性能监视器运行的情况下,单击示例应用程序的按钮。您应该可以看到一个类似于图 6 的图形。
您在示例代码中可以看到使用了 IncrementBy 方法来增加或减少计数器的数值。如果您想要使计数器以 1 为增减幅度增加或减少,只需调用 Increment 和 Decrement 方法即可。
图 6 性能监视器中显示的 TestCounter 结果
对于像本示例这样的使用服务器资源管理器的情形,一些开发人员会想是否可以通过代码完成所有操作。是的,您可以编写如下代码:
If Not
PerformanceCounterCategory.Exists("MSDNSample")
Then
PerformanceCounterCategory.Create("MSDNSample", _
"用于显示如何添加自定义计数器", _
PerformanceCounterCategoryType.SingleInstance, _
"MSDNSampleCounter", _
"用于显示如何添加自定义计数器")
End If
并将其添加到您的按钮单击事件中,以便完全通过代码创建不同的类别和计数器。如果您更新其他代码,使其指向这个类别和计数器,您将发现该项目可以完全像该示例一样运行和工作。
Process 类
本专栏文章至此已经让您了解了如何使用事件日志和性能计数器;下面让我们深入研究 System.Diagnostics 命名空间的第三个方面,即 Process 类。
首先,让我们定义一个进程:进程就是一个运行的应用程序。在具有 Process 类和正确权限的情况下,您可以访问有关用户系统中各个进程的信息,并执行诸如启动或中止这样的操作。
Process 类可以让您访问大量有关应用程序或其他进程的数据。对于此处的示例,让我们提升示例应用程序的基本优先级。
下面是具体方案。您的应用程序正运行于一台服务器上;该应用程序是该服务器上运行的主要应用程序和功能,但由于后台任务占用了过多的处理器时间,该应用程序性能不佳。您可以执行以下两种操作之一:降低后台任务的优先级,或提升您的任务的优先级。(在大多数情况下,您不需要调整某个进程的优先级,因为 Windows 可以很好地运行多个进程并换入和换出各个进程。)
为了使用 Process 类,您首先需要获取对您的进程或其他进程的引用。由于您想要提升当前进程,因此您将使用:
Dim myProcess As Process = _
Process.GetCurrentProcess()
若要访问您的本地计算机或远程计算机上的其他进程,您需要改用 GetProcessessByName 方法:
Dim theProcesses() As Process = _
myprocess.GetProcessesByName( _
"ProcessName", "MachineName")
获得了进程引用后,您可以使用其属性找到有关该进程的大量信息。有关属性的完整列表,请参阅“.NET Framework Class Library Process Members”(英文)。
提升优先级仅需要一行代码:
myProcess.PriorityClass = ProcessPriorityClass.RealTime
您已将应用程序提升为 RealTime。这是您在 OS 中可以设置的最高级别,它可以使应用程序的优先级高于几乎任何其他进程。如果您的应用程序占用所有系统资源,则可能会导致问题。启动任务管理器,以确定 OS 能够看到已提升的该应用程序,如图 7 所示。
图 7 提升至 RealTime
您可以使用 process 命令执行大量常见任务。现在假设您想要以最小化方式启动 Microsoft Internet Explorer®,并导航到 Visual Basic Developer Center。您只需使用以下代码即可:
Dim startInfo As New ProcessStartInfo("IExplore.exe")
startInfo.WindowStyle = ProcessWindowStyle.Minimized
startInfo.Arguments = "msdn.microsoft.com/vbasic"
Process.Start(startInfo)
Process 类还适合于打印。
Dim myDocumentsPath As String = _
Environment.GetFolderPath(Environment.SpecialFolder.Personal)
myProcess.StartInfo.FileName = myDocumentsPath + "\MyFile.doc"
myProcess.StartInfo.Verb = "Print"
myProcess.StartInfo.CreateNoWindow = True
myProcess.Start()
您可以看到,System.Diagnostics 命名空间有许多用途:从向事件日志中记录事件到读写性能计数器,再到处理系统进程。这是一组功能异常强大、用途极为广泛的工具,每个开发人员都应熟悉并在工作中使用。
请将您的问题和意见发送至 basics@microsoft.com。
Brad McCabe 是 Microsoft 的一位项目经理,除了其他事务外,他还负责 MSDN 上的 Visual Basic Developer Center。
本文摘自 2006 年 7 月出版的《MSDN Magazine》。
© 2006 Microsoft Corporation 版权所有。保留所有权利。使用规定。
public string ConStr = ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString;
protected void Page_Load(object sender, EventArgs e)
...{
if (!IsPostBack)
...{
BindData();
//BindMultiData();
//BindMultiDataArray();
//BindDataWithCallBack();
}
}
//异步查询的poll方式,最普通的方式
private void BindData()
...{
string QueryStr = "SELECT * FROM customers";
using (SqlConnection Con = new SqlConnection(ConStr))
...{
SqlCommand Cmd = new SqlCommand(QueryStr, Con);
IAsyncResult ASynResult;
SqlDataReader Da;
try
...{
Con.Open();
//begin方法返回一个IAsyncResult对象,用来检查执行是否完成
ASynResult = Cmd.BeginExecuteReader(CommandBehavior.CloseConnection);
while (!ASynResult.IsCompleted)
...{
Response.Write("异步查询</br>");
ASynResult.AsyncWaitHandle.WaitOne(3000, true);
//System.Threading.Thread.Sleep(10);
}
Da = Cmd.EndExecuteReader(ASynResult);
GridView1.DataSource = Da;
GridView1.DataBind();
}
catch (Exception ex)
...{
Response.Write(ex.Message);
}
}
}
//异步查询的wait方式,使用多个等待句柄来异步查询,必须等待所有进程完成处理结果集
private void BindMultiData()
...{
string CusQueryStr = "SELECT * FROM customers WHERE CompanyName = 'Alfreds Futterkiste'";
string SupQueryStr = "SELECT Customers.CompanyName, Customers.ContactName, " +
"Orders.OrderID, Orders.OrderDate, " +
"Orders.RequiredDate, Orders.ShippedDate " +
"FROM Orders, Customers " +
"WHERE Orders.CustomerID = Customers.CustomerID " +
"AND Customers.CompanyName = 'Alfreds Futterkiste' " +
"ORDER BY Customers.CompanyName, Customers.ContactName";
using (SqlConnection MyCon = new SqlConnection(ConStr))
...{
SqlCommand CusCmd = new SqlCommand(CusQueryStr, MyCon);
SqlCommand SupCmd = new SqlCommand(SupQueryStr, MyCon);
SqlDataReader CusDr;
SqlDataReader SupDr;
IAsyncResult CusIsynResult;
IAsyncResult SupIsynResult;
//创建句柄数组
System.Threading.WaitHandle[] WHandles = new System.Threading.WaitHandle[2];
System.Threading.WaitHandle CusHandle;
System.Threading.WaitHandle SupHandle;
MyCon.Open();
CusIsynResult = CusCmd.BeginExecuteReader(CommandBehavior.CloseConnection);
SupIsynResult = SupCmd.BeginExecuteReader(CommandBehavior.CloseConnection);
CusHandle = CusIsynResult.AsyncWaitHandle;
SupHandle = SupIsynResult.AsyncWaitHandle;
//将等待句柄赋给句柄数组
WHandles[0] = CusHandle;
WHandles[1] = SupHandle;
//将数组传给waitall方法,等待所以的异步查询完成
System.Threading.WaitHandle.WaitAll(WHandles);
CusDr = CusCmd.EndExecuteReader(CusIsynResult);
SupDr = SupCmd.EndExecuteReader(SupIsynResult);
GridView1.DataSource = CusDr;
GridView1.DataBind();
GridView2.DataSource = SupDr;
GridView2.DataBind();
MyCon.Dispose();
CusCmd.Dispose();
SupCmd.Dispose();
}
}
//采用WaitAny方式,优点是不用等待所有进程都完成才处理结果集
private void BindMultiDataArray()
...{
string CusQueryStr = "SELECT * FROM customers WHERE CompanyName = 'Alfreds Futterkiste'";
string SupQueryStr = "SELECT Customers.CompanyName, Customers.ContactName, " +
"Orders.OrderID, Orders.OrderDate, " +
"Orders.RequiredDate, Orders.ShippedDate " +
"FROM Orders, Customers " +
"WHERE Orders.CustomerID = Customers.CustomerID " +
"AND Customers.CompanyName = 'Alfreds Futterkiste' " +
"ORDER BY Customers.CompanyName, Customers.ContactName";
using (SqlConnection MyCon = new SqlConnection(ConStr))
...{
SqlCommand CusCmd = new SqlCommand(CusQueryStr, MyCon);
SqlCommand SupCmd = new SqlCommand(SupQueryStr, MyCon);
SqlDataReader CusDr;
SqlDataReader SupDr;
IAsyncResult CusIsynResult;
IAsyncResult SupIsynResult;
System.Threading.WaitHandle[] WHandles = new System.Threading.WaitHandle[2];
System.Threading.WaitHandle CusHandle;
System.Threading.WaitHandle SupHandle;
int WHindex;
MyCon.Open();
CusIsynResult = CusCmd.BeginExecuteReader(CommandBehavior.CloseConnection);
SupIsynResult = SupCmd.BeginExecuteReader(CommandBehavior.CloseConnection);
CusHandle = CusIsynResult.AsyncWaitHandle;
SupHandle = SupIsynResult.AsyncWaitHandle;
WHandles[0] = CusHandle;
WHandles[1] = SupHandle;
for (int i = 0; i < WHandles.Length; i++)
...{
//waitany好处在于不必等待所有异步操作完成
WHindex = System.Threading.WaitHandle.WaitAny(WHandles);
switch (WHindex)
...{
case 0:
CusDr = CusCmd.EndExecuteReader(CusIsynResult);
GridView1.DataSource = CusDr;
GridView1.DataBind();
break;
case 1:
SupDr = SupCmd.EndExecuteReader(SupIsynResult);
GridView2.DataSource = SupDr;
GridView2.DataBind();
break;
}
}
MyCon.Dispose();
CusCmd.Dispose();
SupCmd.Dispose();
}
}
//通过回调来实现异步查询
private void BindDataWithCallBack()
...{
string SupQueryStr = "SELECT Customers.CompanyName, Customers.ContactName, " +
"Orders.OrderID, Orders.OrderDate, " +
"Orders.RequiredDate, Orders.ShippedDate " +
"FROM Orders, Customers " +
"WHERE Orders.CustomerID = Customers.CustomerID " +
"AND Customers.CompanyName = 'Alfreds Futterkiste' " +
"ORDER BY Customers.CompanyName, Customers.ContactName";
using (SqlConnection MyCon = new SqlConnection(ConStr))
...{
SqlCommand SupCmd = new SqlCommand(SupQueryStr, MyCon);
SqlDataReader SupDr;
IAsyncResult SupIsynResult;
MyCon.Open();
AsyncCallback Callback = new AsyncCallback(CallBackMethod);
SupIsynResult = SupCmd.BeginExecuteReader(Callback, SupCmd,CommandBehavior.CloseConnection);
System.Threading.Thread.Sleep(100);
MyCon.Dispose();
SupCmd.Dispose();
}
}
//回调方法
public void CallBackMethod(IAsyncResult IResult)
...{
SqlCommand Command = (SqlCommand)IResult.AsyncState;
SqlDataReader dr = Command.EndExecuteReader(IResult);
GridView1.DataSource = dr;
GridView1.DataBind();
}
}
本文来自: 中国自学编程网(www.zxbc.cn) 详细出处参考:http://www.zxbc.cn/html/adonet/1713241149778_2.html
永不言败:高级程序员,网站架构师。从事软件开发多年,,承接B/S架构相关项目。有意者请联系QQ:20028205