标题:C#的一些不错的文章
只看楼主
天使不哭
Rank: 6Rank: 6
等 级:贵宾
威 望:23
帖 子:677
专家分:22
注 册:2006-7-9
结帖率:100%
 问题点数:0 回复次数:104 
C#的一些不错的文章

1:C#+低级Windows API钩子拦截键盘输入
2:也说C#实现对Word文件读写
3:获取数据库中的所有表 (C#实现)
4:用Visual C#动态生成组件
5:在C#中运用SQLDMO备份和恢复SQLServer数据库
6:C#数据导出到Excel.
7:C#Socket编程
8:VS.Net 下的Wondows窗体常用项目
9:如何动态加载控件以及插件编程思想(C#)
10:在C#中利用DirectX实现声音播放

11:C#调用API的一些基础(希望有人用的上)

[此贴子已经被作者于2007-4-20 22:51:00编辑过]

搜索更多相关主题的帖子: 数据库 quot viewthread target amp 
2007-03-28 14:34
天使不哭
Rank: 6Rank: 6
等 级:贵宾
威 望:23
帖 子:677
专家分:22
注 册:2006-7-9
得分:0 

1:C#+低级Windows API钩子拦截键盘输入


摘要 在家里,婴儿和其它动物可能会重击你的计算机键盘,致使出现各种无法预言的结果。本文中的这个C#示例应用程序将向你展示如何基于Windows钩子API来实现在击键造成任何危害之前捕获它们。
一. 简介
猫和婴儿有很多共同之处。他们都喜欢吃家中养植的植物,都非常讨厌关门。他们也都爱玩弄你的键盘,结果是,你正发送给你的老板的电子邮件可能是以半截句子发送出去的,你的Excel帐户也被加入了一些乱七八糟的内容,并且你还没有注意到,当打开Windows资源管理器时,若干文件已经被移到了回收站!
其解决方案是,开发一个应用程序实现如下功能:只要键盘处于"威胁状态"你就可以进行切换,并确保任何键盘输入活动都不会造成危害。本文想展示如何使用一种低级Windows API钩子在一个C#应用程序中实现键盘"控制"。下图是本文示例程序的一个运行快照。
二. 背景
其实,已经存在许多有关于Windows钩子的文章和示例代码,并且已经有人编写过与本文几乎一样的C++示例程序。然而,当我搜索相应的C#应用程序的源码时,却找到极少的.NET示例,而且没有一个程序能够提供一个方便的自包含的C#类。
.NET框架能够使你以托管方式来存取你最常使用的键盘事件(通过KeyPress,KeyUp和KeyDown)。遗憾的是,这些事件都不能被用来停止Windows组合键(如Alt+Tab或Windows"开始"键),从而允许用户"远离"某一个应用程序。
本文的想法在操作系统级上捕获键盘事件而不是通过框架级来实现。为此,应用程序需要使用Windows API函数来把它自身添加到应用程序"钩子链"中以监听来自操作系统的键盘消息。当它收到这种类型的消息时,该应用程序能够选择性地传递消息,或者进行正常处理,或者"镇压"它以便不再有其它应用程序(包括Windows)来影响它。本文正是想解释其实现机理。
然而,请注意,本文中的代码仅适用于基于NT版本的Windows(NT,2000和XP),并且无法使用这个方法来停用Ctrl+Alt+Delete。有关于如何实现这一点,你可以参考MSDN有关资料。
三. 使用代码
为了易于使用,我在本文中提供了两个独立的zip文件。一个仅包含KeyboardHook类,这是本文介绍的重点。另一个是一个完整的微软Visual C# 2005 Express Edition应用程序工程,名叫"Baby Keyboard Bash",它实现显示击键的名字或彩色的形状以响应于击键。
四. 实例化类
键盘钩子是通过keyboard.cs中的KeyboardHook类来建立和管理的。这个类实现了IDisposable接口,因此,实例化它的最简单的方法是在应用程序的Main()方法中使用using关键字来封装Application.Run()调用。这将确保只要该应用程序开始即建立钩子并且,更重要的是,当该应用程序结束时立即使这个钩子失效。
这个类引发一个事件来警告应用程序已经有键被按下,因此主表单能够存取在Main()方法中创建的KeyboardHook实例就显得非常重要;最简单的方法是把这个实例存储在一个公共成员变量中。
KeyboardHook提供了三种构造器来启用或禁用某些设置:
• KeyboardHook():捕获所有击键,没有任何内容传递到Windows或另外的应用程序。
• KeyboardHook(string param):把参数串转换为Parameters枚举中的值之一,然后调用下面的构造器:
• KeyboardHook(KeyboardHook.Parameters enum):根据从Parameters枚举中选择的值的不同,分别启动下列设置:
o Parameters.AllowAltTab:允许用户使用Alt+Tab切换到另外的应用程序。
o Parameters.AllowWindowsKey:允许用户使用Ctrl+Esc或一种Windows键存取任务栏和开始菜单。
o Parameters.AllowAltTabAndWindows:启用Alt+Tab,Ctrl+Esc和Windows键。
o Parameters.PassAllKeysToNextApp:如果该参数为true,那么所有的击键将被传递给任何其它监听应用程序(包括Windows)。
当击键继续被键盘钩子捕获时,启用Alt+Tab和/或Windows键允许实际使用该计算机者切换到另一个应用程序并且使用鼠标与之交互。PassAllKeysToNextApp设置有效地禁用了击键捕获;这个类也是建立一个低级键盘钩子并且引发它的KeyIntercepted事件,但是它还负责把键盘事件传递到另一个监听程序。
因此,实例化该类以捕获所有击键的方法如下:
public static KeyboardHook kh;
[STAThread]
static void Main()
{
 //其它代码
 using (kh = new KeyboardHook())
 {
  Application.Run(new Form1());
 }
五. 处理KeyIntercepted事件
当一外键被按下时,这个KeyboardHook类激活一个包含一些KeyboardHookEventArgs的KeyIntercepted事件。这是通过一个KeyboardHookEventHandler类型的方法使用以下方式来实现的:
kh.KeyIntercepted += new KeyboardHook.KeyboardHookEventHandler(kh_KeyIntercepted);
这个KeyboardHookEventArgs返回关于被按下键的下列信息:
• KeyName:键名,通过把捕获的键代码强制转换为System.Windows.Forms.Keys而获得。
• KeyCode:由键盘钩子返回的原来的键代码
• PassThrough:指出是否这个KeyboardHook实例被配置以允许该击键传递到其它应用程序。如果你想允许一用户使用Alt+Tab或 Ctrl+Esc/Windows键切换到其它的应用程序的话,那么对之进行检查是很有用的。
然后,使用一个具有适当签名的方法来执行击键所调用的任何任务。下面是一个示例片断:
void kh_KeyIntercepted(KeyboardHookEventArgs e)
{
 //检查是否这个键击事件被传递到其它应用程序并且停用TopMost,以防他们需要调到前端
 if (e.PassThrough)
 {
  this.TopMost = false;
 }
 ds.Draw(e.KeyName);
}
本文的剩下部分将解释低级键盘钩子是如何在KeyboardHook中实现的。
六. 实现一个低级Windows API键盘钩子
在user32.dll中,Windows API包含三个方法来实现此目的:
• SetWindowsHookEx,它负责建立键盘钩子
• UnhookWindowsHookEx,它负责移去键盘钩子
• CallNextHookEx,它负责把击键信息传递到下一个监听键盘事件的应用程序
创建一个能够拦截键盘的应用程序的关键是,实现前面两个方法,而"放弃"第三个。结果是,任何击键都只能传递到这个应用程序中。
为了实现这一目标,第一步是包括System.Runtime.InteropServices命名空间并且导入API方法,首先是SetWindowsHookEx:
using System.Runtime.InteropServices
...
//在类内部:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
导入UnhookWindowsHookEx和CallNextHookEx的代码请见后面的讨论。
下一步是调用SetWindowsHookEx来建立钩子,这时需要传递下列四个参数:
• idHook:
这个数字决定了要建立的钩子的类型。例如,SetWindowsHookEx可以被用于钩住鼠标事件(当然还有其它事件)。在本文情况下,我们仅对13有兴趣,这是键盘钩子的id。为了使代码更易读些,我们把它赋值给一个常数WH_KEYBOARD_LL。
• Lpfn:
这是一个指向函数的长指针,该函数将负责处理键盘事件。在C#中,"指针"是通过传递一个代理类型的实例而获得的,从而使之引用一个适当的方法。这是我们在每次使用钩子时所调用的方法。
这里值得注意的是,这个代理实例需要被存储于这个类的一个成员变量中。这是为了防止一旦第一个方法调用结束它会被作为垃圾回收。
• hMod:
建立钩子的应用程序的一个实例句柄。我找到的绝大多数实例仅把它设置为IntPtr.Zero,理由是不大可能存在该应用程序的多个实例。然而,这部分代码使用了来自于kernel32.dll的GetModuleHandle来标识准确的实例从而使这个类更具灵活性。
• dwThreadId:
当前进程的id。把它设置为0可以使这个钩子成为全局构子,这是相应于一个低级键盘钩子的正确设置。
SetWindowsHookEx返回一个钩子id,这个id将被用于当应用程序结束时从钩子链中脱钩,因此它需要存储在一个成员变量中以备将来使用。KeyboardHook类中的相关代码如下:
private HookHandlerDelegate proc;
private IntPtr hookID = IntPtr.Zero;
private const int WH_KEYBOARD_LL = 13;
public KeyboardHook()
{
 proc = new HookHandlerDelegate(HookCallback);
 using (Process curProcess = Process.GetCurrentProcess())
 using (ProcessModule curModule = curProcess.MainModule)
 {
  hookID = SetWindowsHookEx(WH_KEYBOARD_LL, proc,GetModuleHandle(curModule.ModuleName), 0);
 }
}
七. 处理键盘事件
如前面所提及,SetWindowsHookEx需要一个到被用来处理键盘事件的回调函数的指针。它期望有一个使用如下签名的函数:
LRESULT CALLBACK LowLevelKeyboardProc( int nCode,WPARAM wParam,LPARAM lParam);
其实,建立一个函数指针的C#方法使用了一个代理,因此,向SetWindowsHookEx指出它需要的内容的第一步是使用正确的签名来声明一个代理:
private delegate IntPtr HookHandlerDelegate(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
然后,使用相同的签名编写一个回调方法;这个方法将包含实际上处理键盘事件的所有代码。在KeyboardHook的情况下,它检查是否击键应该被传递给其它应用程序并且接下来激发KeyIntercepted事件。下面是一个简化版本的不带有击键处理代码的情况:
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
private IntPtr HookCallback(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
{
 //仅为KeyDown事件过滤wParam,否则该代码将再次执行-对于每一次击键(也就是,相应于KeyDown和KeyUp)
 //WM_SYSKEYDOWN是捕获Alt相关组合键所必需的
 if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
 {
  //激发事件
  OnKeyIntercepted(new KeyboardHookEventArgs(lParam.vkCode, AllowKey));
  //返回一个"哑"值以捕获击键
  return (System.IntPtr)1;
 }
 //事件没有被处理,把它传递给下一个应用程序
 return CallNextHookEx(hookID, nCode, wParam, ref lParam);
}
接下来,一个到HookCallback的参考被指派给HookHandlerDelegate的一个实例并且被传递到SetWindowsHookEx的调用,正如前一节所展示的。
无论何时一个键盘事件发生,下列参数将被传递给HookCallBack:
• nCode:
根据MSDN文档,回调函数应该返回CallNextHookEx的结果,如果这个值小于零的话。正常的键盘事件将返回一个大于或等于零的nCode值。
• wParam:
这个值指示发生了什么类型的事件:键被按下还是松开,以及是否按下的键是一个系统键(左边或右边的Alt键)。
• lParam:
这是一个存储精确击键信息的结构,例如被按键的代码。在KeyboardHook中声明的这个结构如下:
private struct KBDLLHOOKSTRUCT
{
 public int vkCode;
 int scanCode;
 public int flags;
 int time;
 int dwExtraInfo;
}
其中的这两个公共参数是在KeyboardHook中的回调方法所使用的仅有的两个参数。vkCoke返回虚拟键代码,它能够被强制转换为System.Windows.Forms.Keys以获得键名,而flags显示是否这是一个扩展键(例如,Windows Start键)或是否同时按下了Alt键。有关于Hook回调方法的完整代码展示在每一种情况下要检查哪些flags值。
如果flags提供的信息和KBDLLHOOKSTRUCT的其它组成元素不需要,那么这个回调方法和代码的签名可以按如下进行修改:
private delegate IntPtr HookHandlerDelegate(
int nCode, IntPtr wParam, IntPtr lParam);
在这种情况中,lParam将仅返回vkCode。
八. 把击键传递到下一个应用程序
一个良好的键盘钩子回调方法应该以调用CallNextHookEx函数并且返回它的结果结束。这可以确保其它应用程序能够有机会处理针对于它们的击键。
然而,KeyboardHook类的主要功能在于,阻止击键被传播到任何其它更多的应用程序。因此它无论在何时处理一次击键,HookCallback都将返回一个哑值:
return (System.IntPtr)1;
另一方面,它确实调用CallNextHookEx-如果它不处理该事件,或如果重载的构造器中的使用KeyboardHook传递的参数允许某些组合键通过。
CallNextHookEx被启用-通过从user32.dll导入该函数,如下列代码所示:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, ref KeyInfoStruct lParam);
然后,被导入的方法被HookCallMethod所调用,这可以确保所有的通过钩子接收到的参数被继续传递到下一个应用程序中:
CallNextHookEx(hookID, nCode, wParam, ref lParam);
如前面所提及,如果在lParam中的flags是不相关的,那么可以修改导入的CallNextHookEx的签名以把lParam定义为System.IntPtr。
九. 移去钩子
处理钩子的最后一步是使用从user32.dll中导入的UnhookWindowsHookEx函数移去它(当破坏KeyboardHook类的实例时),如下所示:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
既然KeyboardHook实现IDisposable,那么这可以在Dispose方法中完成。
public void Dispose()
{
 UnhookWindowsHookEx(hookID);
}
hookID是构造器在调用SetWindowsHookEx所返回的id。这将从钩子链中删除应用程序。

[此贴子已经被作者于2007-3-28 14:36:40编辑过]


C#Winform技术群:25380362
博客:http:///boyliupan/
2007-03-28 14:34
天使不哭
Rank: 6Rank: 6
等 级:贵宾
威 望:23
帖 子:677
专家分:22
注 册:2006-7-9
得分:0 

2:也说C#实现对Word文件读写

手头上的一个项目报表相对比较简单,所以报表打印采用VBA引擎,通过定制Word模版,然后根据模版需要填充数据,然后OK,打印即可。
实现方法:首先需要引用VBA组建,我用的是Office2003 Professional,Dll版本号为Microsoft Word11.0,
另外当然还需要引用Interop.Word.Dll.
代码如下:
#region 打开Word文档,并且返回对象wDoc,wDoc
/// <summary>
/// 打开Word文档,并且返回对象wDoc,wDoc
/// </summary>
/// <param name="FileName">完整Word文件路径+名称</param>
/// <param name="wDoc">返回的Word.Document wDoc对象</param>
/// <param name="WApp">返回的Word.Application对象</param>
public static void CreateWordDocument(string FileName,ref Word.Document wDoc,ref Word.Application WApp)
{
if(FileName == "") return;
Word.Document thisDocument = null;
Word.FormFields formFields = null;
Word.Application thisApplication = new Word.ApplicationClass();
thisApplication.Visible = true;
thisApplication.Caption = "";
thisApplication.Options.CheckSpellingAsYouType = false;
thisApplication.Options.CheckGrammarAsYouType = false;
Object filename = FileName;
Object ConfirmConversions = false;
Object ReadOnly = true;
Object AddToRecentFiles = false;
Object PasswordDocument = System.Type.Missing;
Object PasswordTemplate = System.Type.Missing;
Object Revert = System.Type.Missing;
Object WritePasswordDocument = System.Type.Missing;
Object WritePasswordTemplate = System.Type.Missing;
Object Format = System.Type.Missing;
Object Encoding = System.Type.Missing;
Object Visible = System.Type.Missing;
Object OpenAndRepair = System.Type.Missing;
Object DocumentDirection = System.Type.Missing;
Object NoEncodingDialog = System.Type.Missing;
Object XMLTransform = System.Type.Missing;
try
{
Word.Document wordDoc =
thisApplication.Documents.Open(ref filename, ref ConfirmConversions,
ref ReadOnly, ref AddToRecentFiles, ref PasswordDocument, ref PasswordTemplate,
ref Revert,ref WritePasswordDocument, ref WritePasswordTemplate, ref Format,
ref Encoding, ref Visible, ref OpenAndRepair, ref DocumentDirection,
ref NoEncodingDialog, ref XMLTransform );

thisDocument = wordDoc;
wDoc = wordDoc;
WApp = thisApplication;
formFields = wordDoc.FormFields;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}

}
#endregion
调用上面静态方法,打开目标文件并且把DataGrid中数据填充到对应Word标签中去
#region Word填充数据(For Example)
/// <summary>
/// Word填充数据
/// </summary>
private void WordLoadData()
{
Word.Document wDoc=null;
Word.Application wApp=null;
sysFun.CreateWordDocument("E:\\监测报告(new).dot",ref wDoc,ref wApp);
//对标签"C"进行填充
object bkmC="C";
if(wApp.ActiveDocument.Bookmarks.Exists("C") == true)
{
wApp.ActiveDocument.Bookmarks.get_Item
(ref bkmC).Select();
}
wApp.Selection.TypeText(this.txt1.Text);
object bkmG = "TWaterTable3";
object unit;
object count; //移动数
object extend;

extend = Word.WdMovementType.wdExtend;
unit = Word.WdUnits.wdCell;
//把DataGrid中数据填充到标签TWaterTable3上
if(wApp.ActiveDocument.Bookmarks.Exists("TWaterTable3") == true)
{
wApp.ActiveDocument.Bookmarks.get_Item
(ref bkmG).Select();
for(int i=0;i<this.gridEX1.RecordCount;i++)
{
if(i==0)
{
count=1;
}
else
{
count=0;
}
//需填充5列数据
wApp.Selection.Move(ref unit,ref count);
wApp.Selection.TypeText(gridEX1.GetRow(i).Cells[0].Text);
count=1;

wApp.Selection.Move(ref unit,ref count);
wApp.Selection.TypeText(gridEX1.GetRow(i).Cells[1].Text);

wApp.Selection.Move(ref unit,ref count);
wApp.Selection.TypeText(gridEX1.GetRow(i).Cells[2].Text);

wApp.Selection.Move(ref unit,ref count);
wApp.Selection.TypeText(gridEX1.GetRow(i).Cells[3].Text);

wApp.Selection.Move(ref unit,ref count);
wApp.Selection.TypeText(gridEX1.GetRow(i).Cells[4].Text);
//换行
wApp.Selection.MoveRight(ref unit,ref count,ref extend);
}
}
}
#endregion
然后就OK了,在对标签表控制要注意列循环和换行,不知道还有没有其它好办法,欢迎探讨!

[此贴子已经被作者于2007-3-28 14:38:10编辑过]


C#Winform技术群:25380362
博客:http:///boyliupan/
2007-03-28 14:35
天使不哭
Rank: 6Rank: 6
等 级:贵宾
威 望:23
帖 子:677
专家分:22
注 册:2006-7-9
得分:0 

3:获取数据库中的所有表 (C#实现)

获取数据库中的所有表
本Blog登出后受到了大家的关注,其中“盛国军”朋友提出了使用存储过程“sp_tables”也可以实现这个目的,所有本人对这个Blog有进行了完善。

在很多情况下我们需要将指定的数据库中的所有表都列出来。在使用C#进行软件开发时,我们有哪些方法可是实现这个目的呢?本人对此进行概要的总结,有以下6中方式可以实现这个目的。

1、SQLDMO
SQLDMO是操作SQLServer的理想的方式,如果您的数据库是SQLServer就可以考虑使用这种方式。在C#中使用SQLDMO需要添加SQLDMO的引用,然后在当前的文件中using SQLDMO;即可以使用SQLDMO。SQLDMO的对象模型大家可以在SQLServer的帮助中获得。
private void GetTabels_DMO(string strServerName,string strUser,string strPWD,string strDatabase)
{
SQLDMO.SQLServer Server = new SQLDMO.SQLServerClass();
//连接到服务器
Server.Connect(strServerName,strUser,strPWD);
//对所有的数据库遍历,获得指定数据库
for(int i=0;i<Server.Databases.Count;i++)
{
//判断当前数据库是否是指定数据库
if(Server.Databases.Item(i+1,"dbo").Name ==strDatabase)
{
//获得指定数据库
SQLDMO._Database db= Server.Databases.Item(i+1,"dbo");
//获得指定数据库中的所有表
for(int j=0;j<db.Tables.Count;j++)
{
MessageBox.Show(db.Tables.Item(j+1,"dbo").Name);
}
}
}
}

2、ADOX

ADOX是ADO Extensions for DDL and Security,是微软对ADO技术的扩展,使用它我们可以操作数据库的结构。它是一个COM组件,估计以后在ADO.NET中会增加ADOX的一些功能。如果大家需要ADOX的一些资料,我可以提供。下面的一个例子就是使用ADOX来获得当前数据库的所有表。
private void GetTables_ADOX()
{
//ADO的数据库连接
ADODB.ConnectionClass cn=new ADODB.ConnectionClass();
string ConnectionString="Provider=SQLOLEDB.1;Integrated Security=SSPI;Initial Catalog=Test;Data Source=HBXP";
cn.Open(ConnectionString,"sa","",0);
//操作ADOX的Catalog对象
CatalogClass cat=new CatalogClass();
cat.ActiveConnection=cn;
for(int i=0;i<cat.Tables.Count;i++)
{
MessageBox.Show(cat.Tables[i].Name);
}
}

注意:在上面的代码中cat.ActiveConnection不能是ADO.Net中的Connection,而应该是ADO的Connection。

3、ADO.Net中的OleDbConnection

在C#中我们首先会考虑使用ADO.Net来解决问题,如果没有方法才会考虑使用ADOX或者SQLDMO来解决这个问题。虽然ADOX和SQLDMO也能够解决这个问题,但是他们毕竟是COM组件,在.Net中使用起来和在非.NET平台会有一些差异,不是很顺手。下面的示例就显示了在ADO.Net中的OleDbConnection的方法GetOleDbSchemaTable来获得数据库的架构。大家可以在MSDN中看到这个方法的说明:
public DataTable GetOleDbSchemaTable(
Guid schema,
object[] restrictions);
参数
schema
OleDbSchemaGuid 的值之一,它指定要返回的架构表。
restrictions
限制值的 Object 数组。这些值按照限制列的顺序来应用。即,第一个限制值应用于第一个限制列,第二个限制值应用于第二个限制列,依此类推。
返回值
包含请求的架构信息的 DataTable。
更多的信息大家可以查询MSDN,下面将示例如何实现。
private void GetTables_ADONET()
{
//处理OleDbConnection
string
strConnectionString=@"Integrated Security=SSPI;Data Source=HBXP;Initial Catalog=Test;Provider=SQLOLEDB.1";
OleDbConnection cn=new OleDbConnection(strConnectionString);
cn.Open();
//利用OleDbConnection的GetOleDbSchemaTable来获得数据库的结构
DataTable dt = cn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables,new object[] {null, null, null, "TABLE"});
foreach (DataRow dr in dt.Rows)
{
MessageBox.Show((String)dr["TABLE_NAME"]);
}

}

4、信息架构视图

信息架构视图是SQL-92 标准中定义的架构视图,这些视图独立于系统表。信息架构视图的最大优点是,即使我们对系统表进行了重要的修改,应用程序也可以正常地使用这些视图进行访问。下面的示例使用信息架构视图来工作。
private void GetTables_INFORMATION_SCHEMA()
{
//打开连接
string strConnectionString=System.Configuration.ConfigurationSettings.AppSettings["ConnectionString"];
sqlcn=new SqlConnection(strConnectionString);
sqlcn.Open();
//使用信息架构视图
SqlCommand sqlcmd=new SqlCommand("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'",sqlcn);
SqlDataReader dr=sqlcmd.ExecuteReader();
while(dr.Read())
{
MessageBox.Show(dr.GetString(0));
}
}

5、使用系统表

如果您的数据库系统是SQLServer,就可以使用如下的方式来获得当前数据库的所有表:
private void GetTables_SystemTable()
{
//打开连接
string strConnectionString=System.Configuration.ConfigurationSettings.AppSettings["ConnectionString"];
sqlcn=new SqlConnection(strConnectionString);
sqlcn.Open();
//使用信息架构视图
SqlCommand sqlcmd=new SqlCommand("SELECT OBJECT_NAME (id) FROM sysobjects WHERE xtype = 'U' AND OBJECTPROPERTY (id, 'IsMSShipped') = 0",sqlcn);
SqlDataReader dr=sqlcmd.ExecuteReader();
while(dr.Read())
{
MessageBox.Show(dr.GetString(0));
}
}

6、使用SQLServer的存储过程“sp_tables”
下面是“盛国军”朋友提出的使用存储过程的方法的补充代码。
public void GetTables_StoredProcedure()
{
//处理OleDbConnection
string
strConnectionString=@"Integrated Security=SSPI;Data Source=HBXP;Initial Catalog=Test;Provider=SQLOLEDB.1";
OleDbConnection cn=new OleDbConnection(strConnectionString);
cn.Open();
//执行存储过程
OleDbCommand cmd=new OleDbCommand("sp_tables",cn);
cmd.CommandType=CommandType.StoredProcedure;
OleDbDataReader dr=cmd.ExecuteReader();
while(dr.Read())
{
MessageBox.Show(dr["TABLE_NAME"].ToString());
}
}

总结:获得当前数据库中所有表的方法还不止,本人列出的这些,希望以上方法能够起到抛砖引玉的作用。以上的这些方法各有各的优缺点,希望大家能够灵活的应该,并且希望能够将发现的新的方法告知我,谢谢。

[此贴子已经被作者于2007-3-28 14:39:18编辑过]


C#Winform技术群:25380362
博客:http:///boyliupan/
2007-03-28 14:36
天使不哭
Rank: 6Rank: 6
等 级:贵宾
威 望:23
帖 子:677
专家分:22
注 册:2006-7-9
得分:0 
4:用Visual C#动态生成组件

用Visual C#动态生成组件
  以前在用Delphi写程序的时候,总不喜欢在窗体上排放很多组件,这一方面有点不美观,并且在调试程序时候,也不是十分方便。通常在写程序的时候,当要用到某些组件,采用的方法一般都是动态创建,用完以后就释放掉。Visual C#在程序运行的时候也可以动态创建组件,下面就结合一个程序例子来具体介绍如何用Visual C#动态生成组件。首先让我们了解一下,在动态创建组件的过程中要用到的一些概论和理论。
  一. Boxing (装箱)和Unboxing (出箱):
  在用Visual C#动态创建组件的时候,要涉及到二种数据类型变量的转换,这二种类型变量就是实值类型(Value Type)变量和参考类型(Reference Type)变量,而这种转换过程在Visual C#中被称为Boxing (装箱)和Unboxing (出箱)。其中把实值类型变量转换成参考类型变量就是Boxing (装箱);把参考类型变量转换成实值类型变量就是Unboxing (出箱)。那么什么是实值类型,说的简单些,就是我们平常使用的整型、布尔型、枚举型等,这些类型的变量就是实值类型变量了;所谓参考类型,在Visual C#中指的就是Object、Class、Interface、Delegate、String、Array等,他和实值类型最主要的不同之处就是,参考类型变量存放的是指向实体对象的指针,而实值类型变量却是实实在在地实体对象。在本文介绍的程序中,主要涉及的是出箱。具体的处理方法,在下面有着具体介绍。
  二. 本文中程序设计和运行的环境:
  (1).微软公司视窗2000服务器版
  (2)..Net FrameWork SDK Beta 2
  三. 程序设计中的关键步骤以及解决方法:
  文中软件主要功能是用通过窗体上的二个按钮来创建二个不同类型的WinForm组件--Button组件和TextBox组件,并在创建的同时为每一个组件的属性赋值,给每一个创建的组件也创建了事件。
  (1).如何在窗体上创建Button组件:
    其实用Visual C#创建一个组件是十分方便的,只用下列二行语句就可以完成了:
//创建一个新的Button组件
Button myButton = new Button ( ) ;
//在窗体中显示此按钮
this.Controls.Add ( myButton ) ;
但此时创建的这个Button组件没有任何属性,并且也没有任何事件,在本文中介绍的程序中创建的Button组件,不仅有属性也有事件,下列语句就是本文程序创建Button组件源代码:
//按钮数量计算器在每次按钮按动后加"1"
counter += 1 ;
//对要产生的按钮的纵坐标的相对位置是前一个产生按钮的相对位置的纵坐标加"3"
locY += this.btnAdd.Height + 3 ;
//创建一个新的Button组件
Button myButton = new Button ( ) ;
//设定他的名称和Text属性,以及产生的相对位置
myButton.Name = "Button " + counter ;
myButton.Text = "按钮 " + counter ;
myButton.Location = new Point ( btnAdd.Location.X , locY ) ;
//为产生的新的Button组件设定事件,本文中为产生的按钮设定了三个事件
myButton.MouseEnter += new System.EventHandler ( this.btn_MouseEnter ) ;
myButton.MouseLeave += new System.EventHandler ( this.btn_MouseLeave ) ;
myButton.Click += new System.EventHandler ( this.btn_Click ) ;
//在窗体中显示此按钮
this.Controls.Add ( myButton ) ;
  程序不仅为每一个组件的属性都赋值,而且为每一个组件都创建了三个事件。细心的读者可能已经注意到,程序为每一个组件创建的事件的名称都是一样的。这样就有一个问题,如何在这一样的事件中,识别到底是哪个Button组件触发了事件。
  (2).确定是哪个组件触发了事件:
  由于程序中为每一个创建的Button组件的事件都是一样的,要想正确处理这些组件的事件,就需要在事件触发的程序中判断到底是哪个组件触发了这个事件。这就需要用到上面所提出的装箱和出箱。我们知道Sender对象是一个参考类型变量,他存放的是指向触发当前事件实体对象的指针。要把他给转换成实值对象类型,通过下列语句就可以确定是哪个组件触发了当前事件:
private void btn_MouseEnter ( object sender , System.EventArgs e )
{
//出箱
Button currentButton = ( Button ) sender ;
//设定按钮的背景色
currentButton.BackColor = Color.Red ;
}
  其他事件可以仿照此事件的处理过程来处理。
  (3). 如何在窗体上创建TextBox组件:
  创建TextBox组件的过程和创建Button组件过程相类似,只是在创建的组件类型上面有一点区别,具体实现语句如下:
//文本框数量计算器在每次按钮按动后加"1"
counter01 += 1 ;
//对要产生的文本框的纵坐标的相对位置是前一个产生按钮的相对位置的纵坐标加"3
locY1 += this.txtAdd.Height + 3 ;
//创建一个新的TextBox组件
TextBox myBox = new TextBox ( ) ;
//设定他的名称和Text属性,以及产生的位置
myBox.Name = "TextBox " + counter01 ;
myBox.Text = "文本框 " + counter01 ;
myBox.Location = new Point ( txtAdd.Location.X , locY1 ) ;
//为产生的新的TextBox组件设定事件,本文中为产生的文本框设定了一个事件
myBox.Click += new System.EventHandler ( this.btn_Click ) ;
//在窗体中显示此文本框
this.Controls.Add ( myBox ) ;
  此时细心的读者又会发现,为每一个TextBox组件创建Click事件和为Button组件创建的Click事件也是一样的,这样在Click事件中不仅要判断是哪个组件触发了事件,还要判断是那种类型的组件触发了事件,下面语句是实现这些判断地具体方法:
private void btn_Click ( object sender , System.EventArgs e )
{
if ( sender.GetType ( ) == typeof ( Button ) )
{
Button control = ( Button ) sender ;
MessageBox.Show ( control.Text + "被按动了!");
}
else
{
TextBox control = ( TextBox ) sender ;
MessageBox.Show ( control.Text + "被按动了!" ) ;
}
}
  当然如果你也可以单独为TextBox组件创建Click事件。此时创建的事件语句可改为:
myBox.Click += new System.EventHandler ( this.txt _Click ) ;
  下面是实现txt _Click ( )事件的程序代码:
private void txt_Click ( object sender , System.EventArgs e )
{
TextBox currentButton = ( TextBox ) sender ;
MessageBox.Show ( currentButton.Text + "被按动了!");
}
  四. 本文中源程序已经程序运行的界面:
  下面这些图是程序运行:
图01:程序中动态创建了组件
图02:单击创建的按钮的结果图
图03:单击创建的文本框的结果图
下面是实现上面结果的程序源代码:
using System ;
using System.Drawing ;
using System.Collections ;
using System.ComponentModel ;
using System.Windows.Forms ;
using System.Data ;
namespace DynamicControls
{
public class Form1 : Form
{
private Button btnAdd ;
private System.ComponentModel.Container components = null ;
private Button txtAdd ;
//给产生的按钮定义一个数量计算器
private int counter ;
//给产生的按钮定义相对位置的纵坐标
private int locY ;
//给产生的文本框定义一个数量计算器
private int counter01 ;
//给产生的文本框定义相对位置的纵坐标
private int locY1 ;
public Form1 ( )
{
InitializeComponent ( ) ;
//初始化产生的按钮何文本框位置的纵坐标
locY = this.btnAdd.Location.Y ;
locY1 = this.txtAdd.Location.Y ;
}
//清除在程序中使用到的资源
protected override void Dispose ( bool disposing )
{
if ( disposing )
{
if ( components != null )
{
components.Dispose ( ) ;
}
}
base.Dispose ( disposing ) ;
}
private void InitializeComponent ( )
{
this.btnAdd = new Button ( ) ;
this.txtAdd = new Button ( ) ;
this.SuspendLayout ( ) ;
this.btnAdd.FlatStyle = FlatStyle.Popup ;
this.btnAdd.Location = new System.Drawing.Point ( 8 , 16 ) ;
this.btnAdd.Name = "btnAdd" ;
this.btnAdd.TabIndex = 0 ;
this.btnAdd.Text = "生成按钮!" ;
this.btnAdd.Click += new System.EventHandler ( this.btnAdd_Click ) ;
this.txtAdd.FlatStyle = FlatStyle.Popup ;
this.txtAdd.Location = new System.Drawing.Point ( 108 , 16 ) ;
this.txtAdd.Name = "txtAdd" ;
this.txtAdd.TabIndex = 1 ;
this.txtAdd.Text = "生成文本框!" ;
this.txtAdd.Click += new System.EventHandler ( this.txtAdd_Click ) ;
this.AutoScaleBaseSize = new System.Drawing.Size ( 5 , 13 ) ;
this.ClientSize = new System.Drawing.Size ( 292 , 273 ) ;
this.Controls.Add ( btnAdd ) ;
this.Controls.Add ( txtAdd ) ;
this.Name = "Form1" ;
this.Text = "在Visual C#中如何动态产生组件!" ;
this.ResumeLayout ( false ) ;
}
static void Main ( )
{
Application.Run ( new Form1 ( ) ) ;
}
private void btnAdd_Click ( object sender , System.EventArgs e )
{
//按钮数量计算器在每次按钮按动后加"1"
counter += 1 ;
//对要产生的按钮的纵坐标的相对位置是前一个产生按钮的相对位置的纵坐标加"3"
locY += this.btnAdd.Height + 3 ;
//创建一个新的Button组件
Button myButton = new Button ( ) ;
//设定他的名称和Text属性,以及产生的位置
myButton.Name = "Button " + counter ;
myButton.Text = "按钮 " + counter ;
myButton.Location = new Point ( btnAdd.Location.X , locY ) ;
//为产生的新的Button组件设定事件,本文中为产生的按钮设定了三个事件
myButton.MouseEnter += new System.EventHandler ( this.btn_MouseEnter ) ;
myButton.MouseLeave += new System.EventHandler ( this.btn_MouseLeave ) ;
myButton.Click += new System.EventHandler ( this.btn_Click ) ;
//在窗体中显示此按钮
this.Controls.Add ( myButton ) ;
}
private void txtAdd_Click ( object sender , System.EventArgs e )
{
//文本框数量计算器在每次按钮按动后加"1"
counter01 += 1 ;
//对要产生的文本框的纵坐标的相对位置是前一个产生按钮的相对位置的纵坐标加"3
locY1 += this.txtAdd.Height + 3 ;
//创建一个新的TextBox组件
TextBox myBox = new TextBox ( ) ;
//设定他的名称和Text属性,以及产生的位置
myBox.Name = "TextBox " + counter01 ;
myBox.Text = "文本框 " + counter01 ;
myBox.Location = new Point ( txtAdd.Location.X , locY1 ) ;
//为产生的新的TextBox组件设定事件,本文中为产生的文本框设定了一个事件
myBox.Click += new System.EventHandler ( this.btn_Click ) ;
//在窗体中显示此文本框
this.Controls.Add ( myBox ) ;
}
private void btn_MouseEnter ( object sender , System.EventArgs e )
{
//出箱
Button currentButton = ( Button ) sender ;
//设定按钮的背景色
currentButton.BackColor = Color.Red ;
}
private void btn_MouseLeave ( object sender , System.EventArgs e )
{
//出箱
Button currentButton = ( Button ) sender ;
currentButton.BackColor = Control.DefaultBackColor ;
}
private void btn_Click ( object sender , System.EventArgs e )
{
if ( sender.GetType ( ) == typeof ( Button ) )
{
Button control = ( Button ) sender ;
MessageBox.Show ( control.Text + "被按动了!");
}
else
{
TextBox control = ( TextBox ) sender ;
MessageBox.Show ( control.Text + "被按动了!" ) ;
}
}
}
}

C#Winform技术群:25380362
博客:http:///boyliupan/
2007-03-28 14:41
天使不哭
Rank: 6Rank: 6
等 级:贵宾
威 望:23
帖 子:677
专家分:22
注 册:2006-7-9
得分:0 

5:在C#中运用SQLDMO备份和恢复SQLServer数据库


在C#中运用SQLDMO备份和恢复Microsoft SQL Server数据库

SQLDMO(SQL Distributed Management Objects,SQL分布式管理对象)封装了Microsoft SQL Server数据库中的对象。SQLDMO是Microsoft SQL Server中企业管理器所使用的应用程序接口,所以它可以执行很多功能,其中当然也包括对数据库的备份和恢复。

SQLDMO由Microsoft SQL Server自带的SQLDMO.dll提供,由于SQLDMO.dll是一个COM对象,所以大家在用之前必须在.NET项目中添加对它的引用。

下面是用C#语言书写的用于Microsoft SQL Server数据库备份和恢复的类:

using System;
namespace DbService
{
///
/// DbOper类,主要应用SQLDMO实现对Microsoft SQL Server数据库的备份和恢复
///
public sealed class DbOper
{
///
/// DbOper类的构造函数
///
private DbOper()
{
}
///
/// 数据库备份
///
public static void DbBackup()
{
SQLDMO.Backup oBackup = new SQLDMO.BackupClass();
SQLDMO.SQLServer oSQLServer = new SQLDMO.SQLServerClass();
try
{
oSQLServer.LoginSecure = false;
oSQLServer.Connect("localhost", "sa", "1234");
oBackup.Action = SQLDMO.SQLDMO_BACKUP_TYPE.SQLDMOBackup_Database;
oBackup.Database = "Northwind";
oBackup.Files = @"d:Northwind.bak";
oBackup.BackupSetName = "Northwind";
oBackup.BackupSetDescription = "数据库备份";
oBackup.Initialize = true;
oBackup.SQLBackup(oSQLServer);
}
catch
{
throw;
}
finally
{
oSQLServer.DisConnect();
}
}
///
/// 数据库恢复
///
public static void DbRestore()
{
SQLDMO.Restore oRestore = new SQLDMO.RestoreClass();
SQLDMO.SQLServer oSQLServer = new SQLDMO.SQLServerClass();
try
{
oSQLServer.LoginSecure = false;
oSQLServer.Connect("localhost", "sa", "1234");
oRestore.Action = SQLDMO.SQLDMO_RESTORE_TYPE.SQLDMORestore_Database;
oRestore.Database = "Northwind";
oRestore.Files = @"d:Northwind.bak";
oRestore.FileNumber = 1;
oRestore.ReplaceDatabase = true;
oRestore.SQLRestore(oSQLServer);
}
catch
{
throw;
}
finally
{
oSQLServer.DisConnect();
}
}
}
}

这段代码虽然很短,但是却很实用,希望能够对大家有所帮助。


C#Winform技术群:25380362
博客:http:///boyliupan/
2007-03-28 14:42
天使不哭
Rank: 6Rank: 6
等 级:贵宾
威 望:23
帖 子:677
专家分:22
注 册:2006-7-9
得分:0 
6:C#数据导出到Excel.

C#导出到EXCEL
1.首先声明,这些方法也都是本人搜集的资料,然后为已所用,程序中不足之处,还请高手指点. 这些方法都没有关闭Excel进程。
2.网上有好多关于用SQL语句导入导出的例子,这里不再重复写了。
方法1:调用com组件,导出access数据到Excel,就是直接调用access的导出功能,此方法速度超级快
using Access;
Access.ApplicationClass oAccess = new Access.ApplicationClass();
oAccess.Visible = false;
try
{ //ACCESS9:
oAccess.OpenCurrentDatabase("d:\\wcf.mdb",false,"");
//导出到excel
oAccess.DoCmd.TransferSpreadsheet(Access.AcDataTransferType.acExport,Acce ss.AcSpreadSheetType.acSpreadsheetTypeExcel9,"工作表名","d:\\wcf.xls",true,null,null);
//导入txt
//oAccess.DoCmd.TransferText(Access.AcTextTransferType.acExportDelim,"","Enterprise","d:\\wcf.txt",true,"",0);
oAccess.CloseCurrentDatabase();
oAccess.DoCmd.Quit(Access.AcQuitOption.acQuitSaveNone);
System.Runtime.InteropServices.Marshal.ReleaseComObject (oAccess);
oAccess = null;
MessageBox.Show("导入成功");
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
GC.Collect();
}
方法2:此方法速度也是超级快,只不过导出的格式非标准的Excel格式,默认工作表名与文件名相同
string FileName="d:\\abc.xls";
System.Data.DataTable dt=new System.Data.DataTable();
FileStream objFileStream;
StreamWriter objStreamWriter;
string strLine="";
objFileStream = new FileStream(FileName,FileMode.OpenOrCreate,FileAccess.Write);
objStreamWriter = new StreamWriter(objFileStream,System.Text.Encoding.Unicode);
for(int i=0;i<dt.Columns.Count;i++)
{
strLine=strLine+dt.Columns[i].ColumnName.ToString()+Convert.ToChar(9);
}
objStreamWriter.WriteLine(strLine);
strLine="";
for(int i=0;i<dt.Rows.Count;i++)
{
strLine=strLine+(i+1)+Convert.ToChar(9);
for(int j=1;j<dt.Columns.Count;j++)
{
strLine=strLine+dt.Rows[i][j].ToString()+Convert.ToChar(9);
}
objStreamWriter.WriteLine(strLine);
strLine="";
}
objStreamWriter.Close();
objFileStream.Close();
方法3:用Ado.net 此方法速度较以上两个显得慢了一些,数据量越大越明显
int Id=0;
string Name="测试";
string FileName="d:\\abc.xls";
System.Data.DataTable dt=new System.Data.DataTable();
long totalCount=dt.Rows.Count;
long rowRead=0;
float percent=0;
OleDbParameter[] parm=new OleDbParameter[dt.Columns.Count];
string connString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + FileName +";Extended Properties=Excel 8.0;";
OleDbConnection objConn = new OleDbConnection(connString);
OleDbCommand objCmd = new OleDbCommand();
objCmd.Connection = objConn;
objConn.Open();
//建立表结构
objCmd.CommandText = @"CREATE TABLE Sheet1(序号 Integer,名称 varchar)";
objCmd.ExecuteNonQuery();
//建立插入动作的Command
objCmd.CommandText = "INSERT INTO Sheet1("+Id+","+Name+")";
parm[0]=new OleDbParameter("@Id", OleDbType.Integer);
objCmd.Parameters.Add(parm[0]);
parm[1]=new OleDbParameter("@Company", OleDbType.VarChar);
objCmd.Parameters.Add(parm[1]);
//遍历DataTable将数据插入新建的Excel文件中
for(int i=0;i<dt.Rows.Count;i++)
{
parm[0].Value=i+1;
for(int j=1;j<parm.Length;j++)
{
parm[j].Value =dt.Rows[i][j];
}
objCmd.ExecuteNonQuery();
rowRead++;
percent=((float)(100*rowRead))/totalCount;
//this.FM.CaptionText.Text = "正在导出数据,已导出[" + percent.ToString("0.00") + "%]...";
if(i==dt.Rows.Count-1)
//this.FM.CaptionText.Text = "请稍后......";
System.Windows.Forms .Application.DoEvents();
}
objConn.Close();
//this.FM.CaptionText.Text = "";
方法4:此方法调用com组件,速度都慢于以上3个方法
using Excel;
System.Data.DataTable dt=new System.Data.DataTable();
string FileName="d:\\abc.xls";
long totalCount=dt.Rows.Count;
long rowRead=0;
float percent=0;
Excel.Application xlApp=null;
xlApp=new Excel.Application();
Excel.Workbooks workbooks=xlApp.Workbooks;
Excel.Workbook workbook=workbooks.Add(Excel.XlWBATemplate.xlWBATWorksheet);
Excel.Worksheet worksheet=(Excel.Worksheet)workbook.Worksheets[1];
//取得sheet1
Excel.Range range;
//写入字段
for(int i=0;i<dt.Columns.Count;i++)
{
worksheet.Cells[1,i+1]=dt.Columns[i].ColumnName;
range=(Excel.Range)worksheet.Cells[1,i+1];
}
for(int r=0;r<dt.Rows.Count;r++)
{
worksheet.Cells[r+2,1]=r+1;
for(int i=0;i<dt.Columns.Count;i++)
{
//worksheet.Cells[r+2,i+1]=dt.Rows[r][i];
if(i+1!=dt.Columns.Count)
worksheet.Cells[r+2,i+2]=dt.Rows[r][i+1];
}
rowRead++;
percent=((float)(100*rowRead))/totalCount;
//this.FM.CaptionText.Text = "正在导出数据,已导出[" + percent.ToString("0.00") + "%]...";
System.Windows.Forms .Application.DoEvents();
}
range=worksheet.get_Range(worksheet.Cells[2,1],worksheet.Cells[dt.Rows.Count+2,dt.Columns.Count]);
workbook.Saved =true;
workbook.SaveCopyAs(FileName);
//this.FM.CaptionText.Text = "";
方法5:利用剪贴板 ,有人说此方法很快,但是我用时,这种方法最慢,请高手指点.
System.Data.DataTable dt=new System.Data.DataTable();
string filePath=@"d:\abc.xls";
object oMissing = System.Reflection.Missing.Value;
Excel.ApplicationClass xlApp = new Excel.ApplicationClass();
try
{
xlApp.Visible = false;
xlApp.DisplayAlerts = false;
Excel.Workbooks oBooks = xlApp.Workbooks;
Excel._Workbook xlWorkbook = null;
xlWorkbook = oBooks.Open(filePath,oMissing,oMissing,oMissing,oMissing,oMissing,oMissing,
oMissing,oMissing,oMissing,oMissing,oMissing,oMissing,oMissing,oMissing);
Excel.Worksheet xlWorksheet;
// 添加入一个新的Sheet页。
xlWorksheet = (Excel.Worksheet)xlWorkbook.Worksheets.Add(oMissing,oMissing,1,oMissing);
// 以TableName作为新加的Sheet页名。
xlWorksheet.Name ="企业名录";
// 取出这个DataTable中的所有值,暂存于stringBuffer中。
string stringBuffer = "";
for( int j=0; j<dt.Rows.Count; j++ )
{
for( int k=0; k<dt.Columns.Count; k++ )
{
stringBuffer += dt.Rows[j][k].ToString();
if( k < dt.Columns.Count - 1 )
stringBuffer += "\t";
}
stringBuffer += "\n";
}
// 利用系统剪切板
System.Windows.Forms.Clipboard.SetDataObject("");
// 将stringBuffer放入剪切板。
System.Windows.Forms.Clipboard.SetDataObject(stringBuffer);
// 选中这个sheet页中的第一个单元格
((Excel.Range)xlWorksheet.Cells[1,1]).Select();
// 粘贴!
xlWorksheet.Paste(oMissing,oMissing);
// 清空系统剪切板。
System.Windows.Forms.Clipboard.SetDataObject("");
// 保存并关闭这个工作簿。
xlWorkbook.Close( Excel.XlSaveAction.xlSaveChanges, oMissing, oMissing );
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkbook);
xlWorkbook = null;

C#Winform技术群:25380362
博客:http:///boyliupan/
2007-03-28 14:43
天使不哭
Rank: 6Rank: 6
等 级:贵宾
威 望:23
帖 子:677
专家分:22
注 册:2006-7-9
得分:0 
7:C#Socket编程

Microsoft.Net Framework为应用程序访问Internet提供了分层的、可扩展的以及受管辖的网络服务,其名字空间System.Net和System.Net.Sockets包含丰富的类可以开发多种网络应用程序。.Net类采用的分层结构允许应用程序在不同的控制级别上访问网络,开发人员可以根据需要选择针对不同的级别编制程序,这些级别几乎囊括了Internet的所有需要--从socket套接字到普通的请求/响应,更重要的是,这种分层是可以扩展的,能够适应Internet不断扩展的需要。
抛开ISO/OSI模型的7层构架,单从TCP/IP模型上的逻辑层面上看,.Net类可以视为包含3个层次:请求/响应层、应用协议层、传输层。WebReqeust和WebResponse 代表了请求/响应层,支持Http、Tcp和Udp的类组成了应用协议层,而Socket类处于传输层。可以如下示意:
可见,传输层位于这个结构的最底层,当其上面的应用协议层和请求/响应层不能满足应用程序的特殊需要时,就需要使用这一层进行Socket套接字编程。
而在.Net中,System.Net.Sockets 命名空间为需要严密控制网络访问的开发人员提供了 Windows Sockets (Winsock) 接口的托管实现。System.Net 命名空间中的所有其他网络访问类都建立在该套接字Socket实现之上,如TCPClient、TCPListener 和 UDPClient 类封装有关创建到 Internet 的 TCP 和 UDP 连接的详细信息;NetworkStream类则提供用于网络访问的基础数据流等,常见的许多Internet服务都可以见到Socket的踪影,如Telnet、Http、Email、Echo等,这些服务尽管通讯协议Protocol的定义不同,但是其基础的传输都是采用的Socket。
其实,Socket可以象流Stream一样被视为一个数据通道,这个通道架设在应用程序端(客户端)和远程服务器端之间,而后,数据的读取(接收)和写入(发送)均针对这个通道来进行。
可见,在应用程序端或者服务器端创建了Socket对象之后,就可以使用Send/SentTo方法将数据发送到连接的Socket,或者使用Receive/ReceiveFrom方法接收来自连接Socket的数据;
针对Socket编程,.NET 框架的 Socket 类是 Winsock32 API 提供的套接字服务的托管代码版本。其中为实现网络编程提供了大量的方法,大多数情况下,Socket 类方法只是将数据封送到它们的本机 Win32 副本中并处理任何必要的安全检查。如果你熟悉Winsock API函数,那么用Socket类编写网络程序会非常容易,当然,如果你不曾接触过,也不会太困难,跟随下面的解说,你会发觉使用Socket类开发windows 网络应用程序原来有规可寻,它们在大多数情况下遵循大致相同的步骤。
在使用之前,你需要首先创建Socket对象的实例,这可以通过Socket类的构造方法来实现:
public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolType);
其中,addressFamily 参数指定 Socket 使用的寻址方案,socketType 参数指定 Socket 的类型,protocolType 参数指定 Socket 使用的协议。
下面的示例语句创建一个 Socket,它可用于在基于 TCP/IP 的网络(如 Internet)上通讯。
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
若要使用 UDP 而不是 TCP,需要更改协议类型,如下面的示例所示:
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
一旦创建 Socket,在客户端,你将可以通过Connect方法连接到指定的服务器,并通过Send/SendTo方法向远程服务器发送数据,而后可以通过Receive/ReceiveFrom从服务端接收数据;而在服务器端,你需要使用Bind方法绑定所指定的接口使Socket与一个本地终结点相联,并通过Listen方法侦听该接口上的请求,当侦听到用户端的连接时,调用Accept完成连接的操作,创建新的Socket以处理传入的连接请求。使用完 Socket 后,记住使用 Shutdown 方法禁用 Socket,并使用 Close 方法关闭 Socket。其间用到的方法/函数有:
Socket.Connect方法:建立到远程设备的连接
public void Connect(EndPoint remoteEP)(有重载方法)
Socket.Send 方法:从数据中的指示位置开始将数据发送到连接的 Socket。
public int Send(byte[], int, SocketFlags);(有重载方法)
Socket.SendTo 方法 将数据发送到特定终结点。
public int SendTo(byte[], EndPoint);(有重载方法)
Socket.Receive方法:将数据从连接的 Socket 接收到接收缓冲区的特定位置。
public int Receive(byte[],int,SocketFlags);
Socket.ReceiveFrom方法:接收数据缓冲区中特定位置的数据并存储终结点。
public int ReceiveFrom(byte[], int, SocketFlags, ref EndPoint);
Socket.Bind 方法:使 Socket 与一个本地终结点相关联:
public void Bind( EndPoint localEP );
Socket.Listen方法:将 Socket 置于侦听状态。
public void Listen( int backlog );
Socket.Accept方法:创建新的 Socket 以处理传入的连接请求。
public Socket Accept();
Socket.Shutdown方法:禁用某 Socket 上的发送和接收
public void Shutdown( SocketShutdown how );
Socket.Close方法:强制 Socket 连接关闭
public void Close();
可以看出,以上许多方法包含EndPoint类型的参数,在Internet中,TCP/IP 使用一个网络地址和一个服务端口号来唯一标识设备。网络地址标识网络上的特定设备;端口号标识要连接到的该设备上的特定服务。网络地址和服务端口的组合称为终结点,在 .NET 框架中正是由 EndPoint 类表示这个终结点,它提供表示网络资源或服务的抽象,用以标志网络地址等信息。.Net同时也为每个受支持的地址族定义了 EndPoint 的子代;对于 IP 地址族,该类为 IPEndPoint。IPEndPoint 类包含应用程序连接到主机上的服务所需的主机和端口信息,通过组合服务的主机IP地址和端口号,IPEndPoint 类形成到服务的连接点。
用到IPEndPoint类的时候就不可避免地涉及到计算机IP地址,.Net中有两种类可以得到IP地址实例:
IPAddress类:IPAddress 类包含计算机在 IP 网络上的地址。其Parse方法可将 IP 地址字符串转换为 IPAddress 实例。下面的语句创建一个 IPAddress 实例:
IPAddress myIP = IPAddress.Parse("192.168.1.2");
Dns 类:向使用 TCP/IP Internet 服务的应用程序提供域名服务。其Resolve 方法查询 DNS 服务器以将用户友好的域名(如"host.contoso.com")映射到数字形式的 Internet 地址(如 192.168.1.1)。Resolve方法 返回一个 IPHostEnty 实例,该实例包含所请求名称的地址和别名的列表。大多数情况下,可以使用 AddressList 数组中返回的第一个地址。下面的代码获取一个 IPAddress 实例,该实例包含服务器 host.contoso.com 的 IP 地址。
IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];
你也可以使用GetHostName方法得到IPHostEntry实例:
IPHosntEntry hostInfo=Dns.GetHostByName("host.contoso.com")
在使用以上方法时,你将可能需要处理以下几种异常:
SocketException异常:访问Socket时操作系统发生错误引发
ArgumentNullException异常:参数为空引用引发
ObjectDisposedException异常:Socket已经关闭引发
在掌握上面得知识后,下面的代码将该服务器主机( host.contoso.com的 IP 地址与端口号组合,以便为连接创建远程终结点:
IPEndPoint ipe = new IPEndPoint(ipAddress,11000);
确定了远程设备的地址并选择了用于连接的端口后,应用程序可以尝试建立与远程设备的连接。下面的示例使用现有的 IPEndPoint 实例与远程设备连接,并捕获可能引发的异常:
try {
s.Connect(ipe);//尝试连接
}
//处理参数为空引用异常
catch(ArgumentNullException ae) {
Console.WriteLine("ArgumentNullException : {0}", ae.ToString());
}
//处理操作系统异常
catch(SocketException se) {
Console.WriteLine("SocketException : {0}", se.ToString());
}
catch(Exception e) {
Console.WriteLine("Unexpected exception : {0}", e.ToString());
}
需要知道的是:Socket 类支持两种基本模式:同步和异步。其区别在于:在同步模式中,对执行网络操作的函数(如 Send 和 Receive)的调用一直等到操作完成后才将控制返回给调用程序。在异步模式中,这些调用立即返回。
另外,很多时候,Socket编程视情况不同需要在客户端和服务器端分别予以实现,在客户端编制应用程序向服务端指定端口发送请求,同时编制服务端应用程序处理该请求,这个过程在上面的阐述中已经提及;当然,并非所有的Socket编程都需要你严格编写这两端程序;视应用情况不同,你可以在客户端构造出请求字符串,服务器相应端口捕获这个请求,交由其公用服务程序进行处理。以下事例语句中的字符串就向远程主机提出页面请求:
string Get = "GET / HTTP/1.1\r\nHost: " + server + "\r\nConnection: Close\r\n\r\n";
远程主机指定端口接受到这一请求后,就可利用其公用服务程序进行处理而不需要另行编制服务器端应用程序。
综合运用以上阐述的使用Visual C#进行Socket网络程序开发的知识,下面的程序段完整地实现了Web页面下载功能。用户只需在窗体上输入远程主机名(Dns 主机名或以点分隔的四部分表示法格式的 IP 地址)和预保存的本地文件名,并利用专门提供Http服务的80端口,就可以获取远程主机页面并保存在本地机指定文件中。如果保存格式是.htm格式,你就可以在Internet浏览器中打开该页面。适当添加代码,你甚至可以实现一个简单的浏览器程序。
实现此功能的主要源代码如下:
//"开始"按钮事件
private void button1_Click(object sender, System.EventArgs e) {
//取得预保存的文件名
string fileName=textBox3.Text.Trim();
//远程主机
string hostName=textBox1.Text.Trim();
//端口
int port=Int32.Parse(textBox2.Text.Trim());
//得到主机信息
IPHostEntry ipInfo=Dns.GetHostByName(hostName);
//取得IPAddress[]
IPAddress[] ipAddr=ipInfo.AddressList;
//得到ip
IPAddress ip=ipAddr[0];
//组合出远程终结点
IPEndPoint hostEP=new IPEndPoint(ip,port);
//创建Socket 实例
Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
try
{
//尝试连接
socket.Connect(hostEP);
}
catch(Exception se)
{
MessageBox.Show("连接错误"+se.Message,"提示信息
,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//发送给远程主机的请求内容串
string sendStr="GET / HTTP/1.1\r\nHost: " + hostName +
"\r\nConnection: Close\r\n\r\n";
//创建bytes字节数组以转换发送串
byte[] bytesSendStr=new byte[1024];
//将发送内容字符串转换成字节byte数组
bytesSendStr=Encoding.ASCII.GetBytes(sendStr);
try
{
//向主机发送请求
socket.Send(bytesSendStr,bytesSendStr.Length,0);
}
catch(Exception ce)
{
MessageBox.Show("发送错误:"+ce.Message,"提示信息
,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//声明接收返回内容的字符串
string recvStr="";
//声明字节数组,一次接收数据的长度为1024字节
byte[] recvBytes=new byte[1024];
//返回实际接收内容的字节数
int bytes=0;
//循环读取,直到接收完所有数据
while(true)
{
bytes=socket.Receive(recvBytes,recvBytes.Length,0);
//读取完成后退出循环
if(bytes<=0)
break;
//将读取的字节数转换为字符串
recvStr+=Encoding.ASCII.GetString(recvBytes,0,bytes);
}
//将所读取的字符串转换为字节数组
byte[] content=Encoding.ASCII.GetBytes(recvStr);
try
{
//创建文件流对象实例
FileStream fs=new FileStream(fileName,FileMode.OpenOrCreate,FileAccess.ReadWrite);
//写入文件
fs.Write(content,0,content.Length);
}
catch(Exception fe)
{
MessageBox.Show("文件创建/写入错误:"+fe.Message,"提示信息",MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//禁用Socket
socket.Shutdown(SocketShutdown.Both);
//关闭Socket
socket.Close();
}
}
程序在WindowsXP中文版、.Net Frameworkd 中文正式版、Visual Studio.Net中文正式版下调试通过

C#Winform技术群:25380362
博客:http:///boyliupan/
2007-03-28 14:44
天使不哭
Rank: 6Rank: 6
等 级:贵宾
威 望:23
帖 子:677
专家分:22
注 册:2006-7-9
得分:0 
8:VS.Net 下的Wondows窗体常用项目


使用.Net编写Windows程序,对于窗体控制常见项目
1、让窗体在启动时在指定位置出现
form1.StartPosition
Manual
CenterScreen
WindowsDefaultLocation (default)
WindowsDefaultBounds
CenterParent
只有在窗体启动前设置才有效。
2、设置窗体的图标
form1.Icon
3、设置该窗体成为多个子窗体的父窗体
form1.isMidContainer = true / false(default)
在设计时使用,一个项目中只能有一个父窗体。
4、指定最大化、最小化按钮的行为
form1.ControlBox = true(default) / false // 设置是否出现最大化、最小化和关闭按钮
form1.MaxmizeBox = true(default) / false /// 设置最大化按钮是否有效
form1.MinimizeBox = true(default) / false /// 设置最小化按钮是否有效
如果当ControlBox = true MaxmizeBox = false MinimizeBox = false 而 HelpButton = true 时
就可以看到有个帮助的按钮出现在关闭按钮旁边。
5、窗体如果想显示普通菜单那么需要添加菜单控件MainMenu
form1.Menu 选项用于指定使用那个菜单。
如果想动态加载菜单就先准备好要用的几个MainMenu控件,然后到适当时机在运行时改变form1.Menu 的值。
如果要使用快捷菜单,就像窗体中添加ContextMenu控件。然后指定form1.ContextMenu 为这个控件。
6、设置窗体的透明度
form1.Opacity
它的值是一个百分数,范围是0~100%,当它等于0时整个窗体就不可间隐藏起来了。当他等于100%时,这个窗体就是个普通窗体。
7、设置是否在任务栏中显示窗体
form1.ShowInTaskbar = true(default) / false
默认值是true, 当他设置为false时就从任务栏消失了。通常可以配合NotifyIcon来实现将程序隐藏到系统右下角的系统状态栏。
8、设置是否显示窗体右下角调整大小的手柄
form1.SizeGripStyle = Auto(default) / Show /Hide
默认值为Auto,那么它会根据窗体的显示样式来决定是否显示这个手柄。例如:当窗体设置为对不能调节大小的话框时,这个手柄就不会出现。
9、设置窗体在最前端现实,例如一些播放器和聊天软件,他们通常都在所有窗体的上面。
form1.TopMost = true / false(default)
10、设置窗体部分透明
form1.Transparencykey
它接受一个颜色值,当窗体中有颜色和这个设定颜色一致时,系统将这些颜色的区域设为透明。
//貌似只能在显示器色深24位以下才有效,这是在VS.NET2003下的问题,不知道05下有没有解决
11、设置窗体背景图片
form1.BackColor /// 设置窗体背景颜色
form1.BackgroundImage /// 设置窗体背景图片
12、设置窗体中鼠标指针形状
form1.Cursor
13、设置窗体边框的外观、以前叫窗体的风格
form1.FormBorderStyle //属性
None
FixedSingle
Fixed3D
FixedDialog
Sizable(default)
FixedToolWindow
SizableToolWindow
14、改变窗体的标题
form1.Text
15、设置窗体的默认的确认按钮和取消按钮
form1.AcceptButton /// 设置默认确认按钮
form1.CancelButton /// 设置默认取消按钮
使用时需要先在窗体上添加两个按钮,然后将他们指派到对应的属性上。

C#Winform技术群:25380362
博客:http:///boyliupan/
2007-03-28 14:44
天使不哭
Rank: 6Rank: 6
等 级:贵宾
威 望:23
帖 子:677
专家分:22
注 册:2006-7-9
得分:0 

9:如何动态加载控件以及插件编程思想(C#)


控件,在实现快速开发中起着非常重要的作用,它可以将某一特定功能封装起来,供可户程序员调用,更重要的是它还可以实现插件式开发,使软件的灵活性、可扩充性大大增强。在网络上,也有很多动态加载控件、动态调用类成员等的资料。下面,我就将动态加载控件总结一下,以供大家参考。(不过由于本人水平有限,不一定有参考价值,写出来一方面是为了总结自己,以求提高,另一方面也希望各为朋友看到我的不足,给我提出宝贵意见)
一、动态加载控件
动态加载,最基本用到的就是反射机制。在System.Reflection的namespace下有一系列的关于获取Assembly信息、类(型)信息的类、接口、结构等。可能上面的话对急切想实现动态加载控件的朋友来说可能一点用也没有,那么就看下面的代码吧,也许可以使你马上实现你想要的:
//加载控件
Assembly assembly = Assembly.LoadFrom(@"C:\Controls.dll");
//获得类(型)
Type type = assembly.GetType("Controls.UserControl",false,true);
//设置筛选标志
BindingFlags bflags = BindingFlags.DeclaredOnly | BindingFlags.Public
| BindingFlags.NonPublic | BindingFlags.Instance;
//调用构造函数并获得对象
Object obj = type.InvokeMember("UserControl", bflags |
BindingFlags.CreateInstance, null, null, null);
//将对象转换类型
System.Windows.Forms.Control c = (Control)obj;
//将控件添加到窗体
this.Controls.Add(c);

下面对上面程序段用到的一些变量、方法做一点说明
1、BindingFlags,枚举类型
BindingFlags.Instance : 对象实例
BindingFlags.Static : 静态成员
BindingFlags.Public : 指可在搜索中包含公共成员
BindingFlags.NonPublic : 指可在搜索中包含非公共成员(即私有成员和受保护的成员)
BindingFlags.FlattenHierarchy : 指可包含层次结构上的静态成员
BindingFlags.IgnoreCase : 表示忽略 name 的大小写
BindingFlags.DeclaredOnly : 仅搜索 Type 上声明的成员,而不搜索被简单继承的成员
BindingFlags.CreateInstance : 表示调用构造函数。忽略 name。对其他调用标志无效
2、Type.InvokeMember
public object InvokeMember(
string name,
BindingFlags invokeAttr,
Binder binder,
object target,
object[] args
);
参数
name
String,它包含要调用的构造函数、方法、属性或字段成员的名称。
- 或 -
空字符串 (""),表示调用默认成员。
invokeAttr
一个位屏蔽,由一个或多个指定搜索执行方式的 BindingFlags 组成。 访问可以是 BindingFlags 之一,如Public、 NonPublic、Private、 InvokeMethod 和 GetField 等。不需要指定查找类型。如果省略查找类型, 则将应用 BindingFlags.Public | BindingFlags.Instance。
binder
一个 Binder 对象,该对象定义一组属性并启用绑定,而绑定可能涉及选择重载方法、 强制参数类型和通过反射调用成 员。 - 或 - 若为空引用(Visual Basic 中为 Nothing),则使用 DefaultBinder。
target
要在其上调用指定成员的 Object。
args
包含传递给要调用的成员的参数的数组。
返回值
表示被调用成员的返回值的 Object。
二、插件编程
通过上面代码段,我们基本实现动态加载控件。由此我想到了现在网上提到很多的插件式的开发方法。通过动态加载控件,我们不是能很方便的为软件扩充功能吗?我不知道Eclipse这种插件是怎么实现的,但至少这种动态加载控件的方法实现插件编程的一个变通的方法。不是吗?我把一个功能模块做成一个控件,然后在程序启动是扫描目录,即可获得所有的控件,当点击菜单是,将控件加载到窗体就行了。我在母体程序里,我们所要做的只不过要一个容器窗口类来加载控件。当然,事先要有些约定,比如说,控件有哪些可供调用的方法等等。


C#Winform技术群:25380362
博客:http:///boyliupan/
2007-03-28 14:45



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-127501-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.145847 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved