C#知识总结

一:文件监控

二:自升级

三:流量控制

四:SQLite数据库

五:c#杂谈

六:编写unicode程序遇到的问题及常用的解决方法

 

一:文件监控

实现文件监控的方式共使用了三种方式,这三种实现的方式分别是:ReadDirectoryChangesW事件监控通知,ICopyHook shell扩展接口,第三种:API Hook技术。

每一种方式都有各自的优缺点,现总结如下:

l         ReadDirectoryChangesW方式:

第一种使用ReadDirectoryChangesW函数来监视指定文件内信息改变通知,但是这个函数使用有其使用限制,首先,从原理上来看看,该函数需要预先分配一份空间来存储指定文件信息改变,在本次与下次调用之间对缓冲区信息前后匹对是否有信息发生改变,一旦发生改变的信息超出了预先分配的空间会出现错误,同时对于网络文件监视由于文件共享协议数据包大小的限制,监视发生改变信息缓冲区大小不能超过64KB,其次在我们这里还有一个重要的原因,为了监视被删文件的大小,我们必须要在文件删除前获取到该文件的信息,但是使用这种方式只能在文件删除后才能获取到其信息改变通知。

 

l         ICopyHook

第二种方式: 结合第一种方式的弊端,努力寻求一种类似于钩子的接口,ICopyHook可以截获关于对文件操作的行为,但是ICopyHook只能对文件夹的操作方式[moved, copied, renamed, or deleted]进行监控,对于文件操作则不能提前拦截。

该工程使用ATL来实现的,生成的DLL会被这册到如下位置:

*         HKEY_CLASSES_ROOT

·     Directory

· Shellex

·     CopyHookHandlers

·         your_copyhook
(Default) = {copyhook CLSID value}

 

注册后需要重启才能看到效果。

 

l         API Hook

利用这种技术来完成对DllIAT(import address table)进行改写,用我们自己编写的DLL进行远程注入替换所指定的API 函数,就文件监控而言,对于文件的操作都是在Explorer.exe这个进程中完成的,接下来就是找到我们需要修改API函数所在的DLL即可,关于DLL的导出函数可以借助depends工具查看。

 

对于文件的删除有两种方式:永久删除及放入回收站。

MSDN描述:

When used to delete a file, SHFileOperation attempts to place the deleted file in the Recycle Bin. If you wish to delete a file and guarantee that it is not placed in the Recycle Bin, use DeleteFile.

为了截获这两种方式,当远程成功注入我们的DLLExplorer.exe后,需要分别查询到SHFileOperationDeleteFile所在的DLL,通过Depends工具查看发现分别是SHELL32.dllKernel32.dll,理论上修改这两个函数的IAT本应就可以达到我们的目的,实践结果表明我们可以成功截获DeleteFile操作,但是SHFileOperation不能成功截取,据网上非官方信息透露:当操作系统启动后,系统给予SHFileOperation加载了保护措施,所以在我们的应用程序中不能实现对SHFileOperation进行拦截而只能在驱动级别上实现。

这种方式实现代码比较麻烦,需要捕获API函数较多,同时往往一个API又有多种实现版本(如:UNICODEANSI, 普通及扩展函数),为了保证程序成功执行,需要截获每一种可能使用到API版本。

 

参考方案:

第一:通过detours开源库实现,实现的原理就是API Hook的封装,内部代码有些涉及到了汇编,不太容易看明白。

第二:编写文件驱动【网上大多推荐此方式,但是该方式实现难度相对较大】

 

二:自升级

  最初使用微软Aplication Update Block 2.0的一套SDK来完成自升级功能,后来由于这套SDK实现的方式是在服务器端通过配置文件来指定客户的安装路径,以及程序内部的实现太复杂,很多细节不太清楚,最后放弃使用并自己利用C#实现自升级功能。

  沿用 Appliation Update Block 2.0 自升级的设计思想,分别在服务器端及客户端提供一份配置文件。

 

客户端的配置文件:一个GUID号,应用程序ID以及存放更新文件的路径;

服务器端的配置文件:写入在服务器端已更新的应用程序的ID, 更新的GUID号,更新文件在服务器端的路径。

 

客户端配置文件一开始的属性值全是空的,一旦下载了更新文件,才把服务器端配置文件的相关值写入到客户配置文件中,以方便更新启动时进行比对。

 

在实现自升级代码部分使用的相关知识:

1:文件下载:

具体可参见:

http://www.geekpedia.com/tutorial179_Creating-a-download-manager-in-Csharp.html

[有部分缺陷]

http://www.geekpedia.com/tutorial196_Creating-an-advanced-download-manager-in-Csharp.html

[改进版]

主要用到 HttpWebRequest, HttpWebResponse, FileStream.

HttpWebRequest类的方法:

     public override WebResponse GetResponse();

 

奇怪的问题:

HttpWebResponse webResponse;

Stream strResponse = webResponse.GetResponseStream();

 

WebClient wcDownload;

Stream strResponse = wcDownload.OpenRead(strRemoteFileFullName);

 

First, we drop the WebClient object from the previous project since instead of using the OpenRead method of the WebClient, we are now using the GetResponseStream method of the HttpWebResponse object to get the data from the URL into a stream.

如果不这样做,当我们在本地进行文件下载这两种方法均没有问题,但是一旦使用WebClient.OpenRead来下载远程机器上的文件时,在第二次调用时就会出现Timeout Exception, 机器看上去就像Hang了一样。

而改用webResponse.GetResponseStream();就可以避免这个问题。

 

2xml文件操作:

自定义配置节:首先需要声明,然后再定义;

<section name=”配置节名称  type=”namespace+类名, namespace” />以下就是一个例子.一个是自定义的clientApplication, 另一个是内部配置文件定义好的appSettings配置节.

 

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <configSections>

    <section name="clientApplication" type="VAS.Update.UpdateClientConfigurationSection,VAS.Update" />

  </configSections>

  <appSettings>

    <add key="VASUpdateRootDir" value="http://localhost/" />

  </appSettings>

  <clientApplication>

  </clientApplication>

</configuration>

 

XML文件读写参见:

http://wenku.baidu.com/view/d5db9f80d4d8d15abe234edc.html

 

<root>

  <a attribute="aa">this is  a test </a>

</root>

 

<root>:为跟元素,任何一个XML有且只有一个元素被称为根元素。

<a>:表示为元素名或标签名。

attribute:属性名,属性名与属性值要成对出现。

aa:属性值,值必须要用双,单引号。

this is a test:表示元素的值。

 

3:用C#编写服务程序:

l         首先创建windows services应用程序,在属性窗口里面修改服务的名称,就是那个继承于System.ServiceProcess.ServiceBase的类。

l         在设计视图里面选择鼠标右键,添加安装程序,这时候在解决方案视图里面会产生一个ProcessInstaller文件同时在引用里面自动添加System.Configuration.Install.dllSystem.ServiceProcess.dll文件。ProcessInstaller继承于System.Configuration.Install.Installer

l         在解决方案视图里面,点击ProcessInstaller进入设计视图.

选择serviceProcessInstaller1,将其属性Account的值改为:LocalSystem..

选择serviceInstaller1,将其属性StartType的值改为:Automatic.

l   最后添加新项目:并在“其他项目类型”à“安装和部署”—>“安装项目”。

   选中“×× setup,视图, 文件系统。然后选择“添加”》项目输出,在弹出的对话框,选择正确的“项目”,然后再列表框中选择“主输出”及“配置”。确定后所设计的.exe文件及Dll会自动添加进来。

l       添加卸载功能。  

   添加C:/Windows/System32/msiexec.exe文件,如果需要可以添加快捷方式或重命名。

l       怎样为了使服务一开始就启动?

[类似的还可以添加注册表之类的注册功能,如:COM注册以执行regsvr32命令]

   ProcessInstaller中重载相应的函数如:

protected virtual void OnAfterUninstall(IDictionary savedState); //Installer中的声明

protected override void OnAfterInstall(IDictionary savedState)

{

            base.OnAfterInstall(savedState);

            //安装后,暂不启动服务

            System.ServiceProcess.ServiceController sc = new System.ServiceProcess.ServiceController();

            sc.ServiceName = "VAS Visit";

            sc.Start(); // 启动服务          

}

l       COM组件除了有类似上述的注册方法以外,还有一种简单的设置方法。

    选择需要注册的dll文件,在属性窗口中,选择Register:将属性改为vsdrfCOMSelfReg

 

4:如何停止一般的进程及服务进程:

一般进程:

Process[] processes = GetProcessByName(processName);

foreach(process ps in processes)

{

ps.kill;

}

 

服务进程:

void CloseService(string serviceName)

{

   ServiceController sc = new ServiceController(serviceName)

   if (sc.status == ServiceControllerStatus.Running)

{

    sc.close();

    for (int i=0; i<60; ++i)

{

    System.Thread.Sleep(1000);

    sc.Refresh();

    if (sc.status == ServiceControllerStatus.Stopped)

{

   break;

}

if(i == 59)

{

   throw new Exception(”关闭服务不成功”);

}

}

}

}

 

5:文件操作相关:

l        验证输入的路径是否合法:

static void Main(string[] args)

{

try

{//如果用户输入的存放路径 "C:/temp/" 会被解析为 C:/temp”,以下判断就是为了允许这种格式的输入所作的处理

        if (args[1][args[1].Length - 1] == '"')

        {

              args[1] = args[1].TrimEnd('"');

        }

        //检查输入存放路径是否正确,如果不正确会抛出 ArgumentException 类型异常。

        Path.IsPathRooted(args[1]);

}

catch {}

}

 

三:流量控制

 

四:SQLite数据库

SQLite创建数据库方便简单高效,可以在机器中使用任何一种类型作为文件后缀,安装后会自动与vs2010开发工具集成。

vs2010中,点击“数据”-》显示数据源,添加数据源,添加完之后,打开“视图”》“其他窗口”》“服务器资源管理器”。就可以编辑数据源。

数据源:就是数据库文件存放的路径。例如:Data Source = “***/***/*.db3”.

 

执行SQL语句需要用到的重要类:

SQLiteConnection, SQLiteCommand.

SQLiteCommand的几个需要注意的方法:

SQLiteCommand.ExecuteNonQuery Method

Execute the command and return the number of rows inserted/updated affected by it.

SQLiteCommand.ExecuteScalar Method

Execute the command and return the first column of the first row of the resultset (if present), or null if no resultset was returned.

      

执行对数据写操作时记得要加锁。

 

l         Vs2010SQLite工具集成缺陷:

当时在我的数据库设计中有一些表的字段是DataTime的数据格式,为了检验所写的SQL语句是否正确,当时就把程序里面获取sql语句提取出来,然后放在vs2010集成的sqlite中进行执行,发现执行不能成功,提示告诉可能要使用TO_DATE()函数,后来怎么加也不对,在网上搜索N多方法也是不行,最后实在没有办法,询问前辈,后来发现是工具的问题,将我的SQL语句放在SQLiteDeveloper中执行成功,而且不用什么datetime, to_date之类的函数,直接使用>,<,=逻辑判断符就可以了。从这次经历发现工具有时候也不可尽信。

 

l         SQLite可以执行SQL语句,简单的常用SQL语句如下:

选择:

  select * from 表名 where 字段名 条件

select * from 表名 where字段名 条件order by 字段名DESC

select count(*) from表名 where 字段名 条件

 删除:

   delete from 表名 where 字段名 条件

 更新:

   update 表名set(字段名=新值,字段名=新值,...) where 字段名 条件

 插入:

   insert into表名(字段名,字段名,…) values (新值,新值,…)

 

l         SQLite对于日期字段的处理:

对于日期,你不能直接插入一个类似于这样的日期“YYYY-MM-DD HH-MM-SS

而是要对时间使用ToString(“s”);这样这个时间就变成了

YYYY-MM-DDTHH-MM-SS.

在中间加入了一个字母’T’.

 

l         其他:

[SQLite中对于字符串使用单引号将其包含起来,在oracle数据库也是这样。]

 

五:c#杂谈

l         什么是.net framework, 什么是托管,什么是非托管。

.net framework允许跨语言,如C#写的代码可以调用vb.net写的程序,自动管理内存。

 

托管:就是基于.net framework之上。如C#.

 

非托管:就是不是基于.net framework技术,所以就需要程序员自己来管理内存等。如C++

 

l         委托使用:

public delegate void FinishUpgrade(string localPath, string guid);

FinishUpgrade就好比是一种数据类型。

委托就好比是C++里面的函数指针。

委托在C#的定义及使用方式,举例如下:

class Upgrade

{

public delegate void FinishUpgrade(string localPath, string guid);

public bool Download(string localPath, FinishUpgrade finishUpgradeCallback)

{

   

//下载结束后回调通知

            finishUpgradeCallback(localPath, guid);

            return true;

}

}

 

Upgrade upObj = new Upgrade (uri);

upObj.Download(localPath, FinishUpgradeCallback);

      public void FinishUpgradeCallback(string localPath, string guid)

      {

//执行回调

}

 

 

l         事件使用:

namespace System

{

 public delegate void EventHandler(object sender, EventArgs e);

}

 

namespace System.Windows.Forms

{

  public class Form : ContainerControl

{

public event EventHandler Load;

}

}

 

namespace testTimer

{

        public partial class Form1 : Form

    {

public Form1()

       {

      this.Load += new EventHandler(ExecuteEventHandler);

}

}

}

 

private void ExecuteEventHandler(object sender, EventArgs e)

{

    // 执行事件处理

}

l         string

MSDN中对string的说明:string is an alias for String in the .NET Framework

呵呵stringString的别名而已,都是一家。

 

具体参见:

http://terrylee.cnblogs.com/archive/2005/12/26/304876.aspx,这篇文章关于《不可改变对象》的说服力不是很强,如果再配上即使窗口或内存窗口,就可以很清楚地看到地址的变化。

 

http://msdn.microsoft.com/en-us/library/ms228362(VS.80).aspx

 

配上即时窗口示例代码:

using System;

using System.Collections.Generic;

using System.Text;

 

namespace studyString

{

    class Program

    {

        static void Main(string[] args)

        {

            string b = "1234";

            string a = b;

            b = "24567";

            Console.WriteLine("{0}, {1}", b, a);

        }

    }

}

即时窗口:

string b = "1234";

&a

0x06deecbc

    a: 0x00000000

&b

0x06deecc0

b: 0x0141021c

string a = b;

&a

0x06deecbc

    a: 0x0141021c

&b

0x06deecc0

    b: 0x0141021c

b = "24567";

&a

0x06deecbc

    a: 0x0141021c

&b

0x06deecc0

b: 0x01410238

从这个调试窗口可以看到,在没有改变字符串之前,ab字符串是指向同一个空间的,一旦改变就会创建新的对象。

 

l         几个重要的关键字[using, sealed, internal]

using – 参见文章:http://443065.blog.51cto.com/433065/92805

sealed(密封) – 可以放在类名或基类虚方法或虚属性上。

如:sealed class B{}

    sealed protected override void F(){}

internal – 常应用于组件开发,使组件以私有的方式进行合作,而不必向应用程序的其他部分公开。

如:修饰类及数据成员。

namespace internalClass

{

    internal class BaseClass

{

        static int intM = 0;

         }

}

      

namespace internalClass

{

     class BaseClass

{

        internal static int intM = 0;

         }

}

ref, out

     ref,out就好比是c++里面的引用传递,ref表示引用,在传递前需要初始化,out好比指针,传递前不需要初始化。

 

as, is

as强制转换:  如果转换非法,你不用担心会出现异常,取而代之的是产生一个值为null的转换结果。格式 object = expression is type.

is 操作符:is操作符使得可以在运行时检查一种类型是否与另外一种类型相兼容,这个操作符采用的格式:expresssion is type. 返回布尔值,expression 是一种引用类型。

   class Employee {}

    class ContractEmployee : Employee {}

    public class Example

    {

        public static void Main()

        {

            //方式一:产生System.InvalidCastException异常

            // Employee e = new Employee();

            // ContractEmployee ce = (ContractEmployee)e;

 

            //方式二:产生System.InvalidCastException异常

            // ContractEmployee ce = (ContractEmployee)(new Employee());

 

            // 使用 as 转换的好处是:如果转换非法,你不用担心会出现异常,取而代之的是产生一个值为null的转换结果。

            ContractEmployee ce = new Employee() as ContractEmployee;

            Console.WriteLine("the result is {0}", ce == null ? "null" : ce.ToString());

        }

}

 

abstract, virtual, override

abstract:抽象,由派生类来决定具体的行为,基类只做声明。

virtual: 虚方法,如果派生类要重载这个virtual修饰的属性或方法时,要用override.

override: 重载。派生类中用来重写基类中被abstractvirtual修饰的方法或属性。

static:使用方式同C++.

 

l         控制台阻塞主线程的方法

Console.Read(),Console.ReadLine();

 

l         C#线程间通信及同步技术

事件:人工重置,自动重置事件两种。[ManualResetEvent, AutoResetEvent]

人工重置事件:当事件得到通知,等待该事件的所有线程均变为可调度线程。

自动重置事件:当事件得到通知,等待该事件的线程中只有一个线程变为可调度线程。

重要方法:

Reset: 重置为无信号

Set(): 设置为有信号

WaitOne(): 阻止当前线程。

SignalAndWait(WaitHandle a, WaitHandle b): 向前者 a 发出信号并等待后者 b

示例代码如下:

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

 

namespace MonitorThreadWork

{

    using System;

    using System.Threading;

 

    public class Example

    {

        // The EventWaitHandle used to demonstrate the difference

        // between AutoReset and ManualReset synchronization events.

        //

        private static EventWaitHandle ewh;

 

        // A counter to make sure all threads are started and

        // blocked before any are released. A Long is used to show

        // the use of the 64-bit Interlocked methods.

        //

        private static long threadCount = 0;

 

        // An AutoReset event that allows the main thread to block

        // until an exiting thread has decremented the count.

        //

        private static EventWaitHandle clearCount =

            new EventWaitHandle(false, EventResetMode.AutoReset);

 

        [MTAThread]

        public static void Main()

        {

            // Create an AutoReset EventWaitHandle.

            //

            ewh = new EventWaitHandle(false, EventResetMode.AutoReset);

 

            // Create and start five numbered threads. Use the

            // ParameterizedThreadStart delegate, so the thread

            // number can be passed as an argument to the Start

            // method.

            for (int i = 0; i <= 4; i++)

            {

                Thread t = new Thread(

                    new ParameterizedThreadStart(ThreadProc)

                );

                t.Start(i);

            }

 

            // Wait until all the threads have started and blocked.

            // When multiple threads use a 64-bit value on a 32-bit

            // system, you must access the value through the

            // Interlocked class to guarantee thread safety.

            //

            while (Interlocked.Read(ref threadCount) < 5)

            {

                Thread.Sleep(500);

            }

 

            // Release one thread each time the user presses ENTER,

            // until all threads have been released.

            //

            while (Interlocked.Read(ref threadCount) > 0)

            {

                Console.WriteLine("Press ENTER to release a waiting thread.");

                Console.ReadLine();

 

                // SignalAndWait signals the EventWaitHandle, which

                // releases exactly one thread before resetting,

                // because it was created with AutoReset mode.

                // SignalAndWait then blocks on clearCount, to

                // allow the signaled thread to decrement the count

                // before looping again.

                //

                WaitHandle.SignalAndWait(ewh, clearCount);

            }

            Console.WriteLine();

 

            // Create a ManualReset EventWaitHandle.

            //

            ewh = new EventWaitHandle(false, EventResetMode.ManualReset);

 

            // Create and start five more numbered threads.

            //

            for (int i = 0; i <= 4; i++)

            {

                Thread t = new Thread(

                    new ParameterizedThreadStart(ThreadProc)

                );

                t.Start(i);

            }

 

            // Wait until all the threads have started and blocked.

            //

            while (Interlocked.Read(ref threadCount) < 5)

            {

                Thread.Sleep(500);

            }

 

            // Because the EventWaitHandle was created with

            // ManualReset mode, signaling it releases all the

            // waiting threads.

            //

            Console.WriteLine("Press ENTER to release the waiting threads.");

            Console.ReadLine();

            ewh.Set();

 

        }

 

        public static void ThreadProc(object data)

        {

            int index = (int)data;

 

            Console.WriteLine("Thread {0} blocks.", data);

            // Increment the count of blocked threads.

            Interlocked.Increment(ref threadCount);

 

            // Wait on the EventWaitHandle.

            ewh.WaitOne();

 

            Console.WriteLine("Thread {0} exits.", data);

            // Decrement the count of blocked threads.

            Interlocked.Decrement(ref threadCount);

 

            // After signaling ewh, the main thread blocks on

            // clearCount until the signaled thread has

            // decremented the count. Signal it now.

            //

            clearCount.Set();

        }

    }

}

 

同步技术:临界区

 

临界区(.NetMonitorMutex类以及C#中的Lock),示例代码如下

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

 

namespace MonitorThreadWork

{

    class CDatabase

    {

        Mutex mutex = new Mutex(false); //要慢得多

        public void saveData(string s)

        {

            // Monitor.Enter(this);

            // lock(this){被保护的代码段}

            mutex.WaitOne();

            //{

                for (int i = 0; i < 100; ++i)

                {

                    Console.Write("{0}", s);

                }

            //}

            // Monitor.Exit(this);

            mutex.ReleaseMutex();

        }

    }

   

    class Program

    {

        private static CDatabase db = new CDatabase();

 

        public static void WorkThreadOne()

        {

            db.saveData("x");

        }

 

        public static void WorkThreadTwo()

        {

            db.saveData("o");

        }

 

        static void Main(string[] args)

        {

            ThreadStart work1 = new ThreadStart(WorkThreadOne);

            ThreadStart work2 = new ThreadStart(WorkThreadTwo);

 

            Thread th1 = new Thread(work1);

            Thread th2 = new Thread(work2);

           

            th1.Start();

            th2.Start();

            Console.ReadLine();

        }

    }

}

 

l         C#C++混合调试方法

C++工程:属性->配置属性->调试:“调试器类型”值设为“混合”

C#工程:属性->调试:“启用调试器”将“启用非托管代码调试”打上勾。

    

l         断言

System.Diagnostics.Debug.Assert(bool);//trueOK, false弹出对话框

 

l         Dictionary容器的使用

Dictionary<typeKey, typeValue> dic = new Dicetionary<typeKey, typeValue>();

dic.Add(key, value);

 

dic[Key] = value;

 

bool ContainsKey(key);

 

foreach(typeKey key in dic.Keys)

{

   //枚举所有的key

}

 

l         利用异常来跟踪调试问题原因

有时候程序不知道出了什么状况,一下就出问题了,这种情况下,也许异常处理机制可以帮助你找到出现问题的原因。

 

l         C#语言规范文档

vs开发工具安装文件路径下:

C:/Program Files/Microsoft Visual Studio 9.0/VC#/Specifications/2052/CSharp Language Specification.doc

 

l         InvokeBeginInvoke的区别

 

六:编写unicode程序遇到的问题及常用的解决方法

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值