PowerShellでアセンブリを読み込む方法は?


153

次のPowerShellコード

#Get a server object which corresponds to the default instance
$srv = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Server
... rest of the script ...

次のエラーメッセージが表示されます。

New-Object : Cannot find type [Microsoft.SqlServer.Management.SMO.Server]: make sure 
the assembly containing this type is loaded.
At C:\Users\sortelyn\ ... \tools\sql_express_backup\backup.ps1:6  char:8
+ $srv = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Server
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

インターネット上のすべての答えは、アセンブリをロードする必要があると書いています-エラーメッセージからそれを読むことができることを確認してください:-)-質問は:

アセンブリをロードしてスクリプトを機能させるにはどうすればよいですか?

回答:


179

LoadWithPartialName廃止されました。PowerShell V3の推奨ソリューションは、Add-Typeコマンドレットを使用することです。例:

Add-Type -Path 'C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.Smo.dll'

複数の異なるバージョンがあり、特定のバージョンを選択したい場合があります。:-)


1
PowerShell3を使用します。これらのincludeコマンドは非常に複雑に見えます。「ファイル名を含める」のようなものを期待します。
バクスター

6
PowerShellは大文字と小文字を区別しません(-cmatch、-ceqなどの演算子を使用して大文字と小文字を区別するように指示しない限り)。したがって、コマンド名とパラメーターの大文字小文字は関係ありません。
キース・ヒル

5
はい。msdn.microsoft.com/en-us/library/12xc5368(v=vs.110).aspx 上部の注を参照- This API is now obsolete. もちろん、それによってユーザーが使用するの妨げるものではありません。
キース・ヒル

2
LoadWithPartialName非推奨となったのは技術的には正しいことですが、理由(blogs.msdn.com/b/suzcook/archive/2003/05/30/57159.aspxに概要が示されている)は、対話型のPowershellセッションには当てはまりません。インタラクティブなPowershellの使用にはAPIで問題ないというメモを追加することをお勧めします。
Micha Wiedenmann、2015年

ほとんどの場合、SMOアセンブリに問題はありませんが、PowerShellを強制終了する必要がある場合があります。そうすると、SMOの読み込みの問題が発生し始めます。add-type -Pathを追加すると修正されます。
Nicolas de Fontenay 2016年

73
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")

8
これは非常に便利なので、置き換えずに廃止することはできません。私のチームは、2008年と2012年のクライアントツールを組み合わせて使用​​しています。これは、不器用なバージョンフォールバックロジックを含めずに、私のすべてのチームでPowerShellスクリプトを機能させる唯一の方法です。
Iain Samuel McLean Elder

4
Out-NullGACに情報をエコーさせたくない場合は、出力をパイプすることができます。
Iain Samuel McLean Elder

3
@バクスター-この回答またはキースの回答を受け入れ、この質問に回答済みのマークを付ける必要があります。
Jaykul 2013年

3
私は[void] [System.Reflection.Assembly] :: LoadWithPartialName( "Microsoft.SqlServer.Smo")を使用しています
Soeren L. Nielsen

@IainElder「不器用なバージョン-フォールバックロジック」あなたは、バージョンの非互換性に遭遇するまでそれを言います!言うことはそれほど難しいことではありませんAdd-Type -Path [...]; if (!$?) { Add-Type -Path [...] } elseif [...]
ベーコンビット

44

ほとんどの人はこれSystem.Reflection.Assembly.LoadWithPartialNameが非推奨であることを今では知っAdd-Type -AssemblyName Microsoft.VisualBasic ていますが、それよりも動作がよくないLoadWithPartialNameことがわかります

[Add-Type]は、システムのコンテキストで要求を解析しようとするのではなく、静的な内部テーブルを調べて、「部分的な名前」を「完全な名前」に変換します。

「部分的な名前」がテーブルに表示されない場合、スクリプトは失敗します。

コンピューターにインストールされているアセンブリの複数のバージョンがある場合、それらの間で選択するインテリジェントなアルゴリズムはありません。あなたは彼らのテーブルに現れるもののどれでも、おそらく古い、古いものを手に入れるでしょう。

インストールしたバージョンがすべて、表にある古いバージョンよりも新しい場合、スクリプトは失敗します。

Add-Typeには、次のような「部分名」のインテリジェントなパーサーがありません。 .LoadWithPartialNames

Microsoftが実際に行うべきだと言っているのは、次のようなものです。

Add-Type -AssemblyName 'Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

または、パスがわかっている場合は、次のようになります。

Add-Type -Path 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\Microsoft.VisualBasic\v4.0_10.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualBasic.dll'

アセンブリに付けられた長い名前は、 な名前ますは、バージョンとアセンブリの両方に固有であり、フルネームとも呼ばれます。

しかし、これは未解決のいくつかの質問を残します:

  1. 与えられた部分的な名前でシステムに実際にロードされているものの厳密な名前を判断するにはどうすればよいですか?

    [System.Reflection.Assembly]::LoadWithPartialName($TypeName).Location; [System.Reflection.Assembly]::LoadWithPartialName($TypeName).FullName;

これらも機能するはずです:

Add-Type -AssemblyName $TypeName -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. スクリプトで特定のバージョンの.dllを常に使用したいが、それがインストールされている場所がわからない場合は、.dllから厳密な名前を判別するにはどうすればよいですか?

    [System.Reflection.AssemblyName]::GetAssemblyName($Path).FullName;

または:

Add-Type $Path -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. 厳密な名前がわかっている場合、.dllパスを確認するにはどうすればよいですか?

    [Reflection.Assembly]::Load('Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a').Location;

  2. 同様に、使用しているもののタイプ名がわかっている場合、それがどのアセンブリからのものであるかをどのようにして知ることができますか?

    [Reflection.Assembly]::GetAssembly([Type]).Location [Reflection.Assembly]::GetAssembly([Type]).FullName

  3. 使用可能なアセンブリを確認するにはどうすればよいですか?

GAC PowerShellモジュールをお勧めしますGet-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullNameかなりうまくいきます。

  1. Add-Type使用するリストをどのように表示できますか?

これはもう少し複雑です。.Netリフレクターを備えたPowerShellの任意のバージョンでそれにアクセスする方法を説明できます(PowerShell Core 6.0の下記の更新を参照)。

まず、どのライブラリAdd-Typeからのものかを調べます。

Get-Command -Name Add-Type | Select-Object -Property DLL

結果のDLLをリフレクターで開きます。ILSpyはFLOSS なのでこれに使用しましたが、C#リフレクターはすべて機能するはずです。そのライブラリを開き、を調べますMicrosoft.Powershell.Commands.Utility。下Microsoft.Powershell.CommandsにあるはずですAddTypeCommand

そのコードリストには、プライベートクラスがありInitializeStrongNameDictionary()ます。これは、短い名前を厳密な名前にマップする辞書をリ​​ストします。私が見たライブラリには、ほぼ750のエントリがあります。

更新: PowerShell Core 6.0がオープンソースになりました。そのバージョンの場合、上記の手順をスキップして、GitHubリポジトリでオンラインで直接コードを確認できます。ただし、そのコードがPowerShellの他のバージョンと一致することは保証できません。


3番目の未回答の質問:特定のバージョンを要求したくない場合はどうなりますか?
jpmc26 2017年

1
@ jpmc26まあ、Add-Typeまたはだけを使用できますがLoadWithPartialName()、前者はバージョン間で100%一貫性がなく、後者は廃止された方法であることに注意する必要があります。言い換えると、.Netは、ロードするライブラリのバージョンに注意することを求めています。
ベーコンビット

@BaconBits jpmc26の質問に対する完全な回答は、PowerShell 5を使用しているかPowerShell 6を使用しているかによって、読み込まれるアセンブリが異なる場合があるということです。JSON.NETには、Azure PS関数に関するこの問題があります。
John Zabroski、2018

@BaconBitsこれは、PowerShellの非常にすばらしい詳細です。あなたは本を書くべきです。
John Zabroski、2018

1
@KolobCanyonその場合Add-Type -Path、通常は、2番目のコードである、またはAssembly.LoadFrom()依存関係を解決するを使用する必要があるためです(そして、私が知る限り、これが何をAdd-Type -Path使用するかです)。使用Assembly.LoadFile()する必要があるのは、IDが同じでパスが異なる複数のアセンブリをロードする必要がある場合のみです。それは奇妙な状況です。
ベーコンビット

23

PowerShellセッション中にアセンブリをロックせずにロードする場合は、次のように使用します。

$bytes = [System.IO.File]::ReadAllBytes($storageAssemblyPath)
[System.Reflection.Assembly]::Load($bytes)

どこ $storageAssemblyPathアセンブリのファイルパスは。

これは、セッション内でリソースをクリーンアップする必要がある場合に特に便利です。たとえば、配置スクリプト内です。


1
👍👍👍ファンタスティック。Visual Studioでは、Powershellのデバッグ時に、PSセッションが実行後に(PowerShellToolsProcessHostを介して)ハングするためです。このアプローチはそれを修正します。ありがとう。
CJBS 2017年

15

これは、PowerShell v1、v2、v3でアセンブリをロードする方法の多数の例を含むいくつかのブログ投稿です。

方法は次のとおりです。

  • ソースファイルから動的に
  • アセンブリから動的に
  • 他のコードタイプ、つまりF#を使用する

v1.0 PowerShellセッションで.NETアセンブリを読み込む方法
v2.0 PowerShellスクリプトでCSharp(C#)コードを 使用する2.0
v3.0 Windows PowerShellで.NET Frameworkアセンブリを使用する


10

* .dllアセンブリ全体をロードできます

$Assembly = [System.Reflection.Assembly]::LoadFrom("C:\folder\file.dll");

3

答えはどれも役に立たなかったので、私のために働いた解決策を投稿しています。SQLPSモジュールをインポートするだけで済みました。誤ってRestore-SqlDatabaseコマンドを実行して機能し始めたので、これに気付きました。アセンブリはそのモジュールでなんとか参照されました。

とにかく走れ:

Import-module SQLPS

注:SQLPSが非推奨であることを指摘してくれたJasonに感謝

代わりに実行:

Import-Module SqlServer

または

Install-Module SqlServer

2
このアプローチを使用するすべての人にとってsqlps、モジュールのために非推奨となったFYIsqlserver
Jason


2

あなたは使うことができます LoadWithPartialName。しかし、彼らが言ったように、それは非推奨です。

あなたは確かに一緒に行くことができAdd-Type、他の答えに加えて、.dllファイルのフルパスを指定したくない場合は、単に次のようにすることができます:

Add-Type -AssemblyName "Microsoft.SqlServer.Management.SMO"

SQL Serverがインストールされていないため(おそらく)、これはエラーを返しましたが、同じ考えでWindowsフォームアセンブリを読み込むことができました。

Add-Type -AssemblyName "System.Windows.Forms"

特定のクラスに属する正確なアセンブリ名は、MSDNサイトで確認できます。

特定のクラスに属するアセンブリ名を見つける例


2

以下の機能が順番にインストールされていることを確認してください

  1. SQL ServerのMicrosoftシステムCLRタイプ
  2. Microsoft SQL Server共有管理オブジェクト
  3. Microsoft Windows PowerShell拡張機能

また、ロードする必要があるかもしれません

Add-Type -Path "C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.Smo.dll"
Add-Type -Path "C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.SqlWmiManagement.dll"

アセンブリをロードするのに1週間費やしましたが、アセンブリをロードしたステートメントからの出力は表示されませんでしたが、それを使用しようとするとエラーが発生しました。これら3つをインストールすると、うまくいきました。-ありがとう
pparas

0

上部にアセンブリ参照を追加します。

#Load the required assemblies SMO and SmoExtended.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended") | Out-Null

それから例を作ってもらえますか?
endo.anaconda 2017

1
PowerShellスクリプトの先頭に追加するだけです。たとえば:データベースのバックアップを作成します:[System.Reflection.Assembly] :: LoadWithPartialName( "Microsoft.SqlServer.SMO")| Out-Null [System.Reflection.Assembly] :: LoadWithPartialName( "Microsoft.SqlServer.SmoExtended")| Out-Null $ SQLServer = Read-Host -Prompt 'SQL Server name(optional)' IF([string] :: IsNullOrWhitespace($ SQLServer)){$ SQLServer = "XXX";} $ SQLDBName = Read-Host -Prompt ' SQLデータベース名(オプション) 'IF([string] :: IsNullOrWhitespace($ SQLDBName)){$ SQLDBName = "XXX";} $ SQLLogin = Read-Host -Prompt' Login '
Amrita Basu
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.