Visual Studio 2017中使用正则修改部分内容
最近在项目中想实现一个小工具,需要根据类的属性<summary>
的内容加上相应的[Description]
特性,需要实现的效果如下
修改前:
/// <summary>
/// 条形码
/// </summary>
public List<GoodsBarcodeEditModel> Barcodes { get; set; }
修改后:
/// <summary>
/// 条形码
/// </summary>
[Description("条形码")]
public List<GoodsBarcodeEditModel> Barcodes { get; set; }
作为一个非处女座,但是有处女座特点的程序猿,牢记着DRY(Don't Repeat Yourself), 不想把时间浪费在不停的Copy-Paste上,于是想着VS的Find and Replace里的正则会不会有支持部分替换的功能,顺着这个想法,找到了微软文档《正则表达式中的替代》,里面的$数值
替换捕获组下好满足我的需求。接下来的工作就简单了
(///\s<.*\n*)(\s*)(///)(\s*)(\w*)(\n*)(\s*///.*)
代码段 | 正则分组 | 分组序号 |
---|---|---|
/// <summary> |
(///\s<.*\n*) |
$1 |
空格 | (\s*) |
$2 |
/// |
(///) |
$3 |
空格 | (\s*) |
$4 |
条形码 |
(\w*) |
$5 |
换行 | (\n*) |
$6 |
/// </summary> |
(\s*///.*) |
$7 |
因此使用如下组合,就是得到我们想要的结果
$1$2$3$4$5$6$7 [Description("$5")]
最终效果如下:
如何使用ILAsm与ILDasm修改.Net exe(dll)文件
一、背景
最近项目组新上项目,交付的时间比较急迫,原本好的分支管理习惯没有遵守好,于是出现下面状况:
- 多个小伙伴在不同的分支上开发。
- 原本QA环境也存在一个阻碍性的bug A
- 一位同事在QA环境发布了新的代码,引入了新bug B
- 回滚QA能修改bug B,但是对于bugA却无能为力
- 同时,混乱的代码管理已经导致无法确定原始发布包对应的代码版本。
最终陷入了两难的地步,既不能发布新包,回滚也无法解决问题。
好在之前了解到如何使用微软官方工具ILAsm与ILDasm对dll文件进行修改,于是开始动手实现。下面将会用示例代码讲解如何修改已经的.exe文件。
二、ILAsm与ILDasm
我们知道,.net是一个跨平台的的开发平台,其跨平台则是由其编译的中间语言(Intermediate Language, 简称IL或MSIL)实现,无论我们使用的是C#、VB.Net、还是F#或者C++, 最终都会被编译成IL,由JIT(Just In Time)编译成目标机器语言,在CLR(Commen Language Runtime, 公用语言运行时)上运行。
因此,理论上,我们可以跳过过平时使用的C#代码,直接修改IL,然后生成相应的dll或者exe文件。
那么如何查看与修改IL呢,这就是ILAsm与ILDasm的工作了。ILAsm (MSIL Assembler),用来从IL语言生成PE(Portable Executable),也就是.net中我们使用的.exe、.dll文件。ILDasm (MSIL Disassembler),则与ILAsm相反,从PE文件,生成.IL文件。那么我们可以猜到,要修改dll,我们需要先用ILDasm反编译.dll生成.il文件,再用ILAsm编译修改后的.il文件生成.dll,最终替换.dll文件。
三、使用ILDasm生成IL
先看下示例代码:
class Program
{
static void Main(string[] args)
{
var loginResult = Login("foo", "111111");
if (loginResult)
{
Console.WriteLine("登录成功");
}
else
{
Console.WriteLine("登录失败,请重试");
}
Console.ReadLine();
}
private static bool Login(string userName, string password)
{
if (userName.Equals("johnny") && password.Equals("123456"))
{
return false;
}
return false;
}
}
显然,上述代码针对Login(string userName, string password)
的调用会返回false,导致最后Console中会输出"登录失败,请重试", 我们的目的是通过直接修改.exe文件,让它返回true, Console里面输出"登录成功"。
ILDasm与ILAsm已经包含在Visual Studio发行包中中,无需另外下载安装。按如下步骤执行即可:
1. 开Developer Command Prompt for VS 2017,在里面输入命令ILDasm
。
2. 在打开的IL Dasm窗口中找到需要修改的.exe文件。
3. 选择菜单 File > Dump,弹出的新窗口中点击确认,保存生成的.il文件。
整个过程如下面gif所示:
最后会生成相应的.il与.res文件。
四、修改IL
打开.il文件,会看到如下代码(节选)
.method private hidebysig static bool Login(string userName,
string password) cil managed
{
// Code size 43 (0x2b)
.maxstack 2
.locals init ([0] bool V_0,
[1] bool V_1)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldstr "johnny"
IL_0007: callvirt instance bool [mscorlib]System.String::Equals(string)
IL_000c: brfalse.s IL_001b
IL_000e: ldarg.1
IL_000f: ldstr "123456"
IL_0014: callvirt instance bool [mscorlib]System.String::Equals(string)
IL_0019: br.s IL_001c
IL_001b: ldc.i4.0
IL_001c: stloc.0
IL_001d: ldloc.0
IL_001e: brfalse.s IL_0025
IL_0020: nop
IL_0021: ldc.i4.0
IL_0022: stloc.1
IL_0023: br.s IL_0029
IL_0025: ldc.i4.0
IL_0026: stloc.1
IL_0027: br.s IL_0029
IL_0029: ldloc.1
IL_002a: ret
} // end of method Program::Login
这个就是Login(string userName, string password)
所对应的IL代码了。如果你了解IL语言,可以直接对其修改。
如果不想直接修改IL,我们可以重写一个小的示例方法,直接return true
,如下:
private static bool Login()
{
return true;
}
然后使用ILDasm生成相应的IL代码,替换我们想修改的方法。最终的IL如下:
.method private hidebysig static bool Login(string userName,
string password) cil managed
{
.maxstack 8
IL_0000: ldc.i4.1
IL_0001: ret
} // end of method Program::Login
五、使用ILAsm生成exe
修改保存完.il文件以后,接下来的工作就是利用 ILAsm 让.il文件生成重新生成.exe可执行文件了,在Console中执行如下命令
ilasm ILAsmAndILDasmDemo.il /output:ILAsmAndILDasmDemo_1.exe
// 如果修改的是dll文件,需要加上参数 /dll
成功以后会生成一个ILAsmAndILDasmDemo_1.exe文件,执行这个文件,我们可以看到,现在已经显示"登录成功"了。
使用反编译工具 dotPeekI查看新生成的ILAsmAndILDasmDemo_1.exe文件,我们能够看到,Login(string userName, string password)
已经直接return true
了。如下图,
六、总结
其它,上面所做的事情其实也是《CLR via C#》中提到强名能够 防止代码被不怀好意的人篡改 的一个反面教材了。通过这个例子,大家应该对代码被篡改的风险也有一定的认识了,所以如果大家需要将自己的.dll(.exe)文件露给别人,最好还是打上强名,防止别人恶意篡改你的代码,导致不必要的损失。
经过这次事件,也证明了多了解下底层还是很有必要的,说不定哪天就用上了。平时再忙,也不能只限于只业务和编程语言层面,还需要对底层有一定的了解,这样才能知其然与知其所以然。
虽然此次在不修改c#代码的情况下完美解决QA的环境问题,但是这种方法也只限于小范围的改动,只试用于救急。所以给我的教训是分支管理规范才是王道,要能做到随时可发布,随时可回滚才行,这样才能完全避免再次出现这样尴尬的情况了。
C#学习-图解教程(1):格式化数字字符串
Posted on 2018-05-18 18:39 Leo_olivine 阅读(9) 评论(0) 编辑 收藏
学习内容:C#;学习书籍:图解教程(中文第四版)。
目录:第二章 C#编程概述 -----> 2.7 从程序中输出文本 -----> 2.7.5格式化数字字符串
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _2018_05_04_AM_1520_格式化数字字符串 { class Program { static void Main(string[] args) { /* * 格式化数字字符串 * 格式说明符的语法由3个字段组成:索引号、对齐说明符、格式字段(format field) * 格式:{ index , alignment :format } * index 为索引,是必需要填写的,表示是哪一个需要进行格式化; * alignment 为对齐说明符,由自己决定是否需要填写;是一个整数,其中正整数表示的是字段右对齐,负整数表示的是左对齐,填写的数字表示的是长度(宽度); * format 为格式字段,来指定由index选择的数字的表现形式;需注意冒号后面紧跟格式说明符,中间不能由空格。 * 格式说明符 是一个字母字符;精度说明符 是可以选择是否需要,由1~2位数字组成。 * 具体请看下面的例子 */ double first = 1234.56789; double second = -12.3456789; /* * 标准数字格式说明符 */ Console.WriteLine("格式说明符:"); Console.WriteLine("货币C、c(不区分大小写) |{0, 15 :C}|{1, -15 :c3}|", first, second); /* * 把值格式化为货币,货币符号取决与程序所在的PC的区域自动设置 * 精度说明符 设置的是小数位的位数;如果不设置 精度说明符 那么会自动取2位小数。 */ Console.WriteLine("十进制D、d(不区分大小写) |{0, 15 :D}|{1, -15 :d4}|", 107, -29); /* * 十进制数字字符串,需要注意,只能和整数类型配合使用;如果是其他类型会报错,提示格式说明符无效 * 精度说明符 设置的是整个字符串的位数;如果实际数字位数小于给出的精度说明位数,则在左边补0。 */ Console.WriteLine("定点F、f(不区分大小写) |{0, 15 :F}|{1, -15 :f3}|", first, second); /* * 带有小数的十进制数字字符串 * 精度说明符 设置的是小数位的位数;如果不设置 精度说明符 那么会自动取2位小数。 */ Console.WriteLine("常规G、g(不区分大小写) |{0, 15 :G}|{1, -15 :g4}|", first, second); /* * 在没有说明符的情况下,会根据值转换为定点或科学记数法表示的紧凑形式 * 精度说明符 设置的是整个字符串的位数;如果实际数字位数小于给出的精度说明位数,并不会进行任何补0操作;不设置,则按原样显示 */ Console.WriteLine("十六进制X、x(区分大小写) |{0, 15 :x5}|{1, -15 :X}|", 107, -29); /* * 十六进制数字的A~F会根据格式说明符的大小写来进行匹配,X 匹配A~F ;x 匹配a~f * 精度说明符 设置的是整个字符串的位数;如果实际数字位数小于给出的精度说明位数,则在左边补0。补充:字符串的位数是按转成了十六进制的字符串的位数。 */ Console.WriteLine("数字N、n(不区分大小写) |{0, 15 :N}|{0, -15 :n5}|", first, second); /* * 和定点相似,但在每三个数字的一组中有逗号或空格分隔符,从小数点向左数,取决于程序所在的PC的区域设置 * 精度说明符 设置的是小数位的位数;如果不设置 精度说明符 那么会自动取2位小数。 */ Console.WriteLine("百分比P、p(不区分大小写) |{0, 15 :P}|{1, -15 :p6}|", first, second); /* * 表示百分比,数字会乘以100 * 精度说明符 设置的是小数位的位数;如果不设置 精度说明符 那么会自动取2位小数。 */ Console.WriteLine("往返过程R、r(不区分大小写)|{0, 15 :R}|{1, -15 :r3}|", first, second); /* * 保证输出字符串后,如果使用Parse方法将字符串转化成数字,则该值与原始值一样。 * 精度说明符 设置了也并没有对数据有影响。 */ Console.WriteLine("科学记数法E、e(区分大小写)|{0, 15 :E}|{1, -15 :e10}|", first, second); /* * 具有尾数和指数的科学记数法。指数前加的字母E 是根据说明符的大小写进行匹配的 * 精度说明符 设置的是小数位的位数。 */ Console.ReadKey(); } } }
学习感受:
我现在的工作中还没有遇见这些的使用。
小程序页面代码因为某些人力不可控的原因代码丢失了,这里简单说明一下
调用小程序APIwx.chooseImage(OBJECT)选择相册或拍摄照片,会返回 tempFilePaths,之后通过wx.uploadFile(OBJECT)把照片传至后台,在服务器后台进行Base64编码,
小程序目前不支持在前台页面和本地进行Base64编码的
下面贴一下后台编码方法(注:本方法只进行Base64编码,图片不保存至服务器)
public void ProcessRequest(HttpContext context) { string dataType = context.Request["dataType"]; string responseData = ""; switch (dataType) { case "BaseImg": { try { System.IO.Stream s = context.Request.Files["uploadiamges"].InputStream; System.Drawing.Bitmap image = new System.Drawing.Bitmap(s); MemoryStream ms = new MemoryStream(); image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); byte[] arr = new byte[ms.Length]; ms.Position = 0; ms.Read(arr, 0, (int)ms.Length); ms.Close(); String strbaser64 = Convert.ToBase64String(arr); responseData = strbaser64; //strbaser64 即是编码后的 base64字符串
} catch(Exception ex) { responseData = ex.Message; } break; } } context.Response.Write(responseData);
}
表格代码
<tr> <td> <input type="text" style="border: none; text-align: center;" class="chengwei" /></td> <td> <input type="text" style="border: none; text-align: center;" class="xingming" /></td> <td> <input type="text" style="text-align: center; border: none" class="age" /></td> <td> <input type="text" style="text-align: center; border: none" class="zhengzhimm" /></td> <td> <input type="text" style="text-align: center; border: none" class="gongzuodanwei" /></td> <td> <input type="text" style="text-align: center; border: none" class="zhiwu" /></td> </tr>
js取值-------------------------
//称谓
function getchengweival() { var reVal = ''; $('.chengwei').each(function () { if ($(this).val() != "" & $(this).val() != undefined) { reVal += $(this).val() + ","; } }); reVal = reVal.substr(0, reVal.length - 1); return reVal.split(','); }
赋值
$(function () { var vals = ‘1,2,3;2,3.4’; if (vals.length > 0) { for (var i = 0; i < vals.length; i++) { var cs = vals[i].split(','); $('.chengwei').each(function (x) { if (x == i) { $(this).val(cs[0]) } }); } } });
声明密钥级偏移向量--------/// <summary> /// 加密密钥 /// </summary> private static readonly string Default_AES_Key = ""; /// <summary> /// 偏移向量 /// </summary> private static byte[] Keys = { };
加密算法--------/// <summary> /// 对称加密算法AES RijndaelManaged加密(RijndaelManaged(AES)算法是块式加密算法) /// </summary> /// <param name="encryptString">待加密字符串</param> /// <returns>加密结果字符串</returns> public static string AES_Encrypt(string encryptString) { return AES_Encrypt(encryptString, Default_AES_Key); } /// <summary> /// 对称加密算法AES RijndaelManaged加密(RijndaelManaged(AES)算法是块式加密算法) /// </summary> /// <param name="encryptString">待加密字符串</param> /// <param name="encryptKey">加密密钥,须半角字符</param> /// <returns>加密结果字符串</returns> public static string AES_Encrypt(string encryptString, string encryptKey) { encryptKey = GetSubString(encryptKey, 32, ""); encryptKey = encryptKey.PadRight(32, ' '); RijndaelManaged rijndaelProvider = new RijndaelManaged(); rijndaelProvider.Key = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 32)); rijndaelProvider.IV = Keys; ICryptoTransform rijndaelEncrypt = rijndaelProvider.CreateEncryptor(); byte[] inputData = Encoding.UTF8.GetBytes(encryptString); byte[] encryptedData = rijndaelEncrypt.TransformFinalBlock(inputData, 0, inputData.Length); return Convert.ToBase64String(encryptedData); }
解密算法--------/// <summary> /// 对称加密算法AES RijndaelManaged解密字符串 /// </summary> /// <param name="decryptString">待解密的字符串</param> /// <returns>解密成功返回解密后的字符串,失败返源串</returns> public static string AES_Decrypt(string decryptString) { return AES_Decrypt(decryptString, Default_AES_Key); } /// <summary> /// 对称加密算法AES RijndaelManaged解密字符串 /// </summary> /// <param name="decryptString">待解密的字符串</param> /// <param name="decryptKey">解密密钥,和加密密钥相同</param> /// <returns>解密成功返回解密后的字符串,失败返回空</returns> public static string AES_Decrypt(string decryptString, string decryptKey) { try { decryptKey = GetSubString(decryptKey, 32, ""); decryptKey = decryptKey.PadRight(32, ' '); RijndaelManaged rijndaelProvider = new RijndaelManaged(); rijndaelProvider.Key = Encoding.UTF8.GetBytes(decryptKey); rijndaelProvider.IV = Keys; ICryptoTransform rijndaelDecrypt = rijndaelProvider.CreateDecryptor(); byte[] inputData = Convert.FromBase64String(decryptString); byte[] decryptedData = rijndaelDecrypt.TransformFinalBlock(inputData, 0, inputData.Length); return Encoding.UTF8.GetString(decryptedData); } catch { return string.Empty; } }
function GCJ02toBD09(lng, lat) { var x_pi = 3.14159265358979324 * 3000.0 / 180.0; var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_pi); var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_pi); var bd_lng = z * Math.cos(theta) + 0.0065; var bd_lat = z * Math.sin(theta) + 0.006; return [bd_lng,bd_lat] }
function getDistance(lat1, lng1, lat2, lng2) { var dis = 0; var radLat1 = toRadians(lat1); var radLat2 = toRadians(lat2); var deltaLat = radLat1 - radLat2; var deltaLng = toRadians(lng1) - toRadians(lng2); var dis = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(deltaLat / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(deltaLng / 2), 2))); return dis * 6378137; function toRadians(d) { return d * Math.PI / 180; } }
/// <summary> /// /// </summary> /// <param name="distance">距离</param> /// <param name="lng">A坐标经度</param> /// <param name="lati">A坐标纬度</param> /// <param name="angle">角度 ,以正北方向顺时针开始</param> /// <returns></returns> private string ConvertDistanceToLogLat(int distance, string lng, string lati, double angle) { double juli = (float)distance / 1000; string logLat = null; double lng1 = Convert.ToDouble(lng.Trim()); double lat1 = Convert.ToDouble(lati.Trim()); //将距离转换成经度的计算公式 double lon = lng1 + (juli * Math.Sin(angle * Math.PI / 180)) / (111 * Math.Cos(lat1 * Math.PI / 180)); //将距离转换成纬度的计算公式 double lat = lat1 + (juli * Math.Cos(angle * Math.PI / 180)) / 111; logLat = lon + "," + lat; return logLat; }
private string ConvertLogLatToString(double lon) { string resut = null; string temp = lon.ToString(); string[] du = temp.Split('.'); resut += du[0] + "°"; double fen = (lon - Convert.ToDouble(du[0])) * 60; string[] fen_Arrary = fen.ToString().Split('.'); resut += fen_Arrary[0] + "′"; if (fen_Arrary.Length > 1) { double second = Math.Round((fen - Convert.ToDouble(fen_Arrary[0])) * 60, 0); resut += second.ToString() + "″"; } return resut; }