野外求生系列 - 使用 Powershell 測試現有 DLL

栏目: 编程工具 · 发布时间: 5年前

内容简介:前篇文章介紹了如何在無開發工具的管制環境撰寫 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。

野外求生系列 - 使用 Powershell 測試現有 DLL

如試圖載入 .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:

野外求生系列 - 使用 Powershell 測試現有 DLL

最後一個問題跟 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.


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Computer Age Statistical Inference

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》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具