摘要: 在工作中遇到些数据库数据查询效率问题,现在想请教下各位"老鸟"们.怎么写去优化的查询SQL语句.比如:二张都是2到3千万数据的表(A_Tabel 和 B_Table)A_Table 有字段 add_time(日期类型);msgid(varchar型)B_Table 有字段 stat(varchar型);msgid(varchar型)Select *From A_TableWhere add_tim...阅读全文
posted @ 2007-07-06 17:27 小罗 阅读(943) 评论(1) 编辑
摘要: SQL 2000 是服务器端, SQL 2005 是客户端. SQL2005用的不熟. 主要不会设置SQL 2000的服务器,配置用户,权限都不太清楚, 望多指教.阅读全文
posted @ 2007-07-05 13:17 小罗 阅读(463) 评论(6) 编辑
摘要: 找到一段程序,看到委托操作的地方不太理解,希望朋友们多多指教。特别是委托操作的执行流程或处理机制不理解。程序如下 :Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->1usingSystem;2usingSystem.Collections.Generi...阅读全文
posted @ 2007-05-21 23:12 小罗 阅读(327) 评论(9) 编辑
  1,CertOpenSystemStore打开系统最常用的证书存储区域。假如需要满足复杂的需要,请看CertOpenStore
  HCERTSTORE WINAPI CertOpenSystemStore(
  HCRYPTPROV hprov, //CSP句柄,NULL为默认句柄,或者由CryptAcquireContext返回
  LPTCSTR szSubsystemProtocol //打开的系统存储区的名字。假如名字不为CA,MY,ROOT,SPC则新建一个证书存储区域,可以使用CertEnumSystemStore列出所有的已存在的系统存储区
  );
  2,CertOpenStore使用一个指定的存储区提供类型来打开一个证书存储区。
  HCERTSTORE WINAPI CertOpenStore(
  LPCSTR lpszStoreProvider, //指定提供的存储的类型,大量类型,请参见MSDN
  DWORD dwMsgAndCertEncodingType, //只有当上一参数为CERT_STORE_PROV_MSG, CERT_STORE_PROV_PKCS7, or CERT_STORE_PROV_FILENAME才使用,否则必须为0
  HCRYPTPROV hCryptProv, //密码算法提供方的句柄,推荐使用默认,将参数置为NULL
  DWORD dwFlags, //它由高字节和低字节组成,参数众多。
  const void* pvPara //void类型,可以指向任意类型数据。
  );
  例如:if(hSysStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,0,NULL,CERT_SYSTEM_STORE_CURRENT_USER,L"MY")打开系统的MY存储区3,CertCloseStore关闭打开的证书存储区句柄
  BOOL WINAPI CertCloseStore(
  HCERTSTORE hCertStore, //证书存储区句柄
  DWORD dwFlags
  );
  第二个参数一般为0,关闭了存储区,但是里面内容并没有释放
  假如为CERT_CLOSE_STORE_CHECK_FLAG,检查没有释放空间的证书,CRL和CTL内容。他将返回一个错误报告。一般只用用于程序的诊断工具。
  假如为CERT_CLOSE_STORE_FORCE_FLAG,则强制释放相关空间。他只在存储区打开并且存储区以及其相关内容没有被别的程序调用的时候使用才安全的。
  4,CertDuplicateStore复制存储区,其reference count 也将增一,返回一个存储区的句柄
  HCERTSTORE WINAPI CertDuplicateStore(
  HCERTSTORE hCertStore
  );
  5,CertFindCertificateInStore从证书存储区中寻找符合指定要求的证书内容
  PCCERT_CONTEXT WINAPI CertFindCertificateInStore(
  HCERTSTORE hCertStore, //打开的证书存储区的句柄
  DWORD dwCertEncodingType, //指定编码的类型,一般为X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
  DWORD dwFindFlags, //用来修改查找规则,一般都不用并且把它设置为0
  DWORD dwFindType, //指定的查找类型,类型中多,例如CERT_FIND_CERT_ID
  const void* pvFindPara, //需要查找的内容
  PCCERT_CONTEXT pPrevCertContext //返回最近一次返回的CERT_CONTEXT结构。
  );
  例如:CertFindCertificateInStore(hSystemStore,MY_ENCODING_TYPE,0,CERT_FIND_SUBJECT_STR,L"Cert_subject_1",NULL);
  6,CertCreateCertificateContext从一个编码证书创建一个证书内容
  PCCERT_CONTEXT WINAPI CertCreateCertificateContext(
  DWORD dwCertEncodingType, //指定编码的类型,一般为X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
  const BYTE* pbCertEncoded, //包含编码证书内容的指针
  DWORD cbCertEncoded //编码证书数据的长度
  );
  7,CertAddCertificateContextToStore将一个证书内容增加到证书存储区中。
  BOOL WINAPI CertAddCertificateContextToStore(
  HCERTSTORE hCertStore, //证书存储区的句柄
  PCCERT_CONTEXT pCertContext, //CERT_CONTEXT结构指针
  DWORD dwAddDisposition, //指定与已存在相同证书的操作,例如CERT_STORE_ADD_ALWAYS
  PCCERT_CONTEXT* ppStoreContext //一个证书的复制的指针,可以为NULL,说明不需要返回证书的复制。
  );
  8,CertAddCertificateLinkToStore增加一个从一个证书存储区中证书内容到另一个证书存储区的连接
  BOOL WINAPI CertAddCertificateLinkToStore(
  HCERTSTORE hCertStore, //需要被增加的证书存储区的句柄
  PCCERT_CONTEXT pCertContext, //CERT_CONTEXT结构指针
  DWORD dwAddDisposition, //指定与已存在相同证书的操作,例如CERT_STORE_ADD_ALWAYS
  PCCERT_CONTEXT* ppStoreContext //一个连接的复制的指针,可以为NULL,说明不需要返回证书的复制。
  );
  9, CertEnumCertificatesInStore从证书存储区中获取第一或者下一个证书。使用循环,他可以获取存储区所有的证书序列
  PCCERT_CONTEXT WINAPI CertEnumCertificatesInStore(
  HCERTSTORE hCertStore, //证书存储区的句柄
  PCCERT_CONTEXT pPrevCertContext //上一个证书内容的CERT_CONTEXT结构指针,当第一次开始列举时参数必须为NULL
  );
  10,CryptUIDlgViewContext显示一个证书,CTL或者CRL内容
  BOOL WINAPI CryptUIDlgViewContext(
  DWORD dwContextType, //表示指向何种类型。参数有CERT_STORE_CERTIFICATE_CONTEXT,CERT_STORE_CRL_CONTEXT,CERT_STORE_CTL_CONTEXT
  const void* pvContext, //要显示的证书,CRL或者CTL内容
  HWND hwnd, //需要显示的窗口句柄,当为NULL的时候默认为桌面窗口
  LPCWSTR pwszTitle, //显示的标题名,当为NULL时默认为内容类型
  DWORD dwFlags, //暂时没被使用,并且必须为0
  void* pvReserved //保留字,为将来使用。
  );
  例如:
  CryptUIDlgViewContext(CERT_STORE_CERTIFICATE_CONTEXT,pCertContext,NULL,NULL,0,NULL);
  11,CertGetNameString获取CERT_CONTEXT结构中的主题或者签发者,并且将他转化为以null结尾的字符串
  DWORD WINAPI CertGetNameString(
  PCCERT_CONTEXT pCertContext, //包含CERT_CONTEXT证书内容的指针
  DWORD dwType, //指定如何找到名字,并且输出的格式,例如CERT_NAME_ATTR_TYPE
  DWORD dwFlags, //指定需要处理的类型,CERT_NAME_ISSUER_FLAG请求签发者名字,不设置则代表主题名
  void* pvTypePara, //或者是一个包含dwStrType的字节指针,或者是一个指定名字属性的对象标志符指针
  LPTSTR pszNameString, //转换后返回的字符串指针
  DWORD cchNameString //上一字符串长度,以NULL结尾
  );
  如:CertGetNameString(pCertContext,CERT_NAME_SIMPLE_DISPLAY_TYPE,0,NULL,pszNameString,128)
  12,CertEnumCertificateContextProperties获取与证书内容相关的第一个或者下一个扩展属性
  DWORD WINAPI CertEnumCertificateContextProperties(
  PCCERT_CONTEXT pCertContext, //CERT_CONTEXT结构指针
  DWORD dwPropId //Property number of the last property enumerated. 参数为0时,获取第一个属性
  );
  13,CertGetCertificateContextProperty获取每个扩展属性的信息。
  BOOL WINAPI CertGetCertificateContextProperty(
  PCCERT_CONTEXT pCertContext, //CERT_CONTEXT指针
  DWORD dwPropId, //需要获取的属性,参数众多,例如CERT_ACCESS_STATE_PROP_ID
  void* pvData, //接收返回的数据指针
  DWORD* pcbData //数据长度指针
  );
  例如:CertGetCertificateContextProperty(pCertContext, dwPropId , NULL, &cbData)
  14,CryptUIDlgSelectCertificateFromStore显示一个对话框,允许在其中选择指定的证书。
  PCCERT_CONTEXT WINAPI CryptUIDlgSelectCertificateFromStore(
  HCERTSTORE hCertStore, //被查找的证书存储区句柄
  HWND hwnd, //要显示的窗口句柄,NULL默认为桌面窗口
  LPCWSTR pwszTitle, //对话框标题,NULL默认为“Select Certificate”
  LPCWSTR pwszDisplayString, //在选中的对话框中显示的语句,NULL默认为"Select a certificate you wish to use"
  DWORD dwDontUseColumn, //排除一些显示的列,例如CRYPTUI_SELECT_ISSUEDTO_COLUMN不显示ISSUEDTO信息
  DWORD dwFlags, //暂时设置为0
  void* pvReserved //保留字,还未使用。
  );
  15,CertCompareCertificateName比较两个证书CERT_NAME_BLOB结构是否一致,成功返回true,否则返回false;
  BOOL WINAPI CertCompareCertificateName(
  DWORD dwCertEncodingType, //指定编码的类型,一般为X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
  PCERT_NAME_BLOB pCertName1, //CERT_NAME_BLOB指针
  PCERT_NAME_BLOB pCertName2
  );
  16,BOOL WINAPI CertComparePublicKeyInfo(比较公钥信息。
  DWORD dwCertEncodingType,
  PCERT_PUBLIC_KEY_INFO pPublicKey1,
  PCERT_PUBLIC_KEY_INFO pPublicKey2
  );
  17,BOOL WINAPI CertCompareCertificate(比较两个证书
  DWORD dwCertEncodingType,
  PCERT_INFO pCertId1,
  PCERT_INFO pCertId2
  );
  18,BOOL WINAPI CertDeleteCertificateFromStore(从一个证书存储区中删除一个证书。
  PCCERT_CONTEXT pCertContext
  );
  19,BOOL WINAPI CertSerializeCertificateStoreElement(serializes a certificate context's encoded certificate
  and its encoded properties
  PCCERT_CONTEXT pCertContext, //CERT_CONTEXT指针
  DWORD dwFlags, //保留字,为将来使用,设置为0
  BYTE* pbElement, //Pointer to a buffer that receives the serialized output, including the
  encoded certificate and possibly its properties.
  DWORD* pcbElement //上一参数的数据长度。
  );
  20,CertAddEncodedCertificateToStore从编码证书中创建一个证书内容并且把他加入证书存储区。
  BOOL WINAPI CertAddEncodedCertificateToStore(
  HCERTSTORE hCertStore, //证书存储区句柄
  DWORD dwCertEncodingType, //指定编码的类型,一般为X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
  const BYTE* pbCertEncoded, //编码证书指针
  DWORD cbCertEncoded, //编码证书长度
  DWORD dwAddDisposition, //指定当已存在该证书时的操作。例如CERT_STORE_ADD_ALWAYS
  PCCERT_CONTEXT* ppCertContext//证书内容的指针。可以为NULL。
  );
  例如:CertAddEncodedCertificateToStore(hFileStore,MY_ENCODING_TYPE,pCertContext->pbCertEncoded,pCertContext-
  >cbCertEncoded,CERT_STORE_ADD_USE_EXISTING,NULL)
  21, CertAddEnhancedKeyUsageIdentifier 增加一个增强密钥用途
  BOOL WINAPI CertAddEnhancedKeyUsageIdentifier(
  PCCERT_CONTEXT pCertContext,//Cert_context结构。
  LPCSTR pszUsageIdentifier //指定需要增加的OID
  );
  22,CertSetCertificateContextProperty设置指定的证书内容的扩展属性
  BOOL WINAPI CertSetCertificateContextProperty(
  PCCERT_CONTEXT pCertContext,
  DWORD dwPropId,
  DWORD dwFlags,
  const void* pvData
  );
  22,CertNameToStr 转换编码后名字到以NULL结尾的字符串中。
  DWORD WINAPI CertNameToStr(
  DWORD dwCertEncodingType,
  PCERT_NAME_BLOB pName,
  DWORD dwStrType,
  LPTSTR psz,
  DWORD csz
  );
  23,CertStrToName转化一个以NULL结尾的字符串到证书的名字
  BOOL WINAPI CertStrToName(
  DWORD dwCertEncodingType,
  LPCTSTR pszX500,
  DWORD dwStrType,
  void* pvReserved,
  BYTE* pbEncoded,
  DWORD* pcbEncoded,
  LPCTSTR* ppszError
  );

转自 Sagely's blog
posted @ 2009-09-09 14:30 小罗 阅读(213) 评论(0) 编辑

作者:Michael Bright
原文:http://www.csharphelp.com/archives2/archive439.html

本文将着重介绍Win32 API库中涉及网络管理的函数。首先我要讲一讲在.Net框架中管理用户的两个方法,第一种是Active Directory方法,这种方法要求你安装Active Directory。如果你打算管理一个小网络上的用户,或者一个未安装Active Directory的独立工作站,为了管理用户而安装Active Directory显得有些不值得。另外一种方法则是这篇文章所要讲到的——使用Win32 API库函数。在这篇文章中,我将介绍如何使用C# 添加、删除和修改用户和组,以及如何查询一个主机或网络的用户和网络信息。我们将用到以下函数

NetUserAdd
NetUserDel
NetUserGetInfo
NetUserSetInfo
NetUserChangePassword
NetUserEnum
NetUserGetLocalGroups
初始化

首先,正如许多C#开发者都知道的,我们要引入InteropServices名字空间到我们的工程中,以便能够访问dll中的函数。这可以通过如下的代码片断实现:


///// 代码片断 1.0

using System.Runtime.InteropServices;

//// 代码结束


一旦我们拥有了访问权限,我们就可以将dll中的函数声明引入,我们将使用方法和结构体来关联它们。函数调用将在下面讨论:

使用C#添加一个用户

在网络函数中最重要的操作之一就是向一个网络或计算机添加一个用户。要通过C#添加一个用户,我们需要使用NetAddUser函数,该函数允许我们添加用户到特定的计算机,如果我们将servername置空,用户将被添加到本地计算机。在下面的代码片断中,我们将看到如何声明和使用 NetUserAdd函数。我们在使用该函数前,需要定义一个结构体USER_INFO_1,来作为NetUserAdd的参数。

///// 代码片断 1.1 声明 

[DllImport("Netapi32.dll")]
extern static int NetUserAdd([MarshalAs(UnmanagedType.LPWStr)] string servername, int level, ref USER_INFO_1 buf, int parm_err); //// 代码结束

在使用代码时,你要注意到一点——尽管使用了最后一个int参数,你不需要知道返回的错误值。如果你一定要了解错误,需要修改代码。既然我们已经声明了我们要使用的外部API,我们应该声明结构体了。
/**////// 代码片断 1.2 结构体声明
 
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct USER_INFO_1
{
public string usri1_name; 
public string usri1_password; 
public int usri1_password_age; 
public int usri1_priv; 
public string usri1_home_dir; 
public string comment; 
public int usri1_flags; 
public string usri1_script_path;

 
/**///// 代码结束
 

 

 

 

 

 


在声明之后,我们就可以在我们的程序中调用该函数。下面是NetUserAdd的如何使用的代码:
/**////// 代码片断 1.3 NetUserAdd
 
USER_INFO_1 NewUser = new USER_INFO_1(); // Create an new instance of the USER_INFO_1 struct
 
NewUser.usri1_name = "UserTestOne"; // Allocates the username
NewUser.usri1_password = "password"; // allocates the password
NewUser.usri1_priv = 1; // Sets the account type to USER_PRIV_USER
NewUser.usri1_home_dir = null; // We didn't supply a Home Directory
NewUser.comment = "My First User Made through C#"; // Comment on the User
NewUser.usri1_script_path = null; // We didn't supply a Logon Script Path
 
if(NetUserAdd(null ,1 ,ref NewUser, 0)!=0) // If the call fails we get a non-zero value
{
MessageBox.Show("Error Adding User","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
/**///// 代码结束
 

上面的代码将向当前的本地计算机添加用户,但是正如我说的,如果你想向网络上的另一台计算机添加用户,你可以将第一个参数中的null替换为那台计算机的名称。

使用C#删除一个用户

与前面的函数相比,删除用户的函数要简单得多。在上面的代码中,如果添加用户失败,返回非零值。与上面的代码相同,删除用户函数也是如此。要从本地计算机删除用户,你可以使用下面的代码片断。
/**////// 代码片断 1.4 声明
 
[DllImport("Netapi32.dll")]
extern static int NetUserDel([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string username);
 
/**///// 代码结束
NetUserDel的调用代码如下:
/**////// 代码片断 1.5 NetUserDel
 
if(NetUserDel(null ,"UserTestOne")!=0) // If the call fails we get a non-zero value
{
MessageBox.Show("Error Removing User","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);

 
/**///// 代码结束
 
与NetUserAdd的调用一样,如果想删除远程计算机上的用户,需要将第一个参数替换为远程计算机名。

使用C#获得并修改用户信息

要在C#中获得用户信息,我们需要调用NetUserGetInfo,这个调用需要使用一个结构体来管理数据,并且用户信息会返回到结构体中。与 NetUserGetInfo相关的函数是NetUserSetInfo,你可以使用该函数修改你获得的用户信息。要注意的是,这两个函数相互依赖,例如使用NetUserSetInfo函数,你必须知道用户的权限级别(privilege level),权限级别是通过NetUserGetInfo函数获得的。在下面的代码片断中,我们将看到这两个函数的声明,另外我们将再次使用 USER_INFO_1结构体。

/**////// 代码片断 1.6 声明
 
[DllImport("Netapi32.dll")]
extern static int NetUserGetInfo([MarshalAs(UnmanagedType.LPWStr)] string servername,[MarshalAs(UnmanagedType.LPWStr)] string username,int level,out IntPtr bufptr);
 
[DllImport("Netapi32.dll")]
extern static int NetUserSetInfo([MarshalAs(UnmanagedType.LPWStr)] string servername,[MarshalAs(UnmanagedType.LPWStr)] string username,int level,ref USER_INFO_1 buf, int error);
 
/**///// 代码结束

使用这些声明,我们可以十分轻松地获得和修改用户设置。在下面的代码中,我们将获得我们先前添加的用户UserTestOne的用户信息,并且我们将修改一些用户信息。

/**////// 代码片断 1.7 NetUserGetInfo
 
IntPtr bufPtr;
USER_INFO_1 User = new USER_INFO_1();
if(NetUserGetInfo(null, "Administrator",1,out bufPtr)!=0)
{
MessageBox.Show("Error Getting User Info","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
User = (USER_INFO_1)Marshal.PtrToStructure(bufPtr, typeof(USER_INFO_1)); 
MessageBox.Show("Users Name: " + User.usri1_name + " Users Comments: " + User.comment + " Users Privilege Level: " + User.usri1_priv);
 
/**///// 代码结束

在这个例子中,我们使用了Marshaling来获得数据,这是我唯一找到的有效方法。

/**////// 代码片断 1.8 NetUsetSetInfo
 
USER_INFO_1 Update = new USER_INFO_1();
Update.comment = "This is Our C# Updated Comment";
Update.usri1_priv = 2; // Note that this can only be obtained programmatically using NetUserGetInfo
if(NetUserSetInfo(null, "UserTestOne",1,ref Update,0)!=0)
{
MessageBox.Show("Error Setting User Info","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
 
/**///// 代码结束
我们来总结一下,NetUserSetInfo依靠NetUserGetInfo正常工作。与其他的网络函数一样,如果你修改第一个参数为计算机名称,你将可以远程使用该函数。

使用c#修改用户密码

另外一个网络管理的重要函数是用来修改密码的。函数NetUserChangePassword是在我们能够提供当前用户的原密码的基础上进行工作的。声明NetUserChangePassword可以使用如下代码片断:
/**////// 代码片断 1.9 声明
 
[DllImport("Netapi32.dll")]
extern static int NetUserChangePassword([MarshalAs(UnmanagedType.LPWStr)] string domainname,[MarshalAs(UnmanagedType.LPWStr)] string username,[MarshalAs(UnmanagedType.LPWStr)] string oldpassword,[MarshalAs(UnmanagedType.LPWStr)] string newpassword);
 
/**///// 代码结束 
 
使用以上声明,如果我们知道用户原密码的话,我们就可以修改用户的密码。同样,我们可以远程使用该函数,只需要将null参数改为特定计算机的名称即可。

/**////// 代码片断 2.0 NetUserChangePassword
 
if(NetUserChangePassword(null, "UserTestOne", "password", "ournewpassword")!=0)
{
MessageBox.Show("Error Changing User Password","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
 
/**///// 代码结束 

获得用户列表 

在管理一个网络时,有一份准确的网络用户列表或计算机列表是很重要的。如果要获得这样一份列表,我们必须使用NetUserEnum函数,该函数返回用户的名称和相关数据到一个结构体。在这个例子中,我们打算使用USER_INFO_0结构体来传递值。由于我们只需要获得用户名,于是我就将 username元素添加到结构体的声明中。要注意的是,这个函数使用网络缓冲(network buffer),该缓冲必须被释放,以节省资源,我们可以使用NetAPIBufferFree函数。一旦我们进行了声明,我们就可以使用如下代码片断获得用户列表。

/**////// 代码片断 2.1 声明
 
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct USER_INFO_0
{
public String Username;
}
 
[DllImport("Netapi32.dll")]
extern static int NetUserEnum([MarshalAs(UnmanagedType.LPWStr)] string servername, int level, int filter, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out int resume_handle);
 
[DllImport("Netapi32.dll")]
extern static int NetApiBufferFree(IntPtr Buffer);
 
/**///// 代码结束
 
一旦我们进行了声明,我们就可以使用如下代码片断获得用户列表。

/**////// 代码片断 2.2 NetUserEnum
 
int EntriesRead;
int TotalEntries;
int Resume;
IntPtr bufPtr;
 
NetUserEnum(null, 0, 2, out bufPtr, -1, out EntriesRead, out TotalEntries, out Resume);
 
if(EntriesRead> 0)
{
USER_INFO_0[] Users = new USER_INFO_0[EntriesRead];
IntPtr iter = bufPtr;
for(int i=0; i < EntriesRead; i++)
{
Users[i] = (USER_INFO_0)Marshal.PtrToStructure(iter, typeof(USER_INFO_0)); 
iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(USER_INFO_0)));
MessageBox.Show(Users[i].Username);
}
NetworkAPI.NetApiBufferFree(bufPtr);
}
/**///// 代码结束
上面的代码通过MessageBox来枚举显示本地计算机上的用户。我们也可以替换null字符串参数指定一台远程计算机。 

识别用户组的关系

在这篇文章中我们要了解的最后一个函数是NetUserGetLocalGroups。这个函数允许我们判断一个用户属于哪些组,并且显示这些组。与前面的函数相同,在使用该函数后,我们要清除网络缓冲。NetUserGetLocalGroups的声明同样需要一个结构体 LOCALGROUP_USERS_INFO_0,用于返回组名称。 

/**////// 代码片断 2.3 声明
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct LOCALGROUP_USERS_INFO_0
{
public string groupname;
}
 
[DllImport("Netapi32.dll")]
public extern static int NetUserGetLocalGroups([MarshalAs(UnmanagedType.LPWStr)] string servername,[MarshalAs(UnmanagedType.LPWStr)] string username, int level, int flags, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries);
/**///// 代码结束
通过上面的声明,我们可以使用与前面的函数类似的代码调用该函数。

/**////// 代码片断 2.4 NetUserGetLocalGroups
int EntriesRead;
int TotalEntries;
IntPtr bufPtr;
 
NetUserGetLocalGroups(null, "Administrator",0,0,out bufPtr,1024,out EntriesRead, out TotalEntries);
 
if(EntriesRead> 0)
{
LOCALGROUP_USERS_INFO_0[] RetGroups = new LOCALGROUP_USERS_INFO_0[EntriesRead];
IntPtr iter = bufPtr;
for(int i=0; i < EntriesRead; i++)
{
RetGroups[i] = (LOCALGROUP_USERS_INFO_0)Marshal.PtrToStructure(iter, typeof(LOCALGROUP_USERS_INFO_0)); 
iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(LOCALGROUP_USERS_INFO_0)));
MessageBox.Show(RetGroups[i].groupname);
}
NetApiBufferFree(bufPtr);
}
/**///// 代码结束

上面的例子返回用户Administrator所在的组。通过本文,我们了解了如何通过.Net平台调用方法使用与管理用户相关的网络函数。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/xmzhaoym/archive/2008/10/10/3051407.aspx

posted @ 2009-09-07 11:23 小罗 阅读(135) 评论(0) 编辑
摘要: using System;using System.Collections.Generic;using System.Drawing;using System.Drawing.Imaging;using System.IO;using System.Drawing.Drawing2D;namespace Silent.Web.Controllers.Common{ /**//// <summ...阅读全文
posted @ 2009-09-07 11:20 小罗 阅读(547) 评论(0) 编辑

转自网上的方法

方法1:
Directory.GetCurrentDirectory()。
这个方法只能在.NET的完整版中使用,NETCF中不支持该功能,调用时会引发异常。

方法2:
System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase)。
这个方法是MSDN中给出的针对NETCF平台的,当在PC的NET完整版中获取到的路径中测试时,发现最终的路径中带有file:前缀,如file:\c:\debug,一般情况下我们并不需要这个前缀,可以手动将其去掉。

方法3:
System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().ManifestModule.FullyQualifiedName).
该方法也是针对NETCF给出的解决方案,当在PC的NET完整版测试中可以获取到一致的结果。

小结:
如果考虑到代码在PC和PDA上的兼容性,则建议使用第三种方法。第一种方法是万万不行的,而且第一种方式获取到的路径也并不一定就是真正的应用程序所在的路径。

posted @ 2009-08-02 12:55 小罗 阅读(588) 评论(0) 编辑
摘要: 前言关于ASP.NET的底层的工作机制,最近园子里讨论的甚是火热。相信很多人都看过Rick Strahl先生的一篇经典之作:A low-level Look at the ASP.NET Architecture,经Rick Strahl先生同意,我把他的这篇文章翻译成中文,希望能够给想深入了解ASP.NET工作机制的朋友一点帮助。特别说明:翻译此文的目的仅仅是为了给广大的ASP.NET爱好者提供...阅读全文
posted @ 2008-01-21 23:08 小罗 阅读(190) 评论(0) 编辑

据说通过HttpModules可以将类似于:http://www.infotouch.cn/detail.aspx?id=120 的URL地址重写为:http://www.infotouch.cn/detail/120.aspx 。这样最直接的好处就是可以让搜索引擎搜索到页面,因为搜索引擎对?之后的参数不太理睬。

今天尝试了一下,发现一个需要注意的问题,就是HttpModules只能对特定扩展名的URL进行重写(注:只能对“映射”-“应用程序扩展”中指定交给Asp.Net处理的扩展名文件进行处理),从Google找了些英文资料,发现这种情况是由IIS处理请求的机理决定的。

IIS对于没有扩展名的URL处理请求的机理:

如果请求的路径(path)有扩展名,IIS首先查找是否已设定了对应的应用程序扩展,有则将控制权交给该应用程序:
所以对于 http://www.infotouch.cn/detail.aspx?id=120 这样的情况很容易处理,只要处理为 http://www.infotouch.cn/detail/120.aspx 即可。因为IIS根据*.aspx的扩展名将控制权交给了Asp.Net,进而转给了HttpModules。

如果请求的路径(path)没有扩展名,例如:http://www.infotouch.cn/detail 这样的路径。IIS首先检查该虚拟路径是否对应到一个本地目录,如果具有对应的本地目录,再查找该目录下是否具有缺省文件,如果找到,就重定向为该缺省文件的路径。否则,IIS报告一个Http404-文件未找到错误。

实践:

首先写一个处理URLs重写的类,并且这个类必须继承IHttpHandler接口,以博客园的程序为例:

public class UrlReWriteModule : System.Web.IHttpModule
{
     public void Init(HttpApplication context)
     {
        context.BeginRequest +=new EventHandler(context_BeginRequest);
     }

    public void Dispose()
    {
    }
}

UrlReWriteModule类就是处理URLs重写的类,继承IHttpHandler接口,实现该接口的两个方法,Init和Dispose。在Init方法里注册自己定义的方法,如上例所示:

content.BeginRequest +=new EventHandler(content_BeginRequest);

BeginRequest是一个事件,在收到新的Http请求时触发,content_BeginRequest就是触发时处理的方法。另外说明一点,HttpModules能注册的方法还有很多,如:EndRequest、Error、Disposed、PreSendRequestContent等等。

在content_BeginRequest方法中具体处理URLs重写的细节,比如,将 http://www.cnblogs.com/archive.aspx?user=rrooyy&id=56041 重写为 http://www.cnblogs.com/rrooyy/archive/2004/10/24/56041.html 。然后将重新生成的Url用HttpContext.RewritePath()方法重写即可,如下:

private void context_BeginRequest(object sender, EventArgs e)
{
     HttpContext context   = ((HttpApplication)sender).Context;
     // 获取旧的Url
     string url = context.Request.Path.ToLower();
     // 重新生成新的Url
     string newUrl = ...; // 具体过程略
     // 重写Url
     context.RewritePath(newUrl);
}

提醒:newUrl的格式不是http://www.infotouch.com/user/archive.aspx,而是从当前应用程序根目录算起的绝对路径,如:user\archive.aspx,这一点请特别注意。

最后要web.config中注册重写URLs的类,格式如下:

<HTTPMODULES>
    <ADD type="classname,assemblyname" name="modulename"/>
    <REMOVE name="modulename"/>
    <CLEAR />
</HTTPMODULES>

采用<ADD>标签可以注册一个类;<REMOVE>可以移除某个类,如果某个子目录不希望继承父目录的某个Http Module注册,就需要使用这个标签;<CLEAR />可以移除所有的Http Module注册。

posted @ 2008-01-21 21:58 小罗 阅读(137) 评论(0) 编辑

掌握了会话状态基本知识之后,让我们通过分析会话状态管理的技术细节,增强我们的技能。会话状态处理是一个可以用以下三个步骤概括的任务:分配一个会话ID;从一个提供程序那里获取会话数据;把它填充到页面的上下文中。如前所述,会话状态模块控制所有这些任务的执行。这样做时,它利用两个额外组件:会话ID生成器和会话状态提供程序。在ASP.NET 2.0中,这两个组件都可以用定制组件代替,后文将对此进行介绍。现在,要解决使用会话状态时面临的实际问题。

13.3.1 标识一个会话

每个活动的ASP.NET会话使用一个只由URL允许的字符组成的120位字符串进行标识。会话ID被保证是惟一的并且是随机产生的,以避免数据冲突和防止恶意攻击。根据现有ID通过算法来获得一个有效的会话ID实际上并不可行。在ASP.NET 1.x中,会话ID生成器是一个埋藏在框架中的系统组件,对外部不可见。在ASP.NET 2.0中,会话ID生成器变成了一个可定制的组件,开发人员可以有选择地替换它。

注意

一个古老的谚语提醒了我们,任何事情都不能仅仅因为可行就去做(nothing should be done only because it is doable)。这个谚语特别适合这里的情形,因为我们在讨论ASP.NET 2.0中可定制的会话状态管理。这些子系统(诸如会话ID生成器)只有在理由充分的情况下才进行定制,并且确保这么做不会使事情变得更糟或者降低安全水平。稍后我将更详细地讨论这一点。

1. 创建会话ID

根据设计,会话ID长15字节(15×8=120位)。会话ID是使用随机数生成器(Random Number Generator,简称RNG)密码提供程序生成。该服务提供程序返回一个由15个随机生成的数值组成的数组。然后把该数组映射到有效的URL字符,并作为一个字符串返回。

如果会话没有包含任何数据,则为每个请求生成一个新的会话ID,并且会话状态并不持久地存储在状态提供程序中。然而,如果使用一个 Session_Start处理程序,则会话状态总是被保存,即使它为空。由于这个原因,特别是在没有使用进程内会话提供程序时,定义 Session_Start处理程序要特别小心,并且只有在确实需要时才定义。

相反,在一个非空的会话字典超时或被丢弃之后,会话ID仍然不变。根据设计,即使会话状态到期,会话ID一直延续到浏览器会话结束为止。这就是说,只要浏览器实例保持相同,则同一个会话ID用来表示一段时间内的多个会话。

2. 会话cookie

SessionID字符串传递给浏览器,然后以如下两种方法之一返回给服务器应用程序:使用一个cookie或一个修改过的URL。在默认情况下,会话状态模块在客户端创建一个HTTP cookie,但也可以使用一个修改过的URL,其中嵌入SessionID字符串,特别针对无cookie的浏览器。具体采取哪种方法取决于应用程序的 web.config文件中存储的配置设置。在默认情况下,会话状态使用cookie。

实际上,cookie只不过是网页放在客户端硬盘上的一个文本文件。在ASP.NET中,cookie由HttpCookie类的一个实例表示。一个cookie通常有一个名称、一个值集合和一个到期时间。此外,我们可以对cookie进行配置,以通过安全连接作用于特定的虚拟路径(例如, HTTPS)。

重要提示

ASP.NET 2.0利用HTTP-only特征建立支持会话cookie的浏览器上的会话cookie。例如,在Microsoft Internet Explorer 6.0 sp1或安装了Windows XP SP2的系统上支持会话cookie。HTTP-only特征防止这些cookie被用于客户端脚本,从而竖起一道屏障,以防可能发生的旨在偷取会话ID 的跨站点脚本 攻击。

当cookie被启用时,会话状态模块实际创建一个具有特定名称的cookie,并把会话ID保存在它那里。cookie的创建如下面的伪码所示:

HttpCookie sessionCookie;

sessionCookie = new HttpCookie("ASP.NET_SessionId", sessionID);

sessionCookie.Path = "/";

ASP.NET_SessionId是cookie的名称,而sessionID字符串是它的值。cookie也与当前域的根关联。Path属性描述应用cookie的相对URL。会话cookie被给予一个非常短的到期期限,并在每个成功的请求结束时续订。cookie的Expires属性指出该 cookie在客户端到期的时间。如果没有显式地设置该属性(会话cookie通常就是这样),则Expires属性默认为 DateTime.MinValue——即.NET Framework中可能的最小时间单位。

注意

需要写入一个cookie的服务器端模块添加一个HttpCookie对象到Response.Cookies集合中。客户端存在的并与被请求领域关联的所有cookie都被上传,并使其可以通过Request.Cookies集合读取。

3. 无cookie会话

为了使会话状态起作用,客户端必须能够把会话ID传递给服务器端应用程序。这种情况如何发生依赖于应用程序的配置。ASP.NET应用程序通过配置文件的<sessionState>节定义它们的会话特有的设置。为了决定cookie支持,将cookieless属性设置为表13.7中的一个值。表中所列的值属于HttpCookieMode枚举类型。

表13.7 HttpCookieMode枚举类型

模 式

描 述

AutoDetect

只有在请求浏览器支持cookie时才使用cookie

UseCookies

使用cookie持久地存储会话ID,而不管浏览器是否支持cookie。这是默认选项

UseDeviceProfile

根据配置文件的设备配置文件节所列的浏览器能力作出决策

UseUri

将会话ID存储在URL中,不管浏览器是否支持cookie。如果在任何情况下都不使用cookie,则使用该选项

使用AutoDetect时,ASP.NET将查询浏览器以确定它是否支持cookie。如果浏览器支持cookie,会话ID就存储在一个 cookie中;否则,会话ID将存储在URL中。另一方面,使用UseDeviceProfile模式时,不检查浏览器的有效能力。为了让HTTP会话模块决定使用cookie还是URL,使用由HttpBrowserCapabilities对象的 SupportsRedirectWithCookie属性产生的浏览器所声明的功能。要注意的是,即使浏览器能够支持cookie,但是用户可能会禁用 cookie。在这种情况下,会话状态不能正确起作用。

注意

在ASP.NET 1.x中,可以选择的方案更少。<sessionState>节的cookieless属性只能接收一个布尔值。若要禁用会话中的cookie,则将该属性设置为true。

在禁用cookie支持下,假设请求如下URL处的一个页面:

http://www.contoso.com/test/sessions.aspx

在浏览器的地址栏中显示的内容略有不同,而且现在包含会话ID,如下所示:

http://www.contoso.com/test/(S(5ylg0455mrvws1uz5mmaau45))/sessions.aspx

实例化时,会话状态模块检查cookieless属性的值。如果该值为true,则请求被重定向(HTTP 302状态码)到一个修改过的虚拟URL(正好在页面名称前包含会话ID)。再次被处理时,该请求嵌入会话ID。一个专用的ISAPI筛选器 (aspnet_filter.exe组件)预处理该请求,解析URL,并在该URL融入一个会话ID时重写正确的URL。所检测到的会话ID也存储在一个称为AspFilterSessionId的额外HTTP头部,并在以后进行检索。

4. 无cookie会话的问题

无cookie会话旨在使有状态的应用程序也可以在一个不支持cookie的浏览器或者在一个没有启用cookie的浏览器上运行,但它也存在问题。首先,当会话开始时,以及每当用户从一个应用程序的页面内跟随一个绝对URL时,它们会导致重定向。

使用cookie时,我们可以清除地址栏,进入另一个应用程序,然后转向前一个应用程序,并检索相同的会话值。如果在禁用会话时这么做,会话数据会丢失。该特征对于页面回发没有什么问题,因为这是使用相对URL自动实现的,但若使用绝对URL进行链接,则会引发一个严重的问题。在这种情况下,总是会创建一个新的会话。例如,如下代码中断会话:

<a runat="server" href="/test/sessions.aspx">Click</a>

有没有一种办法自动打破链接或超链接中的绝对URL,以便它们融入会话信息吗?我们可以使用如下技巧,它使用了HttpResponse类的ApplyAppPathModifier方法:

<a href='<% =Response.ApplyAppPathModifier("test/page.aspx")%>' >Click</a>

ApplyAppPathModifier方法取一个表示相对URL的字符串,并返回一个绝对URL,其中嵌入会话信息。需要从一个HTTP页面重定向到一个HTTPS页面(其中强制要求完整的绝对地址)时,则上述技巧特别有用。要注意的是,如果会话cookie被启用,并且路径是绝对路径,则 ApplyAppPathModifier返回原始URL。

警告 在服务器端表达式(即标记了runat=server属性的表达式)中,不能使用<%...%>代码块。之所以在上述代码中可行,是因为<a>标签被逐字发出,没有设置runat属性。

5. 无cookie会话和安全性

使用无cookie会话的另一个问题与安全性有关。会话欺诈是最流行的攻击类型之一,牵涉到通过为另一个合法用户生成的会话ID访问外部系统。请尝试下面的做法:将应用程序设置为在无cookie下工作,并访问一个页面。当会话ID在浏览器的地址栏中出现时,获取它的URL,并立即通过电子邮件发给一位朋友。让你的朋友把URL粘贴到自己的机器上并单击“转到”。只要会话有效,你的朋友就能访问你的会话状态。毫无疑问,会话ID并没有得到良好的保护。为了系统的安全性,关键是有一个不可预测的ID生成器,因为它使猜测有效的会话ID更难了。对于无cookie会话,会话ID在地址栏中提供给外界,并且对所有的人都是可见的。由于这个原因,如果把专用的或机密的信息存储在会话状态中,建议使用安全套接字层(Secure Sockets Layer,SSL)或传输层安全性(Transport Layer Security,TLS)加密浏览器和服务器之间的包含会话ID的任何通信。

此外,当用户认为这么做违反了安全性时,始终应当使他们能够退出登录,并调用Abandon方法。对使用会话ID来窃取会话状态中所存储数据的人来说这种设计减少了他们的时间,而说到安全性,重要的是在使用无cookie会话时要对系统进行配置,以免重用过期的会话ID。此行为在ASP.NET中可以通过<sessionState>节进行配置,下一节将详细 介绍。

6. 配置会话状态

从ASP.NET 1.x迁移到ASP.NET 2.0以后,<sessionState>节的内容显著增多了。该节的内容如下所示:

<sessionState

mode="Off|InProc|StateServer|SQLServer|Custom"

timeout="number of minutes"

cookieName="session cookie name"

cookieless="http cookie mode"

regenerateExpiredSessionId="true|false"

sqlConnectionString="sql connection string"

sqlCommandTimeout="number of seconds"

allowCustomSqlDatabase="true|false"

useHostingIdentity="true|false"

partitionResolverType=""

sessionIDManagerType="custom session ID generator"

stateConnectionString="tcpip=server:port"

stateNetworkTimeout="number of seconds"

customProvider="custom provider name">

<providers>

...

</providers>

</sessionState>

表13.8详细描述了各个属性的目标和特征。要注意,只有mode,timeout,stateConnectionString和 sqlConnectionString属性在ASP.NET 1.x中有对等的属性。cookieless属性在ASP.NET 1.x中也存在,但是它接受布尔值。所有其他属性都是ASP.NET 2.0新引入的。

表13.8 <sessionState>属性

属 性

描 述

allowCustomSqlDatabase

如果为true,则能够指定一个定制数据库表来存储会话数据,而不必使用标准的ASPState

Cookieless

指定如何把会话ID传递给客户端

cookieName

cookie的名称(如果cookie用于会话ID的话)

customProvider

定制的会话状态存储提供程序的名称,用于存储和检索会话状态数据

mode

指定把会话状态存储在哪里

partitionResolverType

当会话状态工作于SQLServer或StateServer模式时,指明被加载以提供连接信息的分区解析器组件的类型和程序集。如果可以正确地加载分区解析器,则忽略sqlConnectionString和stateConnectionString属性

regenerateExpiredSessionId

用一个已过期的会话ID发出一个请求时,如果该属性为true,则生成一个新的会话ID;否则,重新使用过期的会话ID。默认值为false

sessionIDManagerType

默认为Null。如果设置了该属性,则指明用作会话ID的生成器的组件

sqlCommandTimeout

指定一个SQL命令在被取消前可以空闲的秒数。默认为30秒

sqlConnectionString

指定SQL Server的连接字符串

stateConnectionString

指定用来远程存储会话状态的服务器名称或地址和端口

stateNetworkTimeout

指定Web服务器和状态服务器之间的TCP/IP网络连接在请求被取消前可以空闲的秒数。默认为10秒

timeout

指定一个会话在被放弃前可以空闲的秒数。默认为20秒

useHostingIdentity

默认值为True。它指明在访问一个定制的状态提供程序或者进行了集成安全性配置的SQLServer提供程序时,ASP.NET进程标识被假冒

此外,子<providers>节列出了定制的会话状态存储提供程序。ASP.NET会话状态旨在使我们能够轻松地把用户会话数据存储在不同的源中,如Web服务器的内存中或SQL Server。存储提供程序是一个管理会话状态信息的存储并把它存储在另一种介质(例如,Oracle)或布局中的组件。我们将在本章后面重新讨论该主题。

13.3.2 会话的生命期

只有在第一个数据项添加到内存中的会话字典时,会话状态的生命才开始。如下代码说明了如何修改会话字典中的一个数据项。“MyData”是惟一地标识该值的键。如果该字典中已经存在一个称为“MyData”的键,则重写现有的值:

Session["MyData"] = "I love ASP.NET";

Session字典通常包含object类型;要读回数据,需要将返回值转换为一种更具体的 类型:

string tmp = (string) Session["MyData"];

当页面把数据保存到Session字典中时,返回值被加载到一个内存中的字典——即,SessionDictionary内部类的一个实例(参见图13.2)。其他并发运行的页面不能访问该会话,直到正在进行的请求完成为止。

1. Session_Start事件

会话启动事件与会话状态无关。当会话状态模块服务给定用户发出的要求新会话ID的第一个请求时,Session_Start事件激发。ASP.NET运行库可以在一个会话上下文中服务多个请求,但是只对它们中的第一个请求激发Session_Start事件。

每当请求一个不把数据写入会话字典中的页面时,创建一个新的会话ID,并激发一个新的Session_Start事件。会话状态的体系结构非常复杂,因为它必须支持各种状态提供程序。总体方案要求在请求完成时把会话字典的内容序列化到状态提供程序。然而,为了优化性能,只有在字典的内容不空时该过程才会真正执行。然而,如前所述,如果应用程序定义了一个Session_Start事件处理程序,则无论如何都会发生序列化。

2. Session_End事件

Session_End事件表示会话的结束,用来执行终止会话所需的任何清除代码。然而要注意的是,只有在Inproc模式下,即当会话数据存储在ASP.NET工作进程中时,才支持该 事件。

为了使Session_End事件激发,会话状态必须先存在。这就是说,我们必须把一些数据存储在会话状态中,并且至少必须完成一个请求。第一个值添加到会话字典中时,一个数据项插入ASP.NET缓存——上述的Cache对象将在下一章详细介绍。该行为是进程中的状态提供程序所特有的;进程外状态提供程序和SQL Server状态服务器都不使用Cache 对象。

然而,更有趣的是,添加到缓存中的数据项(每个活动会话只有一个数据项)被赋予一个特殊的到期策略。我们将在下一章学习ASP.NET缓存及其相关的到期策略。而现在,只要知道添加到该缓存中的会话状态项被赋予一个活动到期时间,其间隔时间设置为会话超时时间。只要在会话内还有请求要处理,则滑动到期期限自动续订。会话状态模块在处理EndRequest事件时重置超时时间。只要通过对缓存执行一次读取操作,它就能获得期望的结果!给定 ASP.NET Cache对象的内部结构,这就等于续订滑动到期期限。因而,当缓存项到期时,该会话已经超时。

到期的缓存项自动地从缓存中删除。作为该项的到期策略的一部分,状态会话模块还指示一个删除回调函数。缓存自动地调用该删除方法,而后者激发Session_End事件。

注意

Cache中表示一个会话的状态的数据项,不能从system.web程序集的外部进行访问,更不能进行列举,因为它们被存放在一个系统保留的缓存区域。换句话说,我们不能以编程的方式访问驻留在另一个会话中的数据,更不能删除它。

3. 为什么我的会话状态会丢失?

当会话超时或被放弃时,Session对象中存储的值要么以编程的方式从内存中删掉,要么被系统从内存中删掉。然而,在某些情况下,会话状态会不知不觉地丢失。如何解释这种奇怪的行为呢?

当工作模型是InProc时,会话状态在正在服务该页面请求的AppDomain的内存空间中被映射。根据该规定,会话状态受进程回收和 AppDomain重启支配。ASP.NET工作进程定期重启,以维护良好的平均性能;当ASP.NET工作进程重启时,会话状态丢失。进程回收取决于内存消耗百分比,还有可能取决于被服务的请求量。虽然这是周期性的,但是不能对周期的间隔时间做出一般的估计。在设计基于会话的、进程内应用程序时,要注意这一点。通常要记住的是,试图访问会话状态,它可能没有。尽量使用适合自己的应用程序的异常处理或还原技术。

考虑到一些反病毒软件可能把web.config或Global.asax文件标记为已修改,导致一个新的应用程序启动,从而丢失会话状态。如果我们或我们的代码修改那些文件的时间戳,或者修改其中一个专用文件夹(诸如Bin或App_Code等)的内容,也会丢失会话状态。

注意

当一个正在运行的页面碰到一个错误时,会话状态会发生什么呢?在请求结束时,如果页面产生一个错误——即,Server对象的 GetLastError方法返回一个异常,则该会话的状态不会被保存。然而,如果在异常处理程序中通过调用Server.ClearError重置错误状态,则好像没有发生任何错误一样有规律地保存会话的值。
posted @ 2008-01-21 15:56 小罗 阅读(465) 评论(1) 编辑
技巧 os javascript html asp.net
  
    通过ASP.NET可以对一些无用的进程进行远程杀死,下面的代码先列出正在活动的所有进程,然后进行杀死。需要注意的是:这个文件要放在具有Administrator访问权限的虚拟目录下。
    
    以下是C#代码:
    <%@ Page Language="c#" %>
    <HTML>
  
    <HEAD>
    <% @ Import namespace= "System.Diagnostics" %>
    <script language="C#" runat="Server" debug="true">
    void Page_Load(Object Sender, EventArgs e){
    btnKill.Attributes.Add("onclick", "javascript: return confirm('你真的要杀死这个进程吗?');");
    }
    
    private void KillProcess(string processName){
    System.Diagnostics.Process myproc= new System.Diagnostics.Process();
    //得到所有打开的进程
    try{
    foreach (Process thisproc in Process.GetProcessesByName(processName)) {
    if(!thisproc.CloseMainWindow()){
    thisproc.Kill();
    }
    }
    }
    catch(Exception Exc)
    {
    msg.Text+= "杀死" +procname.SelectedItem.Text + "失败!";
    }
    }
    public void btnKill_Click(object sender, System.EventArgs e)
    {
    KillProcess(procname.SelectedItem.Text);
    msg.Text= procname.SelectedItem.Text +" 已经被杀死。";
    }
    
    
    public void btnShow_Click(object sender, System.EventArgs e){
    ArrayList procList =new ArrayList();
    string tempName="";
    int begpos;
    int endpos;
    foreach (Process thisProc in System.Diagnostics.Process.GetProcesses()) {
    tempName=thisProc.ToString();
    begpos = tempName.IndexOf("(")+1;
    endpos= tempName.IndexOf(")");
    tempName=tempName.Substring(begpos,endpos-begpos);
    procList.Add(tempName);
    }
    procname.DataSource=procList;
    procname.DataBind();
    }
    </script>
    </HEAD>
    <body>
    <Basefont Face="Tahoma" />
    <center><h2>ASP.NET 进程杀死器!</h2>
  
    <Table cellspacing=2 cellpadding=2 border=0 BGCOLOR="#fFCC66">
    <form id="frmProc" runat="Server" method="post">
    <TR><TD><ASP:DropDownList id="procname" runat="server" /></TD><TD>
    进程名字</TD></TR>
    <TR><TD>
    <asp:button id="btnKill" Text="杀死进程" runat="server" CausesValidation="False" onclick="btnKill_Click" />
    </TD>
    <TD><asp:button id="btnShow" Text="列出所有进程" runat="server" CausesValidation="False" onclick="btnShow_Click" />
    </TD></TR>
    </TABLE>
    <center><asp:Label id="msg" runat="server"/></center>
    </form>
    </center>
    </body>
    </HTML>
posted @ 2008-01-19 16:44 小罗 阅读(162) 评论(0) 编辑
关于Forms验证的文章网上千百篇,但我花了1天半的时间学会了“一点点”,
  现在把代码分享出来,希望对像我一样的初学者所有帮助,也希望高手给指点一下:
  
  --------------------------------------------------------------------------------
  
  Step 1:新建数据库(库:MyForms ;表:users ;字段:ID,userName, userPwd);
  Step 2:新建网站,web.config 的文件全部代码如下:
  
  
  web.config 的全部代码
  <?xml version="1.0"?>
  <configuration>
   <appSettings/>
   <connectionStrings/>
  
   <system.web>
   <compilation debug="true"/>
  
   <sessionState cookieless="AutoDetect"/>
   <!--解决当浏览器端禁用Cookie时-->
  
   <authentication mode="Forms">
   <forms name="CookieName" loginUrl="login.aspx" protection="All"></forms>
   <!--loginUrl为登录面URL,如果没有身份验证Cookie,客户端将被重定向到此URL-->
   </authentication>
  
   <authorization>
   <deny users="?"/>
   </authorization>
  
   <customErrors mode="On" defaultRedirect="GenericErrorPage.htm">
   <error statusCode="403" redirect="NoAccess.htm" />
   <error statusCode="404" redirect="FileNotFound.htm" />
   </customErrors>
  
   </system.web>
  
  </configuration>
  
  Step 3:添加一个 login.aspx 页面;拖2个 TextBox ,1个Button 和1个CheckBox ;
   并将CheckBox 的text 属性设为:“是否保存Cookis ";
  Step 4:login.aspx 的隐藏代码如下:
  
  login 全部隐藏代码
  using System;
  using System.Data;
  using System.Configuration;
  using System.Web;
  using System.Web.Security;
  using System.Web.UI;
  using System.Web.UI.WebControls;
  using System.Web.UI.WebControls.WebParts;
  using System.Web.UI.HtmlControls;
  using System.Data.SqlClient; //导入命名空间
  
  public partial class _Default : System.Web.UI.Page
  {
   protected void Page_Load(object sender, EventArgs e)
   {
  
   }
   protected void Button1_Click(object sender, EventArgs e)
   {
   string userName = TextBox1.Text.Trim();
   string userPwd = TextBox2.Text.Trim();
   SqlConnection con = new SqlConnection("Server=.;Database=MyForms;User ID=sa;Password=123456");
   con.Open();
   SqlCommand cmd = new SqlCommand("select count(*) from users where userName='" + userName + "' and userPwd='" + userPwd + "'", con);
   int count = Convert.ToInt32(cmd.ExecuteScalar());
   if (count > 0)
   {
   System.Web.Security.FormsAuthentication.SetAuthCookie(this.TextBox1.Text, this.CheckBox1.Checked);
   Response.Redirect("Default.aspx");
   //上面两行,也可以换成下面一行,如通过验证则直接转向请求的页面,而不需要Responsel.Redirect("");
   //System.Web.Security.FormsAuthentication.RedirectFromLoginPage(this.TextBox1.Text, false);
   }
  
   else
   {
   Response.Write("用户不合法");
   }
   }
  }
  
  Step 5:拖一个Button 到 Default.aspx 上,将其text 属性设为"登出",其事件代码如下:
  
  Button 事件代码
  protected void Button1_Click(object sender, EventArgs e)
   {
   System.Web.Security.FormsAuthentication.SignOut();
   }
posted @ 2008-01-19 16:03 小罗 阅读(154) 评论(0) 编辑
SQL Server提供了一个特别的数据类型:image,它是一个包含binary数据的类型。下边这个例子就向你展示了如何将文本或照片放入到数据库中的办法。在这篇文章中我们要看到如何在SQL Server中存储和读取图片。
  
    1、建立一个表:
  
    在SQL SERVER中建立这样结构的一个表: 
   
    2、存储图片到SQL SERVER数据库中
  
    为了能存储到表中,你首先要上传它们到你的WEB 服务器上,你可以开发一个web form,它用来将客户端中TextBox web control中的图片入到你的WEB服务器上来。将你的 encType 属性设置为:myltipart/formdata.
  
    Stream imgdatastream = File1.PostedFile.InputStream;
  
    int imgdatalen = File1.PostedFile.ContentLength;
  
    string imgtype = File1.PostedFile.ContentType;
  
    string imgtitle = TextBox1.Text;
  
    byte[] imgdata = new byte[imgdatalen];
  
    int n = imgdatastream.Read(imgdata,0,imgdatalen);
  
    string connstr=((NameValueCollection)Context.GetConfig("appSettings"))["connstr"];
  
    SqlConnection connection = new SqlConnection(connstr);
  
    SqlCommand command = new SqlCommand
  
    ("Insert INTO ImageStore(imgtitle,imgtype,imgdata)
  
    VALUES ( @imgtitle, @imgtype,@imgdata )", connection );
  
    SqlParameter paramTitle = new SqlParameter
  
    ("@imgtitle", SqlDbType.VarChar,50 );
  
    paramTitle.Value = imgtitle;
  
    command.Parameters.Add( paramTitle);
  
    SqlParameter paramData = new SqlParameter( "@imgdata", SqlDbType.Image );
  
    paramData.Value = imgdata;
  
    command.Parameters.Add( paramData );
  
    SqlParameter paramType = new SqlParameter( "@imgtype", SqlDbType.VarChar,50 );
  
    paramType.Value = imgtype;
  
    command.Parameters.Add( paramType );
  
    connection.Open();
  
    int numRowsAffected = command.ExecuteNonQuery();
  
    connection.Close();
  
    3、从数据库中恢复读取
  
    现在让我们来从SQL Server中读取我们放入的数据吧!我们将要输出图片到你的浏览器上,你也可以将它存放到你要的位置。
  
    private void Page_Load(object sender, System.EventArgs e)
  
    {
  
    string imgid =Request.QueryString["imgid"];
  
    string connstr=((NameValueCollection)
  
    Context.GetConfig("appSettings"))["connstr"];
  
    string sql="Select imgdata, imgtype FROM ImageStore Where id = " + imgid;
  
    SqlConnection connection = new SqlConnection(connstr);
  
    SqlCommand command = new SqlCommand(sql, connection);
  
    connection.Open();
  
    SqlDataReader dr = command.ExecuteReader();
  
    if(dr.Read())
  
    {
  
    Response.ContentType = dr["imgtype"].ToString();
  
    Response.BinaryWrite( (byte[]) dr["imgdata"] );
  
    }
  
    connection.Close();
  
    }
  
    要注意的是Response.BinaryWrite 而不是Response.Write.
  
    下面给大家一个用于C# Winform的存入、读取程序。其中不同请大家自己比较!(为了方便起见,我将数据库字段简化为二个:imgtitle和imgdata。
  
    using System;
  
    using System.Drawing;
  
    using System.Collections;
  
    using System.ComponentModel;
  
    using System.Windows.Forms;
  
    using System.Data;
  
    using System.IO;
  
    using System.Data.SqlClient;
  
    namespace WindowsApplication21
  
    {
  
    ///
  
    /// Form1 的摘要说明。
  
    ///
  
    public class Form1 : System.Windows.Forms.Form
  
    {
  
    private System.Windows.Forms.Button button1;
  
    ///
  
    /// 必需的设计器变量。
  
    ///
  
    private System.ComponentModel.Container components = null;
  
    private string ConnectionString = "Integrated Security=SSPI;Initial Catalog=;DataSource=localhost;";
  
    private SqlConnection conn = null;
  
    private SqlCommand cmd = null;
  
    private System.Windows.Forms.Button button2;
  
    private System.Windows.Forms.PictureBox pic1;
  
    private System.Windows.Forms.OpenFileDialog openFileDialog1;
  
    private string sql = null;
  
    private System.Windows.Forms.Label label2;
  
    private string nowId=null;
  
    public Form1()
  
    {
  
    //
  
    // Windows 窗体设计器支持所必需的
  
    //
  
    InitializeComponent();
  
    conn = new SqlConnection(ConnectionString);
  
    //
  
    // TODO: 在 InitializeComponent 调用后添加任何构造函数代码
  
    //
  
    }
  
    ///
  
    /// 清理所有正在使用的资源。
  
    ///
  
    protected override void Dispose( bool disposing )
  
    {
  
    if (conn.State == ConnectionState.Open)
  
    conn.Close();
  
    if( disposing )
  
    {
  
    if (components != null)
  
    {
  
    components.Dispose();
  
    }
  
    }
  
    base.Dispose( disposing );
  
    }
  
    #region Windows Form Designer generated code
  
    ///
  
    /// 设计器支持所需的方法 - 不要使用代码编辑器修改
  
    /// 此方法的内容。
  
    ///
  
    private void InitializeComponent()
  
    {
  
    this.button1 = new System.Windows.Forms.Button();
  
    this.pic1 = new System.Windows.Forms.PictureBox();
  
    this.button2 = new System.Windows.Forms.Button();
  
    this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
  
    this.label2 = new System.Windows.Forms.Label();
  
    this.SuspendLayout();
  
    //
  
    // button1
  
    //
  
    this.button1.Location = new System.Drawing.Point(0, 40);
  
    this.button1.Name = "button1";
  
    this.button1.Size = new System.Drawing.Size(264, 48);
  
    this.button1.TabIndex = 0;
  
    this.button1.Text = "加入新的图片";
  
    this.button1.Click += new System.EventHandler(this.button1_Click);
  
    //
  
    // pic1
  
    //
  
    this.pic1.Location = new System.Drawing.Point(280, 8);
  
    this.pic1.Name = "pic1";
  
    this.pic1.Size = new System.Drawing.Size(344, 264);
  
    this.pic1.TabIndex = 3;
  
    this.pic1.TabStop = false;
  
    //
  
    // button2
  
    //
  
    this.button2.Location = new System.Drawing.Point(0, 104);
  
    this.button2.Name = "button2";
  
    this.button2.Size = new System.Drawing.Size(264, 40);
  
    this.button2.TabIndex = 4;
  
    this.button2.Text = "从数据库中恢复图像";
  
    this.button2.Click += new System.EventHandler(this.button2_Click);
  
    //
  
    // openFileDialog1
  
    //
  
    this.openFileDialog1.Filter = ""图像文件(*.jpg,*.bmp,*.gif)|*.jpg|*.bmp|*.gif"";
  
    //
  
    // label2
  
    //
  
    this.label2.Location = new System.Drawing.Point(0, 152);
  
    this.label2.Name = "label2";
  
    this.label2.Size = new System.Drawing.Size(264, 48);
  
    this.label2.TabIndex = 5;
  
    //
  
    // Form1
  
    //
  
    this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
  
    this.ClientSize = new System.Drawing.Size(632, 273);
  
    this.Controls.AddRange(new System.Windows.Forms.Control[] {
  
    this.label2,
  
    this.button2,
  
    this.pic1,
  
    this.button1});
  
    this.Name = "Form1";
  
    this.Text = "Form1";
  
    this.Load += new System.EventHandler(this.Form1_Load);
  
    this.ResumeLayout(false);
  
    }
  
    #endregion
  
    ///
  
    /// 应用程序的主入口点。
  
    ///
  
    [STAThread]
  
    static void Main()
  
    {
  
    Application.Run(new Form1());
  
    }
  
    private void button1_Click(object sender, System.EventArgs e)
posted @ 2008-01-19 16:00 小罗 阅读(238) 评论(0) 编辑