Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438/* * Shell Library Functions * * Copyright 1998 Marcus Meissner * Copyright 2002 Eric Pouech * Copyright 2018-2024 Katayama Hirofumi MZ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
#include "precomp.h"#include <winbase_undoc.h>#include <undocshell.h>
WINE_DEFAULT_DEBUG_CHANNEL(exec);
EXTERN_C BOOL PathIsExeW(LPCWSTR lpszPath);
#define SEE_MASK_CLASSALL (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY)
typedef UINT_PTR (*SHELL_ExecuteW32)(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, const SHELLEXECUTEINFOW *sei, LPSHELLEXECUTEINFOW sei_out);
static int Win32ErrFromHInst(HINSTANCE hInst){ if ((SIZE_T)hInst > 32) return ERROR_SUCCESS; switch ((SIZE_T)hInst) { case SE_ERR_FNF: case SE_ERR_PNF: case SE_ERR_ACCESSDENIED: case SE_ERR_OOM: return (UINT)(SIZE_T)hInst; case SE_ERR_SHARE: return ERROR_SHARING_VIOLATION; case SE_ERR_DDETIMEOUT: case SE_ERR_DDEFAIL: case SE_ERR_DDEBUSY: return ERROR_DDE_FAIL; case SE_ERR_DLLNOTFOUND: return ERROR_DLL_NOT_FOUND; //case SE_ERR_ASSOCINCOMPLETE: Note: Windows treats this as a success code case SE_ERR_NOASSOC: return ERROR_NO_ASSOCIATION; case 10: return ERROR_OLD_WIN_VERSION; case 12: return ERROR_APP_WRONG_OS; case 15: return ERROR_RMODE_APP; case 16: return ERROR_SINGLE_INSTANCE_APP; case 20: return ERROR_INVALID_DLL; } return -1;}
// Is the current process a rundll32.exe?static BOOL SHELL_InRunDllProcess(VOID){ WCHAR szModule[MAX_PATH]; static INT s_bInDllProcess = -1;
if (s_bInDllProcess != -1) return s_bInDllProcess;
s_bInDllProcess = GetModuleFileNameW(NULL, szModule, _countof(szModule)) && (StrStrIW(PathFindFileNameW(szModule), L"rundll") != NULL); return s_bInDllProcess;}
static UINT_PTR InvokeOpenWith(HWND hWndOwner, SHELLEXECUTEINFOW &sei){ extern HRESULT SH32_InvokeOpenWith(PCWSTR, LPCMINVOKECOMMANDINFO, HANDLE *);
HANDLE *phProc = (sei.fMask & SEE_MASK_NOCLOSEPROCESS) ? &sei.hProcess : NULL; UINT fCmic = (sei.fMask & SEE_CMIC_COMMON_BASICFLAGS) | CMIC_MASK_FLAG_NO_UI; CMINVOKECOMMANDINFO ici = { sizeof(ici), fCmic, hWndOwner }; ici.nShow = SW_SHOW; HRESULT hr = SH32_InvokeOpenWith(sei.lpFile, &ici, phProc); SetLastError(ERROR_NO_ASSOCIATION); return SUCCEEDED(hr) ? 42 : SE_ERR_NOASSOC;}
static HRESULT InvokeShellExecuteHook(PCWSTR pszClsid, LPSHELLEXECUTEINFOW pSEI){ CComPtr<IUnknown> pUnk; if (FAILED(SHExtCoCreateInstance(pszClsid, NULL, NULL, IID_PPV_ARG(IUnknown, &pUnk)))) return S_FALSE; CComPtr<IShellExecuteHookW> pWide; if (SUCCEEDED(pUnk->QueryInterface(IID_PPV_ARG(IShellExecuteHookW, &pWide)))) return pWide->Execute(pSEI); HRESULT hr = S_FALSE;#if 0 // TODO CComPtr<IShellExecuteHookA> pAnsi; if (SUCCEEDED(pUnk->QueryInterface(IID_PPV_ARG(IShellExecuteHookA, &pAnsi)))) { SHELLEXECUTEINFOA sei = *(SHELLEXECUTEINFOA*)pSEI; // TODO: Convert the strings hr = pAnsi->Execute(sei); pSEI->hProcess = sei.hProcess; pSEI->hInstApp = sei.hInstApp; }#endif return hr;}
static HRESULT TryShellExecuteHooks(LPSHELLEXECUTEINFOW pSEI){ // https://devblogs.microsoft.com/oldnewthing/20080910-00/?p=20933 claims hooks // were removed in Vista but this is incorrect, they are disabled by default. // https://groups.google.com/g/microsoft.public.platformsdk.shell/c/ixdOX1--IKk // says they are now controlled by the EnableShellExecuteHooks policy. if (pSEI->fMask & SEE_MASK_NO_HOOKS) return S_FALSE; if (LOBYTE(GetVersion()) >= 6 && !SH32_InternalRestricted(REST_SH32_ENABLESHELLEXECUTEHOOKS)) return S_FALSE;
HRESULT hr = S_FALSE; HKEY hKey; LRESULT res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, REGSTR_PATH_EXPLORER L"\\ShellExecuteHooks", 0, KEY_READ, &hKey); if (res != ERROR_SUCCESS) return S_FALSE; for (UINT i = 0; hr == S_FALSE; ++i) { WCHAR szClsid[42]; DWORD cch = _countof(szClsid); if (RegEnumValueW(hKey, i, szClsid, &cch, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) break; hr = InvokeShellExecuteHook(szClsid, pSEI); } RegCloseKey(hKey); return hr;}
static void ParseNoTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum){ bool firstCharQuote = false; bool quotes_opened = false; bool backslash_encountered = false;
for (int curArg = 0; curArg <= argNum && *args; ++curArg) { firstCharQuote = false; if (*args == '"') { quotes_opened = true; firstCharQuote = true; args++; }
while(*args) { if (*args == '\\') { // if we found a backslash then flip the variable backslash_encountered = !backslash_encountered; } else if (*args == '"') { if (quotes_opened) { if (*(args + 1) != '"') { quotes_opened = false; args++; break; } else { args++; } } else { quotes_opened = true; }
backslash_encountered = false; } else { backslash_encountered = false; if (*args == ' ' && !firstCharQuote) break; }
if (curArg == argNum) { used++; if (used < len) *res++ = *args; }
args++; }
while(*args == ' ') ++args; }}
static void ParseTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum){ bool quotes_opened = false; bool backslash_encountered = false;
for (int curArg = 0; curArg <= argNum && *args; ++curArg) { while(*args) { if (*args == '\\') { // if we found a backslash then flip the variable backslash_encountered = !backslash_encountered; } else if (*args == '"') { if (quotes_opened) { if (*(args + 1) != '"') { quotes_opened = false; } else { args++; } } else { quotes_opened = true; }
backslash_encountered = false; } else { backslash_encountered = false; if (*args == ' ' && !quotes_opened && curArg != argNum) break; }
if (curArg == argNum) { used++; if (used < len) *res++ = *args; }
args++; } }}
/*********************************************************************** * SHELL_ArgifyW [Internal] * * this function is supposed to expand the escape sequences found in the registry * some diving reported that the following were used: * + %1, %2... seem to report to parameter of index N in ShellExecute pmts * %1 file * %2 printer * %3 driver * %4 port * %I address of a global item ID (explorer switch /idlist) * %L seems to be %1 as long filename followed by the 8+3 variation * %S ??? * %W Working directory * %V Use either %L or %W * %* all following parameters (see batfile) * * The way we parse the command line arguments was determined through extensive * testing and can be summed up by the following rules" * * - %2 * - if first letter is " break on first non literal " and include any white spaces * - if first letter is NOT " break on first " or white space * - if " is opened any pair of consecutive " results in ONE literal " * * - %~2 * - use rules from here http://www.autohotkey.net/~deleyd/parameters/parameters.htm */
static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* lpFile, LPITEMIDLIST pidl, LPCWSTR args, DWORD* out_len, const WCHAR* lpDir){ BOOL done = FALSE; BOOL found_p1 = FALSE; PWSTR res = out; DWORD used = 0; bool tildeEffect = false;
TRACE("Before parsing: %p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt), debugstr_w(lpFile), pidl, args);
while (*fmt) { if (*fmt == '%') { switch (*++fmt) { case '\0': case '%': { used++; if (used < len) *res++ = '%'; }; break;
case '*': { if (args) { if (*fmt == '*') { used++; while(*args) { used++; if (used < len) *res++ = *args++; else args++; } used++; break; } } }; break;
case '~':
case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': //case '0': { if (*fmt == '~') { fmt++; tildeEffect = true; }
if (args) { if (tildeEffect) { ParseTildeEffect(res, args, len, used, *fmt - '2'); tildeEffect = false; } else { ParseNoTildeEffect(res, args, len, used, *fmt - '2'); } } }; break;
case '1': if ((!done || (*fmt == '1')) && lpFile) { SIZE_T filelen = wcslen(lpFile); used += filelen; if (used < len) { wcscpy(res, lpFile); res += filelen; } } found_p1 = TRUE; break;
/* * IE uses this a lot for activating things such as windows media * player. This is not verified to be fully correct but it appears * to work just fine. */ case 'l': case 'L': if (lpFile) { used += wcslen(lpFile); if (used < len) { wcscpy(res, lpFile); res += wcslen(lpFile); } } found_p1 = TRUE; break;
case 'w': case 'W': if (lpDir) { used += wcslen(lpDir); if (used < len) { wcscpy(res, lpDir); res += wcslen(lpDir); } } break;
case 'v': case 'V': if (lpFile) { used += wcslen(lpFile); if (used < len) { wcscpy(res, lpFile); res += wcslen(lpFile); } found_p1 = TRUE; } else if (lpDir) { used += wcslen(lpDir); if (used < len) { wcscpy(res, lpDir); res += wcslen(lpDir); } } break;
case 'i': case 'I': if (pidl) { DWORD chars = 0; /* %p should not exceed 8, maybe 16 when looking forward to 64bit. * allowing a buffer of 100 should more than exceed all needs */ WCHAR buf[100]; LPVOID pv; HGLOBAL hmem = SHAllocShared(pidl, ILGetSize(pidl), 0); pv = SHLockShared(hmem, 0); chars = swprintf(buf, L":%p", pv);
if (chars >= ARRAY_SIZE(buf)) ERR("pidl format buffer too small!\n");
used += chars;
if (used < len) { wcscpy(res, buf); res += chars; } SHUnlockShared(pv); } found_p1 = TRUE; break;
default: /* * Check if this is an env-variable here... */
/* Make sure that we have at least one more %.*/ if (strchrW(fmt, '%')) { WCHAR tmpBuffer[1024]; PWSTR tmpB = tmpBuffer; WCHAR tmpEnvBuff[MAX_PATH]; DWORD envRet;
while (*fmt != '%') *tmpB++ = *fmt++; *tmpB++ = 0;
TRACE("Checking %s to be an env-var\n", debugstr_w(tmpBuffer));
envRet = GetEnvironmentVariableW(tmpBuffer, tmpEnvBuff, MAX_PATH); if (envRet == 0 || envRet > MAX_PATH) { used += wcslen(tmpBuffer); if (used < len) { wcscpy( res, tmpBuffer ); res += wcslen(tmpBuffer); } } else { used += wcslen(tmpEnvBuff); if (used < len) { wcscpy( res, tmpEnvBuff ); res += wcslen(tmpEnvBuff); } } } done = TRUE; break; } /* Don't skip past terminator (catch a single '%' at the end) */ if (*fmt != '\0') { fmt++; } } else { used ++; if (used < len) *res++ = *fmt++; else fmt++; } }
used++; if (res - out < static_cast<int>(len)) *res = '\0'; else out[len-1] = '\0';
TRACE("used %i of %i space\n", used, len); if (out_len) *out_len = used;
TRACE("After parsing: %p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt), debugstr_w(lpFile), pidl, args);
return found_p1;}
static HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR pszPath, UINT uOutSize){ STRRET strret; CComPtr<IShellFolder> desktop;
HRESULT hr = SHGetDesktopFolder(&desktop);
if (SUCCEEDED(hr)) { hr = desktop->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strret);
if (SUCCEEDED(hr)) StrRetToStrNW(pszPath, uOutSize, &strret, pidl); }
return hr;}
static HWND SHELL_GetUsableDialogOwner(HWND hWnd){ // Explicitly block the shell desktop listview from becoming the owner (IContextMenu calling ShellExecute) HWND hProgman = GetShellWindow(); if (hWnd && IsWindowVisible(hWnd) && hWnd != GetDesktopWindow() && hWnd != hProgman && !IsChild(hProgman, hWnd)) { return hWnd; } return NULL;}
/************************************************************************* * PromptAndRunProcessAs [Internal] */typedef struct _RUNASDLGDATA{ LPWSTR Name, Domain; BOOL Safer; UINT LogonFlags; WCHAR NameBuffer[MAX_PATH]; WCHAR Password[MAX_PATH];} RUNASDLGDATA;
staticINT_PTRCALLBACKRunAsDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ RUNASDLGDATA *pData = (RUNASDLGDATA *)GetWindowLongPtrW(hwnd, DWLP_USER); switch (uMsg) { case WM_INITDIALOG: { SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)(pData = (RUNASDLGDATA *)lParam)); SendDlgItemMessageW(hwnd, IDC_RUNAS_NAME, EM_LIMITTEXT, _countof(pData->NameBuffer)-1, 0); SendDlgItemMessageW(hwnd, IDC_RUNAS_PWD, EM_LIMITTEXT, _countof(pData->Password)-1, 0); SendDlgItemMessageW(hwnd, IDC_RUNAS_OTHER, BM_CLICK, 0, 0);
HWND hCtl = GetDlgItem(hwnd, IDC_RUNAS_CURRENT); WCHAR fmtbuf[200], buf[_countof(fmtbuf) + _countof(pData->NameBuffer)]; DWORD cch = _countof(pData->NameBuffer); if (GetUserNameW(pData->NameBuffer, &cch)) { SendMessageW(hCtl, WM_GETTEXT, _countof(fmtbuf), (LPARAM)buf); StringCchPrintfW(fmtbuf, _countof(fmtbuf), buf, L"(%s)"); // Change "Blah blah %s" to "Blah blah (%s)" StringCchPrintfW(buf, _countof(buf), fmtbuf, pData->NameBuffer); SendMessageW(hCtl, WM_SETTEXT, 0, (LPARAM)buf); SendDlgItemMessageW(hwnd, IDC_RUNAS_NAME, CB_ADDSTRING, 0, (LPARAM)pData->NameBuffer); } SendDlgItemMessageW(hwnd, IDC_RUNAS_NAME, CB_SETCURSEL, 0, 0); return TRUE; }
case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: EndDialog(hwnd, IDCANCEL); break;
case IDOK: { SendDlgItemMessageW(hwnd, IDC_RUNAS_NAME, WM_GETTEXT, _countof(pData->NameBuffer), (LPARAM)pData->NameBuffer); SendDlgItemMessageW(hwnd, IDC_RUNAS_PWD, WM_GETTEXT, _countof(pData->Password), (LPARAM)pData->Password); pData->Name = pData->NameBuffer; pData->Domain = wcsstr(pData->Name, L"\\"); if (pData->Domain) { LPWSTR tmp = pData->Domain + 1; pData->Domain[0] = UNICODE_NULL; pData->Domain = pData->Name; pData->Name = tmp; } pData->LogonFlags = GetKeyState(VK_SHIFT) < 0 ? LOGON_NETCREDENTIALS_ONLY : LOGON_WITH_PROFILE; pData->Safer = IsDlgButtonChecked(hwnd, IDC_RUNAS_SAFER); if (!IsDlgButtonChecked(hwnd, IDC_RUNAS_OTHER)) pData->Name = NULL; EndDialog(hwnd, IDOK); break; }
case IDC_RUNAS_BROWSE: return SHELL_ErrorBox(hwnd, ERROR_NOT_SUPPORTED); // TODO } break; } return FALSE;}
staticHRESULTPromptAndRunProcessAs( _In_opt_ HWND hwnd, _In_ LPWSTR Cmd, _In_ DWORD CreationFlags, _In_opt_ LPWSTR Env, _In_opt_ LPCWSTR Dir, _In_ STARTUPINFOW *pSI, _Out_ PROCESS_INFORMATION *pPI){ RUNASDLGDATA data; UINT error; INT_PTR dlgret; hwnd = SHELL_GetUsableDialogOwner(hwnd);
again: dlgret = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_RUN_AS), hwnd, RunAsDlgProc, (LPARAM)&data); if (dlgret == IDOK && data.Name) { if (CreateProcessWithLogonW(data.Name, data.Domain, data.Password, data.LogonFlags, NULL, Cmd, CreationFlags, Env, Dir, pSI, pPI)) error = ERROR_SUCCESS; else error = GetLastError(); switch (error) { case ERROR_LOGON_FAILURE: case ERROR_NO_SUCH_USER: case ERROR_INVALID_ACCOUNT_NAME: case ERROR_ACCOUNT_DISABLED: case ERROR_ACCOUNT_RESTRICTION: case ERROR_INVALID_LOGON_HOURS: case ERROR_PASSWORD_EXPIRED: SHELL_ErrorBox(hwnd, error); goto again; } } else if (dlgret == IDOK) { // TODO: Use the Safer API if requested to if (CreateProcessW(NULL, Cmd, NULL, NULL, FALSE, CreationFlags, Env, Dir, pSI, pPI)) error = ERROR_SUCCESS; else error = GetLastError(); } else if (dlgret == IDCANCEL) { SetLastError(ERROR_CANCELLED); pPI->hProcess = NULL; return S_FALSE; } else { pPI->hProcess = NULL; return E_FAIL; } SecureZeroMemory(&data, sizeof(data)); return HRESULT_FROM_WIN32(error);}
/************************************************************************* * SHELL_ExecuteW [Internal] * */static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait, const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out){ STARTUPINFOW startup; PROCESS_INFORMATION info; UINT_PTR retval = SE_ERR_NOASSOC; UINT gcdret = 0, lasterror = 0; WCHAR curdir[MAX_PATH]; DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT; const WCHAR *lpDirectory = NULL;
TRACE("Execute %s from directory %s\n", debugstr_w(lpCmd), debugstr_w(psei->lpDirectory));
/* make sure we don't fail the CreateProcess if the calling app passes in * a bad working directory */ if (!StrIsNullOrEmpty(psei->lpDirectory)) { DWORD attr = GetFileAttributesW(psei->lpDirectory); if (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY) lpDirectory = psei->lpDirectory; }
/* ShellExecute specifies the command from psei->lpDirectory * if present. Not from the current dir as CreateProcess does */ if (lpDirectory) if ((gcdret = GetCurrentDirectoryW( MAX_PATH, curdir))) if (!SetCurrentDirectoryW( lpDirectory)) ERR("cannot set directory %s\n", debugstr_w(lpDirectory));
ZeroMemory(&startup, sizeof(STARTUPINFOW)); startup.cb = sizeof(STARTUPINFOW); startup.dwFlags = STARTF_USESHOWWINDOW; startup.wShowWindow = psei->nShow; if (!(psei->fMask & SEE_MASK_NO_CONSOLE)) dwCreationFlags |= CREATE_NEW_CONSOLE; if (psei->fMask & SEE_MASK_FLAG_SEPVDM) dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; startup.lpTitle = (LPWSTR)(psei->fMask & (SEE_MASK_HASLINKNAME | SEE_MASK_HASTITLE) ? psei->lpClass : NULL);
if (psei->fMask & SEE_MASK_HASLINKNAME) startup.dwFlags |= STARTF_TITLEISLINKNAME;
if (psei->fMask & SEE_MASK_HOTKEY) { startup.hStdInput = UlongToHandle(psei->dwHotKey); startup.dwFlags |= STARTF_USEHOTKEY; }
if (psei->fMask & SEE_MASK_ICON) // hIcon has higher precedence than hMonitor { startup.hStdOutput = psei->hIcon; startup.dwFlags |= STARTF_SHELLPRIVATE; } else if ((psei->fMask & SEE_MASK_HMONITOR) || psei->hwnd) { if (psei->fMask & SEE_MASK_HMONITOR) startup.hStdOutput = psei->hMonitor; else if (psei->hwnd) startup.hStdOutput = MonitorFromWindow(psei->hwnd, MONITOR_DEFAULTTONEAREST); if (startup.hStdOutput) startup.dwFlags |= STARTF_SHELLPRIVATE; }
BOOL createdProcess; if (psei->lpVerb && !StrCmpIW(L"runas", psei->lpVerb)) { HRESULT hr = PromptAndRunProcessAs(psei->hwnd, (LPWSTR)lpCmd, dwCreationFlags, env, lpDirectory, &startup, &info); createdProcess = hr == S_OK; if (hr == S_FALSE) { retval = 33; // Pretend cancel is success. goto done; } } else { createdProcess = CreateProcessW(NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE, dwCreationFlags, env, lpDirectory, &startup, &info); }
if (createdProcess) { /* Give 30 seconds to the app to come up, if desired. Probably only needed when starting app immediately before making a DDE connection. */ if (shWait) if (WaitForInputIdle(info.hProcess, 30000) == WAIT_FAILED) WARN("WaitForInputIdle failed: Error %d\n", GetLastError() ); retval = 33;
if (psei->fMask & SEE_MASK_NOCLOSEPROCESS) psei_out->hProcess = info.hProcess; else CloseHandle( info.hProcess ); CloseHandle( info.hThread ); } else if ((retval = lasterror = GetLastError()) >= 32) { WARN("CreateProcess returned error %ld\n", retval); retval = ERROR_BAD_FORMAT; }
done: TRACE("returning %lu\n", retval); psei_out->hInstApp = (HINSTANCE)retval;
if (gcdret) { if (!SetCurrentDirectoryW(curdir)) ERR("cannot return to directory %s\n", debugstr_w(curdir)); if (lasterror) RestoreLastError(lasterror); } return retval;}
/*********************************************************************** * SHELL_BuildEnvW [Internal] * * Build the environment for the new process, adding the specified * path to the PATH variable. Returned pointer must be freed by caller. */static LPWSTR SHELL_BuildEnvW( const WCHAR *path ){ CHeapPtr<WCHAR, CLocalAllocator> new_env; WCHAR *strings, *p, *p2; int total = wcslen(path) + 1; BOOL got_path = FALSE;
if (!(strings = GetEnvironmentStringsW())) return NULL; p = strings; while (*p) { int len = wcslen(p) + 1; if (!_wcsnicmp( p, L"PATH=", 5 )) got_path = TRUE; total += len; p += len; } if (!got_path) total += 5; /* we need to create PATH */ total++; /* terminating null */
if (!new_env.Allocate(total)) { FreeEnvironmentStringsW(strings); return NULL; } p = strings; p2 = new_env; while (*p) { int len = wcslen(p) + 1; memcpy(p2, p, len * sizeof(WCHAR)); if (!_wcsnicmp( p, L"PATH=", 5 )) { p2[len - 1] = ';'; wcscpy( p2 + len, path ); p2 += wcslen(path) + 1; } p += len; p2 += len; } if (!got_path) { wcscpy(p2, L"PATH="); wcscat(p2, path); p2 += wcslen(p2) + 1; } *p2 = 0; FreeEnvironmentStringsW(strings); return new_env.Detach();}
/*********************************************************************** * SHELL_TryAppPathW [Internal] * * Helper function for SHELL_FindExecutable * @param lpResult - pointer to a buffer of size MAX_PATH * On entry: szName is a filename (probably without path separators). * On exit: if szName found in "App Path", place full path in lpResult, and return true */static BOOL SHELL_TryAppPathW( LPCWSTR szName, LPWSTR lpResult, WCHAR **env){ HKEY hkApp = NULL; WCHAR buffer[1024]; DWORD len, dwType; LONG res; BOOL found = FALSE;
if (env) *env = NULL; wcscpy(buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"); wcscat(buffer, szName); res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp); if (res) { // Add ".exe" extension, if extension does not exists if (PathAddExtensionW(buffer, L".exe")) { res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp); } if (res) goto end; }
len = MAX_PATH * sizeof(WCHAR); res = SHRegQueryValueExW(hkApp, NULL, NULL, &dwType, (LPBYTE)lpResult, &len); if (res != ERROR_SUCCESS || dwType != REG_SZ) goto end;
found = TRUE;
if (env) { len = sizeof(buffer); res = SHRegQueryValueExW(hkApp, L"Path", NULL, &dwType, (LPBYTE)buffer, &len); if (res == ERROR_SUCCESS && dwType == REG_SZ && buffer[0]) *env = SHELL_BuildEnvW(buffer); }
end: if (hkApp) RegCloseKey(hkApp); return found;}
/************************************************************************* * SHELL_FindExecutableByVerb [Internal] * * called from SHELL_FindExecutable or SHELL_execute_class * in/out: * classname a buffer, big enough, to get the key name to do actually the * command "WordPad.Document.1\\shell\\open\\command" * passed as "WordPad.Document.1" * in: * lpVerb the operation on it (open) * commandlen the size of command buffer (in bytes) * out: * command a buffer, to store the command to do the * operation on the file * key a buffer, big enough, to get the key name to do actually the * command "WordPad.Document.1\\shell\\open\\command" * Can be NULL */static UINT SHELL_FindExecutableByVerb(LPCWSTR lpVerb, LPWSTR key, LPWSTR classname, LPWSTR command, LONG commandlen){ HKEY hkeyClass; WCHAR verb[MAX_PATH];
if (RegOpenKeyExW(HKEY_CLASSES_ROOT, classname, 0, KEY_READ, &hkeyClass)) return SE_ERR_NOASSOC; if (!HCR_GetDefaultVerbW(hkeyClass, lpVerb, verb, ARRAY_SIZE(verb))) return SE_ERR_NOASSOC; RegCloseKey(hkeyClass);
/* Looking for ...buffer\shell\<verb>\command */ wcscat(classname, L"\\shell\\"); // FIXME: Use HCR_GetExecuteCommandW or AssocAPI wcscat(classname, verb); wcscat(classname, L"\\command");
if (RegQueryValueW(HKEY_CLASSES_ROOT, classname, command, &commandlen) == ERROR_SUCCESS) { commandlen /= sizeof(WCHAR); if (key) wcscpy(key, classname);#if 0 LPWSTR tmp; WCHAR param[256]; LONG paramlen = sizeof(param);
/* FIXME: it seems all Windows version don't behave the same here. * the doc states that this ddeexec information can be found after * the exec names. * on Win98, it doesn't appear, but I think it does on Win2k */ /* Get the parameters needed by the application from the associated ddeexec key */ tmp = strstrW(classname, L"\\command"); tmp[0] = '\0'; wcscat(classname, wDdeexec); if (RegQueryValueW(HKEY_CLASSES_ROOT, classname, param, ¶mlen) == ERROR_SUCCESS) { paramlen /= sizeof(WCHAR); wcscat(command, L" "); wcscat(command, param); commandlen += paramlen; }#endif
command[commandlen] = '\0';
return 33; /* FIXME see SHELL_FindExecutable() */ }
return SE_ERR_NOASSOC;}
/************************************************************************* * SHELL_FindExecutable [Internal] * * Utility for code sharing between FindExecutable and ShellExecute * in: * lpFile the name of a file * lpVerb the operation on it (open) * out: * lpResult a buffer, big enough :-(, to store the command to do the * operation on the file * key a buffer, big enough, to get the key name to do actually the * command (it'll be used afterwards for more information * on the operation) */static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpVerb, LPWSTR lpResult, DWORD resultLen, LPWSTR key, WCHAR **env, LPITEMIDLIST pidl, LPCWSTR args){ WCHAR *extension = NULL; /* pointer to file extension */ WCHAR classname[256]; /* registry name for this file type */ LONG classnamelen = sizeof(classname); /* length of above */ WCHAR command[1024]; /* command from registry */ WCHAR wBuffer[256]; /* Used to GetProfileString */ UINT retval = SE_ERR_NOASSOC; WCHAR *tok; /* token pointer */ WCHAR xlpFile[MAX_PATH]; /* result of PathResolve */ DWORD attribs; /* file attributes */ WCHAR curdir[MAX_PATH]; const WCHAR *search_paths[3] = {0};
TRACE("%s\n", debugstr_w(lpFile));
if (!lpResult) return ERROR_INVALID_PARAMETER;
xlpFile[0] = '\0'; lpResult[0] = '\0'; /* Start off with an empty return string */ if (key) *key = '\0';
/* trap NULL parameters on entry */ if (!lpFile) { WARN("(lpFile=%s,lpResult=%s): NULL parameter\n", debugstr_w(lpFile), debugstr_w(lpResult)); return ERROR_FILE_NOT_FOUND; /* File not found. Close enough, I guess. */ }
if (SHELL_TryAppPathW( lpFile, lpResult, env )) { TRACE("found %s via App Paths\n", debugstr_w(lpResult)); return 33; }
GetCurrentDirectoryW(ARRAY_SIZE(curdir), curdir); if (lpPath && *lpPath) { search_paths[0] = lpPath; search_paths[1] = curdir; } else { search_paths[0] = curdir; }
lstrcpyW(xlpFile, lpFile); if (PathResolveW(xlpFile, search_paths, PRF_TRYPROGRAMEXTENSIONS | PRF_VERIFYEXISTS) || PathFindOnPathW(xlpFile, search_paths)) { TRACE("PathResolveW returned non-zero\n"); lpFile = xlpFile; PathRemoveBlanksW(xlpFile);
/* Clear any trailing periods */ SIZE_T i = wcslen(xlpFile); while (i > 0 && xlpFile[i - 1] == '.') { xlpFile[--i] = '\0'; }
lstrcpyW(lpResult, xlpFile); /* The file was found in lpPath or one of the directories in the system-wide search path */ } else { xlpFile[0] = '\0'; }
attribs = GetFileAttributesW(lpFile); if (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY)) { wcscpy(classname, L"Folder"); } else { /* Did we get something? Anything? */ if (xlpFile[0] == 0) { TRACE("Returning SE_ERR_FNF\n"); return SE_ERR_FNF; } /* First thing we need is the file's extension */ extension = wcsrchr(xlpFile, '.'); /* Assume last "." is the one; */ /* File->Run in progman uses */ /* .\FILE.EXE :( */ TRACE("xlpFile=%s,extension=%s\n", debugstr_w(xlpFile), debugstr_w(extension));
if (extension == NULL || extension[1] == 0) { WARN("Returning SE_ERR_NOASSOC\n"); return SE_ERR_NOASSOC; }
/* Three places to check: */ /* 1. win.ini, [windows], programs (NB no leading '.') */ /* 2. Registry, HKEY_CLASS_ROOT\<classname>\shell\open\command */ /* 3. win.ini, [extensions], extension (NB no leading '.' */ /* All I know of the order is that registry is checked before */ /* extensions; however, it'd make sense to check the programs */ /* section first, so that's what happens here. */
/* See if it's a program - if GetProfileString fails, we skip this * section. Actually, if GetProfileString fails, we've probably * got a lot more to worry about than running a program... */ if (GetProfileStringW(L"windows", L"programs", L"exe pif bat cmd com", wBuffer, ARRAY_SIZE(wBuffer)) > 0) { CharLowerW(wBuffer); tok = wBuffer; while (*tok) { WCHAR *p = tok; while (*p && *p != ' ' && *p != '\t') p++; if (*p) { *p++ = 0; while (*p == ' ' || *p == '\t') p++; }
if (_wcsicmp(tok, &extension[1]) == 0) /* have to skip the leading "." */ { wcscpy(lpResult, xlpFile); /* Need to perhaps check that the file has a path * attached */ TRACE("found %s\n", debugstr_w(lpResult)); return 33; /* Greater than 32 to indicate success */ } tok = p; } }
/* Check registry */ if (RegQueryValueW(HKEY_CLASSES_ROOT, extension, classname, &classnamelen) == ERROR_SUCCESS) { classnamelen /= sizeof(WCHAR); if (classnamelen == ARRAY_SIZE(classname)) classnamelen--;
classname[classnamelen] = '\0'; TRACE("File type: %s\n", debugstr_w(classname)); } else { *classname = '\0'; } }
if (*classname) { /* pass the verb string to SHELL_FindExecutableByVerb() */ retval = SHELL_FindExecutableByVerb(lpVerb, key, classname, command, sizeof(command));
if (retval > 32) { DWORD finishedLen; SHELL_ArgifyW(lpResult, resultLen, command, xlpFile, pidl, args, &finishedLen, lpPath); if (finishedLen > resultLen) ERR("Argify buffer not large enough.. truncated\n"); /* Remove double quotation marks and command line arguments */ if (*lpResult == '"') { WCHAR *p = lpResult; while (*(p + 1) != '"') { *p = *(p + 1); p++; } *p = '\0'; } else { /* Truncate on first space */ WCHAR *p = lpResult; while (*p != ' ' && *p != '\0') p++; *p = '\0'; } } } else /* Check win.ini */ { /* Toss the leading dot */ extension++; if (GetProfileStringW(L"extensions", extension, L"", command, ARRAY_SIZE(command)) > 0) { if (wcslen(command) != 0) { wcscpy(lpResult, command); tok = wcschr(lpResult, '^'); /* should be ^.extension? */ if (tok != NULL) { tok[0] = '\0'; wcscat(lpResult, xlpFile); /* what if no dir in xlpFile? */ tok = wcschr(command, '^'); /* see above */ if ((tok != NULL) && (wcslen(tok) > 5)) { wcscat(lpResult, &tok[5]); } } retval = 33; /* FIXME - see above */ } } }
TRACE("returning path %s, retval %d\n", debugstr_w(lpResult), retval); return retval;}
/****************************************************************** * dde_cb * * callback for the DDE connection. not really useful */static HDDEDATA CALLBACK dde_cb(UINT uType, UINT uFmt, HCONV hConv, HSZ hsz1, HSZ hsz2, HDDEDATA hData, ULONG_PTR dwData1, ULONG_PTR dwData2){ TRACE("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n", uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2); return NULL;}
/****************************************************************** * dde_connect * * ShellExecute helper. Used to do an operation with a DDE connection * * Handles both the direct connection (try #1), and if it fails, * launching an application and trying (#2) to connect to it * */static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec, const WCHAR* lpFile, WCHAR *env, LPCWSTR szCommandline, LPITEMIDLIST pidl, SHELL_ExecuteW32 execfunc, const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out){ WCHAR regkey[256]; WCHAR * endkey = regkey + wcslen(key); WCHAR app[256], topic[256], ifexec[256], static_res[256]; CHeapPtr<WCHAR, CLocalAllocator> dynamic_res; WCHAR * res; LONG applen, topiclen, ifexeclen; WCHAR * exec; DWORD ddeInst = 0; DWORD tid; DWORD resultLen, endkeyLen; HSZ hszApp, hszTopic; HCONV hConv; HDDEDATA hDdeData; unsigned ret = SE_ERR_NOASSOC; BOOL unicode = !(GetVersion() & 0x80000000);
if (strlenW(key) + 1 > ARRAY_SIZE(regkey)) { FIXME("input parameter %s larger than buffer\n", debugstr_w(key)); return 2; } wcscpy(regkey, key); endkeyLen = ARRAY_SIZE(regkey) - (endkey - regkey); if (strlenW(L"\\application") + 1 > endkeyLen) { FIXME("endkey %s overruns buffer\n", debugstr_w(L"\\application")); return 2; } wcscpy(endkey, L"\\application"); applen = sizeof(app); if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, app, &applen) != ERROR_SUCCESS) { WCHAR command[1024], fullpath[MAX_PATH]; LPWSTR ptr = NULL; DWORD ret = 0;
/* Get application command from start string and find filename of application */ if (*start == '"') { if (strlenW(start + 1) + 1 > ARRAY_SIZE(command)) { FIXME("size of input parameter %s larger than buffer\n", debugstr_w(start + 1)); return 2; } wcscpy(command, start + 1); if ((ptr = wcschr(command, '"'))) * ptr = 0; ret = SearchPathW(NULL, command, L".exe", ARRAY_SIZE(fullpath), fullpath, &ptr); } else { LPCWSTR p; LPWSTR space; for (p = start; (space = const_cast<LPWSTR>(strchrW(p, ' '))); p = space + 1) { int idx = space - start; memcpy(command, start, idx * sizeof(WCHAR)); command[idx] = '\0'; if ((ret = SearchPathW(NULL, command, L".exe", ARRAY_SIZE(fullpath), fullpath, &ptr))) break; } if (!ret) ret = SearchPathW(NULL, start, L".exe", ARRAY_SIZE(fullpath), fullpath, &ptr); }
if (!ret) { ERR("Unable to find application path for command %s\n", debugstr_w(start)); return ERROR_ACCESS_DENIED; } if (strlenW(ptr) + 1 > ARRAY_SIZE(app)) { FIXME("size of found path %s larger than buffer\n", debugstr_w(ptr)); return 2; } wcscpy(app, ptr);
/* Remove extensions (including .so) */ ptr = app + wcslen(app) - 3; if (ptr > app && !wcscmp(ptr, L".so")) *ptr = 0;
ptr = const_cast<LPWSTR>(strrchrW(app, '.')); assert(ptr); *ptr = 0; }
if (strlenW(L"\\topic") + 1 > endkeyLen) { FIXME("endkey %s overruns buffer\n", debugstr_w(L"\\topic")); return 2; } wcscpy(endkey, L"\\topic"); topiclen = sizeof(topic); if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, topic, &topiclen) != ERROR_SUCCESS) { wcscpy(topic, L"System"); }
if (unicode) { if (DdeInitializeW(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR) return 2; } else { if (DdeInitializeA(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR) return 2; }
hszApp = DdeCreateStringHandleW(ddeInst, app, CP_WINUNICODE); hszTopic = DdeCreateStringHandleW(ddeInst, topic, CP_WINUNICODE);
hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL); exec = ddeexec; if (!hConv) { TRACE("Launching %s\n", debugstr_w(start)); ret = execfunc(start, env, TRUE, psei, psei_out); if (ret <= 32) { TRACE("Couldn't launch\n"); goto error; } /* if ddeexec is NULL, then we just need to exit here */ if (ddeexec == NULL) { TRACE("Exiting because ddeexec is NULL. ret=42.\n"); /* See https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew */ /* for reason why we use 42 here and also "Shell32_apitest ShellExecuteW" regression test */ return 42; } /* if ddeexec is 'empty string', then we just need to exit here */ if (wcscmp(ddeexec, L"") == 0) { TRACE("Exiting because ddeexec is 'empty string'. ret=42.\n"); /* See https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew */ /* for reason why we use 42 here and also "Shell32_apitest ShellExecuteW" regression test */ return 42; } hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL); if (!hConv) { TRACE("Couldn't connect. ret=%d\n", ret); DdeUninitialize(ddeInst); SetLastError(ERROR_DDE_FAIL); return 30; /* whatever */ } if (strlenW(L"\\ifexec") + 1 > endkeyLen) { FIXME("endkey %s overruns buffer\n", debugstr_w(L"\\ifexec")); return 2; } strcpyW(endkey, L"\\ifexec"); ifexeclen = sizeof(ifexec); if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, ifexec, &ifexeclen) == ERROR_SUCCESS) { exec = ifexec; } }
SHELL_ArgifyW(static_res, ARRAY_SIZE(static_res), exec, lpFile, pidl, szCommandline, &resultLen, NULL); if (resultLen > ARRAY_SIZE(static_res)) { dynamic_res.Allocate(resultLen); res = dynamic_res; SHELL_ArgifyW(dynamic_res, resultLen, exec, lpFile, pidl, szCommandline, NULL, NULL); } else res = static_res; TRACE("%s %s => %s\n", debugstr_w(exec), debugstr_w(lpFile), debugstr_w(res));
/* It's documented in the KB 330337 that IE has a bug and returns * error DMLERR_NOTPROCESSED on XTYP_EXECUTE request. */ if (unicode) hDdeData = DdeClientTransaction((LPBYTE)res, (strlenW(res) + 1) * sizeof(WCHAR), hConv, 0L, 0, XTYP_EXECUTE, 30000, &tid); else { DWORD lenA = WideCharToMultiByte(CP_ACP, 0, res, -1, NULL, 0, NULL, NULL); CHeapPtr<char, CLocalAllocator> resA; resA.Allocate(lenA); WideCharToMultiByte(CP_ACP, 0, res, -1, resA, lenA, NULL, NULL); hDdeData = DdeClientTransaction( (LPBYTE)(LPSTR)resA, lenA, hConv, 0L, 0, XTYP_EXECUTE, 10000, &tid ); } if (hDdeData) DdeFreeDataHandle(hDdeData); else WARN("DdeClientTransaction failed with error %04x\n", DdeGetLastError(ddeInst)); ret = 33;
DdeDisconnect(hConv);
error: DdeUninitialize(ddeInst);
return ret;}
/************************************************************************* * execute_from_key [Internal] */static UINT_PTR execute_from_key(LPCWSTR key, LPCWSTR lpFile, WCHAR *env, LPCWSTR szCommandline, LPCWSTR executable_name, SHELL_ExecuteW32 execfunc, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out){ WCHAR cmd[256], param[1024], ddeexec[256]; DWORD cmdlen = sizeof(cmd), ddeexeclen = sizeof(ddeexec); UINT_PTR retval = SE_ERR_NOASSOC; DWORD resultLen; LPWSTR tmp;
TRACE("%s %s %s %s %s\n", debugstr_w(key), debugstr_w(lpFile), debugstr_w(env), debugstr_w(szCommandline), debugstr_w(executable_name));
cmd[0] = '\0'; param[0] = '\0';
/* Get the application from the registry */ if (RegQueryValueW(HKEY_CLASSES_ROOT, key, cmd, (LONG *)&cmdlen) == ERROR_SUCCESS) { TRACE("got cmd: %s\n", debugstr_w(cmd));
/* Is there a replace() function anywhere? */ cmdlen /= sizeof(WCHAR); if (cmdlen >= ARRAY_SIZE(cmd)) cmdlen = ARRAY_SIZE(cmd) - 1; cmd[cmdlen] = '\0'; SHELL_ArgifyW(param, ARRAY_SIZE(param), cmd, lpFile, (LPITEMIDLIST)psei->lpIDList, szCommandline, &resultLen, (psei->lpDirectory && *psei->lpDirectory) ? psei->lpDirectory : NULL); if (resultLen > ARRAY_SIZE(param)) ERR("Argify buffer not large enough, truncating\n"); }
/* Get the parameters needed by the application from the associated ddeexec key */ tmp = const_cast<LPWSTR>(strstrW(key, L"command")); assert(tmp); wcscpy(tmp, L"ddeexec");
if (RegQueryValueW(HKEY_CLASSES_ROOT, key, ddeexec, (LONG *)&ddeexeclen) == ERROR_SUCCESS) { TRACE("Got ddeexec %s => %s\n", debugstr_w(key), debugstr_w(ddeexec)); if (!param[0]) strcpyW(param, executable_name); retval = dde_connect(key, param, ddeexec, lpFile, env, szCommandline, (LPITEMIDLIST)psei->lpIDList, execfunc, psei, psei_out); } else if (param[0]) { TRACE("executing: %s\n", debugstr_w(param)); retval = execfunc(param, env, FALSE, psei, psei_out); } else WARN("Nothing appropriate found for %s\n", debugstr_w(key));
return retval;}
/************************************************************************* * FindExecutableA [SHELL32.@] */HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult){ HINSTANCE retval; WCHAR *wFile = NULL, *wDirectory = NULL; WCHAR wResult[MAX_PATH];
if (lpFile) __SHCloneStrAtoW(&wFile, lpFile); if (lpDirectory) __SHCloneStrAtoW(&wDirectory, lpDirectory);
retval = FindExecutableW(wFile, wDirectory, wResult); WideCharToMultiByte(CP_ACP, 0, wResult, -1, lpResult, MAX_PATH, NULL, NULL); SHFree(wFile); SHFree(wDirectory);
TRACE("returning %s\n", lpResult); return retval;}
/************************************************************************* * FindExecutableW [SHELL32.@] * * This function returns the executable associated with the specified file * for the default verb. * * PARAMS * lpFile [I] The file to find the association for. This must refer to * an existing file otherwise FindExecutable fails and returns * SE_ERR_FNF. * lpResult [O] Points to a buffer into which the executable path is * copied. This parameter must not be NULL otherwise * FindExecutable() segfaults. The buffer must be of size at * least MAX_PATH characters. * * RETURNS * A value greater than 32 on success, less than or equal to 32 otherwise. * See the SE_ERR_* constants. * * NOTES * On Windows XP and 2003, FindExecutable() seems to first convert the * filename into 8.3 format, thus taking into account only the first three * characters of the extension, and expects to find an association for those. * However other Windows versions behave sanely. */HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpResult){ UINT_PTR retval; WCHAR old_dir[MAX_PATH], res[MAX_PATH]; DWORD cch = _countof(res); LPCWSTR dirs[2];
TRACE("File %s, Dir %s\n", debugstr_w(lpFile), debugstr_w(lpDirectory));
*lpResult = UNICODE_NULL;
GetCurrentDirectoryW(_countof(old_dir), old_dir);
if (lpDirectory && *lpDirectory) { SetCurrentDirectoryW(lpDirectory); dirs[0] = lpDirectory; } else { dirs[0] = old_dir; } dirs[1] = NULL;
if (!GetShortPathNameW(lpFile, res, _countof(res))) StringCchCopyW(res, _countof(res), lpFile);
if (PathResolveW(res, dirs, PRF_TRYPROGRAMEXTENSIONS | PRF_FIRSTDIRDEF)) { // NOTE: The last parameter of this AssocQueryStringW call is "strange" in Windows. if (PathIsExeW(res) || SUCCEEDED(AssocQueryStringW(ASSOCF_NONE, ASSOCSTR_EXECUTABLE, res, NULL, res, &cch))) { StringCchCopyW(lpResult, MAX_PATH, res); retval = 42; } else { retval = SE_ERR_NOASSOC; } } else { retval = SE_ERR_FNF; }
TRACE("returning %s\n", debugstr_w(lpResult)); SetCurrentDirectoryW(old_dir); return (HINSTANCE)retval;}
/* FIXME: is this already implemented somewhere else? */static HKEY ShellExecute_GetClassKey(const SHELLEXECUTEINFOW *sei){ if ((sei->fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSKEY) return sei->hkeyClass;
HKEY hKey = NULL; if (sei->fMask & SEE_MASK_CLASSNAME) { TRACE("class = %s\n", debugstr_w(sei->lpClass)); RegOpenKeyExW(HKEY_CLASSES_ROOT, sei->lpClass, 0, KEY_READ, &hKey); return hKey; } PCWSTR ext = PathFindExtensionW(sei->lpFile); TRACE("ext = %s\n", debugstr_w(ext)); if (!StrIsNullOrEmpty(ext) && SUCCEEDED(HCR_GetProgIdKeyOfExtension(ext, &hKey, FALSE))) return hKey; return NULL;}
static HRESULT shellex_get_dataobj( LPSHELLEXECUTEINFOW sei, CComPtr<IDataObject>& dataObj){ CComHeapPtr<ITEMIDLIST> allocatedPidl; LPITEMIDLIST pidl = NULL;
if (sei->fMask & SEE_MASK_CLASSALL) // FIXME: This makes no sense? SEE_MASK_IDLIST? { pidl = (LPITEMIDLIST)sei->lpIDList; } else { WCHAR fullpath[MAX_PATH]; BOOL ret;
fullpath[0] = 0; ret = GetFullPathNameW(sei->lpFile, MAX_PATH, fullpath, NULL); if (!ret) return HRESULT_FROM_WIN32(GetLastError());
pidl = ILCreateFromPathW(fullpath); allocatedPidl.Attach(pidl); } return SHELL_GetUIObjectOfAbsoluteItem(NULL, pidl, IID_PPV_ARG(IDataObject, &dataObj));}
static HRESULT shellex_run_context_menu_default(IShellExtInit *obj, LPSHELLEXECUTEINFOW sei){ CComPtr<IContextMenu> cm = NULL; CMINVOKECOMMANDINFOEX ici; MENUITEMINFOW info; WCHAR string[0x80]; INT i, n, def = -1; HMENU hmenu = 0; HRESULT r;
TRACE("%p %p\n", obj, sei);
r = obj->QueryInterface(IID_PPV_ARG(IContextMenu, &cm)); if (FAILED(r)) return r;
hmenu = CreateMenu(); if (!hmenu) goto end;
/* the number of the last menu added is returned in r */ r = cm->QueryContextMenu(hmenu, 0, 0x20, 0x7fff, CMF_DEFAULTONLY); if (FAILED(r)) goto end;
n = GetMenuItemCount(hmenu); for (i = 0; i < n; i++) { memset(&info, 0, sizeof(info)); info.cbSize = sizeof info; info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID; info.dwTypeData = string; info.cch = sizeof string; string[0] = 0; GetMenuItemInfoW(hmenu, i, TRUE, &info);
TRACE("menu %d %s %08x %08lx %08x %08x\n", i, debugstr_w(string), info.fState, info.dwItemData, info.fType, info.wID); if ((!sei->lpVerb && (info.fState & MFS_DEFAULT)) || (sei->lpVerb && !lstrcmpiW(sei->lpVerb, string))) { def = i; break; } }
r = E_FAIL; if (def == -1) goto end;
memset(&ici, 0, sizeof ici); ici.cbSize = sizeof ici; ici.fMask = (sei->fMask & SEE_CMIC_COMMON_BASICFLAGS) | CMIC_MASK_UNICODE; ici.nShow = sei->nShow; ici.lpVerb = MAKEINTRESOURCEA(def); ici.hwnd = sei->hwnd; ici.lpParametersW = sei->lpParameters;
r = cm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
TRACE("invoke command returned %08x\n", r);
end: if (hmenu) DestroyMenu( hmenu ); return r;}
static HRESULT shellex_load_object_and_run(HKEY hkey, LPCGUID guid, LPSHELLEXECUTEINFOW sei){ TRACE("%p %s %p\n", hkey, debugstr_guid(guid), sei);
CCoInit coInit;
if (FAILED_UNEXPECTEDLY(coInit.hr)) return coInit.hr;
CComPtr<IShellExtInit> obj; HRESULT hr = CoCreateInstance(*guid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellExtInit, &obj)); if (FAILED_UNEXPECTEDLY(hr)) return hr;
CComPtr<IDataObject> dataobj; hr = shellex_get_dataobj(sei, dataobj); if (FAILED_UNEXPECTEDLY(hr)) return hr;
hr = obj->Initialize(NULL, dataobj, hkey); if (FAILED_UNEXPECTEDLY(hr)) return hr;
CComPtr<IObjectWithSite> ows; hr = obj->QueryInterface(IID_PPV_ARG(IObjectWithSite, &ows)); if (FAILED_UNEXPECTEDLY(hr)) return hr;
ows->SetSite(NULL);
return shellex_run_context_menu_default(obj, sei);}
static HRESULT shellex_get_contextmenu(LPSHELLEXECUTEINFOW sei, CComPtr<IContextMenu>& cm){ CComHeapPtr<ITEMIDLIST> allocatedPidl; LPITEMIDLIST pidl = NULL;
if (sei->lpIDList) { pidl = (LPITEMIDLIST)sei->lpIDList; } else { SFGAOF sfga = 0; HRESULT hr = SHParseDisplayName(sei->lpFile, NULL, &allocatedPidl, SFGAO_STORAGECAPMASK, &sfga); if (FAILED(hr)) { WCHAR Buffer[MAX_PATH] = {}; // FIXME: MAX_PATH..... UINT retval = SHELL_FindExecutable(sei->lpDirectory, sei->lpFile, sei->lpVerb, Buffer, _countof(Buffer), NULL, NULL, NULL, sei->lpParameters); if (retval <= 32) return HRESULT_FROM_WIN32(retval);
hr = SHParseDisplayName(Buffer, NULL, &allocatedPidl, SFGAO_STORAGECAPMASK, &sfga); // This should not happen, we found it... if (FAILED_UNEXPECTEDLY(hr)) return hr; }
pidl = allocatedPidl; }
CComPtr<IShellFolder> shf; LPCITEMIDLIST pidllast = NULL; HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shf), &pidllast); if (FAILED(hr)) return hr;
return shf->GetUIObjectOf(NULL, 1, &pidllast, IID_NULL_PPV_ARG(IContextMenu, &cm));}
static HRESULT ShellExecute_ContextMenuVerb(LPSHELLEXECUTEINFOW sei){ TRACE("%p\n", sei);
CCoInit coInit;
if (FAILED_UNEXPECTEDLY(coInit.hr)) return coInit.hr;
CComPtr<IContextMenu> cm; HRESULT hr = shellex_get_contextmenu(sei, cm); if (FAILED_UNEXPECTEDLY(hr)) return hr;
CComHeapPtr<char> verb, parameters, dir; __SHCloneStrWtoA(&verb, sei->lpVerb); __SHCloneStrWtoA(¶meters, sei->lpParameters); __SHCloneStrWtoA(&dir, sei->lpDirectory);
BOOL fDefault = StrIsNullOrEmpty(sei->lpVerb); CMINVOKECOMMANDINFOEX ici = { sizeof(ici) }; ici.fMask = SeeFlagsToCmicFlags(sei->fMask) | CMIC_MASK_UNICODE; ici.nShow = sei->nShow; if (!fDefault) { ici.lpVerb = verb; ici.lpVerbW = sei->lpVerb; } ici.hwnd = sei->hwnd; ici.lpParameters = parameters; ici.lpParametersW = sei->lpParameters; ici.lpDirectory = dir; ici.lpDirectoryW = sei->lpDirectory; ici.dwHotKey = sei->dwHotKey; ici.hIcon = sei->hIcon; if (ici.fMask & (CMIC_MASK_HASLINKNAME | CMIC_MASK_HASTITLE)) ici.lpTitleW = sei->lpClass;
enum { idFirst = 1, idLast = 0x7fff }; HMENU hMenu = CreatePopupMenu(); // Note: Windows does not pass CMF_EXTENDEDVERBS so "hidden" verbs cannot be executed hr = cm->QueryContextMenu(hMenu, 0, idFirst, idLast, fDefault ? CMF_DEFAULTONLY : 0); if (!FAILED_UNEXPECTEDLY(hr)) { if (fDefault) { INT uDefault = GetMenuDefaultItem(hMenu, FALSE, 0); uDefault = (uDefault != -1) ? uDefault - idFirst : 0; ici.lpVerb = MAKEINTRESOURCEA(uDefault); ici.lpVerbW = MAKEINTRESOURCEW(uDefault); }
hr = cm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici); if (!FAILED_UNEXPECTEDLY(hr)) hr = S_OK; }
DestroyMenu(hMenu);
return hr;}
/************************************************************************* * ShellExecute_FromContextMenu [Internal] */static LONG ShellExecute_FromContextMenuHandlers( LPSHELLEXECUTEINFOW sei ){ HKEY hkey, hkeycm = 0; WCHAR szguid[39]; HRESULT hr; GUID guid; DWORD i; LONG r;
TRACE("%s\n", debugstr_w(sei->lpFile));
hkey = ShellExecute_GetClassKey(sei); if (!hkey) return ERROR_FUNCTION_FAILED;
// FIXME: Words cannot describe how broken this is, all of it needs to die r = RegOpenKeyW(hkey, L"shellex\\ContextMenuHandlers", &hkeycm); if (r == ERROR_SUCCESS) { i = 0; while (1) { r = RegEnumKeyW(hkeycm, i++, szguid, ARRAY_SIZE(szguid)); if (r != ERROR_SUCCESS) break;
hr = CLSIDFromString(szguid, &guid); if (SUCCEEDED(hr)) { /* stop at the first one that succeeds in running */ hr = shellex_load_object_and_run(hkey, &guid, sei); if (SUCCEEDED(hr)) break; } } RegCloseKey(hkeycm); }
if (hkey != sei->hkeyClass) RegCloseKey(hkey); return r;}
static UINT_PTR SHELL_quote_and_execute(LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR wszKeyname, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc);
static UINT_PTR SHELL_execute_class(LPCWSTR wszApplicationName, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc){ WCHAR execCmd[1024], classname[1024]; /* launch a document by fileclass like 'WordPad.Document.1' */ /* the Commandline contains 'c:\Path\wordpad.exe "%1"' */ /* FIXME: wcmd should not be of a fixed size. Fixed to 1024, MAX_PATH is way too short! */ ULONG cmask = (psei->fMask & SEE_MASK_CLASSALL); DWORD resultLen; BOOL done; UINT_PTR rslt;
/* FIXME: remove following block when SHELL_quote_and_execute supports hkeyClass parameter */ if (cmask != SEE_MASK_CLASSNAME) { WCHAR wcmd[1024]; HCR_GetExecuteCommandW((cmask == SEE_MASK_CLASSKEY) ? psei->hkeyClass : NULL, (cmask == SEE_MASK_CLASSNAME) ? psei->lpClass : NULL, psei->lpVerb, execCmd, sizeof(execCmd));
/* FIXME: get the extension of lpFile, check if it fits to the lpClass */ TRACE("SEE_MASK_CLASSNAME->%s, doc->%s\n", debugstr_w(execCmd), debugstr_w(wszApplicationName));
wcmd[0] = '\0'; done = SHELL_ArgifyW(wcmd, ARRAY_SIZE(wcmd), execCmd, wszApplicationName, (LPITEMIDLIST)psei->lpIDList, psei->lpParameters, &resultLen, (psei->lpDirectory && *psei->lpDirectory) ? psei->lpDirectory : NULL); if (!done && wszApplicationName[0]) {#if 0 // Given HKCR\.test=SZ:"test" and HKCR\test\shell\open\command=SZ:"cmd.exe /K echo.Hello", no filename is // appended on Windows when there is no %1 nor %L when executed with: shlextdbg.exe /shellexec=c:\file.test /INVOKE strcatW(wcmd, L" "); if (*wszApplicationName != '"') { strcatW(wcmd, L"\""); strcatW(wcmd, wszApplicationName); strcatW(wcmd, L"\""); } else strcatW(wcmd, wszApplicationName);#endif } if (resultLen > ARRAY_SIZE(wcmd)) ERR("Argify buffer not large enough... truncating\n"); return execfunc(wcmd, NULL, FALSE, psei, psei_out); }
strcpyW(classname, psei->lpClass); rslt = SHELL_FindExecutableByVerb(psei->lpVerb, NULL, classname, execCmd, sizeof(execCmd));
TRACE("SHELL_FindExecutableByVerb returned %u (%s, %s)\n", (unsigned int)rslt, debugstr_w(classname), debugstr_w(execCmd)); if (33 > rslt) return rslt; rslt = SHELL_quote_and_execute( execCmd, L"", classname, wszApplicationName, NULL, psei, psei_out, execfunc ); return rslt;
}
static BOOL SHELL_translate_idlist(LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters, DWORD parametersLen, LPWSTR wszApplicationName, DWORD dwApplicationNameLen){ WCHAR buffer[MAX_PATH]; BOOL appKnownSingular = FALSE;
/* last chance to translate IDList: now also allow CLSID paths */ if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW((LPCITEMIDLIST)sei->lpIDList, buffer, ARRAY_SIZE(buffer)))) { if (buffer[0] == ':' && buffer[1] == ':') { /* open shell folder for the specified class GUID */ if (strlenW(buffer) + 1 > parametersLen) ERR("parameters len exceeds buffer size (%i > %i), truncating\n", lstrlenW(buffer) + 1, parametersLen); lstrcpynW(wszParameters, buffer, parametersLen); if (strlenW(L"explorer.exe") > dwApplicationNameLen) ERR("application len exceeds buffer size (%i), truncating\n", dwApplicationNameLen); lstrcpynW(wszApplicationName, L"explorer.exe", dwApplicationNameLen); appKnownSingular = TRUE;
sei->fMask &= ~SEE_MASK_INVOKEIDLIST; } else { WCHAR target[max(MAX_PATH, _countof(buffer))]; DWORD attribs; DWORD resultLen; /* Check if we're executing a directory and if so use the handler for the Folder class */ strcpyW(target, buffer); attribs = GetFileAttributesW(buffer); if (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY) && HCR_GetExecuteCommandW(0, L"Folder", sei->lpVerb, buffer, sizeof(buffer))) { SHELL_ArgifyW(wszApplicationName, dwApplicationNameLen, buffer, target, (LPITEMIDLIST)sei->lpIDList, NULL, &resultLen, !StrIsNullOrEmpty(sei->lpDirectory) ? sei->lpDirectory : NULL); if (resultLen > dwApplicationNameLen) ERR("Argify buffer not large enough... truncating\n"); // FIXME: Report this to the caller? appKnownSingular = FALSE; // HACKFIX: We really want the !appKnownSingular code in SHELL_execute to split the // parameters for us but we cannot guarantee that the exe in the registry is quoted. // We have now turned 'explorer.exe "%1" into 'explorer.exe "c:\path\from\pidl"' and // need to split to application and parameters. LPCWSTR params = PathGetArgsW(wszApplicationName); lstrcpynW(wszParameters, params, parametersLen); PathRemoveArgsW(wszApplicationName); PathUnquoteSpacesW(wszApplicationName); appKnownSingular = TRUE; } sei->fMask &= ~SEE_MASK_INVOKEIDLIST; } } return appKnownSingular;}
static BOOLSHELL_InvokePidl( _In_ LPSHELLEXECUTEINFOW sei, _In_ LPCITEMIDLIST pidl){ // Bind pidl CComPtr<IShellFolder> psfFolder; LPCITEMIDLIST pidlLast; HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psfFolder), &pidlLast); if (FAILED_UNEXPECTEDLY(hr)) return FALSE;
// Get the context menu to invoke a command CComPtr<IContextMenu> pCM; hr = psfFolder->GetUIObjectOf(NULL, 1, &pidlLast, IID_NULL_PPV_ARG(IContextMenu, &pCM)); if (FAILED_UNEXPECTEDLY(hr)) return FALSE;
// Invoke a command CMINVOKECOMMANDINFO ici = { sizeof(ici) }; ici.fMask = (sei->fMask & SEE_CMIC_COMMON_BASICFLAGS) & ~CMIC_MASK_UNICODE; // FIXME: Unicode? ici.nShow = sei->nShow; ici.hwnd = sei->hwnd; char szVerb[VERBKEY_CCHMAX]; if (sei->lpVerb && sei->lpVerb[0]) { WideCharToMultiByte(CP_ACP, 0, sei->lpVerb, -1, szVerb, _countof(szVerb), NULL, NULL); szVerb[_countof(szVerb) - 1] = ANSI_NULL; // Avoid buffer overrun ici.lpVerb = szVerb; } else // The default verb? { HMENU hMenu = CreatePopupMenu(); const INT idCmdFirst = 1, idCmdLast = 0x7FFF; hr = pCM->QueryContextMenu(hMenu, 0, idCmdFirst, idCmdLast, CMF_DEFAULTONLY); if (FAILED_UNEXPECTEDLY(hr)) { DestroyMenu(hMenu); return FALSE; }
INT nDefaultID = GetMenuDefaultItem(hMenu, FALSE, 0); DestroyMenu(hMenu); if (nDefaultID == -1) nDefaultID = idCmdFirst;
ici.lpVerb = MAKEINTRESOURCEA(nDefaultID - idCmdFirst); } hr = pCM->InvokeCommand(&ici);
return !FAILED_UNEXPECTEDLY(hr);}
static UINT_PTR SHELL_quote_and_execute(LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR wszKeyname, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc){ UINT_PTR retval; DWORD len; CHeapPtr<WCHAR, CLocalAllocator> wszQuotedCmd;
/* Length of quotes plus length of command plus NULL terminator */ len = 2 + lstrlenW(wcmd) + 1; if (wszParameters[0]) { /* Length of space plus length of parameters */ len += 1 + lstrlenW(wszParameters); } wszQuotedCmd.Allocate(len); /* Must quote to handle case where cmd contains spaces, * else security hole if malicious user creates executable file "C:\\Program" */ strcpyW(wszQuotedCmd, L"\""); strcatW(wszQuotedCmd, wcmd); strcatW(wszQuotedCmd, L"\""); if (wszParameters[0]) { strcatW(wszQuotedCmd, L" "); strcatW(wszQuotedCmd, wszParameters); }
TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(wszKeyname));
if (*wszKeyname) retval = execute_from_key(wszKeyname, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out); else retval = execfunc(wszQuotedCmd, env, FALSE, psei, psei_out);
return retval;}
static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wcmd, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc){ UINT_PTR retval; CHeapPtr<WCHAR, CLocalAllocator> lpstrProtocol; LPCWSTR lpstrRes; INT iSize; DWORD len;
lpstrRes = strchrW(lpFile, ':'); if (lpstrRes) iSize = lpstrRes - lpFile; else iSize = strlenW(lpFile);
TRACE("Got URL: %s\n", debugstr_w(lpFile)); /* Looking for ...<protocol>\shell\<lpVerb>\command */ len = iSize + lstrlenW(L"\\shell\\") + lstrlenW(L"\\command") + 1; if (psei->lpVerb && *psei->lpVerb) len += lstrlenW(psei->lpVerb); else len += lstrlenW(L"open"); // FIXME: Use HCR_GetExecuteCommandW or AssocAPI lpstrProtocol.Allocate(len); memcpy(lpstrProtocol, lpFile, iSize * sizeof(WCHAR)); lpstrProtocol[iSize] = '\0'; strcatW(lpstrProtocol, L"\\shell\\"); strcatW(lpstrProtocol, psei->lpVerb && *psei->lpVerb ? psei->lpVerb : L"open"); strcatW(lpstrProtocol, L"\\command");
retval = execute_from_key(lpstrProtocol, lpFile, NULL, psei->lpParameters, wcmd, execfunc, psei, psei_out);
return retval;}
static void do_error_dialog(UINT_PTR retval, HWND hwnd, PCWSTR filename){ WCHAR msg[2048]; DWORD_PTR msgArguments[3] = { (DWORD_PTR)filename, 0, 0 }; const DWORD error_code = GetLastError();
if (retval == SE_ERR_NOASSOC) LoadStringW(shell32_hInstance, IDS_SHLEXEC_NOASSOC, msg, ARRAY_SIZE(msg)); else FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, error_code, LANG_USER_DEFAULT, msg, ARRAY_SIZE(msg), (va_list*)msgArguments);
MessageBoxW(hwnd, msg, NULL, MB_ICONERROR); SetLastError(error_code); // Restore}
static WCHAR *expand_environment( const WCHAR *str ){ CHeapPtr<WCHAR, CLocalAllocator> buf; DWORD len;
len = ExpandEnvironmentStringsW(str, NULL, 0); if (!len) return NULL;
if (!buf.Allocate(len)) return NULL;
len = ExpandEnvironmentStringsW(str, buf, len); if (!len) return NULL;
return buf.Detach();}
/************************************************************************* * SHELL_execute [Internal] */static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc){ static const DWORD unsupportedFlags = SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT | SEE_MASK_ASYNCOK;
DWORD len; UINT_PTR retval = SE_ERR_NOASSOC; BOOL appKnownSingular = FALSE;
/* make a local copy of the LPSHELLEXECUTEINFO structure and work with this from now on */ sei->hProcess = NULL; SHELLEXECUTEINFOW sei_tmp = *sei;
TRACE("mask=0x%08x hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n", sei_tmp.fMask, sei_tmp.hwnd, debugstr_w(sei_tmp.lpVerb), debugstr_w(sei_tmp.lpFile), debugstr_w(sei_tmp.lpParameters), debugstr_w(sei_tmp.lpDirectory), sei_tmp.nShow, ((sei_tmp.fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME) ? debugstr_w(sei_tmp.lpClass) : "not used");
// Call hooks before expanding and resolving strings HRESULT hr = TryShellExecuteHooks(sei); if (hr != S_FALSE) { int err = Win32ErrFromHInst(sei->hInstApp); if (err <= 0) { sei->hInstApp = (HINSTANCE)UlongToHandle(42); return TRUE; } SetLastError(err); return FALSE; }
/* make copies of all path/command strings */ CHeapPtr<WCHAR, CLocalAllocator> wszApplicationName; DWORD dwApplicationNameLen = MAX_PATH + 2; if (!sei_tmp.lpFile) { wszApplicationName.Allocate(dwApplicationNameLen); *wszApplicationName = '\0'; } else if (*sei_tmp.lpFile == '\"' && sei_tmp.lpFile[(len = strlenW(sei_tmp.lpFile))-1] == '\"') { if(len-1 >= dwApplicationNameLen) dwApplicationNameLen = len;
wszApplicationName.Allocate(dwApplicationNameLen); memcpy(wszApplicationName, sei_tmp.lpFile + 1, len * sizeof(WCHAR));
if(len > 2) wszApplicationName[len-2] = '\0'; appKnownSingular = TRUE;
TRACE("wszApplicationName=%s\n", debugstr_w(wszApplicationName)); } else { DWORD l = strlenW(sei_tmp.lpFile) + 1; if(l > dwApplicationNameLen) dwApplicationNameLen = l + 1; wszApplicationName.Allocate(dwApplicationNameLen); memcpy(wszApplicationName, sei_tmp.lpFile, l * sizeof(WCHAR));
if (wszApplicationName[2] == 0 && wszApplicationName[1] == L':' && ((L'A' <= wszApplicationName[0] && wszApplicationName[0] <= L'Z') || (L'a' <= wszApplicationName[0] && wszApplicationName[0] <= L'z'))) { // 'C:' --> 'C:\' PathAddBackslashW(wszApplicationName); } }
WCHAR parametersBuffer[1024]; LPWSTR wszParameters = parametersBuffer; CHeapPtr<WCHAR, CLocalAllocator> wszParamAlloc; DWORD parametersLen = _countof(parametersBuffer);
if (sei_tmp.lpParameters) { len = lstrlenW(sei_tmp.lpParameters) + 1; if (len > parametersLen) { wszParamAlloc.Allocate(len); wszParameters = wszParamAlloc; parametersLen = len; } strcpyW(wszParameters, sei_tmp.lpParameters); } else *wszParameters = L'\0';
// Get the working directory WCHAR dirBuffer[MAX_PATH]; LPWSTR wszDir = dirBuffer; wszDir[0] = UNICODE_NULL; CHeapPtr<WCHAR, CLocalAllocator> wszDirAlloc; if (sei_tmp.lpDirectory && *sei_tmp.lpDirectory) { if (sei_tmp.fMask & SEE_MASK_DOENVSUBST) { LPWSTR tmp = expand_environment(sei_tmp.lpDirectory); if (tmp) { wszDirAlloc.Attach(tmp); wszDir = wszDirAlloc; } } else { __SHCloneStrW(&wszDirAlloc, sei_tmp.lpDirectory); if (wszDirAlloc) wszDir = wszDirAlloc; } } if (!wszDir[0]) { ::GetCurrentDirectoryW(_countof(dirBuffer), dirBuffer); wszDir = dirBuffer; } // NOTE: ShellExecute should accept the invalid working directory for historical reason. if (!PathIsDirectoryW(wszDir)) { INT iDrive = PathGetDriveNumberW(wszDir); if (iDrive >= 0) { PathStripToRootW(wszDir); if (!PathIsDirectoryW(wszDir)) { ::GetWindowsDirectoryW(dirBuffer, _countof(dirBuffer)); wszDir = dirBuffer; } } }
/* adjust string pointers to point to the new buffers */ sei_tmp.lpFile = wszApplicationName; sei_tmp.lpParameters = wszParameters; sei_tmp.lpDirectory = wszDir;
if (sei_tmp.fMask & unsupportedFlags) { FIXME("flags ignored: 0x%08x\n", sei_tmp.fMask & unsupportedFlags); }
/* process the IDList */ if (sei_tmp.fMask & SEE_MASK_IDLIST && (sei_tmp.fMask & SEE_MASK_INVOKEIDLIST) != SEE_MASK_INVOKEIDLIST) { LPCITEMIDLIST pidl = (LPCITEMIDLIST)sei_tmp.lpIDList; hr = SHGetNameAndFlagsW(pidl, SHGDN_FORPARSING, wszApplicationName, dwApplicationNameLen, NULL); if (FAILED(hr)) { if (dwApplicationNameLen) *wszApplicationName = UNICODE_NULL; if (!_ILIsDesktop(pidl)) TRACE("Unable to get PIDL parsing path\n"); } appKnownSingular = TRUE; TRACE("-- idlist=%p (%s)\n", sei_tmp.lpIDList, debugstr_w(wszApplicationName)); }
if ((sei_tmp.fMask & SEE_MASK_DOENVSUBST) && !StrIsNullOrEmpty(sei_tmp.lpFile)) { WCHAR *tmp = expand_environment(sei_tmp.lpFile); if (tmp) { wszApplicationName.Attach(tmp); sei_tmp.lpFile = wszApplicationName; } }
if ((sei_tmp.fMask & SEE_MASK_INVOKEIDLIST) == SEE_MASK_INVOKEIDLIST) { hr = ShellExecute_ContextMenuVerb(&sei_tmp); if (SUCCEEDED(hr)) { sei->hInstApp = (HINSTANCE)42; return TRUE; } }
if (ERROR_SUCCESS == ShellExecute_FromContextMenuHandlers(&sei_tmp)) { sei->hInstApp = (HINSTANCE) 33; return TRUE; }
if (sei_tmp.fMask & SEE_MASK_CLASSALL) { retval = SHELL_execute_class(wszApplicationName, &sei_tmp, sei, execfunc); if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI)) { OPENASINFO Info;
//FIXME // need full path
Info.pcszFile = wszApplicationName; Info.pcszClass = NULL; Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC;
//if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK) DBG_UNREFERENCED_LOCAL_VARIABLE(Info); do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName); } return retval > 32; }
if (!(sei_tmp.fMask & SEE_MASK_IDLIST) && // Not an ID List (StrCmpNIW(sei_tmp.lpFile, L"shell:", 6) == 0 || StrCmpNW(sei_tmp.lpFile, L"::{", 3) == 0)) { CComHeapPtr<ITEMIDLIST> pidlParsed; hr = SHParseDisplayName(sei_tmp.lpFile, NULL, &pidlParsed, 0, NULL); if (SUCCEEDED(hr) && SHELL_InvokePidl(&sei_tmp, pidlParsed)) { sei_tmp.hInstApp = (HINSTANCE)UlongToHandle(42); return TRUE; } }
/* Has the IDList not yet been translated? */ if (sei_tmp.fMask & SEE_MASK_IDLIST) { appKnownSingular = SHELL_translate_idlist( &sei_tmp, wszParameters, parametersLen, wszApplicationName, dwApplicationNameLen ); }
/* convert file URLs */ if (UrlIsFileUrlW(sei_tmp.lpFile)) { CHeapPtr<WCHAR, CLocalAllocator> buf; DWORD size = MAX_PATH; if (!buf.Allocate(size) || FAILED(PathCreateFromUrlW(sei_tmp.lpFile, buf, &size, 0))) { SetLastError(ERROR_OUTOFMEMORY); return FALSE; }
wszApplicationName.Attach(buf.Detach()); sei_tmp.lpFile = wszApplicationName; }
/* Else, try to execute the filename */ TRACE("execute: %s,%s,%s\n", debugstr_w(wszApplicationName), debugstr_w(wszParameters), debugstr_w(wszDir));
/* separate out command line arguments from executable file name */ LPCWSTR lpFile = sei_tmp.lpFile; if (!*sei_tmp.lpParameters && !appKnownSingular) { /* If the executable path is quoted, handle the rest of the command line as parameters. */ if (sei_tmp.lpFile[0] == L'"') { LPWSTR pszArgs = PathGetArgsW(wszApplicationName); PathRemoveArgsW(wszApplicationName); PathUnquoteSpacesW(wszApplicationName); parametersLen = lstrlenW(pszArgs); if (parametersLen < _countof(parametersBuffer)) { StringCchCopyW(parametersBuffer, _countof(parametersBuffer), pszArgs); wszParameters = parametersBuffer; } else { wszParamAlloc.Attach(StrDupW(pszArgs)); wszParameters = wszParamAlloc; } } /* We have to test sei instead of sei_tmp because sei_tmp had its * input fMask modified above in SHELL_translate_idlist. * This code is needed to handle the case where we only have an * lpIDList with multiple CLSID/PIDL's (not 'My Computer' only) */ else if ((sei->fMask & SEE_MASK_IDLIST) == SEE_MASK_IDLIST) { WCHAR buffer[MAX_PATH], xlpFile[MAX_PATH]; LPWSTR space, s;
LPWSTR beg = wszApplicationName; for(s = beg; (space = const_cast<LPWSTR>(strchrW(s, L' '))); s = space + 1) { int idx = space - sei_tmp.lpFile; memcpy(buffer, sei_tmp.lpFile, idx * sizeof(WCHAR)); buffer[idx] = '\0';
if (SearchPathW(*sei_tmp.lpDirectory ? sei_tmp.lpDirectory : NULL, buffer, L".exe", _countof(xlpFile), xlpFile, NULL)) { /* separate out command from parameter string */ LPCWSTR p = space + 1;
while(isspaceW(*p)) ++p;
strcpyW(wszParameters, p); *space = L'\0';
break; } } } }
WCHAR wcmdBuffer[1024]; LPWSTR wcmd = wcmdBuffer; DWORD wcmdLen = _countof(wcmdBuffer); CHeapPtr<WCHAR, CLocalAllocator> wcmdAlloc;
/* Only execute if it has an executable extension */ if (PathIsExeW(lpFile)) { len = lstrlenW(wszApplicationName) + 3; if (sei_tmp.lpParameters[0]) len += 1 + lstrlenW(wszParameters); if (len > wcmdLen) { wcmdAlloc.Allocate(len); wcmd = wcmdAlloc; wcmdLen = len; } swprintf(wcmd, L"\"%s\"", (LPWSTR)wszApplicationName); if (sei_tmp.lpParameters[0]) { strcatW(wcmd, L" "); strcatW(wcmd, wszParameters); }
retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei); if (retval > 32) return TRUE; }
/* Else, try to find the executable */ WCHAR wszKeyname[256]; CHeapPtr<WCHAR, CLocalAllocator> env; wcmd[0] = UNICODE_NULL; retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, wszKeyname, &env, (LPITEMIDLIST)sei_tmp.lpIDList, sei_tmp.lpParameters); if (retval > 32) /* Found */ { retval = SHELL_quote_and_execute(wcmd, wszParameters, wszKeyname, wszApplicationName, env, &sei_tmp, sei, execfunc); } else if (PathIsDirectoryW(lpFile)) { WCHAR wExec[MAX_PATH]; CHeapPtr<WCHAR, CLocalAllocator> lpQuotedFile; if (lpQuotedFile.Allocate(strlenW(lpFile) + 3)) { retval = SHELL_FindExecutable(sei_tmp.lpDirectory, L"explorer", L"open", wExec, MAX_PATH, NULL, &env, NULL, NULL); if (retval > 32) { swprintf(lpQuotedFile, L"\"%s\"", lpFile); retval = SHELL_quote_and_execute(wExec, lpQuotedFile, wszKeyname, wszApplicationName, env, &sei_tmp, sei, execfunc); } } else retval = 0; /* Out of memory */ } else if (PathIsURLW(lpFile)) /* File not found, check for URL */ { retval = SHELL_execute_url(lpFile, wcmd, &sei_tmp, sei, execfunc ); } /* Check if file specified is in the form www.??????.*** */ else if (!strncmpiW(lpFile, L"www", 3)) { /* if so, prefix lpFile with http:// and call ShellExecute */ WCHAR lpstrTmpFile[256]; strcpyW(lpstrTmpFile, L"http://"); strcatW(lpstrTmpFile, lpFile); // FIXME: Possible buffer overflow // FIXME: This will not correctly return the hProcess to the caller retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0); }
TRACE("retval %lu\n", retval);
if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI)) { if (retval == SE_ERR_NOASSOC && !(sei->fMask & SEE_MASK_CLASSALL)) retval = InvokeOpenWith(sei_tmp.hwnd, *sei); if (retval <= 32) do_error_dialog(retval, sei_tmp.hwnd, lpFile); }
sei->hInstApp = (HINSTANCE)(retval > 32 ? 33 : retval);
return retval > 32;}
/************************************************************************* * ShellExecuteA [SHELL32.290] */HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd){ SHELLEXECUTEINFOA sei;
TRACE("%p,%s,%s,%s,%s,%d\n", hWnd, debugstr_a(lpVerb), debugstr_a(lpFile), debugstr_a(lpParameters), debugstr_a(lpDirectory), iShowCmd);
sei.cbSize = sizeof(sei); sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_UNKNOWN_0x1000; sei.hwnd = hWnd; sei.lpVerb = lpVerb; sei.lpFile = lpFile; sei.lpParameters = lpParameters; sei.lpDirectory = lpDirectory; sei.nShow = iShowCmd; sei.lpIDList = 0; sei.lpClass = 0; sei.hkeyClass = 0; sei.dwHotKey = 0; sei.hProcess = 0;
if (!(SHGetAppCompatFlags(SHACF_WIN95SHLEXEC) & SHACF_WIN95SHLEXEC)) sei.fMask |= SEE_MASK_NOASYNC; ShellExecuteExA(&sei); return sei.hInstApp;}
static DWORDShellExecute_Normal(_Inout_ LPSHELLEXECUTEINFOW sei){ // FIXME if (SHELL_execute(sei, SHELL_ExecuteW)) return ERROR_SUCCESS; DWORD err = GetLastError();#if DBG if (!err) DbgPrint("FIXME: Failed with error 0 on '%ls'\n", sei->lpFile);#endif return err ? err : ERROR_FILE_NOT_FOUND;}
static VOIDShellExecute_ShowError( _In_ const SHELLEXECUTEINFOW *ExecInfo, _In_opt_ LPCWSTR pszCaption, _In_ DWORD dwError){ // FIXME: Show error message}
/************************************************************************* * ShellExecuteExA [SHELL32.292] */BOOLWINAPIDECLSPEC_HOTPATCHShellExecuteExA(LPSHELLEXECUTEINFOA sei){ SHELLEXECUTEINFOW seiW; BOOL ret; WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL, *wClass = NULL;
TRACE("%p\n", sei);
if (sei->cbSize != sizeof(SHELLEXECUTEINFOA)) { sei->hInstApp = (HINSTANCE)ERROR_ACCESS_DENIED; SetLastError(ERROR_ACCESS_DENIED); return FALSE; }
memcpy(&seiW, sei, sizeof(SHELLEXECUTEINFOW));
seiW.cbSize = sizeof(SHELLEXECUTEINFOW);
if (sei->lpVerb) seiW.lpVerb = __SHCloneStrAtoW(&wVerb, sei->lpVerb);
if (sei->lpFile) seiW.lpFile = __SHCloneStrAtoW(&wFile, sei->lpFile);
if (sei->lpParameters) seiW.lpParameters = __SHCloneStrAtoW(&wParameters, sei->lpParameters);
if (sei->lpDirectory) seiW.lpDirectory = __SHCloneStrAtoW(&wDirectory, sei->lpDirectory);
if ((sei->fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME && sei->lpClass) seiW.lpClass = __SHCloneStrAtoW(&wClass, sei->lpClass); else seiW.lpClass = NULL;
ret = ShellExecuteExW(&seiW);
sei->hInstApp = seiW.hInstApp;
if (sei->fMask & SEE_MASK_NOCLOSEPROCESS) sei->hProcess = seiW.hProcess;
SHFree(wVerb); SHFree(wFile); SHFree(wParameters); SHFree(wDirectory); SHFree(wClass);
return ret;}
/************************************************************************* * ShellExecuteExW [SHELL32.293] */BOOLWINAPIDECLSPEC_HOTPATCHShellExecuteExW(LPSHELLEXECUTEINFOW sei){ HRESULT hrCoInit; DWORD dwError; ULONG fOldMask;
if (sei->cbSize != sizeof(SHELLEXECUTEINFOW)) { sei->hInstApp = (HINSTANCE)UlongToHandle(SE_ERR_ACCESSDENIED); SetLastError(ERROR_ACCESS_DENIED); return FALSE; }
hrCoInit = SHCoInitializeAnyApartment();
if (SHRegGetBoolUSValueW(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", L"MaximizeApps", FALSE, FALSE)) { switch (sei->nShow) { case SW_SHOW: case SW_SHOWDEFAULT: case SW_SHOWNORMAL: case SW_RESTORE: sei->nShow = SW_SHOWMAXIMIZED; break; default: break; } }
fOldMask = sei->fMask;
if (!(fOldMask & SEE_MASK_NOASYNC) && SHELL_InRunDllProcess()) sei->fMask |= SEE_MASK_WAITFORINPUTIDLE | SEE_MASK_NOASYNC;
dwError = ShellExecute_Normal(sei);
if (dwError && dwError != ERROR_DLL_NOT_FOUND && dwError != ERROR_CANCELLED) ShellExecute_ShowError(sei, NULL, dwError);
sei->fMask = fOldMask;
if (SUCCEEDED(hrCoInit)) CoUninitialize();
if (dwError) SetLastError(dwError);
return dwError == ERROR_SUCCESS;}
/************************************************************************* * ShellExecuteW [SHELL32.294] */HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd){ SHELLEXECUTEINFOW sei;
TRACE("\n"); sei.cbSize = sizeof(sei); sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_UNKNOWN_0x1000; sei.hwnd = hwnd; sei.lpVerb = lpVerb; sei.lpFile = lpFile; sei.lpParameters = lpParameters; sei.lpDirectory = lpDirectory; sei.nShow = nShowCmd; sei.lpIDList = 0; sei.lpClass = 0; sei.hkeyClass = 0; sei.dwHotKey = 0; sei.hProcess = 0;
if (!(SHGetAppCompatFlags(SHACF_WIN95SHLEXEC) & SHACF_WIN95SHLEXEC)) sei.fMask |= SEE_MASK_NOASYNC; ShellExecuteExW(&sei); return sei.hInstApp;}
/************************************************************************* * WOWShellExecute [SHELL32.@] * * FIXME: the callback function most likely doesn't work the same way on Windows. */EXTERN_C HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd, void *callback){ SHELLEXECUTEINFOW seiW; WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL; HANDLE hProcess = 0;
seiW.lpVerb = lpVerb ? __SHCloneStrAtoW(&wVerb, lpVerb) : NULL; seiW.lpFile = lpFile ? __SHCloneStrAtoW(&wFile, lpFile) : NULL; seiW.lpParameters = lpParameters ? __SHCloneStrAtoW(&wParameters, lpParameters) : NULL; seiW.lpDirectory = lpDirectory ? __SHCloneStrAtoW(&wDirectory, lpDirectory) : NULL;
seiW.cbSize = sizeof(seiW); seiW.fMask = 0; seiW.hwnd = hWnd; seiW.nShow = iShowCmd; seiW.lpIDList = 0; seiW.lpClass = 0; seiW.hkeyClass = 0; seiW.dwHotKey = 0; seiW.hProcess = hProcess;
SHELL_execute(&seiW, (SHELL_ExecuteW32)callback);
SHFree(wVerb); SHFree(wFile); SHFree(wParameters); SHFree(wDirectory); return seiW.hInstApp;}
/************************************************************************* * OpenAs_RunDLLW [SHELL32.@] */EXTERN_C void WINAPIOpenAs_RunDLLW(HWND hwnd, HINSTANCE hinst, LPCWSTR cmdline, int cmdshow){ OPENASINFO info = { cmdline, NULL, OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC }; TRACE("%p, %p, %s, %d\n", hwnd, hinst, debugstr_w(cmdline), cmdshow); SHOpenWithDialog(hwnd, &info);}
/************************************************************************* * OpenAs_RunDLLA [SHELL32.@] */EXTERN_C void WINAPIOpenAs_RunDLLA(HWND hwnd, HINSTANCE hinst, LPCSTR cmdline, int cmdshow){ LPWSTR pszCmdLineW = NULL; TRACE("%p, %p, %s, %d\n", hwnd, hinst, debugstr_a(cmdline), cmdshow);
if (cmdline) __SHCloneStrAtoW(&pszCmdLineW, cmdline); OpenAs_RunDLLW(hwnd, hinst, pszCmdLineW, cmdshow); SHFree(pszCmdLineW);}
/*************************************************************************/
static LPCWSTRSplitParams(LPCWSTR psz, LPWSTR pszArg0, size_t cchArg0){ LPCWSTR pch; size_t ich = 0; if (*psz == L'"') { // 1st argument is quoted. the string in quotes is quoted 1st argument. // [pch] --> [pszArg0+ich] for (pch = psz + 1; *pch && ich + 1 < cchArg0; ++ich, ++pch) { if (*pch == L'"' && pch[1] == L'"') { // doubled double quotations found! pszArg0[ich] = L'"'; } else if (*pch == L'"') { // single double quotation found! ++pch; break; } else { // otherwise pszArg0[ich] = *pch; } } } else { // 1st argument is unquoted. non-space sequence is 1st argument. // [pch] --> [pszArg0+ich] for (pch = psz; *pch && !iswspace(*pch) && ich + 1 < cchArg0; ++ich, ++pch) { pszArg0[ich] = *pch; } } pszArg0[ich] = 0;
// skip space while (iswspace(*pch)) ++pch;
return pch;}
HRESULT WINAPI ShellExecCmdLine( HWND hwnd, LPCWSTR pwszCommand, LPCWSTR pwszStartDir, int nShow, LPVOID pUnused, DWORD dwSeclFlags){ SHELLEXECUTEINFOW info; DWORD dwSize, dwType, dwFlags = SEE_MASK_DOENVSUBST | SEE_MASK_NOASYNC; LPCWSTR pszVerb = NULL; WCHAR szFile[MAX_PATH], szFile2[MAX_PATH]; HRESULT hr; LPCWSTR pchParams; LPWSTR lpCommand = NULL;
if (pwszCommand == NULL) RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 1, (ULONG_PTR*)pwszCommand);
__SHCloneStrW(&lpCommand, pwszCommand); StrTrimW(lpCommand, L" \t");
if (dwSeclFlags & SECL_NO_UI) dwFlags |= SEE_MASK_FLAG_NO_UI; if (dwSeclFlags & SECL_LOG_USAGE) dwFlags |= SEE_MASK_FLAG_LOG_USAGE; if (dwSeclFlags & SECL_USE_IDLIST) dwFlags |= SEE_MASK_INVOKEIDLIST;
if (dwSeclFlags & SECL_RUNAS) { dwSize = 0; hr = AssocQueryStringW(ASSOCF_NONE, ASSOCSTR_COMMAND, lpCommand, L"runas", NULL, &dwSize); if (SUCCEEDED(hr) && dwSize != 0) { pszVerb = L"runas"; } }
if (PathIsURLW(lpCommand) || UrlIsW(lpCommand, URLIS_APPLIABLE)) { StringCchCopyW(szFile, _countof(szFile), lpCommand); pchParams = NULL; } else { PCWSTR apPathList[2];
pchParams = SplitParams(lpCommand, szFile, _countof(szFile)); if (szFile[0] != UNICODE_NULL && szFile[1] == L':' && szFile[2] == UNICODE_NULL) { PathAddBackslashW(szFile); }
WCHAR szCurDir[MAX_PATH]; GetCurrentDirectoryW(_countof(szCurDir), szCurDir); if (pwszStartDir) { SetCurrentDirectoryW(pwszStartDir); }
if ((PathIsRelativeW(szFile) && GetFullPathNameW(szFile, _countof(szFile2), szFile2, NULL) && PathFileExistsW(szFile2)) || SearchPathW(NULL, szFile, NULL, _countof(szFile2), szFile2, NULL)) { StringCchCopyW(szFile, _countof(szFile), szFile2); }
apPathList[0] = pwszStartDir; apPathList[1] = NULL; PathFindOnPathExW(szFile, apPathList, WHICH_DEFAULT);
if (!(dwSeclFlags & SECL_ALLOW_NONEXE)) { if (!GetBinaryTypeW(szFile, &dwType)) { SHFree(lpCommand);
if (!(dwSeclFlags & SECL_NO_UI)) { WCHAR szText[128 + MAX_PATH], szFormat[128]; LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat)); StringCchPrintfW(szText, _countof(szText), szFormat, szFile); MessageBoxW(hwnd, szText, NULL, MB_ICONERROR); } return CO_E_APPNOTFOUND; } } else { if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES) { SHFree(lpCommand);
if (!(dwSeclFlags & SECL_NO_UI)) { WCHAR szText[128 + MAX_PATH], szFormat[128]; LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat)); StringCchPrintfW(szText, _countof(szText), szFormat, szFile); MessageBoxW(hwnd, szText, NULL, MB_ICONERROR); } return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } } }
ZeroMemory(&info, sizeof(info)); info.cbSize = sizeof(info); info.fMask = dwFlags; info.hwnd = hwnd; info.lpVerb = pszVerb; info.lpFile = szFile; info.lpParameters = (pchParams && *pchParams) ? pchParams : NULL; info.lpDirectory = pwszStartDir; info.nShow = nShow; UINT error = ShellExecuteExW(&info) ? ERROR_SUCCESS : GetLastError(); SHFree(lpCommand); return HRESULT_FROM_WIN32(error);}
/************************************************************************* * RealShellExecuteExA (SHELL32.266) */EXTERN_CHINSTANCE WINAPIRealShellExecuteExA( _In_opt_ HWND hwnd, _In_opt_ LPCSTR lpOperation, _In_opt_ LPCSTR lpFile, _In_opt_ LPCSTR lpParameters, _In_opt_ LPCSTR lpDirectory, _In_opt_ LPSTR lpReturn, _In_opt_ LPCSTR lpTitle, _In_opt_ LPVOID lpReserved, _In_ INT nCmdShow, _Out_opt_ PHANDLE lphProcess, _In_ DWORD dwFlags){ SHELLEXECUTEINFOA ExecInfo;
TRACE("(%p, %s, %s, %s, %s, %p, %s, %p, %u, %p, %lu)\n", hwnd, debugstr_a(lpOperation), debugstr_a(lpFile), debugstr_a(lpParameters), debugstr_a(lpDirectory), lpReserved, debugstr_a(lpTitle), lpReserved, nCmdShow, lphProcess, dwFlags);
ZeroMemory(&ExecInfo, sizeof(ExecInfo)); ExecInfo.cbSize = sizeof(ExecInfo); ExecInfo.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_UNKNOWN_0x1000; ExecInfo.hwnd = hwnd; ExecInfo.lpVerb = lpOperation; ExecInfo.lpFile = lpFile; ExecInfo.lpParameters = lpParameters; ExecInfo.lpDirectory = lpDirectory; ExecInfo.nShow = (WORD)nCmdShow;
if (lpReserved) { ExecInfo.fMask |= SEE_MASK_USE_RESERVED; ExecInfo.hInstApp = (HINSTANCE)lpReserved; }
if (lpTitle) { ExecInfo.fMask |= SEE_MASK_HASTITLE; ExecInfo.lpClass = lpTitle; }
if (dwFlags & 1) ExecInfo.fMask |= SEE_MASK_FLAG_SEPVDM;
if (dwFlags & 2) ExecInfo.fMask |= SEE_MASK_NO_CONSOLE;
if (lphProcess == NULL) { ShellExecuteExA(&ExecInfo); } else { ExecInfo.fMask |= SEE_MASK_NOCLOSEPROCESS; ShellExecuteExA(&ExecInfo); *lphProcess = ExecInfo.hProcess; }
return ExecInfo.hInstApp;}
/************************************************************************* * RealShellExecuteExW (SHELL32.267) */EXTERN_CHINSTANCE WINAPIRealShellExecuteExW( _In_opt_ HWND hwnd, _In_opt_ LPCWSTR lpOperation, _In_opt_ LPCWSTR lpFile, _In_opt_ LPCWSTR lpParameters, _In_opt_ LPCWSTR lpDirectory, _In_opt_ LPWSTR lpReturn, _In_opt_ LPCWSTR lpTitle, _In_opt_ LPVOID lpReserved, _In_ INT nCmdShow, _Out_opt_ PHANDLE lphProcess, _In_ DWORD dwFlags){ SHELLEXECUTEINFOW ExecInfo;
TRACE("(%p, %s, %s, %s, %s, %p, %s, %p, %u, %p, %lu)\n", hwnd, debugstr_w(lpOperation), debugstr_w(lpFile), debugstr_w(lpParameters), debugstr_w(lpDirectory), lpReserved, debugstr_w(lpTitle), lpReserved, nCmdShow, lphProcess, dwFlags);
ZeroMemory(&ExecInfo, sizeof(ExecInfo)); ExecInfo.cbSize = sizeof(ExecInfo); ExecInfo.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_UNKNOWN_0x1000; ExecInfo.hwnd = hwnd; ExecInfo.lpVerb = lpOperation; ExecInfo.lpFile = lpFile; ExecInfo.lpParameters = lpParameters; ExecInfo.lpDirectory = lpDirectory; ExecInfo.nShow = (WORD)nCmdShow;
if (lpReserved) { ExecInfo.fMask |= SEE_MASK_USE_RESERVED; ExecInfo.hInstApp = (HINSTANCE)lpReserved; }
if (lpTitle) { ExecInfo.fMask |= SEE_MASK_HASTITLE; ExecInfo.lpClass = lpTitle; }
if (dwFlags & 1) ExecInfo.fMask |= SEE_MASK_FLAG_SEPVDM;
if (dwFlags & 2) ExecInfo.fMask |= SEE_MASK_NO_CONSOLE;
if (lphProcess == NULL) { ShellExecuteExW(&ExecInfo); } else { ExecInfo.fMask |= SEE_MASK_NOCLOSEPROCESS; ShellExecuteExW(&ExecInfo); *lphProcess = ExecInfo.hProcess; }
return ExecInfo.hInstApp;}
/************************************************************************* * RealShellExecuteA (SHELL32.265) */EXTERN_CHINSTANCE WINAPIRealShellExecuteA( _In_opt_ HWND hwnd, _In_opt_ LPCSTR lpOperation, _In_opt_ LPCSTR lpFile, _In_opt_ LPCSTR lpParameters, _In_opt_ LPCSTR lpDirectory, _In_opt_ LPSTR lpReturn, _In_opt_ LPCSTR lpTitle, _In_opt_ LPVOID lpReserved, _In_ INT nCmdShow, _Out_opt_ PHANDLE lphProcess){ return RealShellExecuteExA(hwnd, lpOperation, lpFile, lpParameters, lpDirectory, lpReturn, lpTitle, lpReserved, nCmdShow, lphProcess, 0);}
/************************************************************************* * RealShellExecuteW (SHELL32.268) */EXTERN_CHINSTANCE WINAPIRealShellExecuteW( _In_opt_ HWND hwnd, _In_opt_ LPCWSTR lpOperation, _In_opt_ LPCWSTR lpFile, _In_opt_ LPCWSTR lpParameters, _In_opt_ LPCWSTR lpDirectory, _In_opt_ LPWSTR lpReturn, _In_opt_ LPCWSTR lpTitle, _In_opt_ LPVOID lpReserved, _In_ INT nCmdShow, _Out_opt_ PHANDLE lphProcess){ return RealShellExecuteExW(hwnd, lpOperation, lpFile, lpParameters, lpDirectory, lpReturn, lpTitle, lpReserved, nCmdShow, lphProcess, 0);}
/************************************************************************* * PathProcessCommandW [Internal] * * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-pathprocesscommand * @see ./wine/shellpath.c */EXTERN_C LONGPathProcessCommandW( _In_ PCWSTR pszSrc, _Out_writes_opt_(dwBuffSize) PWSTR pszDest, _In_ INT cchDest, _In_ DWORD dwFlags){ TRACE("%s, %p, %d, 0x%X\n", wine_dbgstr_w(pszSrc), pszDest, cchDest, dwFlags);
if (!pszSrc) return -1;
CStringW szPath; PCWSTR pchArg = NULL;
if (*pszSrc == L'"') // Quoted? { ++pszSrc;
PCWSTR pch = wcschr(pszSrc, L'"'); if (pch) { szPath.SetString(pszSrc, pch - pszSrc); pchArg = pch + 1; } else { szPath = pszSrc; }
if ((dwFlags & PPCF_FORCEQUALIFY) || PathIsRelativeW(szPath)) { BOOL ret = PathResolveW(szPath.GetBuffer(MAX_PATH), NULL, PRF_TRYPROGRAMEXTENSIONS); szPath.ReleaseBuffer(); if (!ret) return -1; } } else // Not quoted? { BOOL resolved = FALSE; BOOL resolveRelative = PathIsRelativeW(pszSrc) || (dwFlags & PPCF_FORCEQUALIFY); INT cchPath = 0;
for (INT ich = 0; ; ++ich) { szPath += pszSrc[ich];
if (pszSrc[ich] && pszSrc[ich] != L' ') continue;
szPath = szPath.Left(ich);
if (resolveRelative && !PathResolveW(szPath.GetBuffer(MAX_PATH), NULL, PRF_TRYPROGRAMEXTENSIONS)) { szPath.ReleaseBuffer(); szPath += pszSrc[ich]; } else { szPath.ReleaseBuffer();
DWORD attrs = GetFileAttributesW(szPath); if (attrs != INVALID_FILE_ATTRIBUTES && (!(attrs & FILE_ATTRIBUTE_DIRECTORY) || !(dwFlags & PPCF_NODIRECTORIES))) { resolved = TRUE; pchArg = pszSrc + ich;
if (!(dwFlags & PPCF_LONGESTPOSSIBLE)) break;
cchPath = ich; break; } else if (!resolveRelative) { szPath += pszSrc[ich]; } }
if (!szPath[ich]) { szPath.ReleaseBuffer(); // Remove excessive '\0' break; } }
if (!resolved) return -1;
if (cchPath && (dwFlags & PPCF_LONGESTPOSSIBLE)) { szPath = szPath.Left(cchPath); pchArg = pszSrc + cchPath; } }
BOOL needsQuoting = (dwFlags & PPCF_ADDQUOTES) && wcschr(szPath, L' '); CStringW result = needsQuoting ? (L"\"" + szPath + L"\"") : szPath;
if (pchArg && (dwFlags & PPCF_ADDARGUMENTS)) result += pchArg;
LONG requiredSize = result.GetLength() + 1; if (!pszDest) return requiredSize;
if (requiredSize > cchDest || StringCchCopyW(pszDest, cchDest, result) != S_OK) return -1;
return requiredSize;}
// The common helper of ShellExec_RunDLLA and ShellExec_RunDLLWstatic VOIDShellExec_RunDLL_Helper( _In_opt_ HWND hwnd, _In_opt_ HINSTANCE hInstance, _In_ PCWSTR pszCmdLine, _In_ INT nCmdShow){ TRACE("(%p, %p, %s, 0x%X)\n", hwnd, hInstance, wine_dbgstr_w(pszCmdLine), nCmdShow);
if (!pszCmdLine || !*pszCmdLine) return;
// '?' enables us to specify the additional mask value ULONG fNewMask = SEE_MASK_NOASYNC; if (*pszCmdLine == L'?') // 1st question { INT MaskValue; if (StrToIntExW(pszCmdLine + 1, STIF_SUPPORT_HEX, &MaskValue)) fNewMask |= MaskValue;
PCWSTR pch2ndQuestion = StrChrW(pszCmdLine + 1, L'?'); // 2nd question if (pch2ndQuestion) pszCmdLine = pch2ndQuestion + 1; }
WCHAR szPath[2 * MAX_PATH]; DWORD dwFlags = PPCF_FORCEQUALIFY | PPCF_INCLUDEARGS | PPCF_ADDQUOTES; if (PathProcessCommandW(pszCmdLine, szPath, _countof(szPath), dwFlags) == -1) StrCpyNW(szPath, pszCmdLine, _countof(szPath));
// Split arguments from the path LPWSTR Args = PathGetArgsW(szPath); if (*Args) *(Args - 1) = UNICODE_NULL;
PathUnquoteSpacesW(szPath);
// Execute SHELLEXECUTEINFOW execInfo = { sizeof(execInfo) }; execInfo.fMask = fNewMask; execInfo.hwnd = hwnd; execInfo.lpFile = szPath; execInfo.lpParameters = Args; execInfo.nShow = nCmdShow; if (!ShellExecuteExW(&execInfo)) { DWORD dwError = GetLastError(); if (SHELL_InRunDllProcess()) // Is it a RUNDLL process? ExitProcess(dwError); // Terminate it now }}
/************************************************************************* * ShellExec_RunDLLA [SHELL32.358] * * @see https://www.hexacorn.com/blog/2024/11/30/1-little-known-secret-of-shellexec_rundll/ */EXTERN_CVOID WINAPIShellExec_RunDLLA( _In_opt_ HWND hwnd, _In_opt_ HINSTANCE hInstance, _In_ PCSTR pszCmdLine, _In_ INT nCmdShow){ CStringW strCmdLine = pszCmdLine; // Keep ShellExec_RunDLL_Helper(hwnd, hInstance, strCmdLine, nCmdShow);}
/************************************************************************* * ShellExec_RunDLLW [SHELL32.359] * * @see https://www.hexacorn.com/blog/2024/11/30/1-little-known-secret-of-shellexec_rundll/ */EXTERN_CVOID WINAPIShellExec_RunDLLW( _In_opt_ HWND hwnd, _In_opt_ HINSTANCE hInstance, _In_ PCWSTR pszCmdLine, _In_ INT nCmdShow){ ShellExec_RunDLL_Helper(hwnd, hInstance, pszCmdLine, nCmdShow);}