170 lines
5.6 KiB
C++
170 lines
5.6 KiB
C++
#include <windows.h>
|
|
#include <shlwapi.h>
|
|
#include <commctrl.h>
|
|
#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;
|
|
}
|
|
|