การรวมเข้ากับเชลล์ Windows วันนี้ ── เมนูบริบท การเชื่อมโยงไฟล์ และสิ่งที่เปลี่ยนใน Windows 11
· Go Komura · Windows, ส่วนขยายเชลล์, เมนูบริบท, การเชื่อมโยงไฟล์, COM, Windows 11, File Explorer, MSIX, การพัฒนา Windows
มีคนปรึกษาว่า «เราเปลี่ยนพีซีเป็น Windows 11 แล้วเมนูบริบทของแอปที่คุณทำให้เมื่อหลายปีก่อนหายไป» ฟังละเอียดแล้วมันไม่ได้หาย คลิกขวาไฟล์ เลือก «แสดงตัวเลือกเพิ่มเติม» ที่ท้ายเมนู แล้วเมนูคุ้นเคยก็ปรากฏเหมือนเดิม กล่าวคือ รายการเมนูของแอปภายในถูกซ่อนลึกเข้าไปอีกหนึ่งคลิก จากหน้างานได้ยินว่า «คลิกเพิ่มหนึ่งครั้ง» และ «คำถามหาไม่เจอรายการเพิ่มขึ้น»
นี่ไม่ใช่ความล้มเหลวหรือตั้งค่าผิด แต่เป็นการเปลี่ยนการออกแบบของ Windows 11 เมนูบริบทของ File Explorer กลายเป็นโครงสร้างสองชั้น เก่าและใหม่ และเงื่อนไขที่จะวางรายการบนเมนูใหม่กลายเป็นคนละเรื่องกับเมื่อก่อน
ในขณะเดียวกัน กลไกการเชื่อมโยงไฟล์และส่วนขยายเชลล์ด้านล่างยังเป็นโลกเก่าของ COM และรีจิสทรี คีย์นามสกุลชี้ไป ProgID verb ของ ProgID ถือบรรทัดคำสั่ง และส่วนขยายที่ซับซ้อนกว่าทำงานเป็นเซิร์ฟเวอร์ COM ในโพรเซส (DLL) ที่โหลดเข้า Explorer — โครงสร้างนี้ไม่เปลี่ยนมานานกว่ายี่สิบปี ถ้าไม่รู้จักทั้งฐานที่ไม่ได้เปลี่ยนและเมนูที่ Windows 11 แยกเป็นสอง คุณแยก «เมนูไม่ขึ้น» «ถูกซ่อน» หรือ «ขึ้นสองครั้ง» ไม่ได้
บทความนี้มุ่งถึงเจ้าหน้าที่ไอทีของบริษัทขนาดเล็กและกลาง และนักพัฒนา Windows ที่ดูแลแอปธุรกิจ มันร้อย โครงสร้างสามชั้นของการเชื่อมโยงไฟล์ ข้อควรระวังของส่วนขยายเชลล์คลาสสิก วิธีเล็งเมนูบริบทใหม่ของ Windows 11 และการลงทะเบียน การทำความสะอาด และการแก้ปัญหาของตัวติดตั้ง ให้เป็นภาพเดียว
1. สรุปก่อนเลย
- ฐานของเมนูบริบทและการเชื่อมโยงไฟล์คือโครงสร้างรีจิสทรีสามชั้น «คีย์นามสกุล → ProgID → verb» คีย์นามสกุลเป็นตัวชี้ไป ProgID ProgID คือสาระ และ
shell\<verb>\commandด้านล่างถือบรรทัดคำสั่ง1 - HKEY_CLASSES_ROOT (HKCR) ไม่ใช่ไฮฟ์อิสระ แต่เป็นมุมมองรวมของ HKLM\Software\Classes และ HKCU\Software\Classes เขียนการลงทะเบียนทุกผู้ใช้ไป HKLM และการลงทะเบียนต่อผู้ใช้ไป HKCU และถือ HKCR เป็นอ่านอย่างเดียว2
- แอปเริ่มต้น (แอปที่เปิดเมื่อดับเบิลคลิก) ออกแบบให้ผู้ใช้เลือก และโปรแกรมแย่งไม่ได้ ระบบปฏิบัติการปกป้องตัวเลือกของผู้ใช้ สิ่งที่ตัวติดตั้งทำได้คือลงทะเบียนเป็นตัวเลือก3
- ส่วนขยายเชลล์คลาสสิกคือ DLL COM ในโพรเซสที่โหลดเข้า Explorer แครชหรือความล่าช้าของส่วนขยายแพร่ไปทั้ง Explorer (และแอปอื่นที่ใช้เชลล์) สภาพแวดล้อม 64 บิตต้องใช้ DLL 64 บิต และการอิมพลีเมนต์ด้วยโค้ดจัดการไม่รองรับ45
- บน Windows 11 เมนูบริบทแยกเป็นสอง คำสั่งเดียวที่ปรากฏบนเมนูใหม่คือคำสั่งที่ลงทะเบียนด้วย IExplorerCommand บวกอัตลักษณ์แพ็กเกจ ส่วนขยาย IContextMenu คลาสสิกถูกย้ายไปเมนูเก่าใต้ «แสดงตัวเลือกเพิ่มเติม» (Shift+F10)67
- เส้นทางทางการที่จะวางคำสั่งของตัวเองบนเมนูใหม่คือลงทะเบียน DLL เนทีฟที่อิมพลีเมนต์ IExplorerCommand ในแมนิเฟสต์ MSIX (desktop4:FileExplorerContextMenus) แอปที่เป็น MSIX ไม่ได้ให้อัตลักษณ์อย่างเดียวด้วย sparse package (MSIX ที่มีตำแหน่งภายนอก) ได้78
- ถ้าต้องการแค่ «เปิดด้วยแอปนี้» การเชื่อมโยงและ verb แบบคงที่ยังพอ ไม่ต้องมี DLL ส่วนขยายเชลล์ และ Microsoft เองก็พูดชัด «เลือกวิธีที่ง่ายที่สุดที่ตอบความต้องการ (verb แบบคงที่)»9
- หลังลงทะเบียนหรือเปลี่ยน ให้แจ้งด้วย SHChangeNotify(SHCNE_ASSOCCHANGED) ตอนถอนติดตั้ง ให้ลบ ProgID แต่ห้ามลบค่าเริ่มต้นของคีย์นามสกุล — นั่นคือคู่มือทางการ การรวมเชลล์รวมการออกแบบการทำความสะอาดด้วย110
ประโยคเดียว โลกของการเชื่อมโยงและ verb ไม่เปลี่ยน มีแค่การแสดงเมนูที่แยกเป็นสองบน Windows 11 ด้านล่างเดินจากฐานขึ้นมา
2. การเชื่อมโยงไฟล์ทำงานอย่างไร — โครงสร้างสามชั้น คีย์นามสกุล → ProgID → verb
2.1. อ่านโครงสร้างสามชั้นจากตัวอย่างเดียว
สิ่งที่เกิดเมื่อดับเบิลคลิกไฟล์ของนามสกุลที่กำหนด ถูกตัดสินด้วยคีย์รีจิสทรีสามชั้น1
HKEY_CLASSES_ROOT
.kmrpt ← (1) คีย์นามสกุล
(Default) = KomuraSoft.Report.1 ← ตัวชี้ที่ตั้งชื่อ ProgID เท่านั้น
OpenWithProgids
KomuraSoft.Report.1 ← ตัวเลือกใน «เปิดด้วย»
KomuraSoft.Report.1 ← (2) ProgID (สาระของการเชื่อมโยง)
(Default) = Komura Report document
DefaultIcon
(Default) = "C:\Program Files\KomuraSoft\Report.exe",0
shell ← (3) รายการ verb
open
command
(Default) = "C:\Program Files\KomuraSoft\Report.exe" "%1"
- (1) คีย์นามสกุล (
.kmrpt) ชี้ไปชื่อ ProgID เป็นค่าเริ่มต้นเท่านั้น การเขียนคำสั่งตรงนี้เป็นความผิด - (2) ProgID (
KomuraSoft.Report.1) คือสาระของการเชื่อมโยง ถือชื่อที่แสดง ไอคอน และรายการ verb - (3) verb คือการกระทำอย่าง «เปิด» หรือ «พิมพ์» และค่าเริ่มต้นของ
shell\open\commandคือบรรทัดคำสั่งที่ถูกเปิดจริง
การแยกนี้ทำให้ชี้หลายนามสกุล (เช่น .kmrpt และ .kmrpt-file) ไป ProgID เดียวกัน หรือสลับ ProgID เมื่ออัปเกรดแอปได้
flowchart TB
accTitle: โครงสร้างสามชั้นของการเชื่อมโยงไฟล์
accDescr: คีย์นามสกุลเป็นตัวชี้ที่ค่าเริ่มต้นตั้งชื่อ ProgID ProgID คือสาระที่ถือชื่อที่แสดง ไอคอน และรายการ verb และค่าเริ่มต้นของ command ใต้ verb คือบรรทัดคำสั่งที่ถูกเปิดจริง
ext["คีย์นามสกุล .kmrpt"] -->|ตั้งชื่อ ProgID เป็นค่าเริ่มต้น| pid["ProgID KomuraSoft.Report.1"]
pid --> vb["verb(open และอื่นใต้ shell)"]
vb --> cmd["ค่าเริ่มต้นของ command"]
cmd --> exe["Report.exe ถูกเปิด"]
pid -.-> attr["ถือชื่อที่แสดงและ DefaultIcon ด้วย"]
ภาพ 1: คีย์นามสกุลเป็นตัวชี้ ProgID คือสาระ และ command ของ verb คือบรรทัดคำสั่งที่ถูกเปิดจริง
2.2. HKCR คือ «มุมมองรวม» — ที่เขียนเปลี่ยนความหมาย
ตัวอย่างข้างบนแสดงใต้ HKEY_CLASSES_ROOT (HKCR) แต่ HKCR ไม่ใช่ตำแหน่งเก็บทางกายภาพ แต่เป็นมุมมองรวมของ HKLM\Software\Classes และ HKCU\Software\Classes ถ้าคีย์เดียวกันมีทั้งสองข้าง ฝั่ง HKCU ชนะ2
flowchart TB
accTitle: HKCR คือมุมมองรวม
accDescr: HKCR คือ HKLM และ HKCU Classes ซ้อนกัน ถ้าคีย์เดียวกันมีทั้งสองข้าง HKCU ชนะ เขียนการลงทะเบียนไป HKLM หรือ HKCU อย่างชัด และถือ HKCR เป็นอ่านอย่างเดียว
hklm["HKLM\\Software\\Classes(ผู้ใช้ทุกคน)"] --> hkcr["HKCR(มุมมองรวม)"]
hkcu["HKCU\\Software\\Classes(ต่อผู้ใช้)"] --> hkcr
hkcu -.-> win["ถ้าคีย์เดียวกันมี HKCU ชนะ"]
hkcr -.-> ro["ถือเป็นอ่านอย่างเดียว(เพื่อยืนยัน)"]
ภาพ 2: HKCR คือภาพของ HKLM และ HKCU Classes เมื่อซ้อนกัน ระบุฝั่งใดฝั่งหนึ่งเป็นปลายทางเขียนเสมอ
| ปลายทางเขียน | ความหมาย | สิทธิ์ที่ต้องมี |
|---|---|---|
HKLM\Software\Classes |
การลงทะเบียนร่วมทุกผู้ใช้ | ผู้ดูแลระบบ |
HKCU\Software\Classes |
การลงทะเบียนเฉพาะผู้ใช้นั้น | ไม่มี |
เขียนตรงไป HKCR |
ถูกส่งตามที่คีย์เดิมอยู่แล้ว | ขึ้นกับกรณี |
ในทางปฏิบัติ การแยกที่ปลอดภัยคือ เขียนการลงทะเบียนไป HKLM หรือ HKCU อย่างชัดเสมอ และถือ HKCR เป็นอ่านอย่างเดียว (เพื่อยืนยัน) ความสัมพันธ์กับการเปลี่ยนทิศรีจิสทรี WOW64 ก็ควรจัดให้ชัด ข้อมูลการเชื่อมโยงตรงใต้ HKLM\Software\Classes อย่างคีย์นามสกุลและ ProgID ถูกแชร์ ระหว่างมุมมองรีจิสทรี 32 บิตและ 64 บิตตั้งแต่ Windows 7 ดังนั้นตัวติดตั้ง 32 บิตที่เขียนจะไม่หนีไปฝั่ง Wow6432Node คีย์ย่อยการลงทะเบียน COM บางตัวอย่าง Classes\CLSID ในทางกลับกัน ถูกเปลี่ยนทิศ และเมื่อลงทะเบียนส่วนขยายเชลล์ (COM ในโพรเซส) การแยกเขียน 32/64 บิตสำคัญ รายละเอียดอยู่ใน «Registry 32-bit/64-bit Redirection and Virtualization Pitfalls — Wow6432Node and the “The Value I Wrote Isn’t There” Problem»
2.3. การลงทะเบียนฝั่งแอป — App Paths, Applications, RegisteredApplications
ยังมีการลงทะเบียนฝั่งแอปสามแบบ จับคู่กับฝั่งไฟล์ (นามสกุลและ ProgID)11
- App Paths (
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths): การลงทะเบียนที่ให้ShellExecuteExเปิดด้วยชื่อไฟล์ปฏิบัติการอย่างเดียว Microsoft แนะนำเพราะไม่ต้องทำให้ตัวแปรสภาพแวดล้อม PATH สกปรก - Applications (
HKCR\Applications\<app.exe>): กำหนดวิธีเปิดเริ่มต้นเมื่อส่งไฟล์ใดก็ได้ใน «เปิดด้วย» และชื่อที่แสดงของแอป (FriendlyAppName) - RegisteredApplications + Capabilities: ประกาศนามสกุลและชนิด MIME ที่แอปจัดการได้ และเป็นการลงทะเบียนที่ทำให้ปรากฏเป็นตัวเลือกบนหน้าการตั้งค่าแอปเริ่มต้นของ Windows
คำปรึกษา «แอปเราไม่ขึ้นในรายการแอปเริ่มต้น» ส่วนใหญ่คือกรณีลงทะเบียน ProgID แล้วละการลงทะเบียน Capabilities นี้
flowchart TB
accTitle: การลงทะเบียนฝั่งแอปสามแบบ
accDescr: การลงทะเบียนฝั่งแอปมีสามแบบ App Paths Applications และ RegisteredApplications รับผิดชอบการเปิดด้วยชื่อไฟล์อย่างเดียว วิธีเปิดเริ่มต้นในเปิดด้วย และการปรากฏบนหน้าแอปเริ่มต้น ตามลำดับ
app["การลงทะเบียนฝั่งแอป"] --> ap["App Paths"]
app --> apps["Applications"]
app --> ra["RegisteredApplications"]
ap --> r1["เปิดด้วยชื่อไฟล์อย่างเดียว"]
apps --> r2["ค่าเริ่มต้นในเปิดด้วย"]
ra --> r3["ปรากฏบนหน้าแอปเริ่มต้น"]
r3 -.-> cap["ต้องมีประกาศ Capabilities"]
ภาพ 3: การลงทะเบียนฝั่งแอปมีสามแบบ และการปรากฏเป็นตัวเลือกแอปเริ่มต้นต้องมีการลงทะเบียน Capabilities
2.4. แอปเริ่มต้นเป็นของผู้ใช้ — การปกป้อง UserChoice
การเขียน ProgID เป็นค่าเริ่มต้นของคีย์นามสกุลไม่ได้ทำให้เป็นแอปเริ่มต้นเอง ผลของการที่ผู้ใช้เลือกอย่างชัดใน «เปิดด้วย» และคล้ายกัน ถูกเก็บใน HKCU\...\Explorer\FileExts\<extension>\UserChoice และการแก้การเชื่อมโยงให้ฝั่งนั้นก่อน
และจุดสำคัญคือ Windows ไม่รองรับการเปลี่ยนแอปเริ่มต้นด้วยโปรแกรม การตั้งค่าแอปเริ่มต้นออกแบบให้ผู้ใช้ทำผ่าน UI การตั้งค่าระบบ ข้อมูล UserChoice ถูกทำให้คลุมเครือ และไดรเวอร์กรอง (UCPD.sys) บล็อกการเขียนจากแอป ในสภาพแวดล้อมที่จัดการ นโยบายกลุ่ม / นโยบาย MDM คือช่องทางทางการ3
ที่เครื่องมืออย่าง SetUserFTA ซึ่ง «เลียนแบบแฮชแล้วเขียนทับ» ถูกใช้ คืออีกด้านของการปกป้องนี้ สิ่งที่ควรใส่ในตัวติดตั้งของแอปภายในไม่ใช่การแย่งค่าเริ่มต้น แต่สามอย่าง (a) ลงทะเบียน ProgID และ verb ให้ถูก (b) เพิ่มตัวเองใน OpenWithProgIds และ (c) ถ้าจำเป็น พาไปหน้าการตั้งค่า
flowchart TB
accTitle: การแก้แอปเริ่มต้นและการปกป้อง UserChoice
accDescr: ผลของตัวเลือกชัดของผู้ใช้ถูกเก็บใน UserChoice และถูกให้ก่อนในการแก้การเชื่อมโยง UCPD.sys บล็อกการเขียนทับจากแอป ดังนั้นตัวติดตั้งทำได้คือลงทะเบียนเป็นตัวเลือกและพาไปหน้าการตั้งค่า
uc["UserChoice(ตัวเลือกของผู้ใช้)"] -->|ให้ก่อน| res["การแก้การเชื่อมโยง"]
ext["ค่าเริ่มต้นของคีย์นามสกุล"] --> res
wr["เขียนทับจากแอป"] -.->|UCPD.sys บล็อก| uc
res ~~~ inst["งานของตัวติดตั้ง"]
inst --> a1["ลงทะเบียน ProgID และ verb"]
inst --> a2["เพิ่มใน OpenWithProgIds"]
inst --> a3["พาไปหน้าการตั้งค่า"]
ภาพ 4: การแก้การเชื่อมโยงให้ตัวเลือกของผู้ใช้ (UserChoice) ก่อน และระบบปฏิบัติการปกป้องจากการเขียนทับโดยแอป
3. verb นอกจาก «เปิด» — print, edit, runas, verb ของตัวเอง
verb ไม่ใช่แค่ open verb มาตรฐานที่ระบบรู้ความหมายมี edit print play และ preview รวมทั้ง open และ verb มาตรฐานได้ชื่อที่แสดงตามโลแคลของระบบโดยอัตโนมัติ verb เริ่มต้นที่ใช้ตอนดับเบิลคลิกตัดสินตามลำดับ ค่าเริ่มต้นของคีย์ shell → verb แรกในรีจิสทรี → open → openwith12
flowchart TB
accTitle: ลำดับที่ตัดสิน verb เริ่มต้น
accDescr: verb เริ่มต้นที่ใช้ตอนดับเบิลคลิกคืออันแรกที่พบในลำดับค่าเริ่มต้นของคีย์ shell verb แรกในรีจิสทรี open openwith
s1["ค่าเริ่มต้นของคีย์ shell"] -->|ถ้าไม่มี| s2["verb แรกในรีจิสทรี"]
s2 -->|ถ้าไม่มี| s3["open"]
s3 -->|ถ้าไม่มี| s4["openwith"]
ภาพ 5: verb เริ่มต้นตอนดับเบิลคลิกคืออันแรกที่พบในลำดับนี้
เมื่ออยากเพิ่มการกระทำของตัวเอง ให้ลงทะเบียน verb ของตัวเอง
KomuraSoft.Report.1
shell
open
command
(Default) = "C:\Program Files\KomuraSoft\Report.exe" "%1"
print
command
(Default) = "C:\Program Files\KomuraSoft\Report.exe" /print "%1"
verify ← verb ของตัวเอง
(Default) = Verify report (&V) ← ชื่อที่แสดงในเมนู
command
(Default) = "C:\Program Files\KomuraSoft\Report.exe" /verify "%1"
สามข้อเล็กที่รู้แล้วมีประโยชน์
- ลงทะเบียน verb ชื่อ runas แล้วคุณนิยามการเปิดแบบยกระดับเทียบเท่า «เรียกใช้ในฐานะผู้ดูแลระบบ» และถูกใช้เมื่อ API ตระกูล
ShellExecuteระบุrunasด้วย - ใส่ค่าว่างชื่อ
Extendedบนคีย์ verb แล้วมันกลายเป็น verb ขยายที่แสดงเฉพาะเมื่อ Shift+คลิกขวา สะดวกสำหรับซ่อนการทำงานอันตรายที่ใช้น้อย12 - การเชื่อมโยงของแอปเก่าบางส่วนยังมีการตั้งค่าที่ส่งเอกสารเข้าโพรเซสที่มีอยู่ด้วย DDE (คีย์
ddeexec) แต่การเปิด verb ผ่าน DDE เป็นมรดก Deprecated แล้ว ไม่มีเหตุผลเขียนใหม่12
อุบัติเหตุอีกอย่างที่เกิดบ่อยคือ เครื่องหมายคำพูดบนบรรทัดคำสั่ง ถ้าองค์ประกอบของสตริงคำสั่งมีช่องว่างได้ ต้องห่อด้วยเครื่องหมายคำพูด นั่นใช้กับพาธ EXE อย่าง C:\Program Files\... และ %1 (พาธของไฟล์ที่เลือก) ต้องเขียน "%1" เสมอ คุณรับประกันไม่ได้ว่าพาธไฟล์ผู้ใช้ไม่มีช่องว่าง My Program.exe ที่ไม่ใส่คำพูดถูกตีความว่า «เปิด My ด้วยอาร์กิวเมนต์ Program.exe»13
flowchart TB
accTitle: อุบัติเหตุเครื่องหมายคำพูดบนบรรทัดคำสั่ง
accDescr: คำสั่งที่ไม่ใส่คำพูดถูกตัดที่ช่องว่างและเข้าใจผิดว่าเปิด My ด้วยอาร์กิวเมนต์ Program.exe ดังนั้นพาธ EXE ที่อาจมีช่องว่างและ %1 ซึ่งแทนพาธไฟล์ที่เลือก ต้องห่อด้วยเครื่องหมายคำพูดเสมอ
c1["คำสั่งไม่ใส่คำพูด"] -->|ตัดที่ช่องว่าง| bad["เข้าใจผิดว่าเปิด EXE อื่น"]
c2["คำสั่งใส่คำพูด"] --> good["เปิดตามที่ตั้งใจ"]
c2 -.-> q1["ห่อพาธ EXE ด้วยเครื่องหมายคำพูด"]
q1 -.-> q2["ห่อ %1 ด้วยเครื่องหมายคำพูดเสมอด้วย"]
ภาพ 6: คำสั่งไม่ใส่คำพูดถูกตัดผิดที่ช่องว่าง จึงห่อพาธ EXE และ %1 ด้วยเครื่องหมายคำพูดเสมอ
กลไกแค่รีจิสทรีจนถึงตรงนี้ (verb แบบคงที่) ทำให้ได้โดยไม่เขียน DLL เลย และไม่เสี่ยงทำให้ Explorer ไม่เสถียร Microsoft เองพูดซ้ำ «ก่อนเขียนส่วนขยายเชลล์ ให้พิจารณาว่า verb แบบคงที่ที่ง่ายที่สุดที่ตอบความต้องการพอหรือไม่»9
4. ส่วนขยายเชลล์คลาสสิก — DLL ที่รันใน Explorer
4.1. ชนิดของส่วนขยายเชลล์
ความต้องการที่ verb แบบคงที่ทำไม่ได้ — «เปลี่ยนเมนูแบบไดนามิกตามการเลือก» «แทนที่ไอคอนหรือแผ่นคุณสมบัติ» — ใช้ตัวจัดการส่วนขยายเชลล์ ชนิดที่เป็นตัวแทนมีดังนี้4
| ตัวจัดการ | อินเทอร์เฟซหลัก | สิ่งที่ทำได้ |
|---|---|---|
| ตัวจัดการเมนูบริบท | IContextMenu + IShellExtInit | เพิ่มและควบคุมรายการเมนูแบบไดนามิก |
| ตัวจัดการไอคอน / โอเวอร์เลย์ไอคอน | IExtractIcon / IShellIconOverlayIdentifier | ไอคอนต่อไฟล์และโอเวอร์เลย์ |
| ตัวจัดการแผ่นคุณสมบัติ | IShellPropSheetExt | เพิ่มแท็บในแผ่นคุณสมบัติ |
| ภาพย่อ / อินโฟทิป | IThumbnailProvider / IQueryInfo | มุมมองภาพย่อและคำอธิบายเมื่อชี้ |
| ตัวจัดการลากวาง / copy-hook | IDropTarget / ICopyHook | แทรกตอนวางหรือคัดลอก/ย้าย |
ทั้งหมดอิมพลีเมนต์เป็นคลาส COM และลงทะเบียนในรีจิสทรีตาม CLSID ความคิดเรื่อง COM อยู่ใน «What Are COM / ActiveX / OCX? - The Differences and Relationships Explained»
4.2. การเป็นเซิร์ฟเวอร์ COM ในโพรเซสหมายความว่าอะไร
สาระของส่วนขยายเชลล์คลาสสิกคือ เซิร์ฟเวอร์ COM ในโพรเซส (DLL) ที่โหลดเข้า Explorer (หรือแอปใดก็ได้ที่เปิดกล่องโต้ตอบไฟล์ร่วม) ข้อควรระวังทุกข้อตามมาจากนั้น4
- ถ้าส่วนขยายแครช Explorer ถูกพาลงด้วย ถ้าค้าง คลิกขวาจะค้างหลายวินาที ความเสียหายไม่จำกัดที่ Explorer แต่ถึงทุกแอปที่แสดงกล่องเปิดไฟล์
- การสร้างเมนูเกิดบนเธรด UI ดังนั้น ห้ามทำงานช้าอย่างเข้าถึงเครือข่ายหรือ I/O ไฟล์ตอนแสดงเมนู
- ลงทะเบียนโมเดลเธรดเป็น
Apartmentตามกฎ
flowchart TB
accTitle: โครงสร้างความเสียหายข้างเคียงของส่วนขยายในโพรเซส
accDescr: DLL ส่วนขยายเชลล์ถูกโหลดไม่เฉพาะเข้า Explorer แต่เข้าโพรเซสของแอปใดก็ได้ที่เปิดกล่องโต้ตอบไฟล์ ดังนั้นแครชหรือค้างของส่วนขยายแพร่ไปทั้งโพรเซสโฮสต์
dll["DLL ส่วนขยายเชลล์"] -->|โหลดในโพรเซส| exp["Explorer"]
dll -->|โหลดในโพรเซส| any["แอปใดก็ได้ที่เปิดกล่องโต้ตอบ"]
exp --> dmg["แครชหรือค้างแพร่"]
any --> dmg
dmg -.-> rule["อย่าทำงานช้าตอนแสดง"]
ภาพ 7: DLL ของส่วนขยายรันในโพรเซสโฮสต์ ดังนั้นแครชหรือค้างแพร่ไปทั้งโฮสต์
สอบสวนคำปรึกษาอย่าง «Explorer ค้างเมื่อเปิดโฟลเดอร์หนึ่ง» หรือ «คลิกขวาใช้เวลาห้าวินาที» แล้วไม่แปลกที่สาเหตุเป็นส่วนขยายเชลล์ของบุคคลที่สาม ไม่ใช่แอปภายใน วิธีแยกอยู่ในบทที่ 8
4.3. จับบิตให้ตรง — สภาพแวดล้อม 64 บิตต้องใช้ DLL 64 บิต
DLL ในโพรเซสต้องตรงบิตกับโพรเซสที่โหลด Explorer บน Windows 64 บิตเป็นโพรเซส 64 บิต ดังนั้น DLL ส่วนขยายเชลล์ที่สร้างแค่ 32 บิตไม่ถูกโหลดเลยและไม่ขึ้นเมนูเลย ไม่มีข้อผิดพลาดด้วย จึงเป็นสาเหตุประจำของ «ลงทะเบียนแล้วแต่ไม่ขึ้น» การจับตัวแอป 32 บิตกับ DLL ส่วนขยายเชลล์ 64 บิตเป็นการตั้งค่าที่ถูกต้อง แต่ต้องดูว่าการลงทะเบียน COM แยกตามบิต (Wow6432Node) การเปิดจาก command ของ verb เป็น EXE โพรเซสแยก จึงไม่อยู่ใต้ข้อจำกัดนี้ (ปล่อยเป็น EXE 32 บิตได้)
flowchart TB
accTitle: จับบิตของ DLL ส่วนขยายเชลล์ให้ตรง
accDescr: DLL ส่วนขยายเชลล์เดียวที่ Explorer 64 บิตโหลดได้คือ DLL 64 บิต DLL แค่ 32 บิตไม่ขึ้นเมนูและไม่มีข้อผิดพลาด EXE ที่เปิดจาก command ของ verb เป็นโพรเซสแยกและไม่อยู่ใต้ข้อจำกัด
exp["Explorer 64 บิต"] -->|โหลดได้| d64["DLL ส่วนขยายเชลล์ 64 บิต"]
exp -.->|โหลดไม่ได้| d32["DLL แค่ 32 บิต"]
d32 -.-> sym["ไม่ขึ้นเมนู โดยไม่มีข้อผิดพลาด"]
exe["EXE ที่เปิดจาก verb"] -->|โพรเซสแยก| ok32["ปล่อยเป็น 32 บิตได้"]
ภาพ 8: DLL เดียวที่โหลดเข้า Explorer 64 บิตคือ DLL 64 บิต EXE ที่เปิดจาก verb ไม่อยู่ใต้ข้อจำกัดนี้
4.4. ทำไมห้ามเขียนด้วยโค้ดจัดการ
มักถูกถามว่า «เขียนส่วนขยายเชลล์ด้วย C# ได้ไหม» แต่ Microsoft ระบุชัดว่าการเขียนส่วนขยายเชลล์ในโพรเซสด้วยโค้ดจัดการ (.NET) ไม่แนะนำและอยู่นอกการรองรับ5
เหตุผลคือธรรมชาติของส่วนขยายที่ถูกโหลดเข้าโพรเซสใดก็ได้ การชนกันของเวอร์ชัน CLR (โดยเฉพาะต่ำกว่า .NET Framework 4) ปัญหาที่ CLR เข้าลูปข้อความซ้ำขณะรอล็อก และอายุวัตถุที่ไม่กำหนดจากการเก็บขยะที่ชนกับสัญญาการนับอ้างอิงของ COM คือเหตุผลเชิงโครงสร้างที่ทำให้แอปโฮสต์ไม่เสถียร บางข้อถูกบรรเทาบน .NET Framework 4 ขึ้นไปและ .NET สมัยใหม่ แต่จุดยืนทางการไม่เปลี่ยน
แนวปฏิบัติเรียบง่าย เขียนส่วนขยายในโพรเซสด้วย C++ เนทีฟ ถ้าอยากใช้โค้ดจัดการ ให้ทำเป็น EXE ธรรมดาที่เปิดจาก command ของ verb หรือส่วนขยายนอกโพรเซสที่รันในโพรเซสแยก (ตัวจัดการพรีวิวและคล้ายกัน)5
flowchart TB
accTitle: ตัดสินว่าโค้ดจัดการอนุญาตหรือไม่
accDescr: ส่วนขยายในโพรเซสที่รันใน Explorer เขียนด้วย C++ เนทีฟตามกฎ ถ้าอยากได้โค้ดจัดการ ให้ทำเป็น EXE ธรรมดาที่เปิดจาก command ของ verb หรือส่วนขยายนอกโพรเซสที่รันในโพรเซสแยก
q1{"รันในโพรเซส?"} -->|ใช่| cpp["เขียนด้วย C++ เนทีฟ"]
q1 -->|ไม่| mg["โค้ดจัดการได้"]
cpp -.-> why["ความเสี่ยง CLR / เข้าซ้ำทำให้โฮสต์ไม่เสถียร"]
mg --> e1["EXE ที่เปิดจาก verb"]
mg --> e2["พรีวิวนอกโพรเซส"]
ภาพ 9: ส่วนขยายในโพรเซสตามกฎคือ C++ เนทีฟ โค้ดจัดการจำกัดที่การตั้งค่าที่รันในโพรเซสแยก
5. เมนูบริบทใหม่ของ Windows 11 — เมนูที่แยกเป็นสอง
5.1. สิ่งที่เกิดขึ้น
Windows 11 ปรับเมนูบริบทของ File Explorer ใหม่ ตัด คัดลอก และคล้ายกันกลายเป็นแถวไอคอนด้านบน «เปิด» และ «เปิดด้วย» ถูกจัดกลุ่มด้านบน และคำสั่งที่แอปเพิ่มถูกจัดกลุ่มใต้คำสั่งมาตรฐานของเชลล์ เมื่อแอปหนึ่งเพิ่มหลายคำสั่ง จะถูกรวบเข้าฟลายเอาต์ (เมนูย่อย) ที่ตั้งชื่อตามแอป6
และจุดสำคัญคือนี้ ส่วนขยายเชลล์คลาสสิกแบบ IContextMenu ไม่ถูกลบ แต่ถูกย้ายไปฝั่งเมนูเก่าที่เปิดด้วย «แสดงตัวเลือกเพิ่มเติม» (Shift+F10) และโหลดเมนู Windows 10 ตามเดิม6 ตัวตนของคำปรึกษาเปิดเรื่อง «เมนูถูกซ่อน» คือการแยกนี้
flowchart TB
accTitle: เมนูบริบทที่ Windows 11 แยกเป็นสอง
accDescr: สิ่งที่เปิดก่อนตอนคลิกขวาคือเมนูใหม่ คำสั่งเดียวที่ปรากฏที่นั่นคือคำสั่งที่ลงทะเบียนด้วย IExplorerCommand และอัตลักษณ์แพ็กเกจ ส่วนขยาย IContextMenu คลาสสิกถูกย้ายไปเมนูเก่าที่เปิดด้วยแสดงตัวเลือกเพิ่มเติม
rc["คลิกขวาไฟล์"] --> newm["เมนูใหม่(Windows 11)"]
newm --> newi["คำสั่ง IExplorerCommand + อัตลักษณ์"]
newm -->|แสดงตัวเลือกเพิ่มเติม Shift+F10| oldm["เมนูเก่า(เมนู Windows 10)"]
oldm --> oldi["ส่วนขยาย IContextMenu คลาสสิก"]
newi -.-> fly["หลายคำสั่งถูกรวบเข้าฟลายเอาต์"]
ภาพ 10: คำสั่งเดียวที่ปรากฏบนเมนูใหม่คือคำสั่ง IExplorerCommand + อัตลักษณ์ ส่วนขยายคลาสสิกถูกย้ายไปฝั่งเมนูเก่า
5.2. เส้นทางทางการสู่เมนูใหม่ — IExplorerCommand + การลงทะเบียนแมนิเฟสต์
มีวิธีเดียวที่จะวางคำสั่งของตัวเองบนเมนูใหม่ เตรียม DLL เนทีฟที่อิมพลีเมนต์อินเทอร์เฟซ IExplorerCommand และประกาศเซิร์ฟเวอร์ COM กับส่วนขยายเมนูบริบทในแมนิเฟสต์แพ็กเกจ MSIX7
<!-- แมนิเฟสต์แพ็กเกจ (ตัดตอน) -->
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:SurrogateServer DisplayName="Komura commands">
<com:Class Id="01234567-89AB-CDEF-0123-456789ABCDEF"
Path="KomuraCommand.dll" ThreadingModel="STA" />
</com:SurrogateServer>
</com:ComServer>
</com:Extension>
<desktop4:Extension Category="windows.fileExplorerContextMenus">
<desktop4:FileExplorerContextMenus>
<desktop5:ItemType Type=".kmrpt">
<desktop5:Verb Id="VerifyReport"
Clsid="01234567-89AB-CDEF-0123-456789ABCDEF" />
</desktop5:ItemType>
</desktop4:FileExplorerContextMenus>
</desktop4:Extension>
Type ของ ItemType ระบุนามสกุลเฉพาะ หรือ * (ทุกไฟล์) Directory (โฟลเดอร์) หรือ Directory\Background (พื้นหลังโฟลเดอร์) ได้ จับ DLL ให้ตรงสถาปัตยกรรมของ Explorer (64 บิต / ARM64)7
IExplorerCommand เองมีมาตั้งแต่ยุค Windows 7 คุณอิมพลีเมนต์ชื่อเรื่อง (GetTitle) ไอคอน (GetIcon) สถานะเปิดใช้ / ปิดใช้ / ซ่อน (GetState) และการทำงาน (Invoke) เมธอดถูกเรียกจากเธรด UI ดังนั้น ห้ามเข้าถึงทรัพยากรเครือข่าย และเมธอดสร้างเมนูต้องกลับเร็ว งานหนักทำหลัง Invoke147
flowchart TB
accTitle: โครงสร้างแมนิเฟสต์ของการลงทะเบียนเมนูใหม่
accDescr: ประกาศเซิร์ฟเวอร์ COM ของแมนิเฟสต์ MSIX จับคู่ CLSID กับ DLL และประกาศส่วนขยายเมนูบริบทผูกเป้าหมายกับการอิมพลีเมนต์ด้วย ItemType และ Verb ดังนั้นคำสั่งของตัวเองปรากฏบนเมนูใหม่
man["แมนิเฟสต์ MSIX"] --> com["ประกาศเซิร์ฟเวอร์ COM"]
man --> ctx["ประกาศส่วนขยายเมนู"]
com -->|จับคู่ CLSID กับ DLL| impl["DLL อิมพลีเมนต์ IExplorerCommand"]
ctx -->|ระบุด้วย ItemType และ Verb| impl
impl --> shown["คำสั่งปรากฏบนเมนูใหม่"]
ctx -.-> tgt["เป้าหมายคือนามสกุล ทุกไฟล์ และคล้ายกัน"]
ภาพ 11: สองประกาศในแมนิเฟสต์ผูก DLL อิมพลีเมนต์กับเป้าหมาย และคำสั่งปรากฏบนเมนูใหม่
5.3. ตัวเลือกสำหรับแอปที่ไม่ได้แพ็ก — ได้อัตลักษณ์อย่างเดียวด้วย sparse package
ทางหนีเมื่อ «แอปเราแจกได้แค่ MSI MSIX เป็นไปไม่ได้» คือ sparse package (MSIX ที่มีตำแหน่งภายนอก) คุณลงนาม MSIX เล็กที่เป็นแค่แมนิเฟสต์ ไม่มีตัวแอป และลงทะเบียนท้ายตัวติดตั้งที่มีอยู่ แล้วแอปจะได้ อัตลักษณ์แพ็กเกจ และการลงทะเบียนแมนิเฟสต์ด้านบน (= ปรากฏบนเมนูใหม่) จึงเป็นไปได้ มีตั้งแต่ Windows 10 เวอร์ชัน 2004 และแพ็กเกจต้องมีลายเซ็นด้วยใบรับรองที่เครื่องเป้าหมายเชื่อถือ8
flowchart TB
accTitle: ลำดับการได้อัตลักษณ์ด้วย sparse package
accDescr: หลังตัวติดตั้งที่มีอยู่วางตัวแอป การลงทะเบียน sparse package แค่แมนิเฟสต์ด้วยตำแหน่งภายนอกให้อัตลักษณ์แพ็กเกจแก่แอปและทำให้การลงทะเบียนแมนิเฟสต์เมนูใหม่เป็นไปได้
inst["ตัวติดตั้งที่มีอยู่"] --> files["วางตัวแอป"]
sp["sparse package"] -.-> only["แค่แมนิเฟสต์ ไม่มีตัว"]
files --> reg["ลงทะเบียนด้วยตำแหน่งภายนอก"]
sp --> reg
reg --> id["ได้อัตลักษณ์แพ็กเกจ"]
id --> ok["การลงทะเบียนเมนูใหม่เป็นไปได้"]
sp -.-> sign["ต้องมีลายเซ็นที่เชื่อถือ"]
ภาพ 12: ลงทะเบียน sparse package ที่ไม่มีตัว ด้วยตำแหน่งภายนอก แล้วแอปได้อัตลักษณ์แพ็กเกจ
ข้อดีที่สุดคือไม่ต้องเปลี่ยนตัวติดตั้ง นี่คือคำตอบที่เป็นจริงสำหรับแอปที่มีสินทรัพย์ตัวติดตั้ง MSI/EXE อยู่แล้ว เปรียบเทียบกับการย้ายทั้งก้อนไป MSIX ดู «Choosing a Windows App Distribution Method - MSI/MSIX/ClickOnce/xcopy/Custom Updater» ด้วย
5.4. verb ของการเชื่อมโยงปรากฏบนเมนูใหม่อย่างไร
จุดที่เข้าใจผิดง่าย การเชื่อมโยงของบทที่ 2 และ 3 (ProgID และ verb) ยังมีชีวิตบนเมนูใหม่ verb เริ่มต้นตอนดับเบิลคลิก «เปิด» และตัวเลือก «เปิดด้วย» ถูกแก้จากการเชื่อมโยงและแสดงด้านบนของเมนูใหม่ ดังนั้นถ้าต้องการแค่ «เปิดด้วยแอปนี้ได้» Windows 11 ไม่ต้องงานเพิ่ม ในทางกลับกัน การเชื่อมโยงไม่ใช่ส่วนขยายเมนูเอนกประสงค์ ดังนั้นถ้าต้องการคำสั่งของตัวเองใดก็ได้ที่ชั้นแรกของเมนูใหม่ ต้องมี IExplorerCommand บวกอัตลักษณ์ — นั่นคือการแบ่งบทบาท7
flowchart TB
accTitle: การแบ่งบทบาทระหว่างการเชื่อมโยงกับเมนูใหม่
accDescr: การเชื่อมโยง ProgID-และ-verb ยังถูกใช้บนเมนูใหม่เพื่อแก้ verb เริ่มต้น เปิด และเปิดด้วย และแสดงด้านบน การวางคำสั่งของตัวเองใดก็ได้ที่ชั้นแรกของเมนูใหม่ต้องใช้ IExplorerCommand และอัตลักษณ์
assoc["การเชื่อมโยง(ProgID + verb)"] --> sol["แก้ค่าเริ่มต้น / เปิด"]
sol --> top["ด้านบนเมนูใหม่"]
assoc -.-> keep["ไม่มีงานเพิ่มบน Win11"]
cmd["คำสั่งของตัวเอง"] --> need["IExplorerCommand+อัตลักษณ์"]
need --> first["ชั้นแรกของเมนูใหม่"]
ภาพ 13: การเชื่อมโยงยังจัดการการแก้ตระกูล «เปิด» บนเมนูใหม่ มีแค่คำสั่งของตัวเองที่ต้องใช้ IExplorerCommand บวกอัตลักษณ์
6. ตารางตัดสินใจที่ใช้ได้จริง — จะเลือกข้อใดในสามข้อ
เราจัดสิ่งที่ได้มาเป็นทางเลือกสามทางที่ใช้ได้จริง
| สิ่งที่อยากทำให้ได้ | วิธีที่แนะนำ | หน้าตาบน Windows 11 | งานและต้นทุนที่ต้องใช้ |
|---|---|---|---|
| (a) เปิดแอปภายในตอนดับเบิลคลิกหรือ «เปิด» | การเชื่อมโยง + verb แบบคงที่ (ลงทะเบียนรีจิสทรีอย่างเดียว) | รวมเข้า «เปิด» และ «เปิดด้วย» บนเมนูใหม่ | ลงทะเบียนรีจิสทรีของตัวติดตั้งอย่างเดียว ไม่มี DLL ไม่มีข้อกำหนดลายเซ็นเพิ่ม |
| (b) วางคำสั่งของตัวเองสำหรับไฟล์/โฟลเดอร์ที่เลือกบนเมนูใหม่ | อิมพลีเมนต์ IExplorerCommand + ลงทะเบียนแมนิเฟสต์ MSIX แอปที่ไม่ได้แพ็กได้อัตลักษณ์ด้วย sparse package | ชั้นแรกของเมนูใหม่ (หลายคำสั่งถูกรวบเข้าฟลายเอาต์ชื่อแอป) | DLL C++ เนทีฟ + อัตลักษณ์แพ็กเกจ + ลายเซ็นโค้ด |
| (c) ใช้ส่วนขยาย IContextMenu คลาสสิกที่มีอยู่ต่อไป | คงไว้ตามเดิมตอนนี้ (อย่าเลือกสำหรับพัฒนาใหม่) | ฝั่งเมนูเก่าอย่างเดียว ใต้ «แสดงตัวเลือกเพิ่มเติม» (Shift+F10) | รักษาบิลด์ 64 บิตและการลงทะเบียน COM วางแผนย้ายไป (b) ภายหลัง |
จุดตัดสินมีสอง ข้อแรก อย่าดึง (b) หรือ (c) มาสำหรับความต้องการที่ (a) ตอบได้ ทันทีที่เขียนส่วนขยายเชลล์ คุณรับผิดชอบเสถียรภาพของ Explorer ข้อสอง (c) เป็นแค่ «ไม่พัง» ในฐานะประสบการณ์ผู้ใช้ยังด้อยกว่าหนึ่งก้าว คำสั่งที่ใช้บ่อยในงานประจำวันยิ่งมาก ผลตอบแทนของการย้ายไป (b) ยิ่งใหญ่
flowchart TB
accTitle: วิธีเลือกในสามข้อ
accDescr: ถ้าต้องการแค่เปิดตอนดับเบิลคลิกหรือเปิด การเชื่อมโยงและ verb แบบคงที่พอ ถ้าจะวางคำสั่งของตัวเองบนเมนูใหม่ ใช้ IExplorerCommand และการลงทะเบียนแมนิเฟสต์ MSIX ถ้าเป็น MSIX ไม่ได้ ให้อัตลักษณ์ด้วย sparse package คงส่วนขยาย IContextMenu คลาสสิกที่มีอยู่ฝั่งเมนูเก่าไว้ตอนนี้
q1{"เปิดพอ?"} -->|ใช่| pa["การเชื่อมโยง + verb แบบคงที่"]
q1 -->|ไม่| q2{"คำสั่งของตัวเองบนเมนูใหม่?"}
q2 -->|ใช่| q3{"เป็น MSIX ได้?"}
q3 -->|ใช่| pb1["IExplorerCommand+MSIX"]
q3 -->|ไม่| pb2["อัตลักษณ์ sparse-pkg"]
q2 -->|ไม่| pc["คงคลาสสิกไว้ตอนนี้"]
pc -.-> old["ฝั่งเมนูเก่าอย่างเดียว"]
pa -.-> dllfree["ไม่มี DLL ความเสี่ยงเล็ก"]
ภาพ 14: เลือกระหว่าง verb แบบคงที่ IExplorerCommand บวกอัตลักษณ์ และการคงคลาสสิก ตามความต้องการ
7. การปรับใช้และการลงทะเบียนในทางปฏิบัติ — ตัวติดตั้ง sparse package การทำความสะอาด
7.1. HKLM หรือ HKCU
จับให้ตรงรูปของตัวติดตั้ง ผู้ใช้ทุกคน (วางใต้ Program Files สิทธิ์ผู้ดูแลระบบ) คือ HKLM\Software\Classes ติดตั้งต่อผู้ใช้ (ไม่ยกระดับ) คือ HKCU\Software\Classes ผสมแล้วจะเกิดคำถามแบบ «A เปิดได้แต่ B เปิดไม่ได้» สำหรับส่วนขยายเชลล์ที่เกี่ยวข้องกับการลงทะเบียน CLSID Reg-Free COM — ที่ตัดความจำเป็นของการลงทะเบียนรีจิสทรีเอง — เป็นตัวเลือกที่ใช้ได้สำหรับการใช้ COM ในแอป แต่ใช้กับส่วนขยายเชลล์ที่ Explorer โหลดไม่ได้ จึงต้องลงทะเบียนตรง («What Is Reg-Free COM - Using COM Without Registration»)
7.2. หลังเปลี่ยน ให้แจ้ง — SHChangeNotify
หลังลงทะเบียน เปลี่ยน หรือลบการเชื่อมโยง แจ้งเหตุการณ์ SHCNE_ASSOCCHANGED ด้วย SHChangeNotify ข้ามแล้ว Explorer อาจไม่รู้การเปลี่ยนแปลงจนกว่าจะรีบูต110
// เรียกครั้งเดียวหลังเปลี่ยนการเชื่อมโยง เช่น จากแอ็กชันกำหนดเองของตัวติดตั้ง
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
7.3. ลงทะเบียนและถอน sparse package
การลงทะเบียนและถอน sparse package เป็นงานของตัวติดตั้ง ลงทะเบียนหลังวางไฟล์ ถอนก่อนลบไฟล์8
# ตอนติดตั้ง: หลังวางไฟล์ ลงทะเบียนโฟลเดอร์ติดตั้งเป็นตำแหน่งภายนอก
Add-AppxPackage -Path "C:\Program Files\KomuraSoft\KomuraReport.identity.msix" `
-ExternalLocation "C:\Program Files\KomuraSoft"
# ตอนถอนติดตั้ง: ถอนการลงทะเบียนแพ็กเกจก่อนลบไฟล์
Remove-AppxPackage <package full name>
จุดที่ต้องดู: Add-AppxPackage ลงทะเบียน สำหรับผู้ใช้ที่รัน ถ้าเรียกจากแอ็กชันกำหนดเองของ MSI ต่อเครื่อง รันใต้ LocalSystem จะไม่ให้อัตลักษณ์แก่ผู้ใช้ที่ติดตั้ง จึงตั้งให้รันใต้การสวมตัวผู้ใช้ แม้กระนั้น การลงทะเบียนใต้การสวมตัวเป็น ของผู้ใช้ที่รันการติดตั้งนั้นเท่านั้น บนพีซีที่ใช้หลายคน ผู้ใช้อื่นและผู้ใช้ที่สร้างทีหลังไม่มีอัตลักษณ์แพ็กเกจ และคำสั่งไม่ขึ้นเมนูใหม่ ให้ทุกคนใช้ได้ จัดกลไกอย่างตรวจการลงทะเบียนแพ็กเกจของตัวเองตอนเปิดครั้งแรกแล้วลงทะเบียนถ้าขาด (ลงทะเบียนต่อผู้ใช้) และใส่การถอนจากผู้ใช้ที่มีการลงทะเบียนในแผนถอนติดตั้ง การสะท้อนการลงทะเบียนแมนิเฟสต์อาจต้องรีสตาร์ท Explorer (หรือออกจากระบบ) ด้วย7
flowchart TB
accTitle: ลำดับการลงทะเบียนและถอน sparse package
accDescr: ตอนติดตั้งลงทะเบียน sparse package หลังวางไฟล์ ตอนถอนติดตั้งถอนการลงทะเบียนก่อนลบไฟล์ ดูว่าการลงทะเบียนมีผลเฉพาะผู้ใช้ที่รัน
i1["ติดตั้ง"] --> i2["วางไฟล์"]
i2 --> i3["ลงทะเบียน sparse package"]
u1["ถอนติดตั้ง"] --> u2["ถอนการลงทะเบียนแพ็กเกจ"]
u2 --> u3["ลบไฟล์"]
i3 -.-> pu["การลงทะเบียนมีผลเฉพาะผู้ใช้ที่กำลังรัน"]
ภาพ 15: ลงทะเบียนหลังวางไฟล์ ถอนก่อนลบไฟล์ และดูว่าการลงทะเบียนเป็นต่อผู้ใช้ที่กำลังรัน
7.4. การทำความสะอาดตอนถอนติดตั้ง — ลบอะไร เหลืออะไร
การทำความสะอาดตอนถอนติดตั้งมีเส้นคู่มือทางการที่ชัด1
- ลบ: คีย์ ProgID ภายในทั้งก้อน การลงทะเบียน Capabilities/RegisteredApplications การลงทะเบียน CLSID ของส่วนขยายเชลล์ sparse package (Remove-AppxPackage)
- เหลือ: ค่าเริ่มต้นของคีย์นามสกุล (
.kmrpt) คำแนะนำทางการคืออย่าลบแม้ยังชี้ไป ProgID ภายใน การตัดสินหลังติดตั้งว่าแอปอื่นเอาค่าเริ่มต้นไปแล้วหรือยังยาก และ Windows แค่ละเว้น ProgID ค่าเริ่มต้นที่ไม่ได้ลงทะเบียน การเหลือไว้จึงไม่เป็นอันตรายจริง - เรียก SHChangeNotify(SHCNE_ASSOCCHANGED) ท้ายการทำความสะอาดด้วย
ปัญหา «ถอนแล้วแต่เมนูยังมีของเหลือ» ส่วนใหญ่เป็นการรั่วในการออกแบบการทำความสะอาดนี้
flowchart TB
accTitle: การออกแบบการทำความสะอาดตอนถอนติดตั้ง
accDescr: ตอนถอนติดตั้งคุณลบคีย์ ProgID ภายใน การลงทะเบียน CLSID และ sparse package เหลือค่าเริ่มต้นของคีย์นามสกุลเพราะ ProgID ที่ไม่ได้ลงทะเบียนถูกละเว้น แจ้งการเปลี่ยนแปลงด้วย SHChangeNotify ท้ายการทำความสะอาด
un["ถอนติดตั้ง"] --> del["ลบ"]
un --> keep["เหลือ"]
del --> d1["การลงทะเบียน ProgID และ CLSID"]
del --> d2["sparse package"]
keep --> k1["ค่าเริ่มต้นของคีย์นามสกุล"]
k1 -.-> why["ProgID ที่ไม่ได้ลงทะเบียนถูกละเว้น"]
d1 --> fin["แจ้งด้วย SHChangeNotify ท้ายสุด"]
k1 --> fin
ภาพ 16: ลบการลงทะเบียนภายใน เหลือค่าเริ่มต้นของคีย์นามสกุล และแจ้งการเปลี่ยนแปลงท้ายการทำความสะอาด
8. การแก้ปัญหา — ไม่ขึ้น ซ้ำ หนัก
8.1. ไม่ขึ้นบนเมนู
แยกตามลำดับนี้
- คุณกำลังดูเมนูไหน: การลงทะเบียนสไตล์คลาสสิกขึ้นเฉพาะฝั่งเมนูเก่าใต้ Shift+F10 ตรวจทั้งสองก่อน
- บิต: DLL ส่วนขยายเชลล์แค่ 32 บิตไม่ถูกโหลดเข้า Explorer 64 บิต (ข้อ 4.3)
- ปลายทางลงทะเบียน: HKLM/HKCU ความสับสน Wow6432Node ยืนยันคีย์จริงด้วย
reg query - การลงทะเบียนแพ็กเกจ: สำหรับเมนูใหม่ ยืนยันการมีอยู่ด้วย
Get-AppxPackageความเชื่อถือของใบรับรองลายเซ็น และพาธ-ExternalLocationแล้วรีสตาร์ท Explorer7 - การแจ้งที่ลืม: ถ้าลืม SHChangeNotify รู้ได้จากว่ารีสตาร์ท Explorer แล้วมีผลหรือไม่
flowchart TB
accTitle: ลำดับการแยกเมื่อไม่ขึ้นบนเมนู
accDescr: เริ่มด้วยยืนยันว่ากำลังดูเมนูไหน แล้วแยกบิตของ DLL ปลายทางลงทะเบียนรีจิสทรี การลงทะเบียนแพ็กเกจและลายเซ็น และการลืม SHChangeNotify ตามลำดับนั้น
c1["ยืนยันเมนูไหน เก่าหรือใหม่"] --> c2["ยืนยันบิตของ DLL"]
c2 --> c3["ยืนยันปลายทางลงทะเบียน HKLM และ HKCU"]
c3 --> c4["ยืนยันการลงทะเบียนแพ็กเกจและลายเซ็น"]
c4 --> c5["รู้การแจ้งที่ลืมด้วยการรีสตาร์ท"]
ภาพ 17: เมื่อ «ไม่ขึ้น» ให้แยกตามลำดับเมนูที่ดู บิต ปลายทางลงทะเบียน การลงทะเบียนแพ็กเกจ การแจ้งที่ลืม
8.2. ขึ้นสองครั้ง หรือไม่ยอมหาย
สาเหตุทั่วไปคือการอยู่ด้วยกันของการลงทะเบียนรีจิสทรีคลาสสิกกับการลงทะเบียนแมนิเฟสต์ การรั่วของการทำความสะอาดตอนถอน (ข้อ 7.4) หรือของเหลือ ProgID เวอร์ชันเก่า ถ้าขึ้นสองครั้งเฉพาะเมนูเก่า ให้คิดถึงของเหลือ ถ้าขึ้นทั้งเก่าและใหม่ ให้คิดถึงการอยู่ด้วยกัน
flowchart TB
accTitle: แยกการแสดงคู่
accDescr: สองครั้งเฉพาะเมนูเก่าชี้ไปของเหลืออย่างการรั่วของการทำความสะอาดหรือ ProgID เก่า สองครั้งทั้งเก่าและใหม่ชี้ไปการอยู่ด้วยกันของการลงทะเบียนรีจิสทรีคลาสสิกกับการลงทะเบียนแมนิเฟสต์
q{"ขึ้นสองครั้งที่ไหน?"} -->|เฉพาะเมนูเก่า| zan["ของเหลือ"]
q -->|ทั้งเก่าและใหม่| hei["อยู่ด้วยกัน"]
zan -.-> z1["รั่วของการทำความสะอาดหรือ ProgID เก่าที่เหลือ"]
hei -.-> h1["การลงทะเบียนรีจิสทรีคลาสสิกอยู่กับอันใหม่"]
ภาพ 18: สองครั้งเฉพาะเมนูเก่าชี้ไปของเหลือ สองครั้งทั้งเก่าและใหม่ชี้ไปการอยู่ด้วยกัน
8.3. Explorer หนักหรือแครช
เมื่อคลิกขวาช้า หรือโฟลเดอร์หนึ่งแครช ให้สำรวจส่วนขยายเชลล์ที่ติดตั้งก่อน ลิสต์ส่วนขยายที่ไม่ใช่ Microsoft ด้วยเครื่องมืออย่าง ShellExView ของ NirSoft ปิดอันที่น่าสงสัยชั่วคราว แล้วค้นหา DLL ผู้ก่อด้วยการค้นหาแบบทวิภาค ตอนแครช «Faulting module» ของ Event Viewer ก็เป็นเบาะแส ถ้าส่วนขยายภายในเป็นสาเหตุ ให้สงสัย I/O แบบซิงโครนัสหรือการเข้าถึงเครือข่ายบนเส้นสร้างเมนู (ข้อ 4.2 และ 5.2)
flowchart TB
accTitle: หา DLL ผู้ก่อเมื่อหนักหรือแครช
accDescr: ลิสต์ส่วนขยายเชลล์ที่ไม่ใช่ Microsoft ใน ShellExView ปิดอันที่น่าสงสัยชั่วคราวแล้วค้นหา DLL ผู้ก่อด้วยการค้นหาแบบทวิภาค ตอนแครช faulting module ของ Event Viewer ก็เป็นเบาะแส
s1["สำรวจส่วนขยายเชลล์"] --> s2["ลิสต์อันที่ไม่ใช่ Microsoft"]
s2 --> s3["ปิดชั่วคราวและค้นหาแบบทวิภาค"]
s3 --> s4["ระบุ DLL ผู้ก่อ"]
crash["ตอนแครช"] -.-> ev["ตรวจ faulting module"]
ev -.-> s4
ภาพ 19: ปิดส่วนขยายที่ไม่ใช่ Microsoft ชั่วคราวแล้วค้นหาแบบทวิภาค ตอนแครชใช้ Event Viewer ด้วย
8.4. Windows Sandbox สะดวกสำหรับการตรวจ
การตรวจการรวมเชลล์วางบนการยืนยัน «ติดตั้งบนสภาพแวดล้อมสะอาด → ใช้งาน → ถอนติดตั้ง → ของเหลือเป็นศูนย์» ที่สะดวกคือ Windows Sandbox (Pro/Enterprise/Education) ทุกครั้งที่เปิดจะได้ Windows ใช้แล้วทิ้งใหม่ในไม่กี่วินาที จึงรันการทดสอบการลงทะเบียนและการทำความสะอาดของตัวติดตั้งได้เท่าที่ต้องการ ปิดแล้วหายหมด จึงเหมาะกับการสอบสวนรีจิสทรีที่เหลือด้วย15
9. สรุป
- การเชื่อมโยงไฟล์คือโครงสร้างสามชั้น «คีย์นามสกุล → ProgID → verb» และ HKCR คือมุมมองรวมของ HKLM/HKCU Classes ระบุปลายทางเขียนอย่างชัด และห่อ
%1ด้วยเครื่องหมายคำพูดเสมอ - แอปเริ่มต้นออกแบบให้ผู้ใช้เลือก และเปลี่ยนจากโปรแกรมไม่ได้ งานของตัวติดตั้งคือลงทะเบียนเป็นตัวเลือกให้ถูก
- ส่วนขยายเชลล์คลาสสิกคือ DLL COM ในโพรเซสที่โหลดเข้า Explorer แครชหรือความล่าช้าแพร่ไปทั้งก้อน ต้องใช้ 64 บิต โค้ดจัดการไม่รองรับ การอิมพลีเมนต์ด้วย C++ เนทีฟคือกฎ
- บน Windows 11 เมนูบริบทแยกเป็นสอง การวางคำสั่งของตัวเองบนเมนูใหม่ต้องใช้ IExplorerCommand บวกแมนิเฟสต์ MSIX IContextMenu คลาสสิกถูกย้ายไปฝั่ง «แสดงตัวเลือกเพิ่มเติม»
- สำหรับแอปที่เป็น MSIX ไม่ได้ การได้อัตลักษณ์ด้วย sparse package (MSIX ที่มีตำแหน่งภายนอก) คือคำตอบที่เป็นจริง
- ถ้าต้องการแค่ «เปิดด้วยแอปนี้» การเชื่อมโยงและ verb แบบคงที่ยังพอ การเริ่มจากวิธีที่ง่ายที่สุดก็เป็นแนวทางทางการ
- หลังลงทะเบียน เปลี่ยน หรือลบ ให้แจ้งด้วย SHChangeNotify ตอนถอนติดตั้ง ให้ลบ ProgID แต่เหลือค่าเริ่มต้นของคีย์นามสกุล Windows Sandbox สะดวกสำหรับการตรวจ
ถ้าการเปลี่ยนพีซี Windows 11 ทำให้สังเกตว่า «เมนูถูกซ่อน» ให้ยืนยันก่อนว่าเป็น (a) (b) หรือ (c) ในตารางตัดสินใจบทที่ 6 คุณควรประมาณขนาดงานได้ทันที
บทความที่เกี่ยวข้อง
- What Are COM / ActiveX / OCX? - The Differences and Relationships Explained
- What Is Reg-Free COM - Using COM Without Registration
- Registry 32-bit/64-bit Redirection and Virtualization Pitfalls — Wow6432Node and the “The Value I Wrote Isn’t There” Problem
- Choosing a Windows App Distribution Method - MSI/MSIX/ClickOnce/xcopy/Custom Updater
- DLL and COM Interface Backward Compatibility — A Decision Table for Which Changes Break Callers
- กลไกความเข้ากันได้ของแอป Windows ── ยืดอายุแอปเก่าด้วยโหมดความเข้ากันได้ ชิม และ Compatibility Administrator
ด้านให้คำปรึกษาที่เกี่ยวข้อง
KomuraSoft LLC รับออกแบบและอิมพลีเมนต์การเชื่อมโยงไฟล์ เมนูบริบท และส่วนขยายเชลล์ของแอปธุรกิจ การเล็งเมนูบริบทใหม่ของ Windows 11 (ย้ายไป IExplorerCommand นำ sparse package เข้า) การตรวจการลงทะเบียนและการทำความสะอาดของตัวติดตั้งที่มีอยู่ และการสอบสวนสาเหตุเมื่อ Explorer หนักหรือแครช เริ่มจากตัดสินว่าจะทำอย่างไรกับ «เมนูที่ถูกซ่อนใต้แสดงตัวเลือกเพิ่มเติม» ก็ได้
ลิงก์อ้างอิง
-
Microsoft Learn, File Types. เกี่ยวกับโครงสร้างที่คีย์นามสกุลชี้ไป ProgID OpenWithProgIds การแยกการลงทะเบียนระหว่าง HKLM/HKCU\Software\Classes การเรียก SHChangeNotify(SHCNE_ASSOCCHANGED) หลังเปลี่ยนการเชื่อมโยง และการลบ ProgID ตอนถอนติดตั้งโดยเหลือค่าเริ่มต้นของคีย์นามสกุล ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, HKEY_CLASSES_ROOT Key. เกี่ยวกับ HKEY_CLASSES_ROOT เป็นมุมมองรวมของ HKLM\Software\Classes และ HKCU\Software\Classes คำนิยามฝั่งผู้ใช้มาก่อนฝั่งเครื่อง และกฎการส่งตอนเขียน ↩ ↩2
-
Microsoft Learn, Windows app defaults platform. เกี่ยวกับการเปลี่ยนแอปเริ่มต้นที่ออกแบบให้ทำผ่าน UI การตั้งค่าระบบเท่านั้น ข้อมูลการตั้งค่าผู้ใช้ถูกทำให้คลุมเครือและป้องกันการเขียนด้วยไดรเวอร์กรอง (UCPD.sys) การเปลี่ยนบนฐานรีจิสทรีไม่รองรับ และการใช้นโยบายกลุ่ม / นโยบาย MDM ในสภาพแวดล้อมที่จัดการ ↩ ↩2
-
Microsoft Learn, Working with Shell Extensions. เกี่ยวกับชนิดของตัวจัดการส่วนขยายเชลล์ ส่วนขยายเป็น DLL COM ในโพรเซสที่โหลดเข้า Explorer (และโพรเซสที่โฮสต์เชลล์) ดังนั้นแครชหรือค้างแพร่ไปทั้ง Explorer การลงทะเบียนด้วย ThreadingModel=Apartment และการพิจารณาทางเลือกที่ง่ายกว่าก่อนส่วนขยายเชลล์ ↩ ↩2 ↩3
-
Microsoft Learn, Guidance for Implementing In-Process Extensions. เกี่ยวกับ Microsoft ที่ไม่แนะนำและไม่รองรับการอิมพลีเมนต์ส่วนขยายเชลล์ในโพรเซสด้วยโค้ดจัดการ เหตุผลรวมถึงการชนกันของเวอร์ชัน CLR การเข้าซ้ำ และอายุวัตถุที่ไม่กำหนด และโค้ดจัดการที่ใช้ได้สำหรับส่วนขยายนอกโพรเซส (ตัวจัดการพรีวิว หรือการเปิดจาก shell\verb\command) ↩ ↩2 ↩3
-
Windows Developer Blog, Extending the Context Menu and Share Dialog in Windows 11. เกี่ยวกับการออกแบบเมนูบริบทใหม่ของ Windows 11 การขยายผ่าน IExplorerCommand บวกอัตลักษณ์แอป การวาง «เปิด» และ «เปิดด้วย» ด้านบน การรวบหลายคำสั่งเข้าฟลายเอาต์ชื่อแอป และส่วนขยาย IContextMenu คลาสสิกที่ถูกโหลดเป็นเมนู Windows 10 ใต้ «แสดงตัวเลือกเพิ่มเติม» (Shift+F10) ↩ ↩2 ↩3
-
Microsoft Learn, Add a File Explorer context menu command to a packaged desktop app. เกี่ยวกับการลงทะเบียนบนเมนูบริบทใหม่ของ Windows 11 ที่ทำด้วยการอิมพลีเมนต์ IExplorerCommand บวก windows.comServer บวกประกาศแมนิเฟสต์ desktop4:FileExplorerContextMenus ItemType ที่ระบุ * Directory หรือ Directory\Background ได้ การจับสถาปัตยกรรม DLL การรักษาเมธอดสร้างเมนูให้เร็ว การครอบคลุมแอปที่ไม่ได้แพ็กด้วย sparse package การรีสตาร์ท Explorer ที่บางครั้งต้องใช้ให้การลงทะเบียนมีผล และการเชื่อมโยงไฟล์ที่ไม่ใช่ส่วนขยายเมนูเอนกประสงค์ ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, Grant package identity by packaging with external location. เกี่ยวกับการได้อัตลักษณ์แพ็กเกจด้วยการลงทะเบียนแพ็กเกจตำแหน่งภายนอก (sparse package) โดยไม่เปลี่ยนตัวติดตั้งที่มีอยู่ การมีตั้งแต่ Windows 10 เวอร์ชัน 2004 และฟีเจอร์ Windows ที่ต้องใช้อัตลักษณ์ (การลงทะเบียนเมนูบริบท การแจ้งเตือน และคล้ายกัน) ที่ใช้ได้ ↩ ↩2 ↩3
-
Microsoft Learn, Choosing a Static or Dynamic Shortcut Menu Method. เกี่ยวกับการเลือกวิธี verb แบบคงที่ที่ง่ายที่สุดที่ตอบความต้องการ IContextMenu ที่ทรงพลังที่สุดแต่ก็ซับซ้อนที่สุดและถูกจัดไปฝั่งไม่แนะนำ และ IExplorerCommand/IExplorerCommandState เป็นวิธีที่แนะนำ ↩ ↩2
-
Microsoft Learn, SHChangeNotify function. เกี่ยวกับวิธียกเหตุการณ์ SHCNE_ASSOCCHANGED ที่แจ้งระบบเรื่องการเปลี่ยนการเชื่อมโยงไฟล์ และการใช้ให้เชลล์รู้การเปลี่ยนแปลง ↩ ↩2
-
Microsoft Learn, Application Registration. เกี่ยวกับการลงทะเบียนไฟล์ปฏิบัติการผ่านคีย์ย่อย App Paths ที่แนะนำ บทบาทของคีย์ย่อย Applications การลงทะเบียน verb ผ่าน SystemFileAssociations และลำดับความสำคัญของ ProgID และข้อมูลที่เกี่ยวข้องเมื่อแอปเริ่มต้นเปลี่ยน ↩
-
Microsoft Learn, Verbs and File Associations. เกี่ยวกับ verb เป็นการกระทำที่ ShellExecuteEx ใช้ด้วย องค์ประกอบของสตริงคำสั่งที่อาจมีช่องว่างต้องห่อด้วยเครื่องหมายคำพูด และ “%1” ต้องเขียนใส่คำพูดเสมอ และการลงทะเบียนขั้นตอนเริ่มต้นใต้ HKCR\Applications ↩
-
Microsoft Learn, IExplorerCommand interface. เกี่ยวกับองค์ประกอบเมธอด GetTitle GetIcon GetState Invoke EnumSubCommands และคล้ายกัน เมธอดถูกเรียกบนเธรด UI จึงห้ามสื่อสารกับทรัพยากรเครือข่าย และมีตั้งแต่ Windows Vista ↩
-
Microsoft Learn, Windows Sandbox. เกี่ยวกับการเปิดสภาพแวดล้อม Windows แยกใช้แล้วทิ้งในไม่กี่วินาที การเปลี่ยนแปลงทั้งหมดถูกทิ้งเมื่อปิด ความเหมาะสมสำหรับการทดสอบซอฟต์แวร์และการตรวจตัวติดตั้ง และการมีบน Pro/Enterprise/Education ↩
บทความที่เกี่ยวข้อง
บทความล่าสุดที่มีแท็กเดียวกัน เพื่อเจาะลึกหัวข้อใกล้เคียง
คลิปบอร์ดกับลากและวางทำงานอย่างไร — จัดการการถ่ายโอนข้อมูล OLE ให้ถูกต้องในแอปธุรกิจ
วางตาราง Excel แล้วการจัดรูปแบบพัง ปิดแอปต้นทางแล้ววางไม่ได้อีก — ทั้งคู่มาจากคลิปบอร์ดที่วางเนื้อหาเดียวกันในหลายรูปแบบพร้อมกัน บทความนี...
Win32 Thread Pool API — คอนเคอร์เรนซีโดยไม่สร้างเธรดเอง ด้วย CreateThreadpoolWork
โค้ดเนทีฟของคุณกระจายการเรียก CreateThread ไปทั่วหรือไม่ บทความนี้อธิบาย Win32 thread pool API ที่ออกแบบใหม่ใน Vista — ออบเจ็กต์สี่ชนิด w...
Named Pipe ในทางปฏิบัติ — IPC มาตรฐานของ Windows ตั้งแต่การออกแบบถึงความปลอดภัย
คู่มือเชิงปฏิบัติเรื่อง named pipe ซึ่งเป็นการสื่อสารระหว่างโปรเซสมาตรฐานของ Windows บทความนี้จัดระเบียบจากแหล่งปฐมภูมิ ทั้งการเลือกระหว่...
แอปที่พังเมื่อรีซูมจากสลีป — อีเวนต์พลังงานของ Windows ทำงานอย่างไร และสร้างแอปธุรกิจที่รอดได้อย่างไร
เปิดแล็ปท็อปแล้วการเชื่อมต่อของแอปธุรกิจตาย — สาเหตุคือการออกแบบที่ไม่เคยคิดถึงสลีป บทความนี้ครอบคลุมลำดับการแจ้ง WM_POWERBROADCAST พฤติก...
DllMain และ Loader Lock — เหตุผลจริงที่ถูกบอกให้ "อย่าทำอะไรในการเริ่มต้น DLL"
ทำไมคุณต้องไม่เรียก LoadLibrary หรือซิงโครไนซ์กับเธรดอื่นจาก DllMain บทความนี้อธิบายจากแหล่งปฐมภูมิว่า loader lock ทำให้การแจ้ง DLL ทุกคร...
หัวข้อที่เกี่ยวข้อง
หน้าเหล่านี้วางหัวข้อไว้ในบริบทที่กว้างขึ้นของบริการและการตัดสินใจ
หัวข้อเทคนิคของ Windows
ประตูสู่การพัฒนา Windows การตรวจสอบบั๊ก และการใช้ประโยชน์จากสินทรัพย์เดิม
การย้าย ActiveX
การตัดสินใจว่าจะเก็บ ห่อหุ้ม หรือแทนที่คอมโพเนนต์ COM / ActiveX / OCX
บริการที่เกี่ยวข้องกับหัวข้อนี้
บทความนี้เกี่ยวข้องโดยตรงกับบริการต่อไปนี้
พัฒนาแอป Windows
แอปธุรกิจ การเชื่อมต่ออุปกรณ์ และเครื่องมือสื่อสาร ตั้งแต่ความต้องการจนถึงการพัฒนา
บำรุงรักษาและปรับปรุงซอฟต์แวร์ Windows ให้ทันสมัย
ต่อยอด บำรุงรักษา และปรับปรุงซอฟต์แวร์ Windows เดิมให้ทันสมัยทีละขั้น
คำถามที่พบบ่อย
คำถามที่มักพบในการปรึกษาเกี่ยวกับหัวข้อของบทความนี้
- ทำไมรายการเมนูบริบทของแอปเราบน Windows 11 จึงปรากฏเฉพาะใต้ «แสดงตัวเลือกเพิ่มเติม»?
- เพราะบน Windows 11 เมนูบริบทของ File Explorer แยกเป็นสองชั้น เก่าและใหม่ คำสั่งเดียวที่ปรากฏบนเมนูใหม่ได้คือคำสั่งที่อิมพลีเมนต์อินเทอร์เฟซ IExplorerCommand และลงทะเบียนในแมนิเฟสต์แพ็กเกจ MSIX (= มีอัตลักษณ์แพ็กเกจ) ส่วนขยายเชลล์คลาสสิกแบบ IContextMenu ถูกย้ายไปเมนูเก่าที่เปิดด้วย «แสดงตัวเลือกเพิ่มเติม» (Shift+F10) ส่วนขยายเองไม่ได้พัง จึงยังทำงานอยู่ตอนนี้ แต่ถ้าต้องการให้ขึ้นเมนูใหม่ต้องย้ายไป IExplorerCommand และแพ็กเป็น MSIX หรือให้อัตลักษณ์ด้วย sparse package
- ตัวติดตั้งตั้งแอปเราเป็นแอปเริ่มต้นของไฟล์ (แอปที่เปิดเมื่อดับเบิลคลิก) ได้ไหม?
- ไม่ได้ การเลือกแอปเริ่มต้นออกแบบให้ผู้ใช้เป็นคนทำ และ Windows ไม่รองรับการเปลี่ยนแอปเริ่มต้นจากที่อื่นนอก UI การตั้งค่าระบบ ข้อมูล UserChoice ที่เก็บตัวเลือกของแต่ละผู้ใช้ถูกทำให้คลุมเครือ และไดรเวอร์กรอง (UCPD.sys) ยังป้องกันการเขียนจากแอป สิ่งที่ตัวติดตั้งทำได้คือลงทะเบียน ProgID และ verb เพิ่มตัวเองใน OpenWithProgIds ให้ปรากฏเป็นตัวเลือกใน «เปิดด้วย» และพาผู้ใช้ไปหน้าการตั้งค่าแอปเริ่มต้น การอิมพลีเมนต์ที่ถูกคือไม่แย่งค่าเริ่มต้น แต่เตรียมพร้อมให้ถูกเลือก
- เขียนส่วนขยายเชลล์ด้วยโค้ดจัดการอย่าง C# ได้ไหม?
- Microsoft ระบุชัดว่าการเขียนส่วนขยายเชลล์ในโพรเซส (ตัวจัดการเมนูบริบท ตัวจัดการไอคอน และคล้ายกัน) ด้วยโค้ดจัดการไม่แนะนำและไม่รองรับ ส่วนขยายถูกโหลดเข้า Explorer และเข้าโพรเซสของแอปใดก็ได้ที่เปิดกล่องโต้ตอบไฟล์ร่วม จึงทำให้การชนกันของเวอร์ชัน CLR การเข้าซ้ำ และอายุวัตถุที่ไม่กำหนดทำให้แอปโฮสต์ไม่เสถียร กฎคืออิมพลีเมนต์ด้วย C++ เนทีฟ EXE ธรรมดาที่เปิดจาก command ของ verb หรือส่วนขยายนอกโพรเซสอย่างตัวจัดการพรีวิวที่รันในโพรเซสแยก ใช้โค้ดจัดการได้
- sparse package (MSIX ที่มีตำแหน่งภายนอก) คืออะไร?
- แพ็กเกจ MSIX เล็กที่ไม่มีไฟล์แอป มีแค่แมนิเฟสต์ (ข้อมูลอัตลักษณ์) สำหรับแอปที่ติดตั้งตามปกติด้วยตัวติดตั้งที่มีอยู่ (MSI, Inno Setup และคล้ายกัน) ให้ลงทะเบียนด้วย Add-AppxPackage -ExternalLocation ชี้ไปโฟลเดอร์ติดตั้ง แล้วแอปจะได้อัตลักษณ์แพ็กเกจและใช้ฟีเจอร์ที่ต้องใช้อัตลักษณ์ได้ เช่น การลงทะเบียนเมนูบริบทใหม่ของ Windows 11 และการแจ้งเตือนโทสต์ มีตั้งแต่ Windows 10 เวอร์ชัน 2004 และแพ็กเกจต้องมีลายเซ็นโค้ดที่เครื่องเป้าหมายเชื่อถือ นี่คือตัวเลือกที่เป็นจริงเมื่อต้องการรองรับเมนูใหม่โดยไม่ย้ายวิธีแจกจ่ายทั้งหมดไป MSIX
- ควรทำอย่างไรเมื่อรายการเมนูบริบทปรากฏสองครั้ง หรือไม่ยอมหาย?
- แยกสาเหตุก่อนโดยตรวจว่าปรากฏบนเมนูไหน เมนูใหม่หรือเมนูเก่า (แสดงตัวเลือกเพิ่มเติม) การแสดงคู่แบบทั่วไปคือการลงทะเบียนรีจิสทรีคลาสสิกกับการลงทะเบียนแมนิเฟสต์ MSIX อยู่ด้วยกัน หรือการลงทะเบียน ProgID หรือ CLSID ของส่วนขยายเหลือตอนถอนติดตั้ง หลังเปลี่ยนการเชื่อมโยง ให้สงสัย SHChangeNotify(SHCNE_ASSOCCHANGED) ที่ลืมด้วย หลังลงทะเบียนแพ็กเกจทันที ให้สงสัยการรีสตาร์ท Explorer ที่ลืม ถ้ายังไม่หาย ให้ปิดส่วนขยายที่ไม่ใช่ Microsoft ชั่วคราวใน ShellExView แล้วค้นหา DLL ผู้ก่อด้วยการค้นหาแบบทวิภาค