内容简介:前篇文章介紹了如何在無開發工具的管制環境撰寫 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
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Transcending CSS
Andy Clarke、Molly E. Holzschlag / New Riders / November 15, 2006 / $49.99
As the Web evolves to incorporate new standards and the latest browsers offer new possibilities for creative design, the art of creating Web sites is also changing. Few Web designers are experienced p......一起来看看 《Transcending CSS》 这本书的介绍吧!