Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1110 lines
32 KiB

  1. #include "shellprv.h"
  2. #include <regstr.h>
  3. #include <shellp.h>
  4. #include "ole2dup.h"
  5. #include "ids.h"
  6. #include "defview.h"
  7. #include "lvutil.h"
  8. #include "idlcomm.h"
  9. #include "filetbl.h"
  10. #include "undo.h"
  11. #include "vdate.h"
  12. #include "cnctnpt.h"
  13. BOOL g_bCheckRunInSep = FALSE;
  14. HANDLE g_hCheckNow = NULL;
  15. HANDLE h_hRunDlgCS = NULL;
  16. const TCHAR c_szRunMRU[] = REGSTR_PATH_EXPLORER TEXT("\\RunMRU");
  17. const TCHAR c_szRunDlgReady[] = TEXT("MSShellRunDlgReady");
  18. const TCHAR c_szWaitingThreadID[] = TEXT("WaitingThreadID");
  19. BOOL RunDlgNotifyParent(HWND hDlg, HWND hwnd, LPTSTR pszCmd, LPCTSTR pszWorkingDir);
  20. void ExchangeWindowPos(HWND hwnd0, HWND hwnd1);
  21. #define WM_SETUPAUTOCOMPLETE (WM_APP)
  22. // implements the Dialog that can navigate through the Shell Name Space and ShellExec() commands.
  23. class CRunDlg : public IDropTarget
  24. {
  25. public:
  26. CRunDlg();
  27. // *** IUnknown ***
  28. STDMETHODIMP_(ULONG) AddRef(void);
  29. STDMETHODIMP_(ULONG) Release(void);
  30. STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj);
  31. // *** IDropTarget methods ***
  32. STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  33. STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  34. STDMETHODIMP DragLeave(void);
  35. STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  36. private:
  37. ~CRunDlg(void); // This is now an OLE Object and cannot be used as a normal Class.
  38. BOOL OKPushed(void);
  39. void ExitRunDlg(BOOL bOK);
  40. void InitRunDlg(HWND hDlg);
  41. void InitRunDlg2(HWND hDlg);
  42. void BrowsePushed(void);
  43. friend DWORD CALLBACK CheckRunInSeparateThreadProc(void *pv);
  44. friend BOOL_PTR CALLBACK RunDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  45. friend WINAPI RunFileDlg(HWND hwndParent, HICON hIcon, LPCTSTR pszWorkingDir, LPCTSTR pszTitle,
  46. LPCTSTR pszPrompt, DWORD dwFlags);
  47. LONG m_cRef;
  48. HWND m_hDlg;
  49. // parameters
  50. HICON m_hIcon;
  51. LPCTSTR m_pszWorkingDir;
  52. LPCTSTR m_pszTitle;
  53. LPCTSTR m_pszPrompt;
  54. DWORD m_dwFlags;
  55. HANDLE m_hEventReady;
  56. DWORD m_dwThreadId;
  57. BOOL _fDone : 1;
  58. BOOL _fAutoCompInitialized : 1;
  59. BOOL _fOleInited : 1;
  60. };
  61. // optimistic cache for this
  62. HANDLE g_hMRURunDlg = NULL;
  63. HANDLE OpenRunDlgMRU()
  64. {
  65. HANDLE hmru = InterlockedExchangePointer(&g_hMRURunDlg, NULL);
  66. if (hmru == NULL)
  67. {
  68. MRUINFO mi = {
  69. sizeof(MRUINFO),
  70. 26,
  71. MRU_CACHEWRITE,
  72. HKEY_CURRENT_USER,
  73. c_szRunMRU,
  74. NULL // NOTE: use default string compare
  75. // since this is a GLOBAL MRU
  76. } ;
  77. hmru = CreateMRUList(&mi);
  78. }
  79. return hmru;
  80. }
  81. void CloseRunDlgMRU(HANDLE hmru)
  82. {
  83. hmru = InterlockedExchangePointer(&g_hMRURunDlg, hmru);
  84. if (hmru)
  85. FreeMRUList(hmru); // race, destroy copy
  86. }
  87. STDAPI_(void) FlushRunDlgMRU(void)
  88. {
  89. CloseRunDlgMRU(NULL);
  90. }
  91. HRESULT CRunDlg_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  92. {
  93. if (punkOuter)
  94. return E_FAIL;
  95. *ppv = NULL;
  96. CRunDlg * p = new CRunDlg();
  97. if (p)
  98. {
  99. *ppv = SAFECAST(p, IDropTarget *);
  100. return S_OK;
  101. }
  102. return E_OUTOFMEMORY;
  103. }
  104. CRunDlg::CRunDlg() : m_cRef(1)
  105. {
  106. // This needs to be allocated in Zero Inited Memory.
  107. // ASSERT that all Member Variables are inited to Zero.
  108. ASSERT(!m_hDlg);
  109. ASSERT(!m_hIcon);
  110. ASSERT(!m_pszWorkingDir);
  111. ASSERT(!m_pszTitle);
  112. ASSERT(!m_pszPrompt);
  113. ASSERT(!m_dwFlags);
  114. ASSERT(!m_hEventReady);
  115. ASSERT(!_fDone);
  116. ASSERT(!m_dwThreadId);
  117. }
  118. CRunDlg::~CRunDlg()
  119. {
  120. }
  121. // IUnknown
  122. HRESULT CRunDlg::QueryInterface(REFIID riid, void **ppv)
  123. {
  124. static const QITAB qit[] =
  125. {
  126. QITABENT(CRunDlg, IDropTarget),
  127. { 0 },
  128. };
  129. return QISearch(this, qit, riid, ppv);
  130. }
  131. ULONG CRunDlg::AddRef()
  132. {
  133. return InterlockedIncrement(&m_cRef);
  134. }
  135. ULONG CRunDlg::Release()
  136. {
  137. if (InterlockedDecrement(&m_cRef))
  138. return m_cRef;
  139. delete this;
  140. return 0;
  141. }
  142. // IDropTarget
  143. STDMETHODIMP CRunDlg::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  144. {
  145. DAD_DragEnterEx3(m_hDlg, ptl, pdtobj);
  146. *pdwEffect &= DROPEFFECT_LINK | DROPEFFECT_COPY;
  147. return S_OK;
  148. }
  149. STDMETHODIMP CRunDlg::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  150. {
  151. DAD_DragMoveEx(m_hDlg, ptl);
  152. *pdwEffect &= DROPEFFECT_LINK | DROPEFFECT_COPY;
  153. return S_OK;
  154. }
  155. STDMETHODIMP CRunDlg::DragLeave(void)
  156. {
  157. DAD_DragLeave();
  158. return S_OK;
  159. }
  160. typedef struct {
  161. HRESULT (*pfnGetData)(STGMEDIUM *, LPTSTR pszFile);
  162. FORMATETC fmte;
  163. } DATA_HANDLER;
  164. HRESULT _GetHDROPFromData(STGMEDIUM *pmedium, LPTSTR pszPath)
  165. {
  166. return DragQueryFile((HDROP)pmedium->hGlobal, 0, pszPath, MAX_PATH) ? S_OK : E_FAIL;
  167. }
  168. HRESULT _GetText(STGMEDIUM *pmedium, LPTSTR pszPath)
  169. {
  170. LPCSTR psz = (LPCSTR)GlobalLock(pmedium->hGlobal);
  171. if (psz)
  172. {
  173. SHAnsiToTChar(psz, pszPath, MAX_PATH);
  174. GlobalUnlock(pmedium->hGlobal);
  175. return S_OK;
  176. }
  177. return E_FAIL;
  178. }
  179. HRESULT _GetUnicodeText(STGMEDIUM *pmedium, LPTSTR pszPath)
  180. {
  181. LPCWSTR pwsz = (LPCWSTR)GlobalLock(pmedium->hGlobal);
  182. if (pwsz)
  183. {
  184. SHUnicodeToTChar(pwsz, pszPath, MAX_PATH);
  185. GlobalUnlock(pmedium->hGlobal);
  186. return S_OK;
  187. }
  188. return E_FAIL;
  189. }
  190. STDMETHODIMP CRunDlg::Drop(IDataObject * pdtobj, DWORD grfKeyState,
  191. POINTL pt, DWORD *pdwEffect)
  192. {
  193. TCHAR szPath[MAX_PATH];
  194. DAD_DragLeave();
  195. szPath[0] = 0;
  196. DATA_HANDLER rg_data_handlers[] = {
  197. _GetHDROPFromData, {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
  198. _GetUnicodeText, {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
  199. _GetText, {g_cfShellURL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
  200. _GetText, {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL},
  201. };
  202. IEnumFORMATETC *penum;
  203. if (SUCCEEDED(pdtobj->EnumFormatEtc(DATADIR_GET, &penum)))
  204. {
  205. FORMATETC fmte;
  206. while (penum->Next(1, &fmte, NULL) == S_OK)
  207. {
  208. SHFree(fmte.ptd);
  209. fmte.ptd = NULL; // so nobody will look at it
  210. for (int i = 0; i < ARRAYSIZE(rg_data_handlers); i++)
  211. {
  212. STGMEDIUM medium;
  213. if ((rg_data_handlers[i].fmte.cfFormat == fmte.cfFormat) &&
  214. SUCCEEDED(pdtobj->GetData(&rg_data_handlers[i].fmte, &medium)))
  215. {
  216. HRESULT hres = rg_data_handlers[i].pfnGetData(&medium, szPath);
  217. ReleaseStgMedium(&medium);
  218. if (SUCCEEDED(hres))
  219. goto Done;
  220. }
  221. }
  222. }
  223. Done:
  224. penum->Release();
  225. }
  226. if (szPath[0])
  227. {
  228. TCHAR szText[MAX_PATH + MAX_PATH];
  229. GetDlgItemText(m_hDlg, IDD_COMMAND, szText, ARRAYSIZE(szText) - ARRAYSIZE(szPath));
  230. if (szText[0])
  231. lstrcat(szText, c_szSpace);
  232. if (StrChr(szPath, TEXT(' ')))
  233. PathQuoteSpaces(szPath); // there's a space in the file... add qutoes
  234. lstrcat(szText, szPath);
  235. SetDlgItemText(m_hDlg, IDD_COMMAND, szText);
  236. EnableOKButtonFromID(m_hDlg, IDD_COMMAND);
  237. if (g_hCheckNow)
  238. SetEvent(g_hCheckNow);
  239. *pdwEffect &= DROPEFFECT_COPY | DROPEFFECT_LINK;
  240. }
  241. else
  242. *pdwEffect = 0;
  243. return S_OK;
  244. }
  245. BOOL PromptForMedia(HWND hwnd, LPCTSTR pszPath)
  246. {
  247. BOOL fContinue = TRUE;
  248. TCHAR szPathTemp[MAX_URL_STRING];
  249. StrCpyN(szPathTemp, pszPath, ARRAYSIZE(szPathTemp));
  250. PathRemoveArgs(szPathTemp);
  251. PathUnquoteSpaces(szPathTemp);
  252. // We only want to check for media if it's a drive path
  253. // because the Start->Run dialog can receive all kinds of
  254. // wacky stuff. (Relative paths, URLs, App Path exes,
  255. // any shell exec hooks, etc.)
  256. if (-1 != PathGetDriveNumber(szPathTemp))
  257. {
  258. if (FAILED(SHPathPrepareForWrite(hwnd, NULL, szPathTemp, SHPPFW_IGNOREFILENAME)))
  259. fContinue = FALSE; // User decliened to insert or format media.
  260. }
  261. return fContinue;
  262. }
  263. BOOL CRunDlg::OKPushed(void)
  264. {
  265. TCHAR szText[MAX_PATH];
  266. BOOL fSuccess = FALSE;
  267. TCHAR szNotExp[MAX_PATH + 2];
  268. if (_fDone)
  269. return TRUE;
  270. // Get out of the "synchronized input queues" state
  271. if (m_dwThreadId)
  272. {
  273. AttachThreadInput(GetCurrentThreadId(), m_dwThreadId, FALSE);
  274. }
  275. // Get the command line and dialog title, leave some room for the slash on the end
  276. GetDlgItemText(m_hDlg, IDD_COMMAND, szNotExp, ARRAYSIZE(szNotExp) - 2);
  277. PathRemoveBlanks(szNotExp);
  278. // This used to happen only on NT, do it everywhere:
  279. SHExpandEnvironmentStrings(szNotExp, szText, ARRAYSIZE(szText));
  280. // We will go ahead if this isn't a file path. If it is, we
  281. if (PromptForMedia(m_hDlg, szText))
  282. {
  283. TCHAR szTitle[64];
  284. GetWindowText(m_hDlg, szTitle, ARRAYSIZE(szTitle));
  285. // Hide this dialog (REVIEW, to avoid save bits window flash)
  286. SetWindowPos(m_hDlg, 0, 0, 0, 0, 0, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER);
  287. //
  288. // HACK: We need to activate the owner window before we call
  289. // ShellexecCmdLine, so that our folder DDE code can find an
  290. // explorer window as the ForegroundWindow.
  291. //
  292. HWND hwndOwner = GetWindow(m_hDlg, GW_OWNER);
  293. if (hwndOwner)
  294. {
  295. SetActiveWindow(hwndOwner);
  296. }
  297. else
  298. {
  299. hwndOwner = m_hDlg;
  300. }
  301. int iRun = RunDlgNotifyParent(m_hDlg, hwndOwner, szText, m_pszWorkingDir);
  302. switch (iRun)
  303. {
  304. case RFR_NOTHANDLED:
  305. {
  306. DWORD dwFlags;
  307. if (m_dwFlags & RFD_USEFULLPATHDIR)
  308. {
  309. dwFlags = SECL_USEFULLPATHDIR;
  310. }
  311. else
  312. {
  313. dwFlags = 0;
  314. }
  315. if ((!(m_dwFlags & RFD_NOSEPMEMORY_BOX)) && (m_dwFlags & RFD_WOW_APP))
  316. {
  317. if (IsDlgButtonChecked(m_hDlg, IDD_RUNINSEPARATE) == 1)
  318. {
  319. if (IsDlgButtonChecked(m_hDlg, IDD_RUNINSEPARATE ) == 1 )
  320. {
  321. dwFlags |= SECL_SEPARATE_VDM;
  322. }
  323. }
  324. }
  325. dwFlags |= SECL_LOG_USAGE;
  326. fSuccess = ShellExecCmdLine(hwndOwner, szText, m_pszWorkingDir, SW_SHOWNORMAL, szTitle, dwFlags);
  327. }
  328. break;
  329. case RFR_SUCCESS:
  330. fSuccess = TRUE;
  331. break;
  332. case RFR_FAILURE:
  333. fSuccess = FALSE;
  334. break;
  335. }
  336. }
  337. // Get back into "synchronized input queues" state
  338. if (m_dwThreadId)
  339. {
  340. AttachThreadInput(GetCurrentThreadId(), m_dwThreadId, TRUE);
  341. }
  342. if (fSuccess)
  343. {
  344. HANDLE hmru = OpenRunDlgMRU();
  345. if (hmru)
  346. {
  347. // NB the old MRU format has a slash and the show cmd on the end
  348. // we need to maintain that so we don't end up with garbage on
  349. // the end of the line
  350. StrCatBuff(szNotExp, TEXT("\\1"), ARRAYSIZE(szNotExp));
  351. AddMRUString(hmru, szNotExp);
  352. CloseRunDlgMRU(hmru);
  353. }
  354. return TRUE;
  355. }
  356. // Something went wrong. Put the dialog back up.
  357. SetWindowPos(m_hDlg, 0, 0, 0, 0, 0, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER);
  358. if (!SetForegroundWindow(m_hDlg))
  359. {
  360. // HACKHACK:
  361. // If ShellHook is working on, SetForegroundWindow() can failed to
  362. // bring RunDlg to foreground window. To force focus to RunDlg, wait
  363. // a second and retry SetForegroundWindow() again.
  364. SHWaitForSendMessageThread(GetCurrentThread(), 300);
  365. SetForegroundWindow(m_hDlg);
  366. }
  367. SetFocus(GetDlgItem(m_hDlg, IDD_COMMAND));
  368. return FALSE;
  369. }
  370. void CRunDlg::ExitRunDlg(BOOL bOK)
  371. {
  372. if (!_fDone)
  373. {
  374. if (_fOleInited)
  375. {
  376. // Need to call oleinit/uninit, because if anyone else does it down the line,
  377. // and theirs is the last OleUninit, that will NULL out the clipboard hwnd, which
  378. // is what RevokeDragDrop uses to determine if it is being called on the same
  379. // thread as RegisterDragDrop. If the clipboard hwnd is NULL and therefore not
  380. // equal to the original, and it therefore thinks we're on a different thread,
  381. // it will bail, and thus won't release it's ref to CRunDlg ... leak!
  382. RevokeDragDrop(m_hDlg);
  383. OleUninitialize();
  384. }
  385. _fDone = TRUE;
  386. }
  387. if (!(m_dwFlags & RFD_NOSEPMEMORY_BOX))
  388. {
  389. g_bCheckRunInSep = FALSE;
  390. SetEvent(g_hCheckNow);
  391. }
  392. EndDialog(m_hDlg, bOK);
  393. }
  394. void CRunDlg::InitRunDlg(HWND hDlg)
  395. {
  396. HWND hCB;
  397. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)this);
  398. if (m_pszTitle)
  399. SetWindowText(hDlg, m_pszTitle);
  400. if (m_pszPrompt)
  401. SetDlgItemText(hDlg, IDD_PROMPT, m_pszPrompt);
  402. if (m_hIcon)
  403. Static_SetIcon(GetDlgItem(hDlg, IDD_ICON), m_hIcon);
  404. if (m_dwFlags & RFD_NOBROWSE)
  405. {
  406. HWND hBrowse = GetDlgItem(hDlg, IDD_BROWSE);
  407. ExchangeWindowPos(hBrowse, GetDlgItem(hDlg, IDCANCEL));
  408. ExchangeWindowPos(hBrowse, GetDlgItem(hDlg, IDOK));
  409. ShowWindow(hBrowse, SW_HIDE);
  410. }
  411. if (m_dwFlags & RFD_NOSHOWOPEN)
  412. ShowWindow(GetDlgItem(hDlg, IDD_RUNDLGOPENPROMPT), SW_HIDE);
  413. hCB = GetDlgItem(hDlg, IDD_COMMAND);
  414. SendMessage(hCB, CB_LIMITTEXT, MAX_PATH - 1, 0L);
  415. HANDLE hmru = OpenRunDlgMRU();
  416. if (hmru)
  417. {
  418. for (int nMax = EnumMRUList(hmru, -1, NULL, 0), i=0; i<nMax; ++i)
  419. {
  420. TCHAR szCommand[MAX_PATH + 2];
  421. if (EnumMRUList(hmru, i, szCommand, ARRAYSIZE(szCommand)) > 0)
  422. {
  423. // old MRU format has a slash at the end with the show cmd
  424. LPTSTR pszField = StrRChr(szCommand, NULL, TEXT('\\'));
  425. if (pszField)
  426. *pszField = 0;
  427. // The command to run goes in the combobox.
  428. SendMessage(hCB, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)szCommand);
  429. }
  430. }
  431. CloseRunDlgMRU(hmru);
  432. }
  433. if (!(m_dwFlags & RFD_NODEFFILE))
  434. SendMessage(hCB, CB_SETCURSEL, 0, 0L);
  435. SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDD_COMMAND, CBN_SELCHANGE), (LPARAM) hCB);
  436. // Make sure the OK button is initialized properly
  437. EnableOKButtonFromID(hDlg, IDD_COMMAND);
  438. // Create the thread that will take care of the
  439. // "Run in Separate Memory Space" checkbox in the background.
  440. //
  441. if (m_dwFlags & RFD_NOSEPMEMORY_BOX)
  442. {
  443. ShowWindow(GetDlgItem(hDlg, IDD_RUNINSEPARATE), SW_HIDE);
  444. }
  445. else
  446. {
  447. HANDLE hThread = NULL;
  448. ASSERT(g_hCheckNow == NULL);
  449. g_hCheckNow = CreateEvent(NULL, TRUE, FALSE, NULL);
  450. if (g_hCheckNow)
  451. {
  452. DWORD dwDummy;
  453. g_bCheckRunInSep = TRUE;
  454. hThread = CreateThread(NULL, 0, CheckRunInSeparateThreadProc, hDlg, 0, &dwDummy);
  455. }
  456. if ((g_hCheckNow==NULL) || (!g_bCheckRunInSep) || (hThread==NULL))
  457. {
  458. // We've encountered a problem setting up, so make the user
  459. // choose.
  460. CheckDlgButton(hDlg, IDD_RUNINSEPARATE, 1);
  461. EnableWindow(GetDlgItem(hDlg, IDD_RUNINSEPARATE), TRUE);
  462. g_bCheckRunInSep = FALSE;
  463. }
  464. //
  465. // These calls will just do nothing if either handle is NULL.
  466. //
  467. if (hThread)
  468. CloseHandle(hThread);
  469. if (g_hCheckNow)
  470. SetEvent(g_hCheckNow);
  471. }
  472. }
  473. //
  474. // InitRunDlg 2nd phase. It must be called after freeing parent thread.
  475. //
  476. void CRunDlg::InitRunDlg2(HWND hDlg)
  477. {
  478. // Register ourselves as a drop target. Allow people to drop on
  479. // both the dlg box and edit control.
  480. _fOleInited = SUCCEEDED(OleInitialize(NULL));
  481. if (_fOleInited)
  482. {
  483. RegisterDragDrop(hDlg, SAFECAST(this, IDropTarget*));
  484. }
  485. }
  486. void CRunDlg::BrowsePushed(void)
  487. {
  488. HWND hDlg = m_hDlg;
  489. TCHAR szText[MAX_PATH];
  490. // Get out of the "synchronized input queues" state
  491. if (m_dwThreadId)
  492. {
  493. AttachThreadInput(GetCurrentThreadId(), m_dwThreadId, FALSE);
  494. m_dwThreadId = 0;
  495. }
  496. GetDlgItemText(hDlg, IDD_COMMAND, szText, ARRAYSIZE(szText));
  497. PathUnquoteSpaces(szText);
  498. if (GetFileNameFromBrowse(hDlg, szText, ARRAYSIZE(szText), m_pszWorkingDir,
  499. MAKEINTRESOURCE(IDS_EXE), MAKEINTRESOURCE(IDS_PROGRAMSFILTER),
  500. MAKEINTRESOURCE(IDS_BROWSE)))
  501. {
  502. PathQuoteSpaces(szText);
  503. SetDlgItemText(hDlg, IDD_COMMAND, szText);
  504. EnableOKButtonFromID(hDlg, IDD_COMMAND);
  505. SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDOK), TRUE);
  506. }
  507. }
  508. // Use the common browse dialog to get a filename.
  509. // The working directory of the common dialog will be set to the directory
  510. // part of the file path if it is more than just a filename.
  511. // If the filepath consists of just a filename then the working directory
  512. // will be used.
  513. // The full path to the selected file will be returned in szFilePath.
  514. // HWND hDlg, // Owner for browse dialog.
  515. // LPSTR szFilePath, // Path to file
  516. // UINT cchFilePath, // Max length of file path buffer.
  517. // LPSTR szWorkingDir, // Working directory
  518. // LPSTR szDefExt, // Default extension to use if the user doesn't
  519. // // specify enter one.
  520. // LPSTR szFilters, // Filter string.
  521. // LPSTR szTitle // Title for dialog.
  522. STDAPI_(BOOL) _GetFileNameFromBrowse(HWND hwnd, LPTSTR szFilePath, UINT cbFilePath,
  523. LPCTSTR szWorkingDir, LPCTSTR szDefExt, LPCTSTR szFilters, LPCTSTR szTitle,
  524. DWORD dwFlags)
  525. {
  526. TCHAR szBrowserDir[MAX_PATH]; // Directory to start browsing from.
  527. TCHAR szFilterBuf[MAX_PATH]; // if szFilters is MAKEINTRESOURCE
  528. TCHAR szDefExtBuf[10]; // if szDefExt is MAKEINTRESOURCE
  529. TCHAR szTitleBuf[64]; // if szTitleBuf is MAKEINTRESOURCE
  530. szBrowserDir[0] = TEXT('0'); // By default use CWD.
  531. // Set up info for browser.
  532. lstrcpy(szBrowserDir, szFilePath);
  533. PathRemoveArgs(szBrowserDir);
  534. PathRemoveFileSpec(szBrowserDir);
  535. if (*szBrowserDir == TEXT('\0') && szWorkingDir)
  536. {
  537. lstrcpyn(szBrowserDir, szWorkingDir, ARRAYSIZE(szBrowserDir));
  538. }
  539. // Stomp on the file path so that the dialog doesn't
  540. // try to use it to initialise the dialog. The result is put
  541. // in here.
  542. szFilePath[0] = TEXT('\0');
  543. // Set up szDefExt
  544. if (IS_INTRESOURCE(szDefExt))
  545. {
  546. LoadString(HINST_THISDLL, (UINT)LOWORD((DWORD_PTR)szDefExt), szDefExtBuf, ARRAYSIZE(szDefExtBuf));
  547. szDefExt = szDefExtBuf;
  548. }
  549. // Set up szFilters
  550. if (IS_INTRESOURCE(szFilters))
  551. {
  552. LPTSTR psz;
  553. LoadString(HINST_THISDLL, (UINT)LOWORD((DWORD_PTR)szFilters), szFilterBuf, ARRAYSIZE(szFilterBuf));
  554. psz = szFilterBuf;
  555. while (*psz)
  556. {
  557. if (*psz == TEXT('#'))
  558. #if (defined(DBCS) || (defined(FE_SB) && !defined(UNICODE)))
  559. *psz++ = TEXT('\0');
  560. else
  561. psz = CharNext(psz);
  562. #else
  563. *psz = TEXT('\0');
  564. psz = CharNext(psz);
  565. #endif
  566. }
  567. szFilters = szFilterBuf;
  568. }
  569. // Set up szTitle
  570. if (IS_INTRESOURCE(szTitle))
  571. {
  572. LoadString(HINST_THISDLL, (UINT)LOWORD((DWORD_PTR)szTitle), szTitleBuf, ARRAYSIZE(szTitleBuf));
  573. szTitle = szTitleBuf;
  574. }
  575. OPENFILENAME ofn = { 0 }; // Structure used to init dialog.
  576. // Setup info for comm dialog.
  577. ofn.lStructSize = sizeof(ofn);
  578. ofn.hwndOwner = hwnd;
  579. ofn.hInstance = NULL;
  580. ofn.lpstrFilter = szFilters;
  581. ofn.lpstrCustomFilter = NULL;
  582. ofn.nFilterIndex = 1;
  583. ofn.nMaxCustFilter = 0;
  584. ofn.lpstrFile = szFilePath;
  585. ofn.nMaxFile = cbFilePath;
  586. ofn.lpstrInitialDir = szBrowserDir;
  587. ofn.lpstrTitle = szTitle;
  588. ofn.Flags = dwFlags;
  589. ofn.lpfnHook = NULL;
  590. ofn.lpstrDefExt = szDefExt;
  591. ofn.lpstrFileTitle = NULL;
  592. // Call it.
  593. return GetOpenFileName(&ofn);
  594. }
  595. BOOL WINAPI GetFileNameFromBrowse(HWND hwnd, LPTSTR szFilePath, UINT cchFilePath,
  596. LPCTSTR szWorkingDir, LPCTSTR szDefExt, LPCTSTR szFilters, LPCTSTR szTitle)
  597. {
  598. RIPMSG(szFilePath && IS_VALID_WRITE_BUFFER(szFilePath, TCHAR, cchFilePath), "GetFileNameFromBrowse: caller passed bad szFilePath");
  599. DEBUGWhackPathBuffer(szFilePath , cchFilePath);
  600. if (!szFilePath)
  601. return FALSE;
  602. return _GetFileNameFromBrowse(hwnd, szFilePath, cchFilePath,
  603. szWorkingDir, szDefExt, szFilters, szTitle,
  604. OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_NODEREFERENCELINKS);
  605. }
  606. //
  607. // Do checking of the .exe type in the background so the UI doesn't
  608. // get hung up while we scan. This is particularly important with
  609. // the .exe is over the network or on a floppy.
  610. //
  611. DWORD CALLBACK CheckRunInSeparateThreadProc(void *pv)
  612. {
  613. DWORD dwBinaryType;
  614. DWORD cch;
  615. LPTSTR pszFilePart;
  616. TCHAR szFile[MAX_PATH+1];
  617. TCHAR szFullFile[MAX_PATH+1];
  618. TCHAR szExp[MAX_PATH+1];
  619. HWND hDlg = (HWND)pv;
  620. BOOL fCheck = TRUE, fEnable = FALSE;
  621. HRESULT hrInit = SHCoInitialize();
  622. // PERF: Re-write to use PIDL from CShellUrl because it will prevent from having
  623. // to do the Search for the file name a second time.
  624. DebugMsg(DM_TRACE, TEXT("CheckRunInSeparateThreadProc created and running"));
  625. while (g_bCheckRunInSep)
  626. {
  627. WaitForSingleObject(g_hCheckNow, INFINITE);
  628. ResetEvent(g_hCheckNow);
  629. if (g_bCheckRunInSep)
  630. {
  631. CRunDlg * prd;
  632. LPTSTR pszT;
  633. BOOL f16bit = FALSE;
  634. szFile[0] = 0;
  635. szFullFile[0] = 0;
  636. cch = 0;
  637. GetWindowText(GetDlgItem(hDlg, IDD_COMMAND), szFile, ARRAYSIZE(szFile));
  638. // Remove & throw away arguments
  639. PathRemoveBlanks(szFile);
  640. if (PathIsNetworkPath(szFile))
  641. {
  642. f16bit = TRUE;
  643. fCheck = FALSE;
  644. fEnable = TRUE;
  645. goto ChangeTheBox;
  646. }
  647. // if the unquoted string exists as a file, just use it
  648. if (!PathFileExistsAndAttributes(szFile, NULL))
  649. {
  650. pszT = PathGetArgs(szFile);
  651. if (*pszT)
  652. *(pszT - 1) = TEXT('\0');
  653. PathUnquoteSpaces(szFile);
  654. }
  655. if (szFile[0])
  656. {
  657. SHExpandEnvironmentStrings(szFile, szExp, ARRAYSIZE(szExp));
  658. if (PathIsRemote(szExp))
  659. {
  660. f16bit = TRUE;
  661. fCheck = FALSE;
  662. fEnable = TRUE;
  663. goto ChangeTheBox;
  664. }
  665. cch = SearchPath(NULL, szExp, TEXT(".EXE"),
  666. ARRAYSIZE(szExp), szFullFile, &pszFilePart);
  667. }
  668. if ((cch != 0) && (cch <= (ARRAYSIZE(szFullFile) - 1)))
  669. {
  670. if ((GetBinaryType(szFullFile, &dwBinaryType) &&
  671. (dwBinaryType == SCS_WOW_BINARY)))
  672. {
  673. f16bit = TRUE;
  674. fCheck = FALSE;
  675. fEnable = TRUE;
  676. }
  677. else
  678. {
  679. f16bit = FALSE;
  680. fCheck = TRUE;
  681. fEnable = FALSE;
  682. }
  683. }
  684. else
  685. {
  686. f16bit = FALSE;
  687. fCheck = TRUE;
  688. fEnable = FALSE;
  689. }
  690. ChangeTheBox:
  691. CheckDlgButton(hDlg, IDD_RUNINSEPARATE, fCheck ? 1 : 0);
  692. EnableWindow(GetDlgItem(hDlg, IDD_RUNINSEPARATE), fEnable);
  693. prd = (CRunDlg *)GetWindowLongPtr(hDlg, DWLP_USER);
  694. if (prd)
  695. {
  696. if (f16bit)
  697. prd->m_dwFlags |= RFD_WOW_APP;
  698. else
  699. prd->m_dwFlags &= (~RFD_WOW_APP);
  700. }
  701. }
  702. }
  703. CloseHandle(g_hCheckNow);
  704. g_hCheckNow = NULL;
  705. SHCoUninitialize(hrInit);
  706. return 0;
  707. }
  708. void ExchangeWindowPos(HWND hwnd0, HWND hwnd1)
  709. {
  710. HWND hParent;
  711. RECT rc[2];
  712. hParent = GetParent(hwnd0);
  713. ASSERT(hParent == GetParent(hwnd1));
  714. GetWindowRect(hwnd0, &rc[0]);
  715. GetWindowRect(hwnd1, &rc[1]);
  716. MapWindowPoints(HWND_DESKTOP, hParent, (LPPOINT)rc, 4);
  717. SetWindowPos(hwnd0, NULL, rc[1].left, rc[1].top, 0, 0,
  718. SWP_NOZORDER|SWP_NOSIZE);
  719. SetWindowPos(hwnd1, NULL, rc[0].left, rc[0].top, 0, 0,
  720. SWP_NOZORDER|SWP_NOSIZE);
  721. }
  722. BOOL RunDlgNotifyParent(HWND hDlg, HWND hwnd, LPTSTR pszCmd, LPCTSTR pszWorkingDir)
  723. {
  724. NMRUNFILE rfn;
  725. rfn.hdr.hwndFrom = hDlg;
  726. rfn.hdr.idFrom = 0;
  727. rfn.hdr.code = RFN_EXECUTE;
  728. rfn.lpszCmd = pszCmd;
  729. rfn.lpszWorkingDir = pszWorkingDir;
  730. rfn.nShowCmd = SW_SHOWNORMAL;
  731. return (BOOL) SendMessage(hwnd, WM_NOTIFY, 0, (LPARAM)&rfn);
  732. }
  733. void MRUSelChange(HWND hDlg)
  734. {
  735. TCHAR szCmd[MAX_PATH];
  736. HWND hCB = GetDlgItem(hDlg, IDD_COMMAND);
  737. int nItem = (int)SendMessage(hCB, CB_GETCURSEL, 0, 0L);
  738. if (nItem < 0)
  739. return;
  740. // CB_LIMITTEXT has been done, so there's no chance of buffer overrun here.
  741. SendMessage(hCB, CB_GETLBTEXT, nItem, (LPARAM)szCmd);
  742. // We can't use EnableOKButtonFromID here because when we get this message,
  743. // the window does not have the text yet, so it will fail.
  744. EnableOKButtonFromString(hDlg, szCmd);
  745. }
  746. const DWORD aRunHelpIds[] = {
  747. IDD_ICON, NO_HELP,
  748. IDD_PROMPT, NO_HELP,
  749. IDD_RUNDLGOPENPROMPT, IDH_TRAY_RUN_COMMAND,
  750. IDD_COMMAND, IDH_TRAY_RUN_COMMAND,
  751. IDD_RUNINSEPARATE, IDH_TRAY_RUN_SEPMEM,
  752. IDD_BROWSE, IDH_BROWSE,
  753. IDOK, IDH_TRAY_RUN_OK,
  754. IDCANCEL, IDH_TRAY_RUN_CANCEL,
  755. 0, 0
  756. };
  757. BOOL_PTR CALLBACK RunDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  758. {
  759. CRunDlg * prd = (CRunDlg *)GetWindowLongPtr(hDlg, DWLP_USER);
  760. switch (uMsg)
  761. {
  762. case WM_INITDIALOG:
  763. /* The title will be in the lParam. */
  764. prd = (CRunDlg *)lParam;
  765. prd->m_hDlg = hDlg;
  766. prd->_fDone = FALSE;
  767. prd->InitRunDlg(hDlg);
  768. // Let the parent thread run on if it was waiting for us to
  769. // grab type-ahead.
  770. if (prd->m_hEventReady)
  771. {
  772. // We need to grab the activation so we can process input.
  773. // DebugMsg(DM_TRACE, "s.rdp: Getting activation.");
  774. SetForegroundWindow(hDlg);
  775. SetFocus(GetDlgItem(hDlg, IDD_COMMAND));
  776. // Now it's safe to wake the guy up properly.
  777. // DebugMsg(DM_TRACE, "s.rdp: Waking sleeping parent.");
  778. SetEvent(prd->m_hEventReady);
  779. CloseHandle(prd->m_hEventReady);
  780. }
  781. else
  782. {
  783. SetForegroundWindow(hDlg);
  784. SetFocus(GetDlgItem(hDlg, IDD_COMMAND));
  785. }
  786. // InitRunDlg 2nd phase (must be called after SetEvent)
  787. prd->InitRunDlg2(hDlg);
  788. // We're handling focus changes.
  789. return FALSE;
  790. case WM_PAINT:
  791. if (!prd->_fAutoCompInitialized)
  792. {
  793. prd->_fAutoCompInitialized = TRUE;
  794. PostMessage(hDlg, WM_SETUPAUTOCOMPLETE, 0, 0);
  795. }
  796. return FALSE;
  797. case WM_SETUPAUTOCOMPLETE:
  798. SHAutoComplete(GetWindow(GetDlgItem(hDlg, IDD_COMMAND), GW_CHILD), (SHACF_FILESYSTEM | SHACF_URLALL | SHACF_FILESYS_ONLY));
  799. break;
  800. case WM_HELP:
  801. WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP,
  802. (ULONG_PTR) (LPTSTR) aRunHelpIds);
  803. break;
  804. case WM_CONTEXTMENU: // right mouse click
  805. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
  806. (ULONG_PTR) (LPTSTR) aRunHelpIds);
  807. break;
  808. case WM_DESTROY:
  809. break;
  810. case WM_COMMAND:
  811. switch (GET_WM_COMMAND_ID(wParam, lParam))
  812. {
  813. case IDHELP:
  814. break;
  815. case IDD_COMMAND:
  816. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  817. {
  818. case CBN_SELCHANGE:
  819. MRUSelChange(hDlg);
  820. if (g_hCheckNow)
  821. SetEvent(g_hCheckNow);
  822. break;
  823. case CBN_EDITCHANGE:
  824. case CBN_SELENDOK:
  825. EnableOKButtonFromID(hDlg, IDD_COMMAND);
  826. if (g_hCheckNow)
  827. SetEvent(g_hCheckNow);
  828. break;
  829. case CBN_SETFOCUS:
  830. SetModeBias(MODEBIASMODE_FILENAME);
  831. break;
  832. case CBN_KILLFOCUS:
  833. SetModeBias(MODEBIASMODE_DEFAULT);
  834. break;
  835. }
  836. break;
  837. case IDOK:
  838. // fake an ENTER key press so AutoComplete can do it's thing
  839. if (SendMessage(GetDlgItem(hDlg, IDD_COMMAND), WM_KEYDOWN, VK_RETURN, 0x1c0001))
  840. {
  841. if (!prd->OKPushed())
  842. {
  843. if (!(prd->m_dwFlags & RFD_NOSEPMEMORY_BOX))
  844. {
  845. g_bCheckRunInSep = FALSE;
  846. SetEvent(g_hCheckNow);
  847. }
  848. break;
  849. }
  850. }
  851. else
  852. {
  853. break; // AutoComplete wants more user input
  854. }
  855. // fall through
  856. case IDCANCEL:
  857. prd->ExitRunDlg(FALSE);
  858. break;
  859. case IDD_BROWSE:
  860. prd->BrowsePushed();
  861. SetEvent(g_hCheckNow);
  862. break;
  863. default:
  864. return FALSE;
  865. }
  866. break;
  867. default:
  868. return FALSE;
  869. }
  870. return TRUE;
  871. }
  872. // Puts up the standard file.run dialog.
  873. // REVIEW UNDONE This should use a RUNDLG structure for all the various
  874. // options instead of just passing them as parameters, a ptr to the struct
  875. // would be passed to the dialog via the lParam.
  876. STDAPI_(int) RunFileDlg(HWND hwndParent, HICON hIcon,
  877. LPCTSTR pszWorkingDir, LPCTSTR pszTitle,
  878. LPCTSTR pszPrompt, DWORD dwFlags)
  879. {
  880. int rc = 0;
  881. HRESULT hrInit = SHCoInitialize();
  882. IDropTarget *pdt;
  883. if (SUCCEEDED(CRunDlg_CreateInstance(NULL, IID_PPV_ARG(IDropTarget, &pdt))))
  884. {
  885. CRunDlg * prd = (CRunDlg *) pdt;
  886. prd->m_hIcon = hIcon;
  887. prd->m_pszWorkingDir = pszWorkingDir;
  888. prd->m_pszTitle = pszTitle;
  889. prd->m_pszPrompt = pszPrompt;
  890. prd->m_dwFlags = dwFlags;
  891. if (SHRestricted(REST_RUNDLGMEMCHECKBOX))
  892. ClearFlag(prd->m_dwFlags, RFD_NOSEPMEMORY_BOX);
  893. else
  894. SetFlag(prd->m_dwFlags, RFD_NOSEPMEMORY_BOX);
  895. // prd->m_hEventReady = 0;
  896. // prd->m_dwThreadId = 0;
  897. // We do this so we can get type-ahead when we're running on a
  898. // separate thread. The parent thread needs to block to give us time
  899. // to do the attach and then get some messages out of the queue hence
  900. // the event.
  901. if (hwndParent)
  902. {
  903. // HACK The parent signals it's waiting for the dialog to grab type-ahead
  904. // by sticking it's threadId in a property on the parent.
  905. prd->m_dwThreadId = PtrToUlong(GetProp(hwndParent, c_szWaitingThreadID));
  906. if (prd->m_dwThreadId)
  907. {
  908. // DebugMsg(DM_TRACE, "s.rfd: Attaching input to %x.", idThread);
  909. AttachThreadInput(GetCurrentThreadId(), prd->m_dwThreadId, TRUE);
  910. // NB Hack.
  911. prd->m_hEventReady = OpenEvent(EVENT_ALL_ACCESS, TRUE, c_szRunDlgReady);
  912. }
  913. }
  914. rc = (int)DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_RUN), hwndParent,
  915. RunDlgProc, (LPARAM)prd);
  916. if (hwndParent && prd->m_dwThreadId)
  917. {
  918. AttachThreadInput(GetCurrentThreadId(), prd->m_dwThreadId, FALSE);
  919. }
  920. pdt->Release();
  921. }
  922. SHCoUninitialize(hrInit);
  923. return rc;
  924. }