วิธีโหลดแอสเซมบลีใน 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-Typecmdlet เช่น:

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

มีหลายรุ่นแตกต่างกันและคุณอาจต้องการเลือกรุ่นใดรุ่นหนึ่ง :-)


1
โอเคฉันใช้ PowerShell3 - คำสั่งรวมเหล่านี้ดูเหมือนจะซับซ้อนมาก ฉันแค่คาดหวังบางอย่างเช่น "รวมชื่อไฟล์"
Baxter

6
PowerShell ไม่คำนึงถึงขนาดตัวพิมพ์ (เว้นแต่คุณจะบอกให้ตัวพิมพ์เล็กและใหญ่กับตัวดำเนินการเช่น -cmatch, -ceq) ดังนั้นการใส่ชื่อคำสั่งและพารามิเตอร์จึงไม่สำคัญ
Keith Hill

5
ใช่. msdn.microsoft.com/en-us/library/12xc5368(v=vs.110).aspx ดูหมายเหตุที่ด้านบน - This API is now obsolete. แน่นอนว่านั่นไม่ได้หยุดผู้ใช้
Keith Hill

2
ในขณะที่มันถูกต้องทางเทคนิคที่LoadWithPartialNameได้รับการปฏิเสธเหตุผล (ตามที่ระบุไว้ในblogs.msdn.com/b/suzcook/archive/2003/05/30/57159.aspx ) อย่างชัดเจนไม่ได้ใช้สำหรับเซสชัน Powershell แบบโต้ตอบ ฉันขอแนะนำให้คุณเพิ่มหมายเหตุว่า API นั้นใช้ได้สำหรับการใช้ Powershell แบบโต้ตอบ
Micha Wiedenmann

ส่วนใหญ่ฉันไม่มีปัญหากับชุดประกอบ SMO แต่บางครั้งฉันจำเป็นต้องฆ่า powershell และเมื่อฉันทำฉันเริ่มมีปัญหาการโหลด SMO การเพิ่ม add-type -Path แก้ไขที่
Nicolas de Fontenay

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

8
สิ่งนี้มีประโยชน์เกินกว่าที่จะเลิกใช้โดยไม่ต้องเปลี่ยนใหม่! ทีมของฉันใช้เครื่องมือไคลเอ็นต์ผสมกันระหว่างปี 2008 และ 2012 นี่เป็นวิธีเดียวที่จะทำให้สคริปต์ PowerShell ของฉันทำงานกับทีมของฉันได้โดยไม่ต้องรวมตรรกะลอจิกเวอร์ชันสำรอง
เลนซามูเอลแมคลีนเลน

4
คุณสามารถไพพ์เอาท์พุทได้Out-Nullหากคุณไม่ต้องการให้ GAC ทำเสียงก้อง
เลนซามูเอลแมคลีนเลน

3
@Baxter - คุณควรยอมรับคำตอบนี้หรือ Keith's และให้คำถามนี้ตอบคำถาม
Jaykul

3
ฉันใช้ [void] [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.Smo")
Soeren L. Nielsen

@IainElder "ลอจิกเวอร์ชัน fallback ลอจิก" คุณพูดแบบนั้นจนกว่าคุณจะพบกับความไม่ลงรอยกันของเวอร์ชัน! Add-Type -Path [...]; if (!$?) { Add-Type -Path [...] } elseif [...]มันไม่ยากที่จะบอกว่า
เบคอน Bits

44

ตอนนี้คนส่วนใหญ่รู้แล้วว่าSystem.Reflection.Assembly.LoadWithPartialNameเลิกใช้แล้ว แต่กลับกลายเป็นAdd-Type -AssemblyName Microsoft.VisualBasic ว่าไม่ได้ประพฤติดีกว่าLoadWithPartialName :

แทนที่จะพยายามแยกวิเคราะห์คำขอของคุณในบริบทของระบบของคุณ [Add-Type] ดูที่ตารางภายในแบบคงที่เพื่อแปล "ชื่อบางส่วน" เป็น "ชื่อเต็ม"

หาก "ชื่อบางส่วน" ไม่ปรากฏในตารางสคริปต์ของคุณจะล้มเหลว

หากคุณมีแอสเซมบลีหลายรุ่นที่ติดตั้งบนคอมพิวเตอร์ของคุณไม่มีอัลกอริทึมอัจฉริยะให้เลือกระหว่างพวกเขา คุณจะได้อันใดอันหนึ่งปรากฏในตารางของพวกเขาอาจเป็นอันเก่าและล้าสมัย

หากเวอร์ชันที่คุณติดตั้งนั้นใหม่กว่าเวอร์ชั่นที่ล้าสมัยในตารางสคริปต์ของคุณจะล้มเหลว

เพิ่มประเภทไม่มี parser อัจฉริยะของ "บางส่วนของชื่อ" .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ใช้ได้อย่างไร

มันซับซ้อนกว่านี้เล็กน้อย ฉันสามารถอธิบายวิธีเข้าถึงมันสำหรับ PowerShell ทุกรุ่นด้วย. Net reflector (ดูการปรับปรุงด้านล่างสำหรับ PowerShell Core 6.0)

ก่อนอื่นให้ตรวจสอบว่าไลบรารีใดAdd-Typeมาจาก:

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

เปิด DLL ที่เป็นผลลัพธ์ด้วยตัวสะท้อนสัญญาณของคุณ ฉันใช้ILSpyสำหรับเรื่องนี้เพราะมันเป็น FLOSS แต่ C # reflector ใด ๆ ก็ควรใช้งานได้ เปิดMicrosoft.Powershell.Commands.Utilityไลบรารี่นั้นแล้วดู ภายใต้ควรจะมีMicrosoft.Powershell.CommandsAddTypeCommand

ในรายการรหัสสำหรับสิ่งนั้นมีคลาสส่วนตัว, InitializeStrongNameDictionary(). ที่แสดงรายการพจนานุกรมที่แมปชื่อสั้น ๆ กับชื่อที่รัดกุม มีเกือบ 750 รายการในห้องสมุดที่ฉันดู

อัปเดต:ตอนนี้ PowerShell Core 6.0 เป็นโอเพ่นซอร์ส สำหรับรุ่นที่คุณสามารถข้ามขั้นตอนข้างต้นและดูรหัสโดยตรงออนไลน์ในพื้นที่เก็บข้อมูล GitHub ของพวกเขา ฉันไม่สามารถรับประกันได้ว่ารหัสนั้นจะตรงกับ PowerShell รุ่นอื่นใด ๆ


คำถามที่ไม่ได้รับคำตอบที่ 3: ถ้าฉันไม่ต้องการรุ่นที่เฉพาะเจาะจงล่ะ
jpmc26

1
@ jpmc26 เอาล่ะคุณสามารถใช้Add-TypeหรือLoadWithPartialName()แต่คุณต้องระวังว่าเดิมจะไม่สอดคล้องกัน 100% ในทุก ๆ เวอร์ชั่นและอันหลังนั้นเป็นวิธีที่ล้าสมัย กล่าวอีกนัยหนึ่ง. Net ต้องการให้คุณใส่ใจเกี่ยวกับเวอร์ชันของไลบรารีที่คุณโหลด
เบคอน Bits

@BaconBits คำตอบทั้งหมดสำหรับคำถามของ jpmc26 คือขึ้นอยู่กับว่าคุณใช้ PowerShell 5 หรือ PowerShell 6 การโหลดแอสเซมบลีอาจแตกต่างกัน JSON.NET มีปัญหานี้กับฟังก์ชัน Azure PS
John Zabroski

@BaconBits นี่คือการดำน้ำลึกที่ยอดเยี่ยมอย่างแท้จริงใน PowerShell คุณควรเขียนหนังสือ
John Zabroski

1
@KolobCanyon เพราะในกรณีนี้คุณควรใช้โดยทั่วไปAdd-Type -Pathซึ่งเป็นรหัสที่สองที่กล่าวถึงหรือAssembly.LoadFrom()แก้ไขการพึ่งพาสำหรับคุณ (และเท่าที่ฉันสามารถบอกได้ว่าเป็นสิ่งที่Add-Type -Pathใช้) ครั้งเดียวที่คุณควรใช้Assembly.LoadFile()คือถ้าคุณต้องการโหลดชุดประกอบหลายชุดที่มีเอกลักษณ์เหมือนกัน แต่มีเส้นทางที่แตกต่างกัน นั่นเป็นสถานการณ์ที่แปลก
เบคอน Bits

23

หากคุณต้องการโหลดแอสเซมบลีโดยไม่ล็อคในช่วงระยะเวลาของเซสชัน PowerShellให้ใช้สิ่งนี้:

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

$storageAssemblyPathตำแหน่งไฟล์ของชุดประกอบของคุณอยู่ที่ไหน

นี่เป็นประโยชน์โดยเฉพาะอย่างยิ่งถ้าคุณต้องการล้าง ressources ภายในเซสชันของคุณ ตัวอย่างเช่นในสคริปต์การปรับใช้


1
👍👍👍ยอดเยี่ยม เนื่องจากใน Visual Studio เมื่อทำการดีบัก Powershell เซสชัน PS จะหยุดทำงานหลังจากดำเนินการ (ผ่าน PowerShellToolsProcessHost) วิธีนี้แก้ไขได้ ขอบคุณ
CJBS

15

ต่อไปนี้เป็นบทความบล็อกที่มีตัวอย่างมากมายของวิธีการโหลดแอสเซมบลีใน PowerShell v1, v2 และ v3

วิธีรวมถึง:

  • แบบไดนามิกจากไฟล์ต้นฉบับ
  • แบบไดนามิกจากการชุมนุม
  • ใช้รหัสประเภทอื่น ๆ เช่น F #

v1.0 วิธีโหลดแอสเซมบลี. NET ในเซสชัน PowerShell
v2.0 โดยใช้รหัส CSharp (C #) ในสคริปต์ PowerShell 2.0
v3.0 การใช้แอสเซมบลี. NET Framework ใน Windows PowerShell


10

คุณสามารถโหลดแอสเซมบลี * .dll ทั้งหมดด้วย

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

3

ไม่มีคำตอบใดที่ช่วยฉันดังนั้นฉันจึงโพสต์โซลูชันที่เหมาะกับฉันสิ่งที่ฉันต้องทำก็คือนำเข้าโมดูล SQLPS ฉันรู้สิ่งนี้เมื่อบังเอิญฉันรันคำสั่ง Restore-SqlDatabase และเริ่มทำงานหมายความว่า แอสเซมบลีที่ถูกอ้างอิงในโมดูลนั้นอย่างใด

เพิ่งรัน:

Import-module SQLPS

หมายเหตุ: ขอบคุณ Jason สำหรับการสังเกตว่า SQLPS นั้นเลิกใช้แล้ว

วิ่งแทน:

Import-Module SqlServer

หรือ

Install-Module SqlServer

2
สำหรับใครก็ตามที่ใช้วิธีนี้ FYI ที่sqlpsไม่เห็นด้วยกับโมดูลsqlserver
Jason


2

LoadWithPartialNameคุณสามารถใช้ อย่างไรก็ตามนั่นเป็นค่าที่ไม่เป็นไปตามที่กล่าวไว้

แน่นอนคุณสามารถไปพร้อมกับAdd-Typeและนอกเหนือจากคำตอบอื่น ๆ หากคุณไม่ต้องการระบุเส้นทางแบบเต็มของไฟล์. dll คุณเพียงแค่ทำ:

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

สำหรับฉันสิ่งนี้ส่งคืนข้อผิดพลาดเนื่องจากฉันไม่ได้ติดตั้ง SQL Server (ฉันเดา) อย่างไรก็ตามด้วยแนวคิดเดียวกันนี้ฉันสามารถโหลดชุดประกอบ Windows Forms:

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

คุณสามารถค้นหาชื่อแอสเซมบลีที่แม่นยำซึ่งเป็นของคลาสเฉพาะบนไซต์ MSDN:

ตัวอย่างการค้นหาชื่อชุดประกอบที่เป็นของคลาสเฉพาะ


2

ตรวจสอบให้แน่ใจว่าคุณมีการติดตั้งคุณสมบัติด้านล่างตามลำดับ

  1. Microsoft System CLR Types สำหรับ SQL Server
  2. วัตถุการจัดการที่ใช้ร่วมกันของ Microsoft SQL Server
  3. Microsoft Windows PowerShell Extensions

นอกจากนี้คุณอาจต้องโหลด

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"

ฉันใช้เวลาหนึ่งสัปดาห์ในการพยายามโหลดชุดประกอบและไม่เห็นผลลัพธ์ใด ๆ จากคำสั่งที่โหลดพวกเขา แต่เมื่อฉันพยายามใช้มันฉันได้รับข้อผิดพลาดเมื่อฉันติดตั้งสามสิ่งนี้มันทำงานได้ - ขอบคุณ
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

1
คุณเพียงแค่เพิ่มที่จุดเริ่มต้นของสคริปต์ PowerShell ตัวอย่างเช่น: สร้างการสำรองข้อมูลของฐานข้อมูล: [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SMO") | Out-Null [System.Reflection.Assembly] :: LoadWithPartialName ("Microsoft.SqlServer.SmoExtended") | Null $ SQLServer = Read-Host -Prompt 'ชื่อเซิร์ฟเวอร์ SQL (เป็นตัวเลือก)' IF ([string] :: IsNullOrWhitespace ($ SQLServer)) {$ SQLServer = "XXX";} $ SQLDBName = Read-Host -Prompt ' ชื่อฐานข้อมูล SQL (เป็นทางเลือก) 'IF ([string] :: IsNullOrWhitespace ($ SQLDBName)) {$ SQLDBName = "XXX";} $ SQLLogin = โฮสต์สำหรับการอ่าน' พร้อมรับคำ '
Amrita Basu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.