#include #include #include #include "dataitem.h" #include "resource.h" #include "autorun.h" #define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) CDataItem::CDataItem() { m_pszTitle = m_pszMenuName = m_pszDescription = m_pszCmdLine = m_pszArgs = NULL; m_dwFlags = 0; m_chAccel = NULL; } CDataItem::~CDataItem() { if ( m_pszTitle ) delete [] m_pszTitle; if ( m_pszMenuName ) delete [] m_pszMenuName; if ( m_pszDescription ) delete [] m_pszDescription; if ( m_pszCmdLine ) delete [] m_pszCmdLine; if ( m_pszArgs ) delete [] m_pszArgs; } BOOL CDataItem::SetData( LPTSTR szTitle, LPTSTR szMenu, LPTSTR szDesc, LPTSTR szCmd, LPTSTR szArgs, DWORD dwFlags, int iImgIndex ) { TCHAR * psz; // This function should only be called once or else we will leak like a, like a, a thing that leaks a lot. ASSERT( NULL==m_pszTitle && NULL==m_pszMenuName && NULL==m_pszDescription && NULL==m_pszCmdLine && NULL==m_pszArgs ); m_pszTitle = new TCHAR[lstrlen(szTitle)+1]; if ( m_pszTitle ) lstrcpy( m_pszTitle, szTitle ); if ( szMenu ) { // menuname is allowed to remain NULL. This is only used if you want the // text on the menu item to be different than the description. This could // be useful for localization where a shortened name might be required. m_pszMenuName = new TCHAR[lstrlen(szMenu)+1]; if ( m_pszMenuName ) lstrcpy( m_pszMenuName, szMenu ); psz = StrChr(szMenu, TEXT('&')); if ( psz ) m_chAccel = *(CharNext(psz)); } m_pszDescription = new TCHAR[lstrlen(szDesc)+1]; if ( m_pszDescription ) lstrcpy( m_pszDescription, szDesc ); m_pszCmdLine = new TCHAR[lstrlen(szCmd)+1]; if ( m_pszCmdLine ) lstrcpy( m_pszCmdLine, szCmd ); if ( szArgs ) { // Some commands don't have any args so this can remain NULL. This is only used // if the executable requires arguments. m_pszArgs = new TCHAR[lstrlen(szArgs)+1]; if ( m_pszArgs ) lstrcpy( m_pszArgs, szArgs ); } m_dwFlags = dwFlags; m_iImage = iImgIndex; return TRUE; } BOOL CDataItem::Invoke(HWND hwnd) { BOOL fResult; TCHAR szCmdLine[MAX_PATH*2]; PROCESS_INFORMATION ei; STARTUPINFO si = {0}; si.cb = sizeof(si); lstrcpy( szCmdLine, m_pszCmdLine ); if ( m_pszArgs ) { strcat( szCmdLine, TEXT(" ") ); strcat( szCmdLine, m_pszArgs ); } fResult = CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &ei); if (fResult) { if (NULL != ei.hProcess) { DWORD dwObject; // passing in a NULL HWND is used as a signal not to wait in this inner loop. while (hwnd) { dwObject = MsgWaitForMultipleObjects(1, &ei.hProcess, FALSE, INFINITE, QS_ALLINPUT); if (WAIT_OBJECT_0 == dwObject) { break; } else if (WAIT_OBJECT_0+1 == dwObject) { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if ( WM_QUIT == msg.message ) { CloseHandle(ei.hProcess); return fResult; } else { GetMessage(&msg, NULL, 0, 0); // IsDialogMessage cannot understand the concept of ownerdraw default pushbuttons. It treats // these attributes as mutually exclusive. As a result, we handle this ourselves. We want // whatever control has focus to act as the default pushbutton. if ( (WM_KEYDOWN == msg.message) && (VK_RETURN == msg.wParam) ) { HWND hwndFocus = GetFocus(); if ( hwndFocus ) { SendMessage(hwnd, WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndFocus), BN_CLICKED), (LPARAM)hwndFocus); } continue; } if ( IsDialogMessage(hwnd, &msg) ) continue; TranslateMessage(&msg); DispatchMessage(&msg); } } } } if ( !hwnd ) { // A NULL hwnd means we were called in the mode by which we execute the item and then immediately // exit. If our process exits before the other process is ready it'll end up in the wrong place // in the z-order. To prevent this, when we're in "exit when done" mode we need to wait for the // process we created to be ready. The way to do this is to call WaitForInputIdle. This is really // only needed on NT5 and above due to the new "rude window activation" stuff, but since this API // is available all the way back to NT 3.1 we simply call it blindly. WaitForInputIdle(ei.hProcess, 20*1000); // we wait a maximum of 20 seconds } CloseHandle(ei.hProcess); } } else { // do something if we fail to create a process? } return fResult; }