关于log4net配置文件的说明

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!--配置log4net解析-->
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  <connectionStrings>
    <!--配置log4net写入Sqlite数据库的连接字符串-->
    <add name="sqlite" connectionString="Data Source=|DataDirectory|log4net.db;Version=3;Compress=True;UTF8Encoding=True;" />
  </connectionStrings>
  <log4net>
    <!--配置输出到跟踪中-->
    <appender name="trace" type="log4net.Appender.TraceAppender, log4net">
      <!--配置日志输出格式-->
      <layout type="log4net.Layout.PatternLayout, log4net" value="[%d][%t][%p][%c]%m%n%exception" />
    </appender>
    <!--配置输出到文件中-->
    <appender name="file" type="log4net.Appender.RollingFileAppender, log4net">
      <!--配置日志文件-->
      <file value="logs/log4net.txt" />
      <!--配置日志文件存时,继续添加日志-->
      <appendToFile value="true" />
      <!--配置日志文件名以时间格式创建-->
      <rollingStyle value="Date" />
      <!--配置日志文件名的时间格式-->
      <datePattern value="yyyyMMddHHmmss" />
      <!--配置日志输出格式-->
      <layout type="log4net.Layout.PatternLayout, log4net" value="[%d][%t][%p][%c]%m%n%exception" />
    </appender>
    <!--配置输出到控制台中-->
    <appender name="console" type="log4net.Appender.ColoredConsoleAppender, log4net">
      <!--配置日志输出格式-->
      <layout type="log4net.Layout.PatternLayout, log4net" value="[%d][%t][%p][%c]%m%n%exception" />
    </appender>
    <!--配置输出到数据库中-->
    <appender name="database" type="log4net.Appender.AdoNetAppender, log4net">
      <!--日志缓存,当日志数达到设置数时执行写入数据库-->
      <bufferSize value="1" />
      <!--配置数据库连接字符串-->
      <connectionStringName value="sqlite" />
      <!--配置数据库连接对象类型-->
      <connectionType value="System.Data.SQLite.SQLiteConnection, System.Data.SQLite" />
      <!--配置数据库insert语句-->
      <commandText value="insert into T_Log4Net (C_Date, C_Thread, C_Level, C_Logger, C_Message) values (@date, @thread, @level, @logger, @message)" />
      <!--配置数据库insert语句参数-->
      <parameter>
        <parameterName value="@date" />
        <dbType value="DateTime" />
        <layout type="log4net.Layout.RawTimeStampLayout, log4net" />
      </parameter>
      <parameter>
        <parameterName value="@thread" />
        <dbType value="String" />
        <layout type="log4net.Layout.PatternLayout, log4net" value="%t" />
      </parameter>
      <parameter>
        <parameterName value="@level" />
        <dbType value="String" />
        <layout type="log4net.Layout.PatternLayout, log4net" value="%p" />
      </parameter>
      <parameter>
        <parameterName value="@logger" />
        <dbType value="String" />
        <layout type="log4net.Layout.PatternLayout, log4net" value="%c" />
      </parameter>
      <parameter>
        <parameterName value="@message" />
        <dbType value="String" />
        <layout type="log4net.Layout.PatternLayout, log4net" value="%m" />
      </parameter>
    </appender>
    <root>
      <!--输出所有日志级别-->
      <level value="ALL" />
      <!--输出跟踪日志-->
      <appender-ref ref="trace" />
      <!--输出文件日志-->
      <appender-ref ref="file" />
      <!--输出控制台日志-->
      <appender-ref ref="console" />
      <!--输出数据库日志-->
      <appender-ref ref="database" />
    </root>
  </log4net>
</configuration>

关于C#中动态加载程序集文件后无法解除占用的说明

最近在使用“Assembly.LoadFile(string path)”加载程序集文件时发现无法正常释放资源,文件一直被占用,一定要关闭主调程序后才能解除文件占用。
经测试,可以改用“Assembly.Load(byte[] rawAssembly)”方法加载程序集,这样就不占用程序集文件了。

byte[] rawAssembly = File.ReadAllBytes(path);
Assembly assembly = Assembly.Load(rawAssembly);

关于C#控制台程序中分离并关闭控制台窗口的说明

一、根据Win32 API定义控制台分离函数:

/// <summary>
/// 分离与调用进程相关联的控制台
/// </summary>
/// <returns>返回分离是否成功</returns>
[DllImport("kernel32.dll")]
public static extern bool FreeConsole();

二、分离并关闭控制台窗口:
调用函数“FreeConsole”后,程序进程就可以将其自身从其控制台分离,而如果当前控制台没有其他进程与之链接时,该控制台窗口就会被关闭。当然在进程调用FreeConsole函数之后,它仍旧可以调用Win32 API中的AllocConsole函数来创建一个新的控制台或AttachConsole函数来附加到另一个控制台。

关于C#控制台程序中显示或隐藏控制台窗口的说明

一、根据Win32 API定义窗口相关函数:

/// <summary>
/// 查找窗口句柄
/// </summary>
/// <param name="lpClassName">类名</param>
/// <param name="lpWindowName">窗口名</param>
/// <returns>返回查找到的窗口句柄</returns>
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

/// <summary>
/// 显示或隐藏窗口
/// </summary>
/// <param name="hWnd">窗口句柄</param>
/// <param name="nCmdShow">0为隐藏,1为显示</param>
/// <returns>返回显示或隐藏是否成功</returns>
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

二、设置控制台窗口的显示或隐藏:

/// <summary>
/// 设置控制台窗口是否显示
/// </summary>
/// <param name="visible">是否显示</param>
public static void SetConsoleWindowVisible(bool visible)
{
    // 查找控制台窗口句柄
    IntPtr hWnd = FindWindow(null, Console.Title);
    // 判断是否找到控制台窗口句柄
    if (hWnd != IntPtr.Zero)
    {
        // 设置控制台窗口显示或隐藏
        if (visible)
        {
            ShowWindow(hWnd, 1);
        }
        else
        {
            ShowWindow(hWnd, 0);
        }
    }
}

关于C#中读取RTP协议数据包的说明

/// <summary>
/// RTP数据包类
/// </summary>
public class RTPPacket
{
    /// <summary>
    /// 获取数据包头信息V
    /// </summary>
    public int V
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包头信息P
    /// </summary>
    public bool P
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包头信息X
    /// </summary>
    public bool X
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包头信息CC
    /// </summary>
    public int CC
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包头信息M
    /// </summary>
    public bool M
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包头信息PT
    /// </summary>
    public int PT
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包头信息SequenceNumber
    /// </summary>
    public ushort SequenceNumber
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包头信息Timestamp
    /// </summary>
    public uint Timestamp
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包头信息SSRC
    /// </summary>
    public uint SSRC
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包头信息CSRC
    /// </summary>
    public List<uint> CSRC
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包有效数据
    /// </summary>
    public byte[] Payload
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包有效数据长度
    /// </summary>
    public uint PayloadSize
    {
        get;
        private set;
    }

    /// <summary>
    /// 获取数据包头信息长度
    /// </summary>
    public uint HeaderSize
    {
        get;
        private set;
    }

    /// <summary>
    /// 构造RTP数据包类
    /// </summary>
    /// <param name="buffer">RTP数据包数据</param>
    public RTPPacket(byte[] buffer)
    {
        // 判断数据长度是否有效
        if (buffer.LongLength > 12)
        {
            // 根据RTP数据包头信息解析数据
            byte item = buffer[0];
            V = item >> 6;
            P = Convert.ToBoolean((item >> 5) & 1);
            X = Convert.ToBoolean((item >> 4) & 1);
            CC = item & 15;
            item = buffer[1];
            M = Convert.ToBoolean((item >> 7) & 1);
            PT = item & 127;
            SequenceNumber = ReadUInt16(buffer[2], buffer[3]);
            Timestamp = ReadUInt32(buffer[4], buffer[5], buffer[6], buffer[7]);
            SSRC = ReadUInt32(buffer[8], buffer[9], buffer[10], buffer[11]);
            int index = 12;
            CSRC = new List<uint>();
            for (var i = 0; i < CC; i++)
            {
                index = i * 4 + index;
                uint csrc = ReadUInt32(buffer[index], buffer[index + 1], buffer[index + 2], buffer[index + 3]);
                CSRC.Add(csrc);
            }
            index = CC * 4 + index;
            if (X)
            {
                index++;
                index += buffer[index];
            }
            Payload = buffer.Skip(index).ToArray();
            PayloadSize = (uint)Payload.LongLength;
            HeaderSize = (uint)(CSRC.Count * 4 + 12);
        }
    }

    /// <summary>
    /// 构造RTP数据包类
    /// </summary>
    /// <param name="packet">RTP数据包</param>
    public RTPPacket(RTPPacket packet)
    {
        // 复制RTP数据包成员
        V = packet.V;
        P = packet.P;
        X = packet.X;
        CC = packet.CC;
        M = packet.M;
        PT = packet.PT;
        SequenceNumber = packet.SequenceNumber;
        Timestamp = packet.Timestamp;
        SSRC = packet.SSRC;
        CSRC = packet.CSRC;
        Payload = packet.Payload;
        PayloadSize = packet.PayloadSize;
        HeaderSize = packet.HeaderSize;
        Debug.WriteLine(ToString());
    }

    /// <summary>
    /// 根据四个字节读取UInt32数据
    /// </summary>
    /// <param name="byte1">字节1</param>
    /// <param name="byte2">字节2</param>
    /// <param name="byte3">字节3</param>
    /// <param name="byte4">字节4</param>
    /// <returns>返回UInt32数据</returns>
    private uint ReadUInt32(byte byte1, byte byte2, byte byte3, byte byte4)
    {
        uint uint1 = (uint)(byte1 << 24);
        uint uint2 = (uint)(byte2 << 16);
        uint uint3 = (uint)(byte3 << 8);
        uint uint4 = (uint)byte4;
        return (uint)(((uint1 | uint2) | uint3) | uint4);
    }

    /// <summary>
    /// 根据两个字节读取UInt16数据
    /// </summary>
    /// <param name="byte1">字节1</param>
    /// <param name="byte2">字节2</param>
    /// <returns>返回UInt16数据</returns>
    private ushort ReadUInt16(byte byte1, byte byte2)
    {
        ushort ushort1 = (ushort)(byte1 << 8);
        ushort ushort2 = (ushort)byte2;
        return (ushort)(ushort1 | ushort2);
    }

    /// <summary>
    /// 重载获取字符串方法
    /// </summary>
    /// <returns>返回RTP数据包对象的字符串表现形式</returns>
    public override string ToString()
    {
        StringBuilder builder = new StringBuilder();
        builder.Append(string.Format("V: {0}, ", V));
        builder.Append(string.Format("P: {0}, ", P));
        builder.Append(string.Format("X: {0}, ", X));
        builder.Append(string.Format("CC: {0}, ", CC));
        builder.Append(string.Format("M: {0}, ", M));
        builder.Append(string.Format("PT: {0}, ", PT));
        builder.Append(string.Format("SequenceNumber: {0}, ", SequenceNumber));
        builder.Append(string.Format("Timestamp: {0}, ", Timestamp));
        builder.Append(string.Format("SSRC: {0}, ", SSRC));
        builder.Append(string.Format("CSRC: {0}, ", string.Join(" ", CSRC)));
        builder.Append(string.Format("PayloadSize: {0};", PayloadSize));
        return builder.ToString();
    }

    /// <summary>
    /// 重载对象是否相同
    /// </summary>
    /// <param name="obj">要比较的对象</param>
    /// <returns>返回对象是否相同</returns>
    public override bool Equals(object obj)
    {
        RTPPacket packet = obj as RTPPacket;
        if (packet == null)
        {
            return false;
        }
        else
        {
            return SequenceNumber == packet.SequenceNumber;
        }
    }

    /// <summary>
    /// 重载获取HashCode
    /// </summary>
    /// <returns>返回SequenceNumber</returns>
    public override int GetHashCode()
    {
        return SequenceNumber;
    }

    /// <summary>
    /// ==运算符重载
    /// </summary>
    /// <param name="packet1">左边RTP数据包对象</param>
    /// <param name="packet2">右边RTP数据包对象</param>
    /// <returns>返回是否相等</returns>
    public static bool operator ==(RTPPacket packet1, RTPPacket packet2)
    {
        if (ReferenceEquals(packet1, packet2))
        {
            return true;
        }
        else if (ReferenceEquals(packet1, null) || ReferenceEquals(packet2, null))
        {
            return false;
        }
        else
        {
            return packet1.Equals(packet2);
        }
    }

    /// <summary>
    /// !=运算符重载
    /// </summary>
    /// <param name="packet1">左边RTP数据包对象</param>
    /// <param name="packet2">右边RTP数据包对象</param>
    /// <returns>返回是否不相等</returns>
    public static bool operator !=(RTPPacket packet1, RTPPacket packet2)
    {
        return !(packet1 == packet2);
    }
}

关于C#中重置URL参数的说明

/// <summary>
/// 构造URL参数信息
/// </summary>
/// <param name="includeEmpty">是否包括空值参数</param>
/// <param name="nameValues">要构造的URL参数信息,由键值对构成,如name=value</param>
/// <returns>返回构造后URL参数信息</returns>
public virtual Dictionary<string, List<object>> BuildUrlParams(bool includeEmpty, params object[] nameValues)
{
    // 定义新的URL参数字典
    Dictionary<string, List<object>> newParams = new Dictionary<string, List<object>>();
    // 构造要设置的URL参数信息
    string nameValue = string.Join("&", nameValues);
    // 拆分URL参数信息
    string[] oldParams = nameValue.Split(new string[] { "&" }, StringSplitOptions.RemoveEmptyEntries);
    // 遍历每个URL参数
    for (int i = 0, length = oldParams.Length; i < length; i++)
    {
        // 获取URL参数的键值对
        string[] items = oldParams[i].Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries);
        // 判断URL参数的键值对是否有效
        if (items.Length > 0)
        {
            // 获取参数名
            string name = items[0];
            // 获取参数值
            string value = null;
            if (items.Length > 1)
            {
                value = items[1];
            }
            // 判断URL参数的键值对是否有效
            if (includeEmpty || value != null)
            {
                // 获取已构造的参数值
                List<object> values;
                if (!newParams.TryGetValue(name, out values))
                {
                    // 没有构造过参数则添加新的参数信息
                    values = newParams[name] = new List<object>();
                }
                // 添加新的参数值
                if (value != null)
                {
                    // 如参数未URL编码则对其进行编码
                    value = HttpUtility.UrlDecode(value, Encoding) == value ? HttpUtility.UrlEncode(value, Encoding) : value;
                    values.Add(value);
                }
            }
        }
    }
    // 返回构造后URL参数信息
    return newParams;
}

/// <summary>
/// 设置URL参数
/// </summary>
/// <param name="url">要设置参数的URL</param>
/// <param name="nameValues">要设置的参数信息,由键值对构成,如name=value</param>
/// <returns>返回设置参数后的URL</returns>
public virtual string SetUrlParams(string url, params object[] nameValues)
{
    // 构造要设置的URL参数信息
    Dictionary<string, List<object>> newParams = BuildUrlParams(true, nameValues);
    // 获取URL地址中参数开始索引
    int index = url.IndexOf("?");
    // 判断URL地址中是否有参数
    if (index >= 0)
    {
        // 构造原始的URL参数信息
        Dictionary<string, List<object>> oldParams = BuildUrlParams(false, url.Substring(index + "?".Length));
        // 获取不带参数的URL地址
        url = url.Substring(0, index);
        // 判断是否存在原始URL参数信息
        if (oldParams.Count > 0)
        {
            // 遍历要设置的URL参数信息
            foreach (KeyValuePair<string, List<object>> item in newParams)
            {
                // 将要设置的URL参数信息覆盖添加到原始参数信息中
                oldParams[item.Key] = item.Value;
            }
            // 重新设置URL参数信息
            newParams = oldParams;
        }
    }
    // 判断是否有参数
    if (newParams.Count > 0)
    {
        // 构造新的URL参数信息
        string newParam = string.Join("&", newParams.Where(n => n.Value.Count > 0).Select(n => string.Format("{0}={1}", n.Key, string.Join(",", n.Value))));
        // 返回构造后的URL地址
        return string.Format("{0}?{1}", url, newParam);
    }
    else
    {
        // 没有参数则返回不带参数的URL地址
        return url;
    }
}

关于C#中按位处理枚举的说明

一、定义:
在枚举上添加[Flags]属性,并以2的幂定义枚举的各个值:

[Flags]
public enum TestEnum
{
    Value0 = 1,
    Value1 = 2,
    Value2 = 4,
    Value3 = 8,
    Value4 = 16,
    All = Value0 | Value1 | Value2 | Value3 | Value4
}

二、比较:
按位运算比较枚举值是否包括某个值:

// 定义枚举变量
TestEnum testEnum = TestEnum.Value1 | TestEnum.Value2;
// 判断枚举变量是否包含枚举Value2的值
bool hasValue = ((testEnum & TestEnum.Value2) == TestEnum.Value2);
// 返回判断结果
return hasValue;

更多信息请参阅:FlagsAttribute 类 (System)

关于C#中设置URL参数的说明

/// <summary>
/// 设置URL参数
/// </summary>
/// <param name="url">要设置参数的URL</param>
/// <param name="name">参数名</param>
/// <param name="value">参数值</param>
/// <returns>返回设置参数后的URL</returns>
public string SetUrlParam(string url, string name, object value)
{
    // 获取URL地址中参数开始索引
    int index = url.IndexOf("?");
    // 判断URL地址中是否有参数
    if (index >= 0)
    {
        // 获取不带参数的URL地址
        string baseUrl = url.Substring(0, index);
        // 获取URL地址中的参数信息
        string paramUrl = url.Substring(index);
        // 判断要设置的参数是否是第一个参数
        if (paramUrl.StartsWith("?" + name + "="))
        {
            // 获取要设置的参数后面的参数开始的索引
            index = paramUrl.IndexOf("&");
            // 判断后面是否有参数
            if (index >= 0)
            {
                // 有参数则构造新的URL地址
                return string.Format("{0}?{1}={2}{3}", baseUrl, name, value, paramUrl.Substring(index));
            }
            else
            {
                // 没参数则构造新的URL地址
                return string.Format("{0}?{1}={2}", baseUrl, name, value);
            }
        }
        else
        {
            // 不是第一个参数则获取要设置参数的索引位置
            index = paramUrl.IndexOf("&" + name + "=");
            // 判断是否存在要设置的参数
            if (index >= 0)
            {
                // 获取要设置参数的前面的参数
                string temp1 = paramUrl.Substring(0, index);
                // 获取要设置参数的后面的参数
                string temp2 = paramUrl.Substring(index + ("&" + name + "=").Length);
                // 获取要设置的参数后面的参数开始的索引
                index = temp2.IndexOf("&");
                // 判断后面是否有参数
                if (index >= 0)
                {
                    // 有参数则构造新的URL地址
                    return string.Format("{0}{1}&{2}={3}{4}", baseUrl, temp1, name, value, temp2.Substring(index));
                }
                else
                {
                    // 没参数则构造新的URL地址
                    return string.Format("{0}{1}&{2}={3}", baseUrl, temp1, name, value);
                }
            }
            else
            {
                // 不存在参数则构造新的URL地址
                return string.Format("{0}&{1}={2}", url, name, value);
            }
        }
    }
    else
    {
        // 原URL地址中没参数则直接构成新的URL地址
        return string.Format("{0}?{1}={2}", url, name, value);
    }
}

关于C#中控制鼠标位置的说明

// 定义设置鼠标位置的Windows API函数
[DllImport("user32.dll")]
private static extern int SetCursorPos(int x, int y);

// 获取鼠标当前X坐标
int x = Control.MousePosition.X;
// 获取鼠标当前Y坐标
int y = Control.MousePosition.Y;
// 设置新的鼠标位置
SetCursorPos(x + 1, y - 1);