上一篇介绍了Resource排序器的接口设计,这次会把具体的实现进行说明。
Resource排序器主要是将Resource.h里面的ID进行排序。只是需要注意的是该文件中有几个宏定义:APS_NEXT_RESOURCE_VALUE, APS_NEXT_COMMAND_VALUE,_APS_NEXT_CONTROL_VALUE, _APS_NEXT_SYMED_VALUE。因为每在rc文件里面添加一个资源(对话框),都会在resource.h里面自动生成一个ID值,但它怎么知道应该生成的值是多少呢?就是去读这几个宏定义的。然后每次在resource.h里面生成一个ID后,相对应的宏的值就会+1。具体每个宏是什么意思可以参考MSDN文章:Using Multiple Resource Files and Header Files with Visual C++。
为了使用的方便,首先用一个Define类f这些宏定义和换行符等东西
public class Define
{
public const string _APS_NEXT_RESOURCE_VALUE_STR = "_APS_NEXT_RESOURCE_VALUE";
public const string _APS_NEXT_COMMAND_VALUE_STR = "_APS_NEXT_COMMAND_VALUE";
public const string _APS_NEXT_CONTROL_VALUE_STR = "_APS_NEXT_CONTROL_VALUE";
public const string _APS_NEXT_SYMED_VALUE_STR = "_APS_NEXT_SYMED_VALUE";
// _APS_NEXT_RESOURCE_VALUE的范围
public const long _APS_NEXT_RESOURCE_VALUE_BEGIN = 1;
public const long _APS_NEXT_RESOURCE_VALUE_END = 0x6FFF;
// _APS_NEXT_COMMAND_VALUE的范围
public const long _APS_NEXT_COMMAND_VALUE_BEGIN = 0x8000;
public const long _APS_NEXT_COMMAND_VALUE_END = 0xDFFF;
// _APS_NEXT_CONTROL_VALUE的范围
public const long _APS_NEXT_CONTROL_VALUE_BEGIN = 8;
public const long _APS_NEXT_CONTROL_VALUE_END = 0xDFFF;
// 几个换行符
public const string CLRF = "/r/n";
public const string CL = "/r";
public const string RF = "/n";
public const string RESOURCE_NAME = "resource.h";
}
最后就是的ResoureParse类了,需要处理注意的有下面几个地方: 1.不同的资源类型是有区间范围的,例如Dialog, Menu和IDD_开头的普通控件就在不同的区间。因此 ID递增的时候必须在各自的区间内(就是上面所说的宏,换句话说上面的宏就应该是该区间的最大值 再加一)。 2.Int.Parse函数是不支持"0x"开头的16进制数字,因此要去掉0x前缀进行处理。
using System;
using System.Text;
using System.IO;
namespace Commands.FileParser
{
/// <summary>
/// ResourceParser 的摘要说明。
/// </summary>
public class ResourceParser: IParser
{
private long m_counter = 0;
private long m_last = 1;
private long m_APS_NEXT_RESOURCE_VALUE = 0;
private long m_APS_NEXT_COMMAND_VALUE = 0;
private long m_APS_NEXT_CONTROL_VALUE = 0;
//private long m_APS_NEXT_SYMED_VALUE = 0;
public ResourceParser()
{
}
// 函数名: ParseLine
// 参数: line 被解析的字符串
// 描述: 解析传入的一行字符串,并调整资源ID的值
void ParseLine(ref string line)
{
// 首先找到#define 字段
int pos = line.IndexOf("#define");
if (pos == -1)
{
return;
}
// 再找到最后一个空格
pos = line.LastIndexOf(' ');
if (pos == -1)
{
return;
}
// 如果是_APS_NEXT开头的几个宏要进行格外处理
if (!ParseAPS_NEXTLine(ref line))
{
// 将最后一个ID逐行加1再替换
string oldNumber = line.Substring(pos + 1);
string newNumber = GetLineIndex(NumberToInt(oldNumber)).ToString();
line = line.Replace(oldNumber, newNumber);
}
}
// 函数名: ParseNumber
// 参数: oldNumber 需要转换的字符串
// 描述: 字符串转换成整形,并一并处理16进制的情况
private int NumberToInt( string oldNumber )
{
int HexPos = oldNumber.IndexOf("0x");
int Value = 0;
//不是以Hex开头的字符串
if (HexPos == -1)
{
Value = int.Parse(oldNumber);
}
else
{
string HexNumber = oldNumber.Remove(HexPos, 2);
Value = Int32.Parse(HexNumber, System.Globalization.NumberStyles.AllowHexSpecifier);
}
return Value;
}
// 函数名: IsAPSLine
// 参数: line 被解析的字符串
// 描述: 解析传入的一行字符串,并调整资源ID的值
bool ParseAPS_NEXTLine(ref string line)
{
int pos = line.LastIndexOf(' ');
bool found = true;
string oldNumber = line.Substring(pos);
if (line.IndexOf(Define._APS_NEXT_RESOURCE_VALUE_STR) != -1)
{
line = line.Replace(oldNumber, m_APS_NEXT_RESOURCE_VALUE.ToString());
}
else if (line.IndexOf(Define._APS_NEXT_CONTROL_VALUE_STR) != -1)
{
line = line.Replace(oldNumber, m_APS_NEXT_CONTROL_VALUE.ToString());
}
else if (line.IndexOf(Define._APS_NEXT_COMMAND_VALUE_STR) != -1)
{
line = line.Replace(oldNumber, m_APS_NEXT_CONTROL_VALUE.ToString());
}
else if (line.IndexOf(Define._APS_NEXT_SYMED_VALUE_STR) != -1)
{
// Do nothing
}
else
{
found = false;
}
return found;
}
// 函数名: GetRange
// 参数: number
// 描述: 获得该数字所属的区间
// < 100, 返回100
// 100 <= && < 1000, 返回1000
// >= 1000 && <10000, 返回10000
// >10000, 直接返回10000
long GetRange(long number)
{
long range = 1;
if (number < 100)
{
range = 1;
}
else if (number >= 100 && number < 1000)
{
range = 100;
}
else if (number >= 1000 && number < 10000)
{
range = 1000;
}
else if (number >= 10000 && number < Define._APS_NEXT_COMMAND_VALUE_BEGIN)
{
range = 10000;
}
else
{
range = number;
}
return range;
}
// 函数名: GetLineIndex
// 参数: void
// 描述: 每有一个新的#define ID xxx,数字就加1
long GetLineIndex(long number)
{
// 记录上一次的range
long range = GetRange(number);
if (m_last != range)
{
m_counter = 0;
m_last = range;
}
m_counter++;
long result = m_counter + range;
if (result < 1000)
{
m_APS_NEXT_RESOURCE_VALUE = result + 1;
}
else if (result >= 1000 && result < Define._APS_NEXT_COMMAND_VALUE_BEGIN)
{
m_APS_NEXT_CONTROL_VALUE = result + 1;
}
else if (result >= Define._APS_NEXT_COMMAND_VALUE_BEGIN)
{
m_APS_NEXT_COMMAND_VALUE = result + 1;
}
return result;
}
#region IParser 成员
public int Parse(string fileName)
{
string line;
StringBuilder sb = new StringBuilder();
using (System.IO.StreamReader reader = new System.IO.StreamReader(fileName))
{
while((line = reader.ReadLine()) != null)
{
ParseLine(ref line);
sb.Append(line);
sb.Append(Define.CLRF);
}
sb.Append(Define.RF);
reader.Close();
}
File.SetAttributes(fileName,FileAttributes.Normal);
// TODO: 添加 ResourceParser.Parse 实现
using (StreamWriter outWriter = new StreamWriter(fileName))
{
outWriter.Write(sb.ToString());
}
return 0;
}
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: 在此处添加代码以启动应用程序
//
ResourceParser parser = new ResourceParser();
parser.Parse("resource.h");
}
#endregion
}
}
这篇文章写得比较懒,因为比较不喜欢一行一行的说代码,每个人的想法都不一样。还是那句话,只要
接口定义出来了,具体里面的实现就不会差到哪儿去。