From d52663c85f22d82e3978c5e68d0fcb675dac8df7 Mon Sep 17 00:00:00 2001 From: Xander Fiss <69924000+xandfis@users.noreply.github.com> Date: Wed, 15 Jul 2026 08:46:35 -0700 Subject: [PATCH] Update File Explorer context menu guidance --- .../file-explorer/folder-context-menu.png | Bin 2961 -> 0 bytes .../modernize/images/file-explorer/popup.png | Bin 8307 -> 0 bytes .../images/file-explorer/resize-file.png | Bin 10869 -> 0 bytes ...tegrate-packaged-app-with-file-explorer.md | 306 ++++++++---------- 4 files changed, 127 insertions(+), 179 deletions(-) delete mode 100644 hub/apps/desktop/modernize/images/file-explorer/folder-context-menu.png delete mode 100644 hub/apps/desktop/modernize/images/file-explorer/popup.png delete mode 100644 hub/apps/desktop/modernize/images/file-explorer/resize-file.png diff --git a/hub/apps/desktop/modernize/images/file-explorer/folder-context-menu.png b/hub/apps/desktop/modernize/images/file-explorer/folder-context-menu.png deleted file mode 100644 index f3bb886f4c70c72cb8d879a8f6508465e5b6e8e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2961 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 LCI%(?4q^WRXoJj= diff --git a/hub/apps/desktop/modernize/images/file-explorer/popup.png b/hub/apps/desktop/modernize/images/file-explorer/popup.png deleted file mode 100644 index cbe375abc74b8ed7196fb8bb6cc323d08514cc99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8307 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 diff --git a/hub/apps/desktop/modernize/images/file-explorer/resize-file.png b/hub/apps/desktop/modernize/images/file-explorer/resize-file.png deleted file mode 100644 index e184ab0e6d5696ff21ad0702365261e0991c3a17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10869 zcma)ibx>Sivj0HPKn4vE7~Ba?fEg^of?IG25;PEOAh^4`GgyEO5ZpD$;4&mwa2?zU zfe`G+ci-EsxAorcZ>vt-d#dep_vugf{o_WetI9ojO!*i906bBUmw5vKpuqqD^h2CS z50vM)3}*nqBY?WHmh92d(T$YU;^JaSNy!6CTwL71z#uU(F+V?lYikREKspS``k z&CSibyStApnny-POiWAwZq0LZbAX_40Pl7{==Y;C6B`?w)8Dtu*>fxfYXFZ{K={Pi z*w|_j2Ec!uzUY*x;wNL-8U3eI(u5UO_%0h88*|1zOWk?T{LaF{LRD2&eSJND`M!^j zPew)tZ{kYV@-Kj6Eg*JkZEdZ+y}hZiVPj*1nVC5tAVAQhY-MvF5I@bBKDB!Mr}D?) z>dqm+wQ+TIl`iiD5IND+)g@9mZ{K~$kT@byeZb4h%T#{GP<$#|yWcZ|0z^-;7NWSy z_CrI%=<-jGkB=uOC#$D_26dwndQWIVT2UwzT~z1Z;nCpA$<@tWR8*8;*qDNX0(Im} z;m9^&z&r!&0O;Rg(|`ilR|6xyD;CewMK1PCuRGQ4){U)le%U2!|mDqQU9ZI+^NGqsPmh>xl(BB)wiwRoQadn6^AU9hpD|A z7M1I%Uw2}*|zapzUunuV5ej5j3x8_Xr%+yrka{t_OkHyamYN6 zU(dItZGg|1&F4|fu->qSN%wN3d&?^Q$IjAkyPpOZ`WE-i%EmJ~z7-Z0io=jeF@LZ)?m#IA>N*|bEax9Ndd!O_1R@mrg(im{(mND~GAHP3^g@Hy zGUzij5|mCnsy)#144qK~zIUkjWH3zciNgwMP}6AGyJy{T#Bv*($IE!T6W7{VMH+rE zOEw>GZJo@U(3Vl3*oKk(qBjxzpRX0L7k-pL5EpkI*nmMuo_@CHEL{4DDUl3rtZDQx zZ5(CvF2_q_xy3}}l z+E`HUjSwqnDN}|wzl+hCP$E%{Z`3-_xjC5dTQ@Ei)^2bxXB-&qER=BuwB3ypM=TA4 zY~fj-JE$-_fN~-Tt$|mFwR9fYS*92Kwe1n1*ep{V>!XbRU>G{F5V~BE3wL4^_~#%i zu(MXO%op<-beBJC`zq{#BpgTbn3MDG!8b!zA3}J<%C65;ixx-qS&uVSIg{gQv<4=0 z0}$&e*E(9V2SAp@>YKS2MQbbe$?q8W2lTCX8Yr9PT`b zjvH4vI3T&AXKshAy#EhW2$lAON~Zu%P0kPt#t>;N?DYFkV_<&SrqQOk!C>spt%1>r zZ5<5m=*P0()*IZMKld%fR)a@@8Nu@+i?q{YPG5fLx3yHzBXLx-s=7uhKsODJN2Pp) z%zp9PNt4fK#trt#){PJf*6aeTOEl~?&W3P3pBtqgAA^DY)2Dd?Zze`H0?p}>%HZI1 znP;RlE0p1V+CkttQb4#(WadRfO|z;@gv&_K+KxpSq4iAAYL^^$FL~bD7kq8!^)*5@ zcdV)z_SE=j6J8-pyQk|IUEhhzGQ{8U5!vCsZLkjgT_?Vi1wkKpmR;ZQ*x6^jmo7L; z*3VS-N#DnziNNt^=Uva@ROXMP!4l4^MQ@}Vj&WBRqY?Dja--2-Bnd!Zk;61}=bMeL za}C%MR+OHL7L}aQ6im_)R(fn607i3)uvPyN|23_e+e4sn+2E@( z@51bLK^M~x!%4vpKHVh~VmmVd|F)1M1H+9y0}2dCFw98&psxEqo6`9NlcshnknwE< zqag6>`lKJE(v==b2zQ(wPT$PJ;IU9MG^hexpyxCvtXU_ueDiPNMCf277_ zvXgUZ2w8Lk&sJyK7v2Baz9yjczehZ7D%)xQ9{!|_<>)h+;gaF;eLGm>c57pDo1DD! z_H6$Tw8T<4tA1=R(Fj_(k+OHtJv4K-yL&fNE%$O#-Ow(t#*68AdJ!x&aC2~c4VSx= z6B!DnFfV`i`X?8I*m4G&r^hkkPF$DODkAj(P0RR;y@5#s*W)iYZ+}lNxI3C0orV)6 zFE4q~PUS!QYS2j;agy!j401P-@J$LdE`BpEq*+RSbGlI#@%;KQ>eqq;Me}e+DE8O+ z>xI2K|Al)qVP_g@%lr=O(sb`kVV*dF_VC^u;>oK40C-9r3rw>@=G?#T)07$*r&)@K znOyrd`6Vf{aeh%*3E#Q>E7y7q;%Yte#3OzF#%#RbQ`tzvoE|*&3dRmb*{z*!Eoj;P zoKCQgZ`LHq3a&fj@!mdTlye?9&1+q#XoeeD6}8AHGK|!w#L7UOLiEfz6VA{_Bzn71 z?=0=zD(DsxJnFs>nps2;8@g5V2`PeC<=cY>>BD6haB)qIA($xi_ON1iuVx3Y{mDtOu#g2GU9kGa7 z7_WLgV-C(!6^p0&6OUWI#3eAYlAI|W5ZrLvyHo~6lK;dI8N#!D?vu)?+Z>^ojnEL2 z(!y-5SX-NFZ~Ca0B3*VvrxaBZ5Epp-991bI6$jHq1)twE-_vv$=!XfGaDJGLZmt=` zTA(cDcvW8?94>xh&Xa6@I2O(fiTaVqA)Cdq^*1$=M_Z+Lf`9yvf0`!Vu)~FR;1X5N z>y!9$q4XZ%?)`pmkemav3u5K-XFs&UqB>-_I==n=MA@@bq$Vwa&dPqLlA|gOHWd$2 z`P(?J`kzp0m?B3^JXfqDRvr0B0fI%4ZR+^OVp{|96sk*2FNZ|HC-z}?f;X28In&7U zpeL#1o9MK2Bc2i8Xw{|eKjU}Yz{$VK;EZ{ zD9#((yUy#&=-gd(PVA-8@ng9=CtygVCY$wDj}JrmK>)9F6e>SEgolVu8HNtgi^Lp@93U11?R--^Kp{T*hs!!S3Z)gneDADY29uvO2Jk*3s7F}n*Pe{2{fOy_A$mYrUCX&h(SI#wm z0D(_DpjUMwM1__n`wAp+@Vkr+I*44RWm**VKbVzHN89wt3DD3wsHhv98Dytr{q{Rq zh_4Z(CnbtI{& zj5OB}?9rSvYpHxcF7|ffp!akCV9QPKlDw_;7G%COE^W5O8rH9uTygFI)NWs{_QleajC!awp2?C-urRWefBQy_BVdvX} zz$)T=KCf>rk*|WHJ5)U$n`o6l>ZFnH55HTJ0X(m)-d*-N66V)bx|ACwv%^(AqSVss zW8zDHN(fAov$dUG?e%HJGs~&j^}6Fgp(;iPAA2;1j?STKGPGR-_@x9D5AkVpU;~YA z6oa_Jk1N{>`U2`b25V^uf?chwy|h)IdP$94&d#VgS86@-_Q^^MnKMxG1E zo`v#LON<!kE%jKqE?qh|M$-QOx{Y9Cc_0r8D7NbU;z-|(TBGNp_@-xJ_y0v*%Idnkw3 zd==svB(4*H#ih1J+nj!4dtuPFG>};FYGhv-WKK)Ng0UcfD}$c1Zi|p9e4nD~2kk_n z$mA+elAbM`+l@3}ZtFx#<2;Pb#wBJS)RDG8nugHLZGW)H{R@+>KpbKW@-_g<<&@BM znyQNTX(JYqKr`pHpI;+E%eKuAVwFXu^BvDspszynH}MNq^`(X%i)iRYHBCO8$?dbO zE6mX`XjJvG=y|gG&`*$iB5MJV(HuvJ`Jw0i+xq*fOKQqF_=v=a@jE^9oZaF-l=w_G zUp$RQBQ;VFe!)RCB78=@BpJ15>tj_|%2!fNtV6>=#0aH);|X>U>}jB!vZ5_~pF zs0z_`{$wm1W}+G$MUk8p$~WWMsOyFKdJ9 zsa*JsUYvO z&?0YJ-dr;r_a*LA$j6Km3iQND;6rMJWa7U4_5Rl>LQZ9##HtklN&GD6rry;OEQEN~ z-((orq`F~y;2cgIePdJqV`t|}UC5sXHD!H8Ixf_;A=R!&GoW2PsUF9@Bu+4Ih%iw^|%St|`}@w9%*ddWVoo z!=)rf&@Z5uhDT6+D!wrGgn(yknTA3L5an<%eOT4@0aPn9jaPeC42~OJXVA^jWxeHxiMaJADJgJ zvLB|8t>Z6tW=46&wGAe#hbQu3WE20_VE&&s(4SOSfkDU;Ht3we0Ey4>EA6y5yf2=N zkA#(~(`}y`Ui+p5zjW|5LMaD3Lyts!)oAGN!S)dDO}SR~U_p(Ot4)v;OFe)fP2)}wwUnTFrbI^k>kM^% zvCCNYI#U=w<)lC#2v^TYS{(YEpgHIz2a8Q~sU|O$TPA8^ABaZiCnw)ZLib5}SPydA z9H`_-!?4>%8fLO$gS3byC~84n@*MQ7!~_tBaV6mU(-jF7lEtQB*+82-)O9&-e5AcVL;XGfj@TWWHtV z)cmMM7DaKTmO+m`Qc!&8+B3!%r*a1rE6<+x{PR&o0;#4tIi*uIMaJuVJ$BV-PPE&7^+tL`gQZ;IhY(Z)KOC%mY3%u_V{x}FoaD_@SgZ(`3J7eJ^}Hpa zR<|@$hqmh+$+)VKcS)8K$0@)?xK`=-9Wb}CMDefXZ9@e7=hE_7=pgS`#nhm9G)cE7^!rZ{BuQDT zC?aM}mZu0aPh##A-&aRN{!c|)IR;=2wqQG<@fEVrKk_8@cbHBef+NS|3++2zAoza0 zKLcPVq78%=7O;pXOyPtxFf(Cehif|}jF2OPZNUV9fldw$9yHLfd_$CXv@Um37aw)h z`?p+9)u(0Y)McBqxsQi7-hn^ZK;UPL{okMV)Xbl(n)afBMngW3KLP9W1ei~x9nI?0 zOpF+;&hm&Fpv6TWkPAk7ber{qL>Sv~%#lnWj@Kp?_T27ZLpyp{1J`iYB3bG;7{~B? zKaw7eKU=O1Riox@U86OH&l>r9L3*=;S{F3%9`I{@SpeQBqQ$7=z=RYEF#Uzdxr(7NhY zogRI|)M5?l5*lRB%pp4RumUr1GLUk0)-n_e$i+?yep^C>>}hHm;}9~4dF~{lE&!{+ zP?aMF$1rEZDU?=u)4I$!0}*=Y;WZdQb4(}hND(bO5D_gdsQ)h+r~f7LKN&g#Fn8J~ zemb8jr*O9CM!H|^xWZY$Ran8g*Zx4P&N3ZpKe&d9g6|IC7a!??y#d}>Sk`L$;)3A) zC3ai)_T^D4+2(s+m05A&@KU`Xm)Y#l77DZX2U48w=H}t5$;?EWf?b~Xzh3(LThU<0 zr|?({yt+gGWf)&5CgBNYu@L~x3sH0prST_&U0nCpe4KVC7B4P7dkNuUUtdq`pF0a) zSf?>qO?JIc#5JIOv>@?iW#=qazfI1F8Wy|e*ffe*-#S}1&G#`gTvgQ27C^JUa9GuS zw7q=nFLu=c^O@E^r%ITq3Og{mGC!vS0!yJ}tN(A3^G6xve84r}G3huy;SERvLRiH%;qiFQ~3o z@RRMN7VQQ8Iw7}hGm>$4{6eKjtehvyg(RL!9XbCcJ6D+2=zy7g(9P^rk zR9iP|qR$^MhltY-9egr@W1xx)7f#<{U!(H@SW_w~zAnoqY`@wYJL!ZgcIZm8cH|A5 zFRdBy;H_GScb#t4bZ3~~^QOj`brWYz)OSgG&?1$AFYKL0wf^*-^oe^3QQq=r;x-9p z$rtaLe(c78w=Whllj6L!TQ7oDy#K+dlQogwr`jX=n-CcDA z1wgHk)k_2V?+avdoCsLVSp9Y^89i*fqZte6bUAo1@|G>v6_ z^{LWlEnf&s{!yG3uc4=<`AdJRt|+KrGBaz!e=k8@ejSgA+${sDX4yE__%C1D z;}jW+KAP_O9(iH`QBc&VOB^$_u#pbH0k-RPokd{DC%#mAZzc@u)^)(&jNIERMrU=DMVt5xV_A0Ou z2cKN_;w`iA4*wFRxYWg5Q}_wQI}qp(%mah;KZQx}v-{8*XtJ=9gygYLklx>Sk7e`& zqmxw}_Zq9UTd(R;lA$yNogF$=mMeJ$QA_zGl8fh99rXulOWt^~R4!6+&pdy5H-u#4 zi1Nv*IMTuSnXJ(q2e0^HLn8t&uT=~~e6{K129(KEM$W#53m%H zGm!AtTbCd3xLKX)!mp%ICocTC|FL-hZCo3Swb-TcH_Ax%>^u4o{y6;8}# z4UBvU@b=Mn_Hn6f*r$#JbIv!L`LE!rDvF&H{9JD~Uu^YqGH(GDkoZX`qn3+I_16B8q?pcZhsZ;FvJf)kAx<#-^!rI8fjUs@4Y=eTQs0y zL(RJ7crLRjsOqUu50=KrtIm-Xi%!0H+h2wnWmZ5|0xM^F7zc=s-I@v<+&1sv@$pFl z*)Je##}FOwSkQ@HoV|~irz_uv$tA(x7-TB3q8KXEWF4$2%Cc=8S3g&#i<)p zc+#?}5JY8KgTsWCpd&{N8r9ffQSJ2vBHPy8#bLdDxx^g~`}DT%W0z1ev8bp%s@(9r zMg?NU8VvGZ&3Bl;wSGF8-0Wl#x$!iOLg#YDeAndNQrSyP3!lonA-aKk8);SjR6lz{ z@;FZCrdGGA+%V>_%<*+xCFOXFE0N;D1>dCI!QD&aNVYv=Ie{bJM})2W6loMfus^r_ zwwDdX{oocP1d$j_y;2`a&0uB(uI+^bc|X{Ev>9>o5?&I8Eqe^QHEg+qP_svz3-5>@ z&N=NSOB*n2$T6;t*-TkTNIt!UsSe7=b(HL{T40dZfH=S7Tj)7l3=VmQcjbJOhHS1? z#cS-pu!to1f+!BuOyO$sLJoO5R+R6(rC7gXkHeEo^LW|rLHQ7$SSBXtY2|Kw5hk_q zapa6*fsM~AY?b@( z7y9p^^ABuIpi;CO&Jg6D!+vL@Pyyo2^Q&C@K*5&hd1nVRVs(o#n9v?;FY(>(lzoQm@gFZ$v{?&-$sh{nZsO8w}HVi=s>A6YTNJ z@5%fnzuC`4D7H5rWNRN}Z-qwDA#0d&Mh%uf&AwTXVWe)ie$da9frQI>D~odnN9_xL zSTS7$?yEVxex}pp_W}riNj3O*t zb>iS2Bog~y0RM-Ai_6q`HbujT^=nCzQB<4_5oCzU!8Nl|Rc@5onlocuNTlhSd7FbD zYcF-HSgh$f2iCsiQhfyix4gg zAH=Y4Y|k*l+Ymh=G2Zj}6)}d|3lUZekX7B}{lVR+3>5+|#!(1CnWTVStE0&yv~TDKPg}&ZweJM#_j7a)4uc{6z!#AFCU+r)I7&$hZ9osBA8>U^X24^uAzdO&O~4wa zq;F9`I$cp9OE%%d=f(5seHnmj@1sUZ@Tk}nyT)*54s$1jIy=px@0uh<;3`AY4Y<8x z@}@`X>-K??7ElZgg%b=iOJKx@hDgTo4iaHOdg$aR{vA{gk(EY$r`H(8RWD-54T%Kq7VB*P_xzBZ4D!teN$67JS- z#1O6mQFo#=&7^BA$R$zAPtT+52LAQu&X>yLJKq1IaU;`(oAw;R-R3j;MVFai zs7BtM_E_i_ljSed7Tm3uACHAOH8g$+y>(eG&88oLnbV@ub1uI$=~fX^rD&RL#!L%d zzS?f$5@p@LlwGozt>KIg^i6ncwUlzaB#k^x5&5v6cs!$bvA}kjV`9D_dq=Qe?=r^F zkXi`+pzH#P)u5fXI?1d%xDy`c4k9E7RFvsn)Ok77|?uh4UKvoWWvSut7 zltwNSmdV*(Pnoa(Zx35Z#}p z3_p8LkdTG)*dQK}+`UDAyqOW-UsCs)<^dvvme5u|UOb0kDotUR&j}0=GUmtig7t;m*uxQ4}lRz~7kDfqK6LmzHkQ%A)X>~UJkj ziVk_Z;^ms6n_NcT2M_C)`f}HJ_=;)TDhQe-;5o!z#qu$hfci7HsHr_eX(MsUr2CIy z_QnovF@|ZGbFM@!!^n{u?+iJ)x0?@!FQE}d^NN8m>1-?Mo=v?UT#(Vsij=|KKi%Y+ z?oic%c&G>2tYReW{ho(w%E;-u=J6)sg9{80O&dcph@~Nq#4lCgUpD`vBy0u4Uapd# zMS{Me{f8C82l*&Kq(I~>k`%G(5CnZg6J&1|GOfpGO@avpA{tZBF%BGxWsv>fmpw#C zhf29_yV6ts^uO`aJK4T8kGeTG65cJM9m^E@!kx>#;O64Ptcb$5rmBdw7t9JSb+?&l zM4qkJOSq&>_t~SBEdxyt*x?Sg(QcPa7%V;Ys1}E22(TcErita<5gc*!MhHb3?}YW(N(FWe3Uwp;?+J?1EnyS81wiwZ|& zbz==N!G-cx55c4yuh-MFW32xk16J;kYTAD>4YoM3Ev{(wW+l~jpP}VG{O1W! MkX4nblrjzae{6B@!T [!IMPORTANT] +> File Explorer loads shell extension code as part of the shell experience. Keep `GetTitle`, `GetIcon`, `GetState`, and other menu-construction methods fast. Do not perform expensive work on the File Explorer UI path. Run longer operations after `Invoke` is called. -### Implement the context menu entry +## Implement IExplorerCommand -To support this scenario, add an [Extension](/uwp/schemas/appxpackage/uapmanifestschema/element-uap-extension) element with the category `windows.fileTypeAssociation` to your package manifest. This element must be added as a child of the [Extensions](/uwp/schemas/appxpackage/uapmanifestschema/element-1-extensions) element under the [Application](/uwp/schemas/appxpackage/uapmanifestschema/element-application) element. +Implement `IExplorerCommand` in a native COM DLL. C++ is the usual choice because the command runs in the Shell integration path. The following abbreviated implementation provides a command title, enables the command, and receives the selected items when the user invokes it. -The following example demonstrates a registration for an app that enables context menus for files with the `.foo` extension. This example specifies the `.foo` extension because this is a fake extension that is typically not registered to other apps on any given computer. If you need to manage a file type which might already be taken (like .txt or .jpg), remember that you won't be able to see the option until your app is set as default for that file type. This example is an excerpt from the [Package.appxmanifest](https://github.com/microsoft/Windows-AppConsult-Samples-DesktopBridge/blob/main/Docs-ContextMenuSample/ContextMenuSample.Package/Package.appxmanifest) file in the related sample on GitHub. - -```xml - - - - - .foo - - - Resize file - - - - -``` - -This example assumes that the following namespaces and aliases are declared in the root `` element in the manifest. - -```xml - - ... - -``` - -The [FileTypeAssociation](/uwp/schemas/appxpackage/uapmanifestschema/element-uap-filetypeassociation) element associates your app with the file type(s) you want to support. For more details, see [Associate your packaged application with a set of file types](desktop-to-uwp-extensions.md#associate-your-packaged-application-with-a-set-of-file-types). Here are the most important items related to this element. - -| Attribute or element | Description | -|----------------------|-------------| -| `Name` attribute | Matches the name of the extension you want to register minus the dot (in the previous example, `foo`). | -| `Parameters` attribute | Contains the parameters you want to pass to your application when the user double-clicks a file with such extension. Typically, at least, you pass `%1`, which is a special parameter that contains the path of the selected file. This way, when you double click on a file, the application knows its full path and can load it. | -| [SupportedFileTypes](/uwp/schemas/appxpackage/uapmanifestschema/element-uap-supportedfiletypes) element | Specifies the name(s) of the extension you want to register, including the dot (in this example, `.foo`). You can specify multiple `` entries you want to support more file types. | - -To define the context menu integration, you must also add the [SupportedVerbs](/uwp/schemas/appxpackage/uapmanifestschema/element-uap2-supportedverbs) child element. This element contains one or more [Verb](/uwp/schemas/appxpackage/uapmanifestschema/element-uap3-verb) elements that define the options that will be listed when a user right-clicks a file with the .foo extension in File Explorer. For more details, see [Add options to the context menus of files that have a certain file type](desktop-to-uwp-extensions.md#add-options-to-the-context-menus-of-files-that-have-a-certain-file-type). Here are the most important items related to the [Verb](/uwp/schemas/appxpackage/uapmanifestschema/element-uap3-verb) element. - -| Attribute or element | Description | -|----------------------|-------------| -| `Id` attribute | Specifies the unique identifier for the action.| -| `Parameters` attribute | Similar to the [FileTypeAssociation](/uwp/schemas/appxpackage/uapmanifestschema/element-uap-filetypeassociation) element, this attribute for the [Verb](/uwp/schemas/appxpackage/uapmanifestschema/element-uap3-verb) element contains the parameters that are passed to your application when the user clicks the context menu entry. Typically, other than the `%1` special parameter to get the path of the selected file, you pass also one or more parameters to get the context. This enables your app to understand that it was opened from a context menu entry. | -| Element value | The value of the [Verb](/uwp/schemas/appxpackage/uapmanifestschema/element-uap3-verb) element contains the label to display in the context menu entry (in this example, **Resize file**). | - -### Access the startup parameters in your app code - -The way your app receives the parameters depends on the type of app you have created. For example, a WPF app typically processes startup event args in the `OnStartup` method of the `App` class. You can check if there are startup parameters and, based on the outcome, take the most appropriate action (like opening a specific window of the application instead of the main one). - -```csharp -public partial class App : Application +```cpp +class EditCommand final : public IExplorerCommand { - protected override void OnStartup(StartupEventArgs e) +public: + IFACEMETHODIMP GetTitle(IShellItemArray*, PWSTR* title) { - if (e.Args.Contains("Resize")) - { - // Open a specific window of the app. - } - else - { - MainWindow main = new MainWindow(); - main.Show(); - } + return SHStrDup(L"Edit with Contoso", title); } -} -``` - -The following screenshot demonstrates the **Resize File** context menu entry created by the previous example. - -![Screenshot of Resize file command in the shortcut menu](images/file-explorer/resize-file.png) -## Support generic files or folders and perform complex tasks - -Although using the [FileTypeAssociation](/uwp/schemas/appxpackage/uapmanifestschema/element-uap-filetypeassociation) extension in the package manifest as described in the previous section is sufficient for many scenarios, you may find it limiting. The two biggest challenges are: - -* You can handle only file types you are associated with. For example, you can't handle a generic folder. -* You can only launch the app with a series of parameters. You can't perform advanced operations, like launching another executable or performing a task without opening the main app. - -To achieve these goals, you must create a [Shell extension](/windows/win32/shell/shell-exts), which provides more powerful ways to integrate with File Explorer. In this scenario, you create a DLL that contains everything required to manage the file context menu, including the label, icon, state, and tasks to perform. Because this functionality is implemented in a DLL, you can do nearly everything that you can do with a normal app. After you implement the DLL, you must register it via extensions you define in your package manifest. - -> [!NOTE] -> The process described in this section has one limitation. After the MSIX package that contains the extension is installed on a target computer, File Explorer must be restarted before the Shell extension can be loaded. To accomplish this, the user can restart the computer, or they can restart the **explorer.exe** process using **Task Manager**. + IFACEMETHODIMP GetState( + IShellItemArray*, BOOL, EXPCMDSTATE* state) + { + *state = ECS_ENABLED; + return S_OK; + } -### Implement the Shell extension + IFACEMETHODIMP Invoke(IShellItemArray* items, IBindCtx*) + { + // Obtain selected items from items and start the app or operation. + return S_OK; + } -Shell extensions are based on [COM (Component Object Model)](/windows/win32/com/component-object-model--com--portal). Your DLL exposes one or more COM objects that are registered in the system registry. Windows discovers these COM objects and integrates your extension with File Explorer. Because you're integrating your code with the Windows Shell, performance and memory footprint is important. Therefore, these kinds of extensions are typically built with C++. + IFACEMETHODIMP GetIcon(IShellItemArray*, PWSTR* icon) + { + return SHStrDup(L"ContosoCommand.dll,-101", icon); + } -For sample code that illustrates how to implement Shell extensions, see the [ExplorerCommandVerb](https://github.com/microsoft/Windows-AppConsult-Samples-DesktopBridge/tree/main/Docs-ContextMenuSample/ExplorerCommandVerb) project in the related sample on GitHub. This project is based on [this sample](https://github.com/microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/winui/shell/appshellintegration/ExplorerCommandVerb) in the Windows desktop samples, and it has several revisions to make the sample more easy to use with the latest versions of Visual Studio. + IFACEMETHODIMP GetToolTip(IShellItemArray*, PWSTR* tooltip) + { + *tooltip = nullptr; + return E_NOTIMPL; + } -This project contains a lot of boilerplate code for different tasks, such as dynamic vs static menus and manual registration of the DLL. Most of this code isn't needed if you're packaging your app using MSIX, because the packaging support will take care of these tasks for you. The [ExplorerCommandVerb.cpp](https://github.com/microsoft/Windows-AppConsult-Samples-DesktopBridge/blob/main/Docs-ContextMenuSample/ExplorerCommandVerb/ExplorerCommandVerb.cpp) file contains the implementation of the context menu, and this is the main code file of interest for this walkthrough. + IFACEMETHODIMP GetCanonicalName(GUID* canonicalName) + { + *canonicalName = GUID_NULL; + return S_OK; + } -The key function is `CExplorerCommandVerb::Invoke`. This is the function that is invoked when a user clicks on the entry in the context menu. In the sample, to minimize the impact on the performance, the operation is performed on another thread, so you will actually find the real implementation in `CExplorerCommandVerb::_ThreadProc`. + IFACEMETHODIMP GetFlags(EXPCMDFLAGS* flags) + { + *flags = ECF_DEFAULT; + return S_OK; + } -```cpp -DWORD CExplorerCommandVerb::_ThreadProc() -{ - IShellItemArray* psia; - HRESULT hr = CoGetInterfaceAndReleaseStream(_pstmShellItemArray, IID_PPV_ARGS(&psia)); - _pstmShellItemArray = NULL; - if (SUCCEEDED(hr)) - { - DWORD count; - psia->GetCount(&count); - - IShellItem2* psi; - HRESULT hr = GetItemAt(psia, 0, IID_PPV_ARGS(&psi)); - if (SUCCEEDED(hr)) - { - PWSTR pszName; - hr = psi->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszName); - if (SUCCEEDED(hr)) - { - WCHAR szMsg[128]; - StringCchPrintf(szMsg, ARRAYSIZE(szMsg), L"%d item(s), first item is named %s", count, pszName); - - MessageBox(_hwnd, szMsg, L"ExplorerCommand Sample Verb", MB_OK); - - CoTaskMemFree(pszName); - } - - psi->Release(); - } - psia->Release(); - } - - return 0; -} + IFACEMETHODIMP EnumSubCommands(IEnumExplorerCommand** commands) + { + *commands = nullptr; + return E_NOTIMPL; + } +}; ``` -When a user right-clicks on a file or folder, this function displays a message box with the full path of the selected file or folder. If you want to customize the Shell extension in other ways, you can extend the following functions in the sample: - -- You can change the [GetTitle](/windows/win32/api/shobjidl_core/nf-shobjidl_core-iexplorercommand-gettitle) function to customize the label of the entry in the context menu. -- You can change the [GetIcon](/windows/win32/api/shobjidl_core/nf-shobjidl_core-iexplorercommand-geticon) function to customize the icon displayed near the entry in the context menu. -- You can change the [GetTooltip](/windows/win32/api/shobjidl_core/nf-shobjidl_core-iexplorercommand-gettooltip) function to customize the [tooltip](../../design/controls/tooltips.md) which is displayed when you hover the entry in the context menu. - -### Register the Shell extension +Use the `IShellItemArray` passed to `Invoke` to enumerate the current selection. `GetState` can return states such as `ECS_HIDDEN`, `ECS_DISABLED`, or `ECS_ENABLED` to control whether the command appears and can be selected. For the complete contract, see [IExplorerCommand](/windows/win32/api/shobjidl_core/nn-shobjidl_core-iexplorercommand). -Because the Shell extension is based on COM, the implementation DLL must be exposed as a COM server so that Windows can integrate it with File Explorer. Typically, this is done by assigning a unique ID (called CLSID) to the COM server and by registering it in a specific hive of the system registry. In the [ExplorerCommandVerb](https://github.com/microsoft/Windows-AppConsult-Samples-DesktopBridge/tree/main/Docs-ContextMenuSample/ExplorerCommandVerb) project, the CLSID for the `CExplorerCommandVerb` extension is defined in the [Dll.h](https://github.com/microsoft/Windows-AppConsult-Samples-DesktopBridge/blob/main/Docs-ContextMenuSample/ExplorerCommandVerb/Dll.h) file. +Assign the COM class a CLSID and ensure that the same CLSID is used in the package manifest. For example: ```cpp -class __declspec(uuid("CC19E147-7757-483C-B27F-3D81BCEB38FE")) CExplorerCommandVerb; +class __declspec(uuid("01234567-89AB-CDEF-0123-456789ABCDEF")) + EditCommand; ``` -When you package a Shell extension DLL in an MSIX package, you follow a similar approach. However, the GUID must registered inside the package manifest instead of the registry, as explained [here](https://blogs.windows.com/windowsdeveloper/2017/04/13/com-server-ole-document-support-desktop-bridge/). +## Register the command in the package manifest -In your package manifest, start by adding the following namespaces to your **Package** element. +Declare the `com`, `desktop4`, and `desktop5` namespaces on the root `Package` element and add them to `IgnorableNamespaces`. ```xml - - ... + IgnorableNamespaces="com desktop4 desktop5"> + ``` -To register the CLSID, add a [com.Extension](/uwp/schemas/appxpackage/uapmanifestschema/element-com-comserver) element with the category `windows.comServer` to your package manifest. This element must be added as a child of the [Extensions](/uwp/schemas/appxpackage/uapmanifestschema/element-1-extensions) element under the [Application](/uwp/schemas/appxpackage/uapmanifestschema/element-application) element. This example is an excerpt from the [Package.appxmanifest](https://github.com/microsoft/Windows-AppConsult-Samples-DesktopBridge/blob/main/Docs-ContextMenuSample/ContextMenuSample.Package/Package.appxmanifest) file in the related sample on GitHub. +Under the application `Extensions` element, first register the DLL as a COM server. `com:Class/@Id` is the command's CLSID, and `Path` is the DLL's package-relative path. ```xml - - + + ``` -There are two critical attributes to configure in the [com:Class](/uwp/schemas/appxpackage/uapmanifestschema/element-com-surrogateserver-class) element. - -| Attribute | Description | -|----------------------|-------------| -| `Id` attribute | This must match with the CLSID of the object you want to register. In this example, this is the CLSID declared in the `Dll.h` file associated to the `CExplorerCommandVerb` class. | -| `Path` attribute | This must contain the name of the DLL that exposes the COM object. This example includes the DLL in the root of the package, so it can just specify the name of the DLL generated by the `ExplorerCommandVerb` project. | - -Next, add another extension that registers the file context menu. To do this, add a [desktop4:Extension](/uwp/schemas/appxpackage/uapmanifestschema/element-desktop4-extension) element with the category `windows.fileExplorerContextMenus` to your package manifest. This element must also be added as a child of the [Extensions](/uwp/schemas/appxpackage/uapmanifestschema/element-1-extensions) element under the [Application](/uwp/schemas/appxpackage/uapmanifestschema/element-application) element. +Then associate the CLSID with the context in which the command should appear. ```xml - - + + ``` -There are two critical attributes to configure under the [desktop4:Extension](/uwp/schemas/appxpackage/uapmanifestschema/element-desktop4-extension) element. +`desktop5:ItemType/@Type` identifies the target shell items. Use `*` for files, `Directory` for selected folders, or `Directory\Background` for the background of a folder. You can register multiple `ItemType` entries for the same CLSID when a command supports multiple contexts. + +The `desktop5:Verb/@Id` identifies the manifest registration. `desktop5:Verb/@Clsid` must match the `com:Class/@Id` value and the CLSID on the COM class. + +For the complete manifest schema, see [desktop4:FileExplorerContextMenus](/uwp/schemas/appxpackage/uapmanifestschema/element-desktop4-fileexplorercontextmenus) and [com:Class](/uwp/schemas/appxpackage/uapmanifestschema/element-com-surrogateserver-class). + +## Package the DLL + +Include the command DLL and any resources it requires in the package at the path specified by `com:Class/@Path`. If you use a Windows Application Packaging Project, add the DLL to the project and configure it to copy to the package output. Configure a build dependency or post-build copy so the package contains the current DLL. -| Attribute or element | Description | -|----------------------|-------------| -| `Type` attribute of [desktop5:ItemType](/uwp/schemas/appxpackage/uapmanifestschema/element-desktop5-itemtype) | This defines the type of items you want to associate with the context menu. It could be a star (`*`) if you want to display it for all the files; it could be a specific file extension (`.foo`); or it can be available for folders (`Directory`). | -| `Clsid` attribute of [desktop5:Verb](/uwp/schemas/appxpackage/uapmanifestschema/element-desktop5-verb) | This must match the CLSID that you have previously registered as COM server in the package manifest file. | +The DLL architecture must match the File Explorer architecture that loads it. Build and package the appropriate architecture for the target device. -### Configure the DLL in the package +## Use a sparse package for an unpackaged app -Include the DLL that implements the Shell extension (in this sample, **ExplorerCommandVerb.dll**) in the root of the MSIX package. If you're using the [Windows Application Packaging Project](/windows/msix/desktop/desktop-to-uwp-packaging-dot-net), the easiest solution is to copy and paste the DLL into the project and make sure that the **Copy to Output Directory** option for the DLL file properties is set to **Copy if newer**. +An unpackaged Win32 app can use the same `IExplorerCommand` and manifest registrations by installing a sparse package that gives the app package identity. A sparse package does not contain the app binaries; it references the externally installed app instead. -To make sure that the package always includes the most recent version of the DLL, you can add a [post-build event](/visualstudio/ide/specifying-custom-build-events-in-visual-studio) to the Shell extension project so that, every time you build it, the DLL is copied to the Windows Application Packaging Project. +In addition to the COM and context-menu extensions shown previously, a sparse package manifest typically declares `uap10:AllowExternalContent` and configures the application as a Win32 app. The following example shows the relevant sparse-package declarations. + +```xml + + + + true + + + + + + + + + + + + + +``` -### Restart File Explorer +See [Sparse packages](/windows/msix/packaging-tool/sparse-packages) for packaging and registration requirements for unpackaged apps. -After you install the Shell extension package, you must restart File Explorer before the Shell extension can be loaded. This is a limitation of Shell extensions that are deployed and registered via MSIX packages. +## Test the extension -To test the Shell extension, restart your PC or restart the **explorer.exe** process using **Task Manager**. After you do to that, you should be able to see the entry in the context menu. +Install or register the package, then open File Explorer and right-click an item matching one of the registered item types. If the command does not appear after installing or updating a package, restart File Explorer or sign out and back in so the shell reloads the extension registration. -![Screenshot of the custom context menu entry](images/file-explorer/folder-context-menu.png) +Use **Show more options** only to inspect legacy context-menu extensions. A command registered through `windows.fileExplorerContextMenus` and implemented with `IExplorerCommand` appears in the Windows 11 context menu. -If you click on it, the `CExplorerCommandVerb::_ThreadProc` function will be called to display the message box with the path of the selected folder. +## File type associations -![Screenshot of the custom popup](images/file-explorer/popup.png) +Register a [file type association](/windows/uwp/launch-resume/handle-file-activation) when the app opens or edits that file type. It can make the app available through **Open with** and can add an edit verb for associated file types. Use the context-menu mechanism in this article for commands that apply to generic files, folders, or backgrounds, or for commands that perform operations without opening the app's normal file-open experience. \ No newline at end of file