Leaked source code of windows server 2003
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.

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