C#的Timer - PowerCoder - 博客园

mikel阅读(826)

来源: C#的Timer – PowerCoder – 博客园

C#里现在有3个Timer类:

  • System.Windows.Forms.Timer
  • System.Threading.Timer
  • System.Timers.Timer

这三个Timer我想大家对System.Windows.Forms.Timer已经很熟悉了,唯一我要说的就是这个Timer在激发Timer.Tick事件的时候,事件的处理函数是在程序主线程上执行的,所以在WinForm上面用这个Timer很方便,因为在Form上的所有控件都是在程序主线程上创建的,那么在Tick的处理函数中可以对Form上的所有控件进行操作,不会造成WinForm控件的线程安全问题。

 

1、Timer运行的核心都是System.Threading.ThreadPool

 

在这里要提到ThreadPool(线程池)是因为,System.Threading.Timer 和System.Timers.Timer运行的核心都是线程池,Timer每到间隔时间后就会激发响应事件,因此要申请线程来执行对应的响应函数,Timer将获取线程的工作都交给了线程池来管理,每到一定的时间后它就去告诉线程池:“我现在激发了个事件要运行对应的响应函数,麻烦你给我向操作系统要个线程,申请交给你了,线程分配下来了你就运行我给你的响应函数,没分配下来先让响应函数在这儿排队(操作系统线程等待队列)”,消息已经传递给线程池了,Timer也就不管了,因为它还有其他的事要做(每隔一段时间它又要激发事件),至于提交的请求什么时候能够得到满足,要看线程池当前的状态:

  • 1、如果线程池现在有线程可用,那么申请马上就可以得到满足,有线程可用又可以分为两种情况:
    • <1>线程池现在有空闲线程,现在马上就可以用
    • <2>线程池本来现在没有线程了,但是刚好申请到达的时候,有线程运行完毕释放了,那么申请就可以用别人释放的线程。
    • 这两种情况就如同你去游乐园玩赛车,如果游乐园有10辆车,现在有3个人在玩,那么还剩7辆车,你去了当然可以选一辆开。另外还有一种情况就是你到达游乐园前10辆车都在开,但是你运气很好,刚到游乐园就有人不玩了,正好你坐上去就可以接着开。
  • 2、如果线程池现在没有线程可用,也分为两种情况:
    • <1>线程池现有线程数没有达到设置的最大工作线程数,那么隔半秒钟.net framework就会向操作系统申请一个新的线程(为避免向线程分配不必要的堆栈空间,线程池按照一定的时间间隔创建新的空闲线程。该时间间隔目前为半秒,但它在 .NET Framework 的以后版本中可能会更改)。
    • <2>线程池现有工作线程数达到了设置的最大工作线程数,那么申请只有在等待队列一直等下去,直到有线程执行完任务后被释放。

那么上面提到了线程池有最大工作线程数,其实还有最小空闲线程数,那么这两个关键字是什么意思呢:

  • 1、最大工作线程数:实际上就是指的线程池能够向操作系统申请的最大线程数,这个值在.net framework中有默认值,这个默认值是根据你计算机的配置来的,当然你可以用ThreadPool.GetMaxThreads返回线程池当前最大工作线程数,你也可以用ThreadPool.SetMaxThreads设置线程池当前最大工作线程数。
  • 2、最小空闲线程数:是指在程序开始后,线程池就默认向操作系统申请最小空闲线程数个线程,另外这也是线程池维护的空闲线程数(如果线程池最小空闲线程数为3,当前因为一些线程执行完任务被释放,线程池现在实际上有10个空闲线程,那么线程池会让操作系统释放多余的7个线程,而只维持3个空闲线程供程序使用),因为上面说了,在执行程序的时候再要求线程池申请线程会有半秒的延迟时间,这也会影响程序的性能,所以把握好这个值很重要,同样你可以用ThreadPool.GetMinThreads返回线程池当前最小空闲线程数,你也可以用ThreadPool.SetMinThreads设置线程池当前最小空闲线程数。

下面是我给的例子,这个例子让线程池申请800个线程,其中设置最大工作线程数为500,800个线程任务每个都要执行100000000毫秒目的是让线程不会释放,并且让用户选择,是否预先申请500个空闲线程免受那半秒钟的延迟时间,其结果可想而知当线程申请到500的时候,线程池达到了最大工作线程数,剩余的300个申请进入漫长的等待时间:

复制代码
/***************************************************
 * 项目:测试线程池
 * 描述:验证线程池的最大工作线程数和最小空闲线程数
 * 作者:@PowerCoder
 * 日期:2010-2-22
***************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static int i = 1;
        static int MaxThreadCount = 800;

        static void OutPut(object obj)
        {
            Console.Write("\r申请了:{0}个工作线程", i);
            i++;
            Thread.Sleep(100000000);//设置一个很大的等待时间,让每个申请的线程都一直执行
        }

        static void Main(string[] args)
        {
            int j;

            Console.Write("是否先申请500个空闲线程以保证前500个线程在线程池中开始就有线程用(Y/N)?");//如果这里选择N,那么前两个任务是用的线程池默认空闲线程(可以用ThreadPool.GetMinThreads得到系统默认最小空闲线程数为2,该值也和你计算机的配置有关,本例中ThreadPool.GetMinThreads返回的2)申请立即得到满足,然而由于每个线程等待时间非常大都不会释放当前自己持有的线程,因此线程池中已无空闲线程所用,后面的任务需要在线程池中申请新的线程,那么新申请的每个线程在线程池中都要隔半秒左右的时间才能得到申请(原因请见下面的注释)
            string key = Console.ReadLine();
            if (key.ToLower() == "y")
                ThreadPool.SetMinThreads(500, 10);//设置最小空闲线程为500,就好像我告诉系统给我预先准备500个线程我来了就直接用,因为这样就不用现去申请了,在线程池中每申请一个新的线程.NET Framework 会安排一个间隔时间,目前是半秒,以后的版本MS有可能会改

            ThreadPool.SetMaxThreads(500, 10);//设置最大工作线程为500

            int a, b;
            ThreadPool.GetMaxThreads(out a, out b);
            Console.WriteLine("线程池最大工作线程数:" + a.ToString() + "     最大异步 I/O 线程数:" + b.ToString());
            Console.WriteLine("需要向系统申请" + MaxThreadCount.ToString() + "个工作线程");

            for (j = 0; j <= MaxThreadCount - 1; j++)//由于ThreadPool.GetMaxThreads返回的最大工作线程数为500,那么向线程池申请大于500个线程的时候,500之后的申请会进入线程池的等待队列排队,等待前面500个线程中某个线程执行完后释放,线程池等待队列中的申请才能得到线程
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(OutPut));
                Thread.Sleep(10);
            }

            Console.ReadLine();
        }
    }
}
复制代码

 

 

2、System.Threading.Timer

谈完了线程池,就可以开始讨论Timer,这里我们先从System.Threading.Timer开始,System.Threading.Timer的作用就是每到间隔时间后激发响应事件并执行相应函数,执行响应函数要向线程池申请线程,当然申请中会遇到一些情况在上面我们已经说了。值得注意的一点就是System.Threading.Timer在创建对象后立即开始执行,比如System.Threading.Timer timer = new System.Threading.Timer(Excute, null, 0, 10);这句执行完后每隔10毫秒就执行Excute函数不需要启动什么的。下面就举个例子,我先把代码贴出来:

复制代码

代码

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace ConsoleApplication1
{
class UnSafeTimer
{
static int i = 0;
static System.Threading.Timer timer;
static object mylock = new object();
static int sleep;
static bool flag;
public static Stopwatch sw = new Stopwatch();

static void Excute(object obj)
{
Thread.CurrentThread.IsBackground = false;
int c;

lock (mylock)
{
i++;
c = i;
}

if (c == 80)
{
timer.Dispose();//执行Dispose后Timer就不会再申请新的线程了,但是还是会给Timmer已经激发的事件申请线程
sw.Stop();
}

if (c < 80)
Console.WriteLine(“Now:” + c.ToString());
else
{
Console.WriteLine(“Now:” + c.ToString()+”———–Timer已经Dispose耗时:”+sw.ElapsedMilliseconds.ToString()+”毫秒”);
}

if (flag)
{
Thread.Sleep(sleep);//模拟花时间的代码
}
else
{
if(i<=80)
Thread.Sleep(sleep);//前80次模拟花时间的代码
}
}

public static void Init(int p_sleep,bool p_flag)
{
sleep = p_sleep;
flag = p_flag;
timer = new System.Threading.Timer(Excute, null, 0, 10);
}
}

class SafeTimer
{
static int i = 0;
static System.Threading.Timer timer;

static bool flag = true;
static object mylock = new object();

static void Excute(object obj)
{
Thread.CurrentThread.IsBackground = false;

lock (mylock)
{
if (!flag)
{
return;
}

i++;

if (i == 80)
{
timer.Dispose();
flag = false;
}
Console.WriteLine(“Now:” + i.ToString());
}

Thread.Sleep(1000);//模拟花时间的代码
}

public static void Init()
{
timer = new System.Threading.Timer(Excute, null, 0, 10);
}
}

class Program
{
static void Main(string[] args)
{
Console.Write(“是否使用安全方法(Y/N)?”);
string key = Console.ReadLine();
if (key.ToLower() == “y”)
SafeTimer.Init();
else
{
Console.Write(“请输入Timmer响应事件的等待时间(毫秒):”);//这个时间直接决定了前80个任务的执行时间,因为等待时间越短,每个任务就可以越快执行完,那么80个任务中就有越多的任务可以用到前面任务执行完后释放掉的线程,也就有越多的任务不必去线程池申请新的线程避免多等待半秒钟的申请时间
string sleep = Console.ReadLine();
Console.Write(“申请了80个线程后Timer剩余激发的线程请求是否需要等待时间(Y/N)?”);//这里可以发现选Y或者N只要等待时间不变,最终Timer激发线程的次数都相近,说明Timer的确在执行80次的Dispose后就不再激发新的线程了
key = Console.ReadLine();
bool flag = false;
if (key.ToLower() == “y”)
{
flag = true;
}

UnSafeTimer.sw.Start();
UnSafeTimer.Init(Convert.ToInt32(sleep), flag);
}

Console.ReadLine();
}
}
}

复制代码

 

这个例子包含了两个Timer的类UnSafeTimer和SafeTimer,两个类的代码的大致意思就是使用Timer每隔10毫秒就执行Excute函数,Excute函数会显示当前执行的次数,在80次的时候通过timer.Dispose()让Timer停止不再激发响应事件。

首先我们来分析下UnSafeTimer

class UnSafeTimer
{
static int i = 0;
static System.Threading.Timer timer;
static object mylock = new object();
static int sleep;
static bool flag;
public static Stopwatch sw = new Stopwatch();

static void Excute(object obj)
{
Thread.CurrentThread.IsBackground = false;
int c;

lock (mylock)
{
i++;
c = i;
}

if (c == 80)
{
timer.Dispose();//执行Dispose后Timer就不会再申请新的线程了,但是还是会给Timmer已经激发的事件申请线程
sw.Stop();
}

if (c < 80)
Console.WriteLine(“Now:” + c.ToString());
else
{
Console.WriteLine(“Now:” + c.ToString() + “———–Timer已经Dispose耗时:” + sw.ElapsedMilliseconds.ToString() + “毫秒”);
}

if (flag)
{
Thread.Sleep(sleep);//模拟花时间的代码
}
else
{
if (i <= 80)
Thread.Sleep(sleep);//前80次模拟花时间的代码
}
}

public static void Init(int p_sleep, bool p_flag)
{
sleep = p_sleep;
flag = p_flag;
timer = new System.Threading.Timer(Excute, null, 0, 10);
}
}

你可以执行试一试,在输入是否执行安全方法的时候选N,等待时间1000,申请了80个线程后Timer剩余激发的线程选N,本来想在80次的时候停下来,可是你会发现直到执行到660多次之后才停下来(具体看机器配置),申请前80个线程的时间为10532毫秒,反正执行的次数大大超出了限制的80次,回头想想让Timer不在激发事件的方法是调用timer.Dispose(),难不成是Dispose有延迟?延迟的过程中多执行了500多次?那么我们再来做个试验,我们在申请了80个线程后Timer剩余激发的线程选y,请耐心等待结果,在最后你会发现执行时间还是660次左右,这很显然是不合理的,如果Dispose有延迟时间造成所执行500多次,那么加长80次后面每个线程的申请时间在相同的延迟时间内申请的线程数应该减少,因为后面500多个线程每个线程都要执行1000毫秒,那么势必有些线程会去申请新的线程有半秒钟的等待时间(你会发现申请了80个线程后Timer剩余激发的线程选y明显比选n慢得多,就是因为这个原因),所以看来不是因为Dispose造成的。

 

那么会是什么呢?我们这次这样选在输入是否执行安全方法的时候选N,等待时间500,申请了80个线程后Timer剩余激发的线程选N

 

那么会是什么呢?我们这次这样选在输入是否执行安全方法的时候选N,等待时间50,申请了80个线程后Timer剩余激发的线程选N

我们发现随着每次任务等待时间的减少多执行的次数也在减少,最关键的一点我们从图中可以看到,前80次任务申请的时间也在减少,这是最关键的,根据上面线程池所讲的内容我们可以归纳出:每次任务的等待时间直接决定了前80个任务的执行时间,因为等待时间越短,每个任务就可以越快执行完,那么80个任务中就有越多的任务可以用到前面任务执行完后释放掉的线程,也就有越多的任务不必去线程池申请新的线程避免多等待半秒钟的申请时间,而Timer并不会去关心线程池申请前80个任务的时间长短,只要它没有执行到timer.Dispose(),它就会每隔10毫秒激发一次响应时间,不管前80次任务执行时间是长还是短,timer都在第80次任务才执行Dispose,执行Dispose后timer就不会激发新的事件了,但是如果前80次任务申请的时间越长,那么timer就会在前80次任务申请的时间内激发越多响应事件,那么线程池中等待队列中就会有越多的响应函数等待申请线程,System.Threading.Timer没有机制取消线程池等待队列中多余的申请数,所以导致等待时间越长,80次后执行的任务数越多。

 

由此只用timer.Dispose()来终止Timer激发事件是不安全的,所以又写了个安全的执行机制:

 

class SafeTimer
{
static int i = 0;
static System.Threading.Timer timer;

static bool flag = true;
static object mylock = new object();

static void Excute(object obj)
{
Thread.CurrentThread.IsBackground = false;

lock (mylock)
{
if (!flag)
{
return;
}

i++;

if (i == 80)
{
timer.Dispose();
flag = false;
}
Console.WriteLine(“Now:” + i.ToString());
}

Thread.Sleep(1000);//模拟花时间的代码
}

public static void Init()
{
timer = new System.Threading.Timer(Excute, null, 0, 10);
}
}

安全类中我们用了个bool类型的变量flag来判断当前是否执行到80次了,执行到80次后将flag置为false,然后timer.Dispose,这时虽然任务还是要多执行很多次但是由于flag为false,Excute函数一开始就做了判断flag为false会立即退出,Excute函数80次后相当于就不执行了。

 

 

3、System.Timers.Timer

在上面的例子中我们看到System.Threading.Timer很不安全,即使在安全的方法类,也只能让事件响应函数在80次后立刻退出让其执行时间近似于0,但是还是浪费了系统不少的资源。

所以本人更推荐使用现在介绍的System.Timers.Timer,System.Timers.Timer大致原理和System.Threading.Timer差不多,唯一几处不同的就是:

  • 构造函数不同,构造函数可以什么事情也不做,也可以传入响应间隔时间:System.Timers.Timer timer = new System.Timers.Timer(10);
  • 响应事件的响应函数不在构造函数中设置:timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
  • 声明System.Timers.Timer对象后他不会自动执行,需要调用 timer.Start()或者timer.Enabled = true来启动它, timer.Start()的内部原理还是设置timer.Enabled = true
  • 调用 timer.Stop()或者timer.Enabled = false来停止引发Elapsed事件, timer.Stop()的内部原理还是设置timer.Enabled = false,最重要的是timer.Enabled = false后会取消线程池中当前等待队列中剩余任务的执行。

那么我们来看个例子:

复制代码

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using System.Threading;

namespace ConsoleApplication2
{
class UnSafeTimer
{
static int i = 0;
static System.Timers.Timer timer;
static object mylock = new object();

public static void Init()
{
timer = new System.Timers.Timer(10);
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}

static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Thread.CurrentThread.IsBackground = false;
int c;

lock (mylock)
{
i++;
c = i;
}

Console.WriteLine(“Now:” + i.ToString());

if (c == 80)
{
timer.Stop();//可应看到System.Timers.Timer的叫停机制比System.Threading.Timer好得多,就算在不安全的代码下Timer也最多多执行一两次(我在试验中发现有时会执行到81或82),说明Stop方法在设置Timer的Enable为false后不仅让Timer不再激发响应事件,还取消了线程池等待队列中等待获得线程的任务,至于那多执行的一两次任务我个人认为是Stop执行过程中会耗费一段时间才将Timer的Enable设置为false,这段时间多余的一两个任务就获得了线程开始执行
}

Thread.Sleep(1000);//等待1000毫秒模拟花时间的代码,注意:这里的等待时间直接决定了80(由于是不安全模式有时会是81或82、83)个任务的执行时间,因为等待时间越短,每个任务就可以越快执行完,那么80个任务中就有越多的任务可以用到前面任务执行完后释放掉的线程,也就有越多的任务不必去线程池申请新的线程避免多等待半秒钟的申请时间
}
}

class SafeTimer
{
static int i = 0;
static System.Timers.Timer timer;

static bool flag = true;
static object mylock = new object();

public static void Init()
{
timer = new System.Timers.Timer(10);
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}

static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Thread.CurrentThread.IsBackground = false;

lock (mylock)
{
if (!flag)
{
return;
}
i++;

Console.WriteLine(“Now:” + i.ToString());

if (i == 80)
{
timer.Stop();
flag = false;
}
}

Thread.Sleep(1000);//同UnSafeTimer
}

class Program
{
static void Main(string[] args)
{
Console.Write(“是否使用安全Timer>(Y/N)?”);
string Key = Console.ReadLine();

if (Key.ToLower() == “y”)
SafeTimer.Init();
else
UnSafeTimer.Init();

Console.ReadLine();
}
}
}
}

复制代码

这个例子和System.Threading.Timer差不多,这里也分为:安全类SafeTimer和不安全类UnSafeTimer,原因是 timer.Stop()有少许的延迟时间有时任务会执行到81~83,但是就算是不安全方法也就最多多执行几次,不像System.Threading.Timer多执行上百次…

 

在《CLR Via C#》书中,作者推荐使用的是System.Threading.Timer

[问]VS2005,C#winform程序,代码修改app.config的结果保存到哪里了? - 老坛酸菜WH - 博客园

mikel阅读(845)

来源: [问]VS2005,C#winform程序,代码修改app.config的结果保存到哪里了? – 老坛酸菜WH – 博客园

通过Properties.Settings用代码形式读写app.config文件,其中Properties.Settings变量的范围”scope”都设置为用户”user”(注:如果改为”Application”,编译时系统提示其为只读属性),读写都正常,并且重新打开exe文件时,上次输入的值仍然存在,但是手动打开”test.exe.config”,所有的设置变量值均为空,写入的值都保存到什么地方去了呢?临时文件?我用360安全卫士清除临时文件后在此打开exe文件,上次输入的值依然存在。有谁知道这是怎么回事?

示例代码:
private void button1_Click(object sender, EventArgs e)
{
//读操作,将读到的值送textBox1显示
Properties.Settings config = Properties.Settings.Default;
textBox1.Text = config.myvar;
}

private void button2_Click(object sender, EventArgs e)
{
//写操作,将textBox2里的值写入myvar
Properties.Settings config = Properties.Settings.Default;
config.myvar = textBox2.Text;
config.Save();

C:\Documents and Settings\Administrator\Local Settings\Application Data\XXXX\XXXX.exe_Url_oovadpuqzn4jm2rhpjt5sp0hl11zsg3f\1.0.0.0\user.config
在这呐!!!
我也找了好久。

C# winForm 开机自动启动 并启动后最小化到任务栏 右侧通知栏并交互操作 - 无恨星晨 - 博客园

mikel阅读(1113)

来源: C# winForm 开机自动启动 并启动后最小化到任务栏 右侧通知栏并交互操作 – 无恨星晨 – 博客园

//设置自动启动
string path = Application.StartupPath;
SettingHel.SetAutoRun(path +@”\MostImpressive.DSCJ.DcbInterface.exe”, true);

public static class SettingHel
{
///

/// 设置应用程序开机自动运行
///

///应用程序的文件名 ///是否自动运行,为false时,取消自动运行 /// 设置不成功时抛出异常
/// 返回1成功,非1不成功
public static String SetAutoRun(string fileName, bool isAutoRun)
{
string reSet = string.Empty;
RegistryKey reg = null;
try
{
if (!System.IO.File.Exists(fileName))
{
reSet = "该文件不存在!";
}
string name = fileName.Substring(fileName.LastIndexOf(@"\") + 1);
reg = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
if (reg == null)
{
reg = Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run");
}
if (isAutoRun)
{
reg.SetValue(name, fileName);
reSet = "1";
}
else
{
reg.SetValue(name, false);
}

}
catch (Exception ex)
{
//“记录异常”
}
finally
{
if (reg!=null)
{
reg.Close();
}
}
return reSet;
}

}

设置.exe程序自动启动

C# winForm启动最小化到任务栏右侧通知栏并交互操作

一。主要功能:
(1)、程序启动自动隐藏到任务栏右侧通知栏显示。(与系统托盘同义)
(2)、双击系统托盘图标显示、隐藏窗口;
(3)、右击系统托盘图标提供三个菜单选项,“退出”、“隐藏”、“显示”;

二。相关控件:
1、建一个WinForm程序—IconForm,将Form属性ShowInTaskbar改为false,这样程序将不会在任务栏中显示。
2、将Form属性WindowState选择为 Minimized,以便起来自动最小化隐藏
3、在工具栏中的“公共控件”里,拖入NotifyIcon控件—notifyIcon1,这个是程序运行任务栏右侧通知区域图标显示控件,为控件notifyIcon的属性Icon添加一个icon图标,或从代码中加入。
4、在工具栏中的“菜单和工具栏”里,拖入ContextMenuStrip—contextMenuStrip1,这个控件是右击时关联菜单。
5、右键notifyIcon1选择属性,将其属性ContextMenuStrip改加为contextMenuStrip1,这个时候notifyIcon1和contextMenuStrip1两个控件就关联了。
6、右键contextMenuStrip1,选择属性,进入Items,然后点击“添加”,这里添加三个菜单选项:exitMenuItem、hideMenuItem、showMenuItem,同时分别将其Text属性改为:退出、隐藏和显示。

三。主要代码:
1、双击IconForm,即添加Load事件然后

//一 右击窗体,选择属性,转到事件页面,双击 Load,SizeChanged事件,给窗体添加代码
private void Form1_Load(object sender, EventArgs e)
{
//1.将Form属性ShowInTaskbar改为false,这样程序将不会在任务栏中显示。
//2.将Form属性WindowState选择为 Minimized,以便起来自动最小化隐藏。
string startup = Application.ExecutablePath; //取得程序路径
int pp = startup.LastIndexOf(“\\”);
startup = startup.Substring(0, pp);
string icon = startup + “\\testIcon.ico“;
//3.一定为notifyIcon1其设置图标,否则无法显示在通知栏。或者在其属性中设置
notifyIcon1.Icon = new Icon(icon);

}

private void Form1_SizeChanged(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.Hide(); //或者是this.Visible = false;
this.notifyIcon1.Visible = true;
}

}

 

//二 双击窗体上的菜单项,添加相关代码
private void exitMenuItem_Click(object sender, EventArgs e)
{
if (MessageBox.Show(“你确定要退出程序吗?”, “确认”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.OK)
{
notifyIcon1.Visible = false;
this.Close();
this.Dispose();
Application.Exit();
}

}

private void hideMenuItem_Click(object sender, EventArgs e)
{
this.Hide();

}

private void showMenuItem_Click(object sender, EventArgs e)
{
this.Show();
this.WindowState = FormWindowState.Normal;
this.Activate();

}

//三 转到窗体设计模式,右击notifyIcon1 ,选择属性,双击其中DoubleClick,添加相关代码
private void notifyIcon1_DoubleClick(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Normal)
{
this.WindowState = FormWindowState.Minimized;
this.Hide();
}
else if (this.WindowState == FormWindowState.Minimized)
{
this.Show();
this.WindowState = FormWindowState.Normal;
this.Activate();
}

}

 

四。完整的代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace IconForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//说明,程序运行后自动隐藏到任务栏右侧的通知栏里,
//1 右击选择退出,隐藏,显示
//2 双击可以隐藏和显示切换

//一 右击窗体,选择属性,转到事件页面,双击 Load,SizeChanged事件,给窗体添加代码
private void Form1_Load(object sender, EventArgs e)
{
//1.将Form属性ShowInTaskbar改为false,这样程序将不会在任务栏中显示。
//2.将Form属性WindowState选择为 Minimized,以便起来自动最小化隐藏。
string startup = Application.ExecutablePath; //取得程序路径
int pp = startup.LastIndexOf(“\\”);
startup = startup.Substring(0, pp);
string icon = startup + “\\testIcon.ico“;
//3.一定为notifyIcon1其设置图标,否则无法显示在通知栏。或者在其属性中设置
notifyIcon1.Icon = new Icon(icon);

}

private void Form1_SizeChanged(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.Hide(); //或者是this.Visible = false;
this.notifyIcon1.Visible = true;
}

}

//二 双击窗体上的菜单项,添加相关代码
private void exitMenuItem_Click(object sender, EventArgs e)
{
if (MessageBox.Show(“你确定要退出程序吗?”, “确认”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.OK)
{
notifyIcon1.Visible = false;
this.Close();
this.Dispose();
Application.Exit();
}

}

private void hideMenuItem_Click(object sender, EventArgs e)
{
this.Hide();
}

private void showMenuItem_Click(object sender, EventArgs e)
{
this.Show();
this.WindowState = FormWindowState.Normal;
this.Activate();

}

//三 转到窗体设计模式,右击notifyIcon1 ,选择属性,双击其中DoubleClick,添加相关代码
private void notifyIcon1_DoubleClick(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Normal)
{
this.WindowState = FormWindowState.Minimized;

this.Hide();
}
else if (this.WindowState == FormWindowState.Minimized)
{
this.Show();
this.WindowState = FormWindowState.Normal;
this.Activate();
}

}
}
}

“log4net.Core.LoggerManager”的类型初始值设定项引发异常 - eatonfang的专栏 - CSDN博客

mikel阅读(1421)

来源: “log4net.Core.LoggerManager”的类型初始值设定项引发异常 – eatonfang的专栏 – CSDN博客

1、今天在使用log4net的时候,配置都完毕了,启动运行程序,

运行到以下代码实例化log对象的时候,出错,提示:“log4net.Core.LoggerManager”的类型初始值设定项引发异常

log4net.ILog log = log4net.LogManager.GetLogger(typeof(Program));

 

看了一下app.config配置,都没问题,后来把错误详细信息(如下)打开一看,才发现配置的时候,<configSections> 元素,必须是根 <configuration> 元素的第一个子元素。

改了以后,问题解决。

 

错误信息:

未处理 System.TypeInitializationException
Message=“log4net.Core.LoggerManager”的类型初始值设定项引发异常。
Source=log4net
TypeName=log4net.Core.LoggerManager
StackTrace:
在 log4net.Core.LoggerManager.GetLogger(Assembly repositoryAssembly, String name)
在 log4net.LogManager.GetLogger(Assembly repositoryAssembly, String name)
在 log4net.LogManager.GetLogger(Type type)
在 Program.Main(String[] args) 位置 E:\Program.cs:行号 16
在 System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
在 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
在 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
在 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
在 System.Threading.ThreadHelper.ThreadStart()
InnerException: System.TypeInitializationException
Message=“log4net.Util.LogLog”的类型初始值设定项引发异常。
Source=log4net
TypeName=log4net.Util.LogLog
StackTrace:
在 log4net.Util.LogLog.Debug(String message)
在 log4net.Core.LoggerManager..cctor()
InnerException: System.Configuration.ConfigurationErrorsException
Message=配置系统未能初始化
Source=System.Configuration
BareMessage=配置系统未能初始化
Line=0
StackTrace:
在 System.Configuration.ConfigurationManager.PrepareConfigSystem()
在 System.Configuration.ConfigurationManager.GetSection(String sectionName)
在 System.Configuration.PrivilegedConfigurationManager.GetSection(String sectionName)
在 System.Diagnostics.DiagnosticsConfiguration.GetConfigSection()
在 System.Diagnostics.DiagnosticsConfiguration.Initialize()
在 System.Diagnostics.DiagnosticsConfiguration.get_IndentSize()
在 System.Diagnostics.TraceInternal.InitializeSettings()
在 System.Diagnostics.TraceInternal.WriteLine(String message)
在 System.Diagnostics.Trace.WriteLine(String message)
在 log4net.Util.LogLog.EmitErrorLine(String message)
在 log4net.Util.LogLog.Error(String message, Exception exception)
在 log4net.Util.LogLog..cctor()
InnerException: System.Configuration.ConfigurationErrorsException
Message=每个配置文件中只允许存在一个 <configSections> 元素,并且,如果存在该元素,它还必须是根 <configuration> 元素的第一个子元素。 (E:\★項目\4.物流\0.VSS物流\中邮云南供应链信息平台\02_source\SCM\SCM.Batch\BAT.AlarmManagement\AlarmBatch\bin\Release\BAT.AlarmManagement.vshost.exe.Config line 10)
Source=System.Configuration
BareMessage=每个配置文件中只允许存在一个 <configSections> 元素,并且,如果存在该元素,它还必须是根 <configuration> 元素的第一个子元素。
Filename=E:\★項目\4.物流\0.VSS物流\中邮云南供应链信息平台\02_source\SCM\SCM.Batch\BAT.AlarmManagement\AlarmBatch\bin\Release\BAT.AlarmManagement.vshost.exe.Config
Line=10
StackTrace:
在 System.Configuration.ConfigurationSchemaErrors.ThrowIfErrors(Boolean ignoreLocal)
在 System.Configuration.BaseConfigurationRecord.ThrowIfParseErrors(ConfigurationSchemaErrors schemaErrors)
在 System.Configuration.BaseConfigurationRecord.ThrowIfInitErrors()
在 System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey)
InnerException:

2、运行没错了,但是后面又有问题了,就是log文件怎么跑都没见生成,程序运行也不出错,后来调查,原来是配置完以后,要正常使用,使用前通过下面方法需要加载配置:

log4net.Config.XmlConfigurator.Configure();

加载的时候,web程序一般都是写在Global 的Application_Start里面,这样,web网站启动的时候就全局加载。 其它的winform或者控制台应用程序在program的开头加上即可。
————————————————
版权声明:本文为CSDN博主「eatonfang」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/eatonfang/article/details/40622945

C#遍历DataSet中数据的几种方法总结 - 隋旭峰的专栏 - CSDN博客

mikel阅读(1345)

来源: C#遍历DataSet中数据的几种方法总结 – 隋旭峰的专栏 – CSDN博客

//多表多行多列的情况
foreach   (DataTable   dt   in   YourDataset.Tables)   //遍历所有的datatable
{
foreach   (DataRow   dr   in   dt.Rows)   ///遍历所有的行
foreach   (DataColumn   dc   in   dt.Columns)   //遍历所有的列
Console.WriteLine(“{0},   {1},   {2}”,   dt.TableName,

dc.ColumnName,   dr[dc]);   //表名,列名,单元格数据
}
//遍历一个表多行多列
foreach(DataRow   mDr   in   dataSet.Tables[0].Rows   )
{
foreach(DataColumn   mDc   in   dataSet.Tables[0].Columns)
{
Console.WriteLine(mDr[mDc].ToString());
}
}
//遍历一个表多行一列
foreach(DataRow   row   in   DataSet1.Tables[0].Rows)
{
Console.WriteLine(row[0].ToString());
}

//一行一列
ds.Tables[0].Rows[0][“字段”]

MS SQL Server 数据库连接字符串详解 - X君 - 博客园

mikel阅读(1150)

来源: MS SQL Server 数据库连接字符串详解 – X君 – 博客园

原地址:http://blog.csdn.net/jhhja/article/details/6096565

问题 : 超时时间已到。在从池中获取连接之前超时时间已过。出现这种情况可能是因为所有池连接都已被使用并已达到最大池大小。

解决办法 1. 在代码里面 , 把未关闭的连接关闭 2. 扩大共享池 , 方法如下 : 解决方法可以是修改连接池的连接生存期 , 因为默认值是 60 秒 , 即连接从应用程序被释放后可以在池中保存的时间。 具体操作步骤如下: 如果是 ODBC 的话,则可以在 ODBC Data Source Administrator 中手动更改,该程序位于 “Start” 菜单中的 “Programs”->”Adminstrative Tools” (中文名:管理工具)下,或从 “Control Panel”( 中文名:控制面板 ) 中打开 “ODBC Data Source Administrator” ( ODBC 数据源管理器),再选择 “Connection Pooling” (连接池)选项卡,双击驱动程序中的 “SQL Server” ,选择 “Pool Connetions to this drive”( 使用池连接该驱动程序 ) ,把下面的时间 60 秒改的大一点,例如 120 。 如果是 SQLConnection 的共享连接,则为 ConnectionString 属性 Connection Lifetime 数值名称指定一个值,该值默认情况下是 0 ,表示连接永远不会从池中被自动移走。如果指定一个不同的值,那么一旦连接被返回到池中,该值将和连接的创建时间及当前时间进行比较。如果生存期超过了 Connection Lifetime 的值,那么连接将从池中被移走。

问题的原因是与数据库的连接没有显示关闭 , 而等系统自动回收是要时间的 . 解决的方法是改变连接池的大小 . 在连接字符串的后面加上以下代码 : Enlist=true;Pooling=true;Max Pool Size=300;Min Pool Size=0;Connection Lifetime=300;user id=sa;packet size=1000 packet size 参数取 512 到 32767 中的任一整数值

 

注 释:

SQLConnection.ConnectionString 属性

ConnectionString 类似于 OLE DB 连接字符串,但并不相同。与 OLE DB 或 ADO 不同,如果 “Persist Security Info ” 值设置为 false (默认值),则返回的连接字符串与用户设置的 ConnectionString 相同但去除了安全信息。除非将 “Persist Security Info ” 设置为 true ,否则, SQL Server .NET Framework 数据提供程序将不会保持,也不会返回连接字符串中的密码。

可以使用 ConnectionString 属性连接到数据库。下面的示例阐释了一个典型的连接字符串。

Persist Security Info=False;Integrated Security=SSPI;database=northwind;server=mySQLServer
 

 

只有在连接关闭时才能设置 ConnectionString 属性。许多连接字符串值都具有相应的只读属性。当设置连接字符串时,将更新所有这些属性(除非检测到错误)。检测到错误时,不会更新任何属性。 SqlConnection 属性只返回那些包含在 ConnectionString 中的设置。

若要连接到本地机器,请将服务器指定为 “(local)” 。(必须始终指定一个服务器。)

重置已关闭连接上的 ConnectionString 会重置包括密码在内的所有连接字符串值(和相关属性)。例如,如果设置一个连接字符串,其中包含 “Database= northwind” ,然后再将该连接字符串重置为 “Data Source=myserver;Integrated Security=SSPI” ,则 Database 属性将不再设置为 Northwind 。

在设置后会立即分析连接字符串。如果在分析时发现语法中有错误,则产生运行库异常,如 ArgumentException 。只有当试图打开连接时,才会发现其他错误。

连接字符串的基本格式包括一系列由分号分隔的关键字 / 值对。等号  (=) 连接各个关键字及其值。若要包括含有分号、单引号字符或双引号字符的值,则该值必须用双引号括起来。如果该值同时包含分号和双引号字符,则该值可以用单引号括起来。如果该值以双引号字符开始,则还可以使用单引号。相反地,如果该值以单引号开始,则可以使用双引号。如果该值同时包含单引号和双引号字符,则用于将值括起来的引号字符每次出现时,都必须成对出现。

若要在字符串值中包括前导或尾随空格,则该值必须用单引号或双引号括起来。即使将整数、布尔值或枚举值用引号括起来,其周围的任何前导或尾随空格也将被忽略。然而,保留字符串关键字或值内的空格。若要在关键字或值中包括等号 (=) ,则它之前必须还有另一个等号。例如,在假设的连接字符串中,

“key==word=value”

关键字是 “key=word” 并且值是 “value” 。

如果 “keyword= value” 对中的一个特定关键字多次出现在连接字符串中,则将所列出的最后一个用于值集。

关键字不区分大小写。

下表列出了 ConnectionString 中的关键字值的有效名称。

名称

默认值

说明

Application Name

应用程序的名称,如果不提供应用程序名称,默认是: “.Net  SqlClient Data Provider”

AttachDBFilename   – 或 – extended properties   – 或 – Initial File Name

可连接数据库的主文件的名称,包括完整的路径名。

必须使用关键字 “database” 来指定数据库的名称。

Connect Timeout   – 或 – Connection Timeout

15

在终止尝试连接并产生错误之前,等待与服务器的连接的时间长度(以秒为单位)。

Current Language

SQL Server 语言

Data Source   – 或 – Server   – 或 – Address   – 或 – Addr   – 或 – Network Address

要连接的 SQL Server 实例的名称或网络地址。

Encrypt

‘false’

当该值为 true 时,如果服务器端安装了证书,则 SQL Server 将对所有在客户端和服务器之间传送的数据使用 SSL 加密。可识别的值为 true 、 false 、 yes 和 no 。

Initial Catalog   – 或 – Database

数据库的名称。

Integrated Security   – 或 – Trusted_Connection

‘false’

当为 false 时,将在连接中指定用户 ID 和密码。当为 true 时,将使用当前的 Windows 帐户凭据进行身份验证。

可识别的值为 true 、 false 、 yes 、 no 以及与 true 等效的 sspi (强烈推荐)。

Network Library   – 或 – Net

‘dbmssocn’

用于建立与 SQL Server 实例的连接的网络库。支持的值包括 dbnmpntw (命名管道)、 dbmsrpcn (多协议)、 dbmsadsn (Apple  Talk) 、 dbmsgnet  (VIA) 、 dbmslpcn (共享内存)及 dbmsspxn (IPX/SPX) 和 dbmssocn (TCP/IP) 。

相应的网络 DLL 必须安装在要连接的系统上。如果不指定网络而使用一个本地服务器(比如 “.” 或 “(local)” ),则使用共享内存。

Packet Size

8192

用来与 SQL Server 的实例进行通讯的网络数据包的大小,以字节为单位。

Password   – 或 – Pwd

SQL Server 帐户登录的密码(建议不要使用。为了维护最高级别的安全性,强烈建议改用 Integrated Security 或 Trusted_Connection 关键字)。

Persist Security Info

‘false’

当该值设置为 false 或 no (强烈推荐)时,如果连接是打开的或者一直处于打开状态,那么安全敏感信息(如密码)将不会作为连接的一部分返回。重置连接字符串将重置包括密码在内的所有连接字符串值。可识别的值为 true 、 false 、 yes 和 no 。

User ID

SQL Server 登录帐户(建议不要使用。为了维护最高级别的安全性,强烈建议改用 Integrated Security 或 Trusted_Connection 关键字)。

Workstation ID

本地计算机名称

连接到 SQL Server 的工作站的名称。

下表列出了 ConnectionString 内连接池值的有效名称。有关连接池的更多信息,请参见 SQL Server .NET Framework 数据提供程序连接池。

名称

默认值

说明

Connection Lifetime

0

当连接被返回到池时,将其创建时间与当前时间作比较,如果时间长度(以秒为单位)超出了由 Connection Lifetime 指定的值,该连接就会被销毁。这在聚集配置中很有用(用于强制执行运行中的服务器和刚置于联机状态的服务器之间的负载平衡)。

零 (0) 值将使池连接具有最大的连接超时。

Connection Reset

‘true’

确定从池中提取数据库连接时是否重置数据库连接。对于 Microsoft SQL Server 7.0 版,设置为 false 可避免获取连接时再有一次额外的服务器往返行程,但须注意此时并未重置连接状态(如数据库上下文)。

Enlist

‘true’

当该值为 true 时,池程序在创建线程的当前事务上下文中自动登记连接。可识别的值为 true 、 false 、 yes 和 no 。

Max Pool Size

100

池中允许的最大连接数。

Min Pool Size

0

池中允许的最小连接数。

Pooling

‘true’

当该值为 true 时,系统将从相应池中提取 SQLConnection 对象,或在必要时创建该对象并将其添加到相应池中。可识别的值为 true 、 false 、 yes 和 no 。

当设置需要布尔值的关键字或连接池值时,您可以使用 ‘yes’ 代替 ‘true’ , ‘no’ 代替 ‘false’ 。整数值表示为字符串。

注意    SQL Server .NET Framework 数据提供程序使用它自己的协议与 SQL Server 进行通信。因此,当连接到 SQL Server 时,它不支持 ODBC 数据源名称 (DSN) 的使用,因为它不添加 ODBC 层。  警告     在此版本中,在应用程序中根据用户输入构造连接字符串时(例如,从对话框中检索用户 ID 和密码信息并将其追加到连接字符串时)应相当谨慎。应用程序应确保用户无法在这些值中嵌入额外的连接字符串参数(例如,输入 “validpassword;database=somedb” 作为密码,以试图连接到其他数据库)。

无法识别的配置节log4net的(Unrecognized configuration section log4net) - herizai - 博客园

mikel阅读(1458)

来源: 无法识别的配置节log4net的(Unrecognized configuration section log4net) – herizai – 博客园

每个配置文件中只允许存在一个 <configSections> 元素,并且,如果存在该元素,它还必须是根 <configuration> 元素的第一个子元素。

 

问题:

I have this code in web.config:

复制代码
<log4net>
  <root>
    <level value="ALL" />
    <appender-ref ref="LogFileAppender" />
  </root>
  <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
    <param name="File" value="D:\logFileFaculty.txt" />
    <param name="AppendToFile" value="true" />
    <rollingStyle value="Size" />
    <maxSizeRollBackups value="10" />
    <maximumFileSize value="10MB" />
    <staticLogFileName value="true" />
    <layout type="log4net.Layout.PatternLayout">
      <param name="ConversionPattern" value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
    </layout>
  </appender>
</log4net>
复制代码

And I have downloaded log4net.dll and placed it in Bin Folder.

In one of My aspx.cs pages I have added this code:

using log4net;
[assembly: log4net.Config.XmlConfigurator(Watch = true)]


private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

But it’s giving error as Unrecognized configuration section log4net.

 

解决方案:

You need to declare the log4net section:

<configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>

Take a closer look at the documentation which explains the necessary things to do.

C# log4net 的配置 - ???笨小孩 - 博客园

mikel阅读(1017)

来源: C# log4net 的配置 – ???笨小孩 – 博客园

项目的日志组件是必备可少的,任何项目中都需要。这样既方便前期的开发测试也方便项目后期的项目维护。C#项目的一个不错的日志组件是log4net,下面我就把网站项目、桌面应用程序项目log4net的如何配置粗略的介绍一下。

网站项目中的配置

首先,你得确保你的电脑是联网的。确保这一点后,我们就是先给项目中安装log4net。安装的方法有很多种,我粗略介绍一下我经常使用的方法:使用Visual Studio 2013(我这里使用的是2013)打开项目,在“解决方案资源管理器”的项目目录中找到“引用”鼠标右键弹出菜单中点击“管理NuGet程序包(N)…”就会弹出一个管理窗口如下图:

图1

如果这样不行的话可以试试“程序包管理器控制台”用命令安装:先要打开控制台,在“工具”“NuGet程序包管理器”“程序包管理器控制台”这样就可以打开程序包管理器控制台。

那么安装命令不会怎么办?这个好办还是像图1的窗口做如下操作:

图2

 

图3

 

图4

这样就把log4net的程序包安装到项目中,接下来就是怎么在项目中配置了。首先讲解网站项目中是怎么配置的。

第一步:在Web.config文件中做如下配置,先上图看一下究竟:

图5

图6

下是图5、图6中的配置文件中的代码:

 

复制代码
 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />




  <log4net>
    <!-- OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL -->
    <!-- Set root logger level to ERROR and its appenders -->
    <root>
      <level value="ALL" />
      <appender-ref ref="SysAppender" />
    </root>
    <!-- Print only messages of level DEBUG or above in the packages -->
    <logger name="WebLogger">
      <level value="DEBUG" />
    </logger>
    <appender name="SysAppender" type="log4net.Appender.RollingFileAppender,log4net">
      <param name="File" value="App_Data/" />
      <param name="AppendToFile" value="true" />
      <param name="RollingStyle" value="Date" />
      <param name="DatePattern" value="&quot;Logs_&quot;yyyyMMdd&quot;.txt&quot;" />
      <param name="StaticLogFileName" value="false" />
      <layout type="log4net.Layout.PatternLayout,log4net">
        <param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
      </layout>
    </appender>
    <appender name="consoleApp" type="log4net.Appender.ConsoleAppender,log4net">
      <layout type="log4net.Layout.PatternLayout,log4net">
        <param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
      </layout>
    </appender>
  </log4net>
复制代码

第二步,先贴图下面贴出代码:

图7

图7中的代码:

 //配置log4
            log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo(Server.MapPath("~/Web.config")));

这样网站项目中log4net的配置工作就完成了。

最后一步是怎么在项目代码中应用了,我也简单的说一下,就是简单的使用,复杂的就不说了。还是跟上面一样上图说话:

图8

下面图9是项目中产生的日志:

图9

网站项目中log4net的配置就全部完成了,网上还有很多其他的配置方法,我这里只是讲解自己比较常使用的简单的配置方法。

桌面应用程序、控制台应用程序、Windows服务项目中log4net的配置

第一步:跟上面网站项目配置一样先把log4net程序包安装到项目中,两种方式。这里就不再重复讲解了,有什么不懂看上文就可以了。

第二步:也是一样只是由Web.config变成App.config文件代码还是一样,如图10:

图10

第三步:有点不一样了,我们要在项目中的AssemblyInfo.cs中加一行代码:

 

//log4net从配置文件中读取配置
[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "config", Watch = true)]

 

如图11:

图11

 

最后一步就是在项目中使用了,跟网站项目中的使用一样就可以了。

有关log4net的配置工作就全部讲解完了,希望对大家有所帮助,有不足之地和错误希望指正和谅解!!!

细细品味C#——Timer及多线程编程 - 虾皮 - 博客园

mikel阅读(1139)

来源: 细细品味C#——Timer及多线程编程 – 虾皮 – 博客园

你对C#中的Timer了解多少?知道有三种Timer吗?他们的区别是什么?编程时选择哪个比较合适?

  你会线程(Thread)编程吗?多线程编程又知道多少?断点续传又知多少?

  一口气提了这么多问题,其实也是鄙人心中的疑惑,在前几日的编写Vod下载器中就遇到了线程的问题,包括Timer的使用,所以为了进一步学习,才把相关的好文章整理成册,重点强化一下,同时分享出来让那些和我一样欠缺这方面的同仁一同提高。

  文章下载地址:http://files.cnblogs.com/xia520pi/C_Sharp_Timer_Thread.rar

  文章的目录:

1、C#的Timer解析

1.1、版权声明

1.2、内容详情

2、Timer范例教程

2.1、版权声明

2.2、内容详情

3、Socket网络编程大全

3.1、版权声明

3.2、内容详情

3.2.1 简单服务器端

3.2.2 简单客户端

3.2.3 获得本机IP

3.2.4 端对端通信

3.2.5 点对点通信

3.2.6 UDP对时服务器端

3.2.7 UDP对时客户端

3.2.8 点对点传输文件

3.2.9 发送邮件

3.2.10 接收邮件

4、多线程使用thread、threadpool、timer

4.1、版权声明

4.2、内容详情

5、谈谈多线程的思维方式

5.1、版权声明

5.2、内容详情

6、断点续传、多线程上载

6.1、版权声明

6.2、内容详情

7、C#多线程学习

7.1、版权声明

7.2、内容详情

7.2.1 多线程的相关概念

7.2.2 如何操纵一个线程

7.2.3 生产者和消费者

7.2.4 多线程的自动管理(线程池)

7.2.5 多线程的自动管理(定时器Timer)

7.2.6 互斥对象Mutex

8、C#多线程编程

8.1、版权声明

8.2、内容详情

8.2.1 lock使用注意事项

8.2.2 集合类中Synchronized与SyncRoot属性原理分析

8.2.3 Monitor使用示例及Mutex简介

8.2.4 同步事件和等待句柄

8.2.5 Timer(定时器)使用示例

8.2.6 volatile关键字的原理探讨

8.2.7 Interlocked类操作

8.2.8 使用Semaphore类限制资源并发访问数

8.2.9 用ReaderWriterLock类实现多用户读/单用户写同步

8.2.10 异步方法调用

8.2.11 异步事件调用

8.2.12 BackgroundWorker

C# 指定平台编译项目 - 古兴越 - 博客园

mikel阅读(1576)

来源: C# 指定平台编译项目 – 古兴越 – 博客园

如CefSharp就需要指定平台,项目为Any CPU时,无法编译,总会提示出错。

如:

CefSharp.Common contains unmanaged resoures, set your project and solution platform to x86 or x64.

 

配置方式:

在解决方案右键属性 – 配置属性 – 配置 – 配置管理器 – 活动解决方案平台 – 新建 – 新建解决方案平台。

大致是这样。