内容简介:前篇文章介紹了如何在無開發工具的管制環境撰寫 Program.cs 並轉為 Program.exe,以便在執行環境修改與測試現有 DLL 程式庫重現問題。前陣子但實際做過一回才發現沒想像來得簡單,其中眉角不少,以下是我的實戰經驗分享。
前篇文章介紹了如何在無開發 工具 的管制環境撰寫 Program.cs 並轉為 Program.exe,以便在執行環境修改與測試現有 DLL 程式庫重現問題。
前陣子 從頭學習了 Powershell ,知道 Powershell 可直接引用 .NET 類別,理論上也能做到同樣的事。
但實際做過一回才發現沒想像來得簡單,其中眉角不少,以下是我的實戰經驗分享。
直接看程式,解說部分我直接寫成註解。至於程式用途及 C# 範例請直接參考前文,在此不重複贅述。
try { # 引用 System.Configuration 以使用 OpenMappedExeConfiguration、AppSettings Add-Type -AssemblyName System.Configuration; $fileMap = New-Object System.Configuration.ExeConfigurationFileMap; $fileMap.ExeConfigFilename = 'C:\MyApp\MyApp.exe.config'; # 用 OpenMappedExeConfiguration() 讀取其他程式的 .exe.config $config = [System.Configuration.ConfigurationManager]::OpenMappedExeConfiguration($fileMap, [System.Configuration.ConfigurationUserLevel]::None); # 由 MyApp.exe.config 取出 appSetting $idKey = $config.AppSettings.Settings['idKey'].Value; # 直接指向 Dll 所在位置 Add-Type -Path C:\MyApp\MyAppLibrary.dll; Add-Type -Path C:\MyApp\Oracle.DataAccess.dll; # 呼叫自訂程式庫取得 OracleConnection $cn = [MyAppLibrary.DataLayer]::GetOraConnection($idKey); # 以下為標準 ODP.NET 操作 $cmd = $cn.CreateCommand(); $cmd.CommandText = @" SELECT A,B,C FROM T WHERE D = :p_date "@; $cmd.BindByName = $true; $p = $cmd.Parameters.Add("p_date", [Oracle.DataAccess.Client.OracleDbType]::Date); $p.Value = New-Object DateTime -ArgumentList 2019, 1, 1; $dr = $cmd.ExecuteReader(); $cnt = 0; while ($dr.Read()) { $cnt++; } Write-Host "Data Count=$($cnt)"; } catch { # 用 catch 配合 $error[0] 可得到較詳細的訊息 $error[0] | Format-List -Force; # 找不到相依組件出錯時,LoaderExceptions 才有缺少組件名稱 $error[0].Exception.LoaderExceptions | Select -First 1 | Format-List -Force; } finally { # Powershell 沒有 using,用 finally 確保結束物件釋放資源 if ($cn -ne $null) { $cn.Dispose(); } }
補充說明加入 catch 段的好處。未加入 try ... catch 前,若呼叫 .NET 方法出錯,程式預設會繼續往下執行,而錯誤訊息類似這樣。
以 "2" 引數呼叫 "OpenMappedExeConfiguration" 時發生例外狀況: "The string parameter 'fileMap.ExeConfigFilename' cannot be null or empty. Parameter name: fileMap.ExeConfigFilename" 位於 D:\Temp\OraTest\TestOra.ps1:5 字元:86 + $config = [System.Configuration.ConfigurationManager]::OpenMappedExeConfiguration <<<< ($fileMap, [System.Configuration.ConfigurationUserLevel]::None); + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : DotNetMethodException
加上 catch 顯示 $error[0] | Fromat-List -Force 後,程式遇錯會跳至 catch 段,中止後續程式執行,而錯誤訊息內容包含詳細的 .NET StackTrace,較易除錯。
Exception : System.Management.Automation.MethodInvocationException: 以 "2" 引數呼叫 "OpenMappedExeConfiguration" 時發生例外狀況: "The string parameter 'fileMap.ExeConfigFilename' cannot be null or empty. Parameter name: fileMap.ExeConfigFilename" ---> System.ArgumentException: The string parameter 'fileMap.ExeConfigFilename' cannot be null or empty. Parameter name: fileMap.ExeConfigFilename at System.Configuration.ClientConfigurationHost.OpenExeConfiguration(ConfigurationFileMap fileMap, Boolean isMachine, ConfigurationUserLevel userLevel, String exePath) at System.Configuration.ConfigurationManager.OpenExeConfigurationImpl(ConfigurationFileMap fileMap, Boolean isMachine, ConfigurationUserLevel userLevel, String exePath, Boolean preLoad) at OpenMappedExeConfiguration(Object , Object[] ) at System.Management.Automation.MethodInformation.Invoke(Object target, Object[] arguments) at System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(Object target, Object[] arguments, MethodInformation methodInformation, Object[] originalArguments) --- End of inner exception stack trace --- at System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode statement, Array input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context) at System.Management.Automation.StatementListNode.Execute(Array input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context) at System.Management.Automation.TryStatementNode.Execute(Array input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context) TargetObject : CategoryInfo : NotSpecified: (:) [], MethodInvocationException FullyQualifiedErrorId : DotNetMethodException ErrorDetails : InvocationInfo : System.Management.Automation.InvocationInfo PipelineIterationInfo : {} PSMessageDetails :
至於 LoaderExceptions 刖是用在找不到相依組件時,預設 $error[0] 只會看到如下訊息,無從得知缺少哪個組件:
Exception : System.Reflection.ReflectionTypeLoadException: 無法載入一或多個要求類型。請擷取 LoaderExceptions 屬性以取得詳細資訊。 於 System.Reflection.RuntimeModule.GetTypes(RuntimeModule module) 於 System.Reflection.Assembly.GetTypes() 於 Microsoft.PowerShell.Commands.AddTypeCommand.LoadAssemblyFromPathOrName(List`1 generatedTypes) 於 Microsoft.PowerShell.Commands.AddTypeCommand.EndProcessing() 於 System.Management.Automation.CommandProcessorBase.Complete() TargetObject : CategoryInfo : NotSpecified: (:) [Add-Type], ReflectionTypeLoadException FullyQualifiedErrorId : System.Reflection.ReflectionTypeLoadException,Microsoft.PowerShell .Commands.AddTypeCommand ErrorDetails : InvocationInfo : System.Management.Automation.InvocationInfo ScriptStackTrace : 位於 <ScriptBlock>,E:\OraTest\RunOra.ps1: 第 15 行 位於 <ScriptBlock>,<無檔案>: 第 1 行 PipelineIterationInfo : {} PSMessageDetails :
透過 $error[0].Exception.LoaderExceptions | Select -First 1 | Format-List -Force 可得到詳細說明,明確指出缺少的 DLL 為何,補上 Add-Type -Path ... 即可解決:
Message : Could not load file or assembly 'Some.DepLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. 系統找不到指定的檔案。 FileName : Some.DepLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null FusionLog : 警告: 組件繫結記錄切換為 OFF。 若要記錄組件繫結失敗,請將登錄值 [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) 設為 1。 注意: 與組件繫結失敗記錄相關的效能會有部分負面影響。 若要關閉此功能,請移除登錄值 [HKLM\Software\Microsoft\Fusion!EnableLog]。 Data : {} InnerException : TargetSite : StackTrace : HelpLink : Source : HResult : -2147024894
最後是執行的一些小技巧,我發現 Windows 2008 R2 預載的 Powershell 版本是 2.0,其搭配 .NET CLR 版本為 2.0。
如試圖載入 .NET 4.0 寫的組件會噴出以下錯誤:
System.BadImageFormatException: 無法載入檔案或組件 'file:///C:\MyApp\SomeNet4.dll' 或其相依性的其中之一。 此組件是由比目前載入的執行階段還新的執行階段所建置,因此無法載入。 於 System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) 於 System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) 於 System.Reflection.Assembly.LoadFrom(String assemblyFile) 於 Microsoft.PowerShell.Commands.AddTypeCommand.LoadAssemblyFromPathOrName(List`1 generatedTypes) 於 Microsoft.PowerShell.Commands.AddTypeCommand.EndProcessing() 於 System.Management.Automation.CommandProcessorBase.Complete()
除了升級 Powersehll 外,我找到一個不需動用管理者權限的解法,將 C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe 複製到自訂資料夾,於該資料夾新增一個 powershell.exe.config:
<?xml version="1.0"?> <configuration> <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0.30319"/> <supportedRuntime version="v2.0.50727"/> </startup> </configuration>
改用這個加了額外 config 的 powershell.exe,.NET CLR 的版本就會升級到 4.0:
最後一個問題跟 ODP.NET 32/64 有關,Powershell 也有分 32/64 版,64 位元版在 C:\Windows\System32\WindowsPowerShell\v1.0\,32 位元在 C:\Windows\SysWOW64\WindowsPowerShell\v1.0\,版本匹配不對時會看到「System.BadImageFormatException: Could not load file or assembly 'file:///C:\MyApp\Oracle.DataAccess.dll' or one of its dependencies. 試圖載入格式錯誤的程式。」訊息,換用對應版本即可解決。
參考: Enable .NET 4 Runtime for PowerShell and Other Applications
This article provide some tips of calling existing custom .NET assembly with Powershell.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 帝都程序猿 996 摸鱼求生指南
- 分布式系统设计的求生之路
- 2019年强求生欲指南:怎样防黑客?
- 野外求生系列 - 無工具 WebApi 徒手測試
- 委身后辈,昔日巨星MIPS能否靠开源绝地求生?
- 野外求生系列 - 無工具 WebApi 徒手測試 Part 2
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Computer Age Statistical Inference
Bradley Efron、Trevor Hastie / Cambridge University Press / 2016-7-21 / USD 74.99
The twenty-first century has seen a breathtaking expansion of statistical methodology, both in scope and in influence. 'Big data', 'data science', and 'machine learning' have become familiar terms in ......一起来看看 《Computer Age Statistical Inference》 这本书的介绍吧!