I'm developing an application which captures frames using DXGI mechanism from an application.
First creation of the IDXGIOutputDuplication is correct. When the application changes its display (for example from fullscreen to windowed or vice versa), the frame acquire failed (expected behaviour), then I recreate the IDXGIOutputDuplication and regularly the call to DuplicateOutput crash.
Below creation of the D3d11 device:
D3D_FEATURE_LEVEL FeatureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_1
};
UINT NumFeatureLevels = ARRAYSIZE( FeatureLevels );
D3D_FEATURE_LEVEL FeatureLevel = D3D_FEATURE_LEVEL_11_1;
D3D11CreateDevice( _pDxgiAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, FeatureLevels, NumFeatureLevels, D3D11_SDK_VERSION, &_pD3D11Device, &FeatureLevel, &_pD3D11DeviceCtx );
_pDxgiAdapter is created before the device.
Below creation of the IDXGIOutputDuplication:
int DxgiVideoCapture::open() {
findDxgiAdapter();
if( nullptr != _pDxgiAdapter ) {
// Duplicate output
uint32_t ui32OutputIndex = 0;
HRESULT hr = S_OK;
while( SUCCEEDED( hr ) ) {
IDXGIOutput* pDxgiOutput = nullptr;
hr = _pDxgiAdapter->EnumOutputs( ui32OutputIndex, &pDxgiOutput );
if( SUCCEEDED( hr ) ) {
IDXGIOutput1* pDxgiOutput1 = nullptr;
if( SUCCEEDED ( pDxgiOutput->QueryInterface( __uuidof( IDXGIOutput1 ), ( void** )&pDxgiOutput1 ) ) ) {
uint32_t ui32EnumMode = 0;
uint32_t ui32Flags = 0xffffffff;
}
if( SUCCEEDED ( pDxgiOutput->QueryInterface( __uuidof( IDXGIOutput1 ), ( void** )&_pDxgiOutput1 ) ) ) {
LOGI( "/!\\ Call which crash regularly /!\\" );
HRESULT hrDup = _pDxgiOutput1->DuplicateOutput( _pD3D11Device, &_pOutputDuplication );
if( SUCCEEDED( hrDup ) ) {
LOGI( "Output duplication created." );
} else {
switch( hrDup ) {
case E_INVALIDARG :
{
LOGW( "Invalid device or output already duplicated" );
}
break;
case E_ACCESSDENIED :
{
LOGW( "Access denied" );
}
break;
case DXGI_ERROR_UNSUPPORTED :
{
LOGW( "DXGI_ERROR_UNSUPPORTED" );
}
break;
case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE :
{
LOGW( "DXGI_ERROR_NOT_CURRENTLY_AVAILABLE" );
}
break;
case DXGI_ERROR_SESSION_DISCONNECTED :
{
LOGW( "DXGI_ERROR_NOT_CURRENTLY_AVAILABLE" );
}
break;
default:
{
LOGW( "default" );
}
}
} else {
LOGE( "Unable to retrieve interface creating the ouput duplication" );
}
}
pDxgiOutput->Release();
}
ui32OutputIndex++;
}
return RET_OK;
}
Below the frame acquisition:
int DxgiVideoCapture::captureGameFrame() {
int iRet = RET_ERROR;
ID3D11Texture2D* pCapturedTexture = nullptr;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
ZeroMemory(&frameInfo, sizeof(frameInfo));
HRESULT hr = _pOutputDuplication->AcquireNextFrame( 1000, &frameInfo, &_pDxgiResource );
if( FAILED( hr ) ) {
if( hr == DXGI_ERROR_WAIT_TIMEOUT ) {
LOGW( "Wait for %d ms timed out", 1000);
}
if (hr == DXGI_ERROR_INVALID_CALL) {
LOGW( "Invalid Call, previous frame not released?" );
}
if (hr == DXGI_ERROR_ACCESS_LOST) {
LOGW( "Error Access lost - is it a game end ?" );
}
iRet = RET_RESET_CAPTURE;
return iRet;
}
if( FAILED( hr = _pDxgiResource->QueryInterface( __uuidof( ID3D11Texture2D ), ( void** ) &pCapturedTexture ) ) ) {
LOGW( "unable to retrieve D3D11 texture 0x%x", hr );
return RET_WARNING;
} else {
// Store window of the game
D3D11_TEXTURE2D_DESC d3D11TextureDesc;
pCapturedTexture->GetDesc( &d3D11TextureDesc );
// Compute the zone to extract.
D3D11_BOX srcBox;
memset( &srcBox, 0, sizeof( srcBox ) );
if( _pGameWindow->getLeftPos() > 0 ) {
srcBox.left = _pGameWindow->getLeftPos();
}
if( _pGameWindow->getTopPos() > 0 ) {
srcBox.top = _pGameWindow->getTopPos();
}
srcBox.front = 0;
srcBox.right = _pGameWindow->getLeftPos() + _pGameWindow->getWidth();
if( srcBox.right > _pGameWindow->getMonitorWidth() ) {
srcBox.right = _pGameWindow->getMonitorWidth();
}
if( ( srcBox.right - srcBox.left ) % 2 != 0 ) {
srcBox.right--;
}
srcBox.bottom = _pGameWindow->getTopPos() + _pGameWindow->getHeight();
if( srcBox.bottom > _pGameWindow->getMonitorHeight() ) {
srcBox.bottom = _pGameWindow->getMonitorHeight();
}
if( ( srcBox.bottom - srcBox.top ) % 2 != 0 ) {
srcBox.bottom--;
}
srcBox.back = 1;
// AVFrame info are udpate just when the captured game window and the texture are diffrent.
// In the same time texture is reallocated.
if( ( ( srcBox.right - srcBox.left ) != _CapturedTextureDesc.Width )
|| ( ( srcBox.bottom - srcBox.top ) != _CapturedTextureDesc.Height )
) {
LOGD( "Game window: %dx%d ; %d->%d", _pGameWindow->getLeftPos(), _pGameWindow->getTopPos(), _pGameWindow->getWidth(), _pGameWindow->getHeight() );
LOGD( "Texture creation %dx%d -> %dx%d", srcBox.left, srcBox.top, srcBox.right, srcBox.bottom );
// Create the new texture
iRet = createCapturedTexture( srcBox.right - srcBox.left, srcBox.bottom - srcBox.top );
}
DirectX11Util::GetInstance()->getD3D11DeviceContext()->CopySubresourceRegion( _pGameTexture, 0, 0, 0, 0, pCapturedTexture, 0, &srcBox );
}
if( nullptr != _pDxgiResource ) {
_pOutputDuplication->ReleaseFrame();
pCapturedTexture->Release();
_pDxgiResource->Release();
_pDxgiResource = nullptr;
}
iRet = RET_OK;
return iRet;
}
Below the release of the D3d11 capture before recreation of the IDXGIOutputDuplication:
int DxgiVideoCapture::close() {
if( nullptr != _pGameTexture ) {
ZeroMemory( &_CapturedTextureDesc, sizeof( D3D11_TEXTURE2D_DESC ) );
_pGameTexture->Release();
_pGameTexture = nullptr;
}
if( nullptr != _pDxgiResource ){
_pOutputDuplication->ReleaseFrame();
_pDxgiResource->Release();
_pDxgiResource = nullptr;
}
if( nullptr != _pOutputDuplication ) {
_pOutputDuplication->Release();
_pOutputDuplication = nullptr;
}
return RET_OK;
}
What I would like to know is how can I invest this crash (application ends without any message). To be more accurate I cross compile my application, but the behaviour seems the same. Do you have any idea how to invest this issue ?
Tell me if you want more details.
Thanks in advance !
I have a working example using the Windows text-to-speech API, but the problem is it freezes when given a large amount of text.
To solve this I want to make the process asynchronous. I've found the SpeechVoiceSpeakFlags::SVSFlagsAsync flag, but how do I get the results of a previously-submitted request?
Here is my code:
char* TextToWavInner( const wchar_t* voiceRequiredAttributes, const wchar_t* voiceOptionalAttributes, long rate, const wchar_t* textToRender, ULONG* pBytesRead )
{
HRESULT hr;
CComPtr<ISpVoice> cpVoice; //Will send data to ISpStream
CComPtr<ISpStream> cpStream; //Will contain IStream
CComPtr<IStream> cpBaseStream; //raw data
ISpObjectToken* cpToken( NULL ); //Will set voice characteristics
GUID guidFormat;
WAVEFORMATEX* pWavFormatEx = nullptr;
hr = cpVoice.CoCreateInstance( CLSID_SpVoice );
if ( FAILED( hr ) )
return NULL;
hr = SpFindBestToken( SPCAT_VOICES, voiceRequiredAttributes, voiceOptionalAttributes, &cpToken );
if ( FAILED( hr ) )
return NULL;
hr = cpVoice->SetVoice( cpToken );
cpToken->Release();
if ( FAILED( hr ) )
return NULL;
cpVoice->SetRate( rate );
hr = cpStream.CoCreateInstance( CLSID_SpStream );
if ( FAILED( hr ) )
return NULL;
hr = CreateStreamOnHGlobal( NULL, true, &cpBaseStream );
if ( FAILED( hr ) )
return NULL;
hr = SpConvertStreamFormatEnum( SPSF_44kHz16BitMono, &guidFormat, &pWavFormatEx );
if ( FAILED( hr ) )
return NULL;
hr = cpStream->SetBaseStream( cpBaseStream, guidFormat, pWavFormatEx );
if ( FAILED( hr ) )
return NULL;
hr = cpVoice->SetOutput( cpStream, false );
if ( FAILED( hr ) )
return NULL;
SpeechVoiceSpeakFlags voiceFlags = ( SpeechVoiceSpeakFlags ) ( SpeechVoiceSpeakFlags::SVSFlagsAsync | SpeechVoiceSpeakFlags::SVSFPurgeBeforeSpeak );
hr = cpVoice->Speak( textToRender, voiceFlags, NULL );
if ( FAILED( hr ) )
return NULL;
LARGE_INTEGER a = { 0 };
hr = cpStream->Seek( a, STREAM_SEEK_SET, NULL );
if ( FAILED( hr ) )
return NULL;
STATSTG stats;
cpStream->Stat( &stats, STATFLAG_NONAME );
ULONG sSize = stats.cbSize.LowPart;
char* pBuffer = new char[ sSize ];
cpStream->Read( pBuffer, sSize, pBytesRead );
return pBuffer;
}
I've run into a situation that is not so unique (others have been asking exact same question) Offsite similar question..
Basically, for some reason, the code in IShellExtInit::Initialize implementation that is supposed to be invoked once after each right-click on a file, ends up being invoked 4 times.
STDMETHODIMP My_ShellExtInit::Initialize (LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID ) {
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT,
-1, TYMED_HGLOBAL };
STGMEDIUM stg = { TYMED_HGLOBAL };
HDROP hDrop;
if ( FAILED( pDataObj->GetData ( &fmt, &stg ) ))
return E_INVALIDARG;
hDrop = (HDROP) GlobalLock ( stg.hGlobal );
if ( NULL == hDrop )
return E_INVALIDARG;
UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );
HRESULT hr = S_OK;
if ( 0 == uNumFiles ) {
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return E_INVALIDARG;
}
if ( 0 == DragQueryFile ( hDrop, 0, m_szFile, MAX_PATH ) )
hr = E_INVALIDARG;
system("echo INVOKED >> log.txt");
// QMessageBox::warning(NULL, "Foo!", TCHARToQString(m_szFile));
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return hr;
}
Depending on the file/type, your context menu handler is called multiple times:
for the file/folder itself
for the parent folder of the file
for the folder background
in case of a *.lnk file also for the target it points to
And if explorer shows the tree view, then that part also calls your handler.
How can I pin application icon to metro start screen in win8 programmatically(c++)? I know how to do it manually. I also know that it will be added automatically once I launch that application.
I found this solution here
BOOL PinToStart( LPCWSTR szFilePath )
{
BOOL bSuccess = FALSE;
// break into file name and path
WCHAR lpszDirectoryName[ MAX_PATH ] = { 0 };
LPCWSTR lpszFileName = ::PathFindFileName( szFilePath );
wcscpy_s( lpszDirectoryName, szFilePath );
::PathRemoveFileSpec( lpszDirectoryName );
// load shell32.dll
HMODULE hShell32 = LoadLibrary( L"SHELL32" );
if( hShell32 != NULL )
{
// get the localized translation of 'Pin to Start' verb
WCHAR szPinToStartLocalized[ MAX_PATH ] = { 0 };
int nPinToStartLocalizedLength = LoadString( (HINSTANCE)hShell32, 51201, szPinToStartLocalized, MAX_PATH );
if( nPinToStartLocalizedLength > 0 )
{
// create the shell object
IShellDispatch *pShellDispatch = NULL;
HRESULT hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void**)&pShellDispatch);
if( SUCCEEDED( hr ) )
{
Folder *pFolder = NULL;
variant_t vaDirectory( lpszDirectoryName );
// get the namespace
if( SUCCEEDED( pShellDispatch->NameSpace( vaDirectory, &pFolder ) ) )
{
FolderItem *pItem = NULL;
bstr_t vaFileName( lpszFileName );
// parse the name
if( SUCCEEDED( pFolder->ParseName( vaFileName, &pItem ) ) )
{
FolderItemVerbs* pVerbs = NULL;
// get the verbs
if( SUCCEEDED( pItem->Verbs(&pVerbs) ) )
{
long nCount = 0;
if( SUCCEEDED ( pVerbs->get_Count( &nCount ) ) )
{
variant_t vaIndex;
vaIndex.vt = VT_I4;
// iterate through verbs
for( vaIndex.lVal = 0; vaIndex.lVal<nCount; vaIndex.lVal++ )
{
FolderItemVerb* pVerb = NULL;
if( SUCCEEDED( pVerbs->Item( vaIndex, &pVerb ) ) )
{
BSTR bstrVerbName = NULL;
// check for 'Pin to Start' verb
if( SUCCEEDED( pVerb->get_Name( &bstrVerbName ) ) )
{
if( 0 == wcscmp( bstrVerbName, szPinToStartLocalized ) )
{
bSuccess = SUCCEEDED( pVerb->DoIt() );
vaIndex.lVal = nCount; // break for
}
::SysFreeString( bstrVerbName );
}
pVerb->Release();
} // if
} // for
}
pVerbs->Release();
}
pItem->Release();
}
pFolder->Release();
}
pShellDispatch->Release();
}
}
::FreeLibrary( hShell32 );
}
return bSuccess;
}
Hope it's help you
Hello i'm trying to get cascade window inside context menu in shell extension. I add two submenus inside context menu of .dll extension, but want to make one cascade submenu (i like to open first menu and after clicking on some menu inside, want to open next submenu).
How to get cascade submenus from this code, where did i make mistake?
// OpenWithCtxMenuExt.cpp : Implementation of COpenWithCtxMenuExt
#include "stdafx.h"
#include "OpenWithExt.h"
#include "OpenWithCtxMenuExt.h"
#pragma comment(lib,"shlwapi")
/////////////////////////////////////////////////////////////////////////////
// COpenWithCtxMenuExt
HRESULT COpenWithCtxMenuExt::Initialize ( LPCITEMIDLIST pidlFolder,
LPDATAOBJECT pDataObj,
HKEY hProgID )
{
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stg = { TYMED_HGLOBAL };
HDROP hDrop;
// Look for CF_HDROP data in the data object.
if ( FAILED( pDataObj->GetData ( &fmt, &stg )))
{
// Nope! Return an "invalid argument" error back to Explorer.
return E_INVALIDARG;
}
// Get a pointer to the actual data.
hDrop = (HDROP) GlobalLock ( stg.hGlobal );
// Make sure it worked.
if ( NULL == hDrop )
return E_INVALIDARG;
// Sanity check - make sure there is at least one filename.
UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );
if ( 0 == uNumFiles )
{
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return E_INVALIDARG;
}
HRESULT hr = S_OK;
// Get the name of the first file and store it in our member variable m_szFile.
if ( 0 == DragQueryFile ( hDrop, 0, m_szSelectedFile, MAX_PATH ))
hr = E_INVALIDARG;
else
{
// Quote the name if it contains spaces (needed so the cmd line is built properly)
PathQuoteSpaces ( m_szSelectedFile );
}
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return hr;
}
HRESULT COpenWithCtxMenuExt::QueryContextMenu ( HMENU hmenu, UINT uMenuIndex,
UINT uidFirstCmd, UINT uidLastCmd,
UINT uFlags )
{
// If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
if ( uFlags & CMF_DEFAULTONLY )
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );
// First, create and populate a submenu.
HMENU hSubmenu = CreatePopupMenu();
HMENU hSubmenu1 = CreatePopupMenu();
UINT uID = uidFirstCmd;
InsertMenu ( hSubmenu, 0, MF_BYPOSITION, uID++, _T("&Notepad") );
InsertMenu ( hSubmenu, 1, MF_BYPOSITION, uID++, _T("&Internet Explorer") );
InsertMenu ( hSubmenu, 2, MF_BYPOSITION, uID++, _T("&Mspaint") );
InsertMenu ( hSubmenu, 3, MF_BYPOSITION, uID++, _T("&Pop") );
// provjeriti uID da se ne zbraja
InsertMenu ( hSubmenu1, 0, MF_BYPOSITION, uID++, _T("&Notepad") );
InsertMenu ( hSubmenu1, 1, MF_BYPOSITION, uID++, _T("&Mspaint") );
// Insert the submenu into the ctx menu provided by Explorer.
MENUITEMINFO mii = { sizeof(MENUITEMINFO) };
mii.fMask = MIIM_SUBMENU | /*MIIM_STRING*/ 0x00000040 | MIIM_ID;
mii.wID = uID++;
mii.hSubMenu = hSubmenu;
mii.dwTypeData = _T("C&P Open With");
InsertMenuItem ( hmenu, uMenuIndex, TRUE, &mii );
// Insert the submenu into the ctx menu provided by Explorer.
MENUITEMINFO mii1 = { sizeof(MENUITEMINFO) };
mii1.fMask = MIIM_SUBMENU | /*MIIM_STRING*/ 0x00000040 | MIIM_ID;
mii1.wID = uID++;
mii1.hSubMenu = hSubmenu;
mii1.dwTypeData = _T("C&P pod_folder");
InsertMenuItem ( hmenu, uMenuIndex, TRUE, &mii1 );
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, uID - uidFirstCmd );
}
HRESULT COpenWithCtxMenuExt::GetCommandString ( UINT idCmd, UINT uFlags,
UINT* pwReserved, LPSTR pszName,
UINT cchMax )
{
USES_CONVERSION;
// Check idCmd, it must be 0 or 1 since we have two menu items.
if ( idCmd > 3 )
return E_INVALIDARG;
// If Explorer is asking for a help string, copy our string into the
// supplied buffer.
if ( uFlags & GCS_HELPTEXT )
{
LPCTSTR szNotepadText = _T("Open the selected file in Notepad");
LPCTSTR szIEText = _T("Open the selected file in Internet Explorer");
LPCTSTR szPintText = _T("Open the selected file with Mspaint");
LPCTSTR szPopText = _T("Popout");
LPCTSTR szNotepad1Text = _T("Open the selected file in Notepad");
LPCTSTR szPint1Text = _T("Open the selected file with Mspaint");
//LPCTSTR pszText = (0 == idCmd) ? szNotepadText : szIEText;
LPCTSTR pszText;
if(idCmd == 0){
pszText = szNotepadText;
}
if(idCmd == 1){
pszText = szIEText;
}
if(idCmd == 2){
pszText = szPintText;
}
if(idCmd == 3){
pszText = szPopText;
}
if(idCmd == 4){
pszText = szNotepad1Text;
}
if(idCmd == 5){
pszText = szPint1Text;
}
if ( uFlags & GCS_UNICODE )
{
// We need to cast pszName to a Unicode string, and then use the
// Unicode string copy API.
lstrcpynW ( (LPWSTR) pszName, T2CW(pszText), cchMax );
}
else
{
// Use the ANSI string copy API to return the help string.
lstrcpynA ( pszName, T2CA(pszText), cchMax );
}
return S_OK;
}
return E_INVALIDARG;
}
HRESULT COpenWithCtxMenuExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )
{
// If lpVerb really points to a string, ignore this function call and bail out.
if ( 0 != HIWORD( pCmdInfo->lpVerb ))
return E_INVALIDARG;
// Get the command index.
switch ( LOWORD( pCmdInfo->lpVerb ))
{
case 0:
{
ShellExecute ( pCmdInfo->hwnd, _T("open"), _T("notepad.exe"),
m_szSelectedFile, NULL, SW_SHOW );
return S_OK;
}
break;
case 1:
{
ShellExecute ( pCmdInfo->hwnd, _T("open"), _T("iexplore.exe"),
m_szSelectedFile, NULL, SW_SHOW );
return S_OK;
}
break;
case 2:
{
ShellExecute ( pCmdInfo->hwnd, _T("open"), _T("mspaint.exe"),
m_szSelectedFile, NULL, SW_SHOW );
return S_OK;
}
break;
case 3:
{
ShellExecute ( pCmdInfo->hwnd, _T("open"), _T("mspaint.exe"),
m_szSelectedFile, NULL, SW_SHOW );
return S_OK;
}
break;
case 4:
{
ShellExecute ( pCmdInfo->hwnd, _T("open"), _T("notepad.exe"),
m_szSelectedFile, NULL, SW_SHOW );
return S_OK;
}
break;
case 5:
{
ShellExecute ( pCmdInfo->hwnd, _T("open"), _T("mspaint.exe"),
m_szSelectedFile, NULL, SW_SHOW );
return S_OK;
}
break;
default:
return E_INVALIDARG;
break;
}
}
After some time found solution for making cascade context menu inside of existing context menu ... here is code:
// OpenWithCtxMenuExt.cpp : Implementation of COpenWithCtxMenuExt
#include "stdafx.h"
#include "OpenWithExt.h"
#include "OpenWithCtxMenuExt.h"
#pragma comment(lib,"shlwapi")
/////////////////////////////////////////////////////////////////////////////
// COpenWithCtxMenuExt
HRESULT COpenWithCtxMenuExt::Initialize ( LPCITEMIDLIST pidlFolder,
LPDATAOBJECT pDataObj,
HKEY hProgID )
{
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stg = { TYMED_HGLOBAL };
HDROP hDrop;
// Look for CF_HDROP data in the data object.
if ( FAILED( pDataObj->GetData ( &fmt, &stg )))
{
// Nope! Return an "invalid argument" error back to Explorer.
return E_INVALIDARG;
}
// Get a pointer to the actual data.
hDrop = (HDROP) GlobalLock ( stg.hGlobal );
// Make sure it worked.
if ( NULL == hDrop )
return E_INVALIDARG;
// Sanity check - make sure there is at least one filename.
UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );
if ( 0 == uNumFiles )
{
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return E_INVALIDARG;
}
HRESULT hr = S_OK;
// Get the name of the first file and store it in our member variable m_szFile.
if ( 0 == DragQueryFile ( hDrop, 0, m_szSelectedFile, MAX_PATH ))
hr = E_INVALIDARG;
else
{
// Quote the name if it contains spaces (needed so the cmd line is built properly)
PathQuoteSpaces ( m_szSelectedFile );
}
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return hr;
}
HRESULT COpenWithCtxMenuExt::QueryContextMenu ( HMENU hmenu, UINT uMenuIndex,
UINT uidFirstCmd, UINT uidLastCmd,
UINT uFlags )
{
// If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
if ( uFlags & CMF_DEFAULTONLY )
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );
// First, create and populate a submenu.
HMENU hSubmenu = CreatePopupMenu();
HMENU hSub = CreatePopupMenu();
UINT uID = uidFirstCmd;
InsertMenu ( hSubmenu, 0, MF_BYPOSITION, uID++, _T("&Notepad") );
InsertMenu ( hSubmenu, 1, MF_BYPOSITION, uID++, _T("&Internet Explorer") );
InsertMenu ( hSubmenu, 2, MF_BYPOSITION, uID++, _T("&Mspaint") );
InsertMenu ( hSubmenu, 3, MF_BYPOSITION, uID++, _T("&Pop") );
InsertMenu ( hSub, 4, MF_BYPOSITION, uID++, _T("&Case") );
InsertMenu ( hSub, 5, MF_BYPOSITION, uID++, _T("&Case") );
// Insert the submenu into the ctx menu provided by Explorer.
MENUITEMINFO mii = { sizeof(MENUITEMINFO) };
mii.fMask = MIIM_SUBMENU | /*MIIM_STRING*/ 0x00000040 | MIIM_ID;
mii.wID = uID++;
mii.hSubMenu = hSubmenu;
mii.dwTypeData = _T("Open With&x");
InsertMenuItem ( hmenu, uMenuIndex, TRUE, &mii );
mii.hSubMenu = hSub;
mii.dwTypeData = _T("Novi Subm&enu");
InsertMenuItem ( hSubmenu, uMenuIndex, TRUE, &mii );
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, uID - uidFirstCmd );
}
HRESULT COpenWithCtxMenuExt::GetCommandString ( UINT idCmd, UINT uFlags,
UINT* pwReserved, LPSTR pszName,
UINT cchMax )
{
USES_CONVERSION;
// Check idCmd, it must be 0 or 1 since we have two menu items.
if ( idCmd > 4 )
return E_INVALIDARG;
// If Explorer is asking for a help string, copy our string into the
// supplied buffer.
if ( uFlags & GCS_HELPTEXT )
{
LPCTSTR szNotepadText = _T("Open the selected file in Notepad");
LPCTSTR szIEText = _T("Open the selected file in Internet Explorer");
LPCTSTR szPintText = _T("Open the selected file with Mspaint");
LPCTSTR szPopText = _T("Popout");
//LPCTSTR pszText = (0 == idCmd) ? szNotepadText : szIEText;
LPCTSTR pszText;
if(idCmd == 0){
pszText = szNotepadText;
}
if(idCmd == 1){
pszText = szIEText;
}
if(idCmd == 2){
pszText = szPintText;
}
if(idCmd == 3){
pszText = szPopText;
}
if(idCmd == 4){
pszText = szPopText;
}
if ( uFlags & GCS_UNICODE )
{
// We need to cast pszName to a Unicode string, and then use the
// Unicode string copy API.
lstrcpynW ( (LPWSTR) pszName, T2CW(pszText), cchMax );
}
else
{
// Use the ANSI string copy API to return the help string.
lstrcpynA ( pszName, T2CA(pszText), cchMax );
}
return S_OK;
}
return E_INVALIDARG;
}
HRESULT COpenWithCtxMenuExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )
{
// If lpVerb really points to a string, ignore this function call and bail out.
if ( 0 != HIWORD( pCmdInfo->lpVerb ))
return E_INVALIDARG;
// Get the command index.
switch ( LOWORD( pCmdInfo->lpVerb ))
{
case 0:
{
ShellExecute ( pCmdInfo->hwnd, _T("open"), _T("notepad.exe"),
m_szSelectedFile, NULL, SW_SHOW );
return S_OK;
}
break;
case 1:
{
ShellExecute ( pCmdInfo->hwnd, _T("open"), _T("iexplore.exe"),
m_szSelectedFile, NULL, SW_SHOW );
return S_OK;
}
break;
case 2:
{
ShellExecute ( pCmdInfo->hwnd, _T("open"), _T("mspaint.exe"),
m_szSelectedFile, NULL, SW_SHOW );
return S_OK;
}
break;
case 4:
{
MessageBox(0, "New command from sub menu", "Case 4", 0);
return S_OK;
}
break;
case 5:
{
MessageBox(0, "New second command from sub menu", "Case 5", 0);
return S_OK;
}
break;
default:
return E_INVALIDARG;
break;
}
}