ODP.NET版本暴力解法之懶人工具
0 |
ODP.NET的版本問題一直像鬼魅般苦苦糾纏,最近又碰上了... orz
經前一篇文章證實,在參考ODP.NET 10.2的ASP.NET網站,若再引用參考了ODP.NET 11.2的其他元件,將產生has a higher version than referenced assembly編譯錯誤,且無法以bindingRedirect解決。有顆共用元件要提供給多個專案使用,有些專案仍須維持ODP.NET 10.2,有些專案則已經更新到ODP.NET 11.2,且實務上無法要求大家統一。因此,要解決此一問題,我想到幾種做法:
方法一是共用元件改回參照ODP.NET 10.2,在11.2版本環境中則用bindingRedirect設定或是發行者原則檔進行繫結導向。
方法二則是共用元件分別編譯成10.2與11.2兩個版本,供不同專案使用。
兩種做法都要需要準備10.2舊版ODP.NET的編譯環境,不想回頭裝舊版的任性鬼想到另一個點子--ODP.NET版本相容問題的暴力解法,決定把這個程序寫成自動工具,將來不管什麼共用元件DLL,只要有ODP.NET 11.2改10.2的需求,就拿來過一下香爐,問題馬上解決。
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Win32;
namespace ChgOdpRefVersion
{
static class Program
{
static void Main(string[] args)
{
if (args.Length < 2 || !args[0].Contains(".dll") ||
!Regex.IsMatch(args[1], @"\d+\.\d+\.\d+\.\d+"))
{
Console.WriteLine("Syntax Example: ChgOdpRefVersion Boo.dll 2.112.3.0");
return;
}
string dllFile = args[0].Trim('\"'), version = args[1];
string outDllFile =
Path.Combine(Path.GetDirectoryName(dllFile),
Path.GetFileNameWithoutExtension(dllFile) + ".odp-" + version + ".dll");
string ilFile = Path.GetTempFileName() + ".il";
ildasm(dllFile, ilFile);
StringBuilder sb = new StringBuilder();
bool found = false;
using (StreamReader sr = new StreamReader(ilFile))
{
string line = null;
while ((line = sr.ReadLine()) != null)
{
if (line.StartsWith(".assembly extern Oracle.DataAccess"))
{
sb.AppendFormat(@"
.assembly extern Oracle.DataAccess
{{
.publickeytoken = (89 B4 83 F4 29 C4 73 42 )
.ver {0}
}}
", version.Replace(".", ":"));
do
{
line = sr.ReadLine();
} while (!line.StartsWith("}"));
sb.Append(sr.ReadToEnd());
found = true;
break;
}
else
{
sb.AppendLine(line);
}
}
}
if (found)
{
File.WriteAllText(ilFile, sb.ToString());
ilasm(ilFile, outDllFile);
Console.WriteLine("Done!");
}
else
Console.WriteLine("Oracle.DataAccess reference not found!");
}
public static void ildasm(string dllPath, string ilPath)
{
exec(Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
@"Microsoft SDKs\Windows\v6.0A\bin\ildasm.exe"),
string.Format("\"{0}\" /output:\"{1}\"", dllPath, ilPath));
}
public static void ilasm(string ilPath, string dllPath)
{
exec(Path.Combine(GetFrameworkDirectory(), "ilasm.exe"),
string.Format("\"{0}\" /dll /output:\"{1}\"", ilPath, dllPath));
}
public static void exec(string program, string arguments)
{
ProcessStartInfo psi = new ProcessStartInfo(program, arguments);
Process p = Process.Start(psi);
p.WaitForExit();
}
//REF: http://bit.ly/PLA9Zw
public static string GetFrameworkDirectory()
{
string framworkRegPath = @"Software\Microsoft\.NetFramework";
RegistryKey netFramework =
Registry.LocalMachine.OpenSubKey(framworkRegPath, false);
string installRoot =
netFramework.GetValue("InstallRoot").ToString();
string version = string.Format(@"v{0}.{1}.{2}\",
Environment.Version.Major, Environment.Version.Minor, Environment.Version.Build);
return System.IO.Path.Combine(installRoot, version);
}
}
}
程式是以Console Application形式編譯,主要著眼於未來可加掛在Visual Studio AfterBuild事件或MSBuild程序中執行,而程式必須在有安裝.NET Framework SDK的機器上執行(才有ildasm及ilasm),透過Command視窗下達指令:
ChgOdpRefVersion myAssembly.dll 2.102.2.20
程式會呼叫ildasm將myAssembly.dll反組譯成.il檔,尋找其中的Oracle.DataAccess參考版號註記,修改成指定版號,再使用ilasm重新編譯成myAssembly.odp-2.102.2.20.dll儲存在myAssembly.dll的相同目錄下。就醬,未來就不用再為了ODP.NET版本切換傷腦筋囉~
【提醒】 純改變版號宣告的表面功夫當然不像重新編譯能解決新舊版本間的API差異,採用前請自行評估風險,但依我自己的實務使用經驗,用在ODP.NET 10.2與11.2間的切換,運作得蠻順利的,僅供大家參考。
Comments
Be the first to post a comment