I am trying to get the installed location of all application using COM. I am able to get the display name of each application. But I am not able to get installed path of each application.
MY Code:
CComPtr<IShellItem> spPrinters;
CoInitialize(nullptr);
HRESULT hresult = ::SHCreateItemFromParsingName(L"::{26EE0668-A00A-44D7-9371-BEB064C98683}\\8\\"
L"::{7B81BE6A-CE2B-4676-A29E-EB907A5126C5}", nullptr, IID_PPV_ARGS(&spPrinters));
CComPtr<IEnumShellItems> spEnum;
spPrinters->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&spEnum));
for (CComPtr<IShellItem> spProgram; spEnum->Next(1, &spProgram, nullptr) == S_OK; spProgram.Release())
{
CComHeapPtr<wchar_t> spszName;
spProgram->GetDisplayName(SIGDN_NORMALDISPLAY, &spszName);
CString cDisplayName = spszName;
}
Any idea how to get installed path from IEnumShellItems?
Here is a piece of code that will dump this out. The child's IPropertyStore does not return these, I don't know why, so we have to use the old
IShellFolder2::GetDetailsEx method with a special column id (which is the same as a PROPERTYKEY).
CComPtr<IShellItem> cpl;
CComPtr<IShellFolder2> folder;
CComPtr<IEnumShellItems> enumerator;
PROPERTYKEY pkLocation;
SHCreateItemFromParsingName(L"::{26EE0668-A00A-44D7-9371-BEB064C98683}\\8\\::{7B81BE6A-CE2B-4676-A29E-EB907A5126C5}", nullptr, IID_PPV_ARGS(&cpl));
// bind to IShellFolder
cpl->BindToHandler(NULL, BHID_SFObject, IID_PPV_ARGS(&folder));
// bind to IEnumShellItems
cpl->BindToHandler(NULL, BHID_EnumItems, IID_PPV_ARGS(&enumerator));
// get this property key's value
PSGetPropertyKeyFromName(L"System.Software.InstallLocation", &pkLocation);
for (CComPtr<IShellItem> child; enumerator->Next(1, &child, nullptr) == S_OK; child.Release())
{
// get child's display name
CComHeapPtr<wchar_t> name;
child->GetDisplayName(SIGDN_NORMALDISPLAY, &name);
wprintf(L"%s\n", name);
// get child's PIDL
CComHeapPtr<ITEMIDLIST> pidl;
SHGetIDListFromObject(child, &pidl);
// the PIDL is absolute, we need the relative one (the last itemId in the list)
// get it's install location
CComVariant v;
if (SUCCEEDED(folder->GetDetailsEx(ILFindLastID(pidl), &pkLocation, &v)))
{
// it's a VT_BSTR
wprintf(L" %s\n", v.bstrVal);
}
}
Note it's using an undocumented System.Software.InstallLocation PROPERTYKEY. To find it I just dumped all columns with a code like this for each child:
int iCol = 0;
do
{
SHCOLUMNID colId;
if (FAILED(folder->MapColumnToSCID(iCol, &colId)))
break; // last column
CComHeapPtr<wchar_t> name;
PSGetNameFromPropertyKey(colId, &name);
CComVariant v;
if (SUCCEEDED(folder->GetDetailsEx(ILFindLastID(pidl), &colId, &v)))
{
if (v.vt == VT_BSTR)
{
wprintf(L" %s: %s\n", name, v.bstrVal);
}
else
{
wprintf(L" %s vt: %i\n", name, v.vt);
}
}
iCol++;
} while (true);
}
PS: I've not added much error checking, but you should.
Related
With the CHtmlView class I was able to select all the text and copy to the clipboard like this:
void CChristianLifeMinistryHtmlView::CopyToClipboard()
{
ExecWB(OLECMDID_COPY, OLECMDEXECOPT_DONTPROMPTUSER, nullptr, nullptr);
}
void CChristianLifeMinistryHtmlView::SelectAll()
{
ExecWB(OLECMDID_SELECTALL, OLECMDEXECOPT_DONTPROMPTUSER, nullptr, nullptr);
}
I am trying to find out how to do the same with the new WebView2 API.
Update
The WebView2 control supports by design:
CTRL + A to select all the content.
CTRL + C to copy the selected text to the clipboard.
I found a VB.NET solution to programatically copy all the page to the clipboard:
Sub Async GetText()
v = Await wv.ExecuteScriptAsync("document.body.innerText")
End Sub
But I am using Visual C++ and I do not see this method exposed. Also, I am not sure it is what I want because I do not want to copy as plain text data but HTML data (suitable for pasting in Word) like with the hotkeys. I have made a GitHub issue for this too.
Update
So I have now tried this code but it does not appear to do anything:
void CWebBrowser::CopyToClipboard()
{
if (m_pImpl->m_webView != nullptr)
{
m_pImpl->m_webView->ExecuteScript(_T("document.body.innerText"), nullptr);
}
}
Update
According to this article it states:
Alternately, for ICoreWebView2::ExecuteScript, you provide an instance that has an Invoke method that provides you with the success or failure code of the ExecuteScript request. Also provide the second parameter that is the JSON of the result of running the script.
Trying to find example.
Update
I now understand that I need to do something like this:
void CWebBrowser::CopyToClipboard()
{
if (m_pImpl->m_webView != nullptr)
{
m_pImpl->m_webView->ExecuteScript(L"document.body.innerText",
Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
[](HRESULT error, PCWSTR result) -> HRESULT
{
if (error != S_OK) {
ShowFailure(error, L"ExecuteScript failed");
}
SetClipboardText(result);
AfxMessageBox(L"HTML copied to clipboard!", MB_OK);
return S_OK;
}).Get());
}
}
The variable result has the contents of the HTML page. But it does not like my call to SetClipboardText now. This is the error:
This is the function:
void CWebBrowser::SetClipboardText(CString strText)
{
BYTE* pbyText;
TCHAR* pcBuffer;
HANDLE hText;
UINT uLength;
//USES_CONVERSION ;
if (::OpenClipboard(nullptr))
{
// Empty it of all data first.
::EmptyClipboard();
// Replace previous text contents.
uLength = strText.GetLength();
//pcBuffer = T2A( (LPTSTR)(LPCTSTR)strText);
pcBuffer = strText.GetBuffer(uLength);
if (pcBuffer != nullptr)
{
hText = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (uLength + 1) * sizeof(TCHAR));
if (hText != nullptr)
{
pbyText = (BYTE*)::GlobalLock(hText);
if (pbyText != nullptr)
{
// Deliberately not _tcscpy().
//strcpy_s( (char *)pbyText, uLength+1, pcBuffer);
_tcscpy_s((TCHAR*)pbyText, uLength + 1, pcBuffer);
::GlobalUnlock(hText);
::SetClipboardData(CF_UNICODETEXT, hText);
// Don't free this memory, clipboard owns it now.
}
}
}
::CloseClipboard();
strText.ReleaseBuffer(uLength);
}
}
This is the only way I know (to date) to copy to clipboard. So I have two issues here:
It will not let me use this function.
I expect it will copy just text (CF_UNICODETEXT) and I don't know if that is sufficient for pasting the data as HTML into Word?
Concerning issue 1, I needed to make the method static. Then it compiled and copied the information to the clipboard.
Concerning issue 2, as expected, the pasted data was stripped HTML and I was left with just the text. And the new line characters were displayed as "\n" in the text. It was one huge paragraph. So I do indeed need the CF_HTML format.
It is a pity that the WebView2 does not expose the copy to clipboard feature that it already has functional.
Update
This is the clipboard method that I found on the internet for CF_HTML:
// CopyHtml() - Copies given HTML to the clipboard.
// The HTML/BODY blanket is provided, so you only need to
// call it like CopyHtml("<b>This is a test</b>");
void CopyHTML(char* html)
{
// Create temporary buffer for HTML header...
char* buf = new char[400 + strlen(html)];
if (!buf) return;
// Get clipboard id for HTML format...
static int cfid = 0;
if (!cfid) cfid = RegisterClipboardFormat("HTML Format");
// Create a template string for the HTML header...
strcpy(buf,
"Version:0.9\r\n"
"StartHTML:00000000\r\n"
"EndHTML:00000000\r\n"
"StartFragment:00000000\r\n"
"EndFragment:00000000\r\n"
"<html><body>\r\n"
"<!--StartFragment -->\r\n");
// Append the HTML...
strcat(buf, html);
strcat(buf, "\r\n");
// Finish up the HTML format...
strcat(buf,
"<!--EndFragment-->\r\n"
"</body>\r\n"
"</html>");
// Now go back, calculate all the lengths, and write out the
// necessary header information. Note, wsprintf() truncates the
// string when you overwrite it so you follow up with code to replace
// the 0 appended at the end with a '\r'...
char* ptr = strstr(buf, "StartHTML");
wsprintf(ptr + 10, "%08u", strstr(buf, "<html>") - buf);
*(ptr + 10 + 8) = '\r';
ptr = strstr(buf, "EndHTML");
wsprintf(ptr + 8, "%08u", strlen(buf));
*(ptr + 8 + 8) = '\r';
ptr = strstr(buf, "StartFragment");
wsprintf(ptr + 14, "%08u", strstr(buf, "<!--StartFrag") - buf);
*(ptr + 14 + 8) = '\r';
ptr = strstr(buf, "EndFragment");
wsprintf(ptr + 12, "%08u", strstr(buf, "<!--EndFrag") - buf);
*(ptr + 12 + 8) = '\r';
// Now you have everything in place ready to put on the clipboard.
// Open the clipboard...
if (OpenClipboard(0))
{
// Empty what's in there...
EmptyClipboard();
// Allocate global memory for transfer...
HGLOBAL hText = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, strlen(buf) + 4);
// Put your string in the global memory...
char* ptr = (char*)GlobalLock(hText);
strcpy(ptr, buf);
GlobalUnlock(hText);
::SetClipboardData(cfid, hText);
CloseClipboard();
// Free memory...
GlobalFree(hText);
}
// Clean up...
delete[] buf;
}
But it is not compatible with the PCWSTR variable.
Update
I have now realised after doing debugging that ExecuteScript does return the actual HTML data if I use document.body.innerText. The resulting string is just text with \n characters for the new lines. It is not in HTML format. So I am back to square one.
Just use ICoreWebView2::ExecuteScript.
I am showing this as an alternative because the current approach does not include the actual HTML content. The information I found on the internet was misleading about using document.body.innerText.
One solution to this is to use the document.execCommand() route. I added the following methods to my browser class:
void CWebBrowser::SelectAll()
{
if (m_pImpl->m_webView != nullptr)
{
m_pImpl->m_webView->ExecuteScript(L"document.execCommand(\"SelectAll\")", nullptr);
}
}
void CWebBrowser::CopyToClipboard2()
{
if (m_pImpl->m_webView != nullptr)
{
m_pImpl->m_webView->ExecuteScript(L"document.execCommand(\"Copy\")", nullptr);
}
}
Then I added the following button handlers to my dialog to test:
void CMFCTestWebView2Dlg::OnBnClickedButtonSelectAll()
{
if (m_pWebBrowser != nullptr)
{
m_pWebBrowser->SelectAll();
}
}
void CMFCTestWebView2Dlg::OnBnClickedButtonCopyToClipboard()
{
if (m_pWebBrowser != nullptr)
{
m_pWebBrowser->CopyToClipboard2();
}
}
This does work and appears to be the simplest solution for now. I see references to the Calendar API as an alternative but I am not sure yet how to implement that approach.
My only concern with execCommand is that I have seen it documented as deprecated.
I'm working on an application that would capture the screen through Desktop duplication APIs (using DirectX 11) (only the diff to the previous screen update) and render it on another window (The viewer might be running on another machine connected via LAN). The code is an improved version of the sample provided in MSDN. Everything works fine except the device did not give any screen update though there is one some times in the mid, that happens around 10% of the time on some machines (mostly on windows 8/8.1 machines and rarely on windows 10 machines). I tried all the possible ways to sort out this problem. Reduced the number of device resets, that provided me some what reliable output but not always work fine for 100%.
The device fails to provide an initial screen (a full screen) some times (This happens 60% of the time on all windows operating systems where Desktop duplication is supported), I came up with a work around that retried for an initial update from the device until it provides one but that too resulted in multiple issues, the device might not even give the initial screen ever.
I have already invested weeks of my efforts to fix the problem but did not figure out a proper solution and there are no forums I know that discusses these kind of issues. Any help would be appreciated.
Below is my code to get the screen diff to the previous one, init the device, populating the adapters and monitors.
Please bear with me for a very long code snippet, Thanks in advance.
To Get the screen update:
INT getChangedRegions(int timeout, rectangles &dirtyRects, std::vector <MOVE_RECT> &moveRects, UINT &rect_count, RECT ScreenRect)
{
UINT diffArea = 0;
FRAME_DATA currentFrameData;
bool isTimeOut = false;
TRY
{
m_LastErrorCode = m_DuplicationManager.GetFrame(¤tFrameData, timeout, &isTimeOut);
if(SUCCEEDED(m_LastErrorCode) && (!isTimeOut))
{
if(currentFrameData.FrameInfo.TotalMetadataBufferSize)
{
m_CurrentFrameTexture = currentFrameData.Frame;
if(currentFrameData.MoveCount)
{
DXGI_OUTDUPL_MOVE_RECT* moveRectArray = reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*> (currentFrameData.MetaData);
if (moveRectArray)
{
for(UINT index = 0; index < currentFrameData.MoveCount; index++)
{
//WebRTC
// DirectX capturer API may randomly return unmoved move_rects, which should
// be skipped to avoid unnecessary wasting of differing and encoding
// resources.
// By using testing application it2me_standalone_host_main, this check
// reduces average capture time by 0.375% (4.07 -> 4.055), and average
// encode time by 0.313% (8.042 -> 8.016) without other impacts.
if (moveRectArray[index].SourcePoint.x != moveRectArray[index].DestinationRect.left || moveRectArray[index].SourcePoint.y != moveRectArray[index].DestinationRect.top)
{
if(m_UseD3D11BitmapConversion)
{
MOVE_RECT moveRect;
moveRect.SourcePoint.x = moveRectArray[index].SourcePoint.x * m_ImageScalingFactor;
moveRect.SourcePoint.y = moveRectArray[index].SourcePoint.y * m_ImageScalingFactor;
moveRect.DestinationRect.left = moveRectArray[index].DestinationRect.left * m_ImageScalingFactor;
moveRect.DestinationRect.top = moveRectArray[index].DestinationRect.top * m_ImageScalingFactor;
moveRect.DestinationRect.bottom = moveRectArray[index].DestinationRect.bottom * m_ImageScalingFactor;
moveRect.DestinationRect.right = moveRectArray[index].DestinationRect.right * m_ImageScalingFactor;
moveRects.push_back(moveRect);
diffArea += abs((moveRect.DestinationRect.right - moveRect.DestinationRect.left) *
(moveRect.DestinationRect.bottom - moveRect.DestinationRect.top));
}
else
{
moveRects.push_back(moveRectArray[index]);
diffArea += abs((moveRectArray[index].DestinationRect.right - moveRectArray[index].DestinationRect.left) *
(moveRectArray[index].DestinationRect.bottom - moveRectArray[index].DestinationRect.top));
}
}
}
}
else
{
return -1;
}
}
if(currentFrameData.DirtyCount)
{
RECT* dirtyRectArray = reinterpret_cast<RECT*> (currentFrameData.MetaData + (currentFrameData.MoveCount * sizeof(DXGI_OUTDUPL_MOVE_RECT)));
if (!dirtyRectArray)
{
return -1;
}
rect_count = currentFrameData.DirtyCount;
for(UINT index = 0; index < rect_count; index ++)
{
if(m_UseD3D11BitmapConversion)
{
RECT dirtyRect;
dirtyRect.bottom = dirtyRectArray[index].bottom * m_ImageScalingFactor;
dirtyRect.top = dirtyRectArray[index].top * m_ImageScalingFactor;
dirtyRect.left = dirtyRectArray[index].left * m_ImageScalingFactor;
dirtyRect.right = dirtyRectArray[index].right * m_ImageScalingFactor;
diffArea += abs((dirtyRect.right - dirtyRect.left) *
(dirtyRect.bottom - dirtyRect.top));
dirtyRects.push_back(dirtyRect);
}
else
{
diffArea += abs((dirtyRectArray[index].right - dirtyRectArray[index].left) *
(dirtyRectArray[index].bottom - dirtyRectArray[index].top));
dirtyRects.push_back(dirtyRectArray[index]);
}
}
}
}
return diffArea;
}
CATCH_ALL(e)
{
LOG(CRITICAL) << _T("Exception in getChangedRegions");
}
END_CATCH_ALL
return -1;
}
Here is the code to init the device
//
// Initialize duplication interfaces
//
HRESULT cDuplicationManager::InitDupl(_In_ ID3D11Device* Device, _In_ IDXGIAdapter *_pAdapter, _In_ IDXGIOutput *_pOutput, _In_ UINT Output)
{
HRESULT hr = E_FAIL;
if(!_pOutput || !_pAdapter || !Device)
{
return hr;
}
m_OutputNumber = Output;
// Take a reference on the device
m_Device = Device;
m_Device->AddRef();
/*
// Get DXGI device
IDXGIDevice* DxgiDevice = nullptr;
HRESULT hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&DxgiDevice));
if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for DXGI Device"), _T("Error"), hr);
}
// Get DXGI adapter
IDXGIAdapter* DxgiAdapter = nullptr;
hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&DxgiAdapter));
DxgiDevice->Release();
DxgiDevice = nullptr;
if (FAILED(hr))
{
return ProcessFailure(m_Device, _T("Failed to get parent DXGI Adapter"), _T("Error"), hr);//, SystemTransitionsExpectedErrors);
}
// Get output
IDXGIOutput* DxgiOutput = nullptr;
hr = DxgiAdapter->EnumOutputs(Output, &DxgiOutput);
DxgiAdapter->Release();
DxgiAdapter = nullptr;
if (FAILED(hr))
{
return ProcessFailure(m_Device, _T("Failed to get specified output in DUPLICATIONMANAGER"), _T("Error"), hr);//, EnumOutputsExpectedErrors);
}
DxgiOutput->GetDesc(&m_OutputDesc);
IDXGIOutput1* DxgiOutput1 = nullptr;
hr = DxgiOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));
*/
_pOutput->GetDesc(&m_OutputDesc);
// QI for Output 1
IDXGIOutput1* DxgiOutput1 = nullptr;
hr = _pOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));
if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for DxgiOutput1 in DUPLICATIONMANAGER"), _T("Error"), hr);
}
// Create desktop duplication
hr = DxgiOutput1->DuplicateOutput(m_Device, &m_DeskDupl);
DxgiOutput1->Release();
DxgiOutput1 = nullptr;
if (FAILED(hr) || !m_DeskDupl)
{
if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
{
return ProcessFailure(nullptr, _T("Maximum number of applications using Desktop Duplication API"), _T("Error"), hr);
}
return ProcessFailure(m_Device, _T("Failed to get duplicate output in DUPLICATIONMANAGER"), _T("Error"), hr);//, CreateDuplicationExpectedErrors);
}
return S_OK;
}
Finally to get the current frame and difference to the previous one:
//
// Get next frame and write it into Data
//
_Success_(*Timeout == false && return == DUPL_RETURN_SUCCESS)
HRESULT cDuplicationManager::GetFrame(_Out_ FRAME_DATA* Data, int timeout, _Out_ bool* Timeout)
{
IDXGIResource* DesktopResource = nullptr;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
try
{
// Get new frame
HRESULT hr = m_DeskDupl->AcquireNextFrame(timeout, &FrameInfo, &DesktopResource);
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
*Timeout = true;
return S_OK;
}
*Timeout = false;
if (FAILED(hr))
{
return ProcessFailure(m_Device, _T("Failed to acquire next frame in DUPLICATIONMANAGER"), _T("Error"), hr);//, FrameInfoExpectedErrors);
}
// If still holding old frame, destroy it
if (m_AcquiredDesktopImage)
{
m_AcquiredDesktopImage->Release();
m_AcquiredDesktopImage = nullptr;
}
if (DesktopResource)
{
// QI for IDXGIResource
hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&m_AcquiredDesktopImage));
DesktopResource->Release();
DesktopResource = nullptr;
}
if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER"), _T("Error"), hr);
}
// Get metadata
if (FrameInfo.TotalMetadataBufferSize)
{
// Old buffer too small
if (FrameInfo.TotalMetadataBufferSize > m_MetaDataSize)
{
if (m_MetaDataBuffer)
{
delete [] m_MetaDataBuffer;
m_MetaDataBuffer = nullptr;
}
m_MetaDataBuffer = new (std::nothrow) BYTE[FrameInfo.TotalMetadataBufferSize];
if (!m_MetaDataBuffer)
{
m_MetaDataSize = 0;
Data->MoveCount = 0;
Data->DirtyCount = 0;
return ProcessFailure(nullptr, _T("Failed to allocate memory for metadata in DUPLICATIONMANAGER"), _T("Error"), E_OUTOFMEMORY);
}
m_MetaDataSize = FrameInfo.TotalMetadataBufferSize;
}
UINT BufSize = FrameInfo.TotalMetadataBufferSize;
// Get move rectangles
hr = m_DeskDupl->GetFrameMoveRects(BufSize, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(m_MetaDataBuffer), &BufSize);
if (FAILED(hr))
{
Data->MoveCount = 0;
Data->DirtyCount = 0;
return ProcessFailure(nullptr, L"Failed to get frame move rects in DUPLICATIONMANAGER", L"Error", hr);//, FrameInfoExpectedErrors);
}
Data->MoveCount = BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);
BYTE* DirtyRects = m_MetaDataBuffer + BufSize;
BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;
// Get dirty rectangles
hr = m_DeskDupl->GetFrameDirtyRects(BufSize, reinterpret_cast<RECT*>(DirtyRects), &BufSize);
if (FAILED(hr))
{
Data->MoveCount = 0;
Data->DirtyCount = 0;
return ProcessFailure(nullptr, _T("Failed to get frame dirty rects in DUPLICATIONMANAGER"), _T("Error"), hr);//, FrameInfoExpectedErrors);
}
Data->DirtyCount = BufSize / sizeof(RECT);
Data->MetaData = m_MetaDataBuffer;
}
Data->Frame = m_AcquiredDesktopImage;
Data->FrameInfo = FrameInfo;
}
catch (...)
{
return S_FALSE;
}
return S_OK;
}
Update :
Failed to acquire next frame in DUPLICATIONMANAGER is getting printed whenever the device has hung (That is in the mid of streaming the screens, Ex: Continuously capturing a video and sending it to the other end)
// Get new frame
HRESULT hr = m_DeskDupl->AcquireNextFrame(timeout, &FrameInfo, &DesktopResource);
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
*Timeout = true;
return S_OK;
}
*Timeout = false;
if (FAILED(hr))
{
return ProcessFailure(m_Device, _T("Failed to acquire next frame in DUPLICATIONMANAGER"), _T("Error"), hr);//, FrameInfoExpectedErrors);
}
here is the detailed error info :
Id3d11DuplicationManager::ProcessFailure - Error: Failed to acquire next frame in DUPLICATIONMANAGER, Detail: The keyed mutex was abandoned.
Update 2 :
I have got the error code whenever the device failed to give screen updates forever, And here is the same
Id3d11DuplicationManager::ProcessFailure - Error: Failed to get duplicate output in DUPLICATIONMANAGER, Detail: Access is denied.
The error code is E_ACCESSDENIED.
I do not understand why I am getting this error as I am running in SYSTEM mode already and the SetThreadDesktop had been executed twice (One during the init and another after detecting a failure)
This is what the explanation of the error on MSDN : E_ACCESSDENIED if the application does not have access privilege to the current desktop image. For example, only an application that runs at LOCAL_SYSTEM can access the secure desktop.
Is there anything else that would result in this kind of issue?
It's always good to check the return codes and immediately fall back to GDI or any other available screen capturing approach in case of non-recoverable errors. Retrying doesn't work most of the time for certain hardware errors like max limit reached, out of memory, device removed, etc, I learned it in a hard way. Furthermore, DirectX device takes a few iterations before producing an initial frame on rare occasions. It wouldn't be useful to retry more than 10 times, you can safely fallback or try re-initializing the device to check one more time before falling back.
Here are some basic checks to do:
Handle DXGI_ERROR_NOT_CURRENTLY_AVAILABLE error:
_pOutput->GetDesc(&m_OutputDesc);
// QI for Output 1
IDXGIOutput1* DxgiOutput1 = nullptr;
hr = _pOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));
if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for DxgiOutput1 in DUPLICATIONMANAGER"), _T("Error"), hr);
}
// Create desktop duplication
hr = DxgiOutput1->DuplicateOutput(m_Device, &m_DeskDupl);
DxgiOutput1->Release();
DxgiOutput1 = nullptr;
if (FAILED(hr) || !m_DeskDupl)
{
if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
{
return ProcessFailure(nullptr, _T("Maximum number of applications using Desktop Duplication API"), _T("Error"), hr);
}
return ProcessFailure(m_Device, _T("Failed to get duplicate output in DUPLICATIONMANAGER"), _T("Error"), hr);//, CreateDuplicationExpectedErrors);
}
Check for device removed(DXGI_ERROR_DEVICE_REMOVED) or Device reset(DXGI_ERROR_DEVICE_RESET) & Out of memory(E_OUTOFMEMORY) error codes (I have received E_OUTOFMEMORY sometimes, though it's uncommon):
HRESULT ProcessFailure(_In_opt_ ID3D11Device* Device, _In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr)//, _In_opt_z_ HRESULT* ExpectedErrors = NULL)
{
HRESULT TranslatedHr;
// On an error check if the DX device is lost
if (Device)
{
HRESULT DeviceRemovedReason = Device->GetDeviceRemovedReason();
switch (DeviceRemovedReason)
{
case DXGI_ERROR_DEVICE_REMOVED:
case DXGI_ERROR_DEVICE_RESET:
case static_cast<HRESULT>(E_OUTOFMEMORY) :
{
// Our device has been stopped due to an external event on the GPU so map them all to
// device removed and continue processing the condition
TranslatedHr = DXGI_ERROR_DEVICE_REMOVED;
break;
}
case S_OK:
{
// Device is not removed so use original error
TranslatedHr = hr;
break;
}
default:
{
// Device is removed but not a error we want to remap
TranslatedHr = DeviceRemovedReason;
}
}
}
else
{
TranslatedHr = hr;
}
_com_error err(TranslatedHr);
LPCTSTR errMsg = err.ErrorMessage();
return TranslatedHr;
}
Furthermore, Desktop duplication requires a real graphics device to be active in order to work. You may get E_ACCESSDENIED otherwise.
There are also other scenarios you may get this error, like, Desktop switch cases, abandoned keyed mutex. You can try reinitializing the device in such cases.
I have also uploaded my sample project here.
I'm writing a program that sets acl permissions for a given user/group. Very similar to the "setfacl" command.
According to a given user/group I search first if the entry already exists. If I get a NULL then I call acl_create_entry. When I check the ACL via acl_check, I get an ACL_ENTRY_ERROR.
The function is something similar to this:
/*ar_pobj=Path or file name, ar_tagType=ACL_USER or ACL_GROUP, ar_ptagQual=User name or group name, ar_pperms=string with permissions "rwx-"*/
int addPermissions(char *ar_pobj, acl_tag_t ar_tagType, char *ar_ptagQual, char *ar_pperms)
{
acl_t la_acl;
id_t la_qual;
acl_entry_t la_entry;
int la_error;
la_acl=acl_get_file(par_obj,ACL_TYPE_ACCESS);
if (la_acl == NULL)
{
return -1;
}
switch(ar_tagType)
{
case ACL_USER:
la_qual=userIdFromName(ar_ptagQual);/*This function works fine*/
if(-1==la_qual)
{
acl_free(la_acl);
return -1;
}
break;
case ACL_GROUP:
la_qual=groupIdFromName(ar_ptagQual);/*This function works fine*/
if(-1==la_qual)
{
acl_free(la_acl);
return -1;
}
break;
default:
acl_free(la_acl);
return -1;
}
la_entry = findEntry(la_acl,ar_tagType,la_qual);/*This function returns NULL as the entry was not found*/
if(NULL==la_entry)
{
if (acl_create_entry(&la_acl, &la_entry) == -1)/*Returns OK*/
{
acl_free(la_acl);
return -1;
}
la_error=acl_check(la_acl,NULL);/*HERE IS WHERW I GET THE ERROR*/
...
}
...
}
Any clues? Thank you in advance.
edit This error always happens if you did not set the tag type and qualifier. But I am still getting it after doing this
¡Issue solved! The problem was that I forgot to recalculate the ACL mask. Calling acl_calc_mask(&la_acl) right before calling acl_set_file solved the problem.
I recently ported the following code to wx3.0 under visual studio 2013:
void PanelFileControl::on_retrieve_clicked(wxCommandEvent &event)
{
if(!chosen_files.empty())
{
Csi::ModalCounter counter;
wxDirDialog query(
this,
make_wxString(my_strings[strid_choose_retrieve_dir]),
make_wxString(wxGetApp().get_config()->get_last_prog_dir()));
int rcd;
query.CentreOnParent();
rcd = query.ShowModal();
if(rcd == wxID_OK)
{
// we need to generate an operation for every selected file.
StrAsc path(make_StrAsc(query.GetPath()));
DlgFileControl::operations_type operations;
if(path.last() != Csi::FileSystemObject::dir_separator())
path.append(Csi::FileSystemObject::dir_separator());
for(files_type::iterator fi = chosen_files.begin(); fi != chosen_files.end(); ++fi)
{
file_type const &file(*fi);
StrAsc file_path(path + file.get_file_name());
bool use_file(true);
if(Csi::file_exists(file_path.c_str()))
{
OwxStringStream message;
message << boost::format(my_strings[strid_overwrite_file_confirm].c_str()) %
file_path;
wxMessageDialog overwrite_query(
this,
message.str(),
wxT(""),
wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
int rcd;
overwrite_query.CentreOnParent();
rcd = overwrite_query.ShowModal();
if(rcd != wxID_YES)
use_file = false;
}
if(use_file)
operations.push_back(new ReceiveFileOperation(file, file_path));
}
// we can now display the operation dialogue
if(!operations.empty())
{
DlgFileControl dialogue(this, device_name, operations);
dialogue.show_modal();
}
}
}
} // on_retrieve_clicked
Following this change (which didn't require any changes to the code above), I have received complaints that, if the user selects the desktop and then double clicks on a directory on the desktop, that the file save operation fails. This appears to be a result of the path produced by the wxDirDialog::GetPath() and has only been seen under windows vista. I have followed up some testing and I find that, under Vista, the last path component is mentioned twice in the string returned by GetPath().
Has anyone seen this issue? Are there any work arounds?
I found that I can address the issue by preventing the wxDirDialog from using the IFILEDIALOG interface from being used. My ShowModal() method now looks like this:
int wxDirDialog::ShowModal()
{
WX_HOOK_MODAL_DIALOG();
wxWindow* const parent = GetParent();
WXHWND hWndParent = parent ? GetHwndOf(parent) : NULL;
// Use IFileDialog under new enough Windows, it's more user-friendly.
int rc;
#if wxUSE_IFILEDIALOG
if ( wxGetWinVersion() > wxWinVersion_Vista )
{
rc = ShowIFileDialog(hWndParent);
}
else
{
rc = wxID_NONE;
}
if ( rc == wxID_NONE )
#endif // wxUSE_IFILEDIALOG
{
rc = ShowSHBrowseForFolder(hWndParent);
}
// change current working directory if asked so
if ( rc == wxID_OK && HasFlag(wxDD_CHANGE_DIR) )
wxSetWorkingDirectory(m_path);
return rc;
}
I'm working on a checker's simulation game for my C++ class. My issue is with the linked list that holds the checkers. I can delete any checker perfectly with the exception of the head of the list. I've looked around here and other websites and I believe there's a memory leak somewhere. I'm fairly new to C++ so I'm not sure what to really do other than playing around with things (which will probably just create a bigger problem). I've never posted here before, so excuse me if the formatting is slightly off or too messy. I'll try to make it brief. First, here's a snippet of the node class for the linked list.
class CheckerpieceNode
{
private:
Checkerpiece *Node;
CheckerpieceNode *Next;
public:
CheckerpieceNode(); // sets Node and Next to NULL in .cpp file
void setNode(Checkerpiece *node);
void setNext(CheckerpieceNode *next);
Checkerpiece* getNode();
CheckerpieceNode* getNext();
};
And the functions are set up pretty much as you would expect in a Checkerpiece.cpp class.
Here's how the code is used. Its called by a Checkerboard object in my main class.
theCheckerboard.removeChecker(theCheckerboard.findChecker(selector->getCurrentX() + 0, selector->getCurrentY() - VERTICAL_SHIFT, listHead), listHead);
The VERTICAL_SHIFT simply has to do with the way my checkerboard graphic is on the console. Since it works perfectly for all other nodes (excluding the head) I've ruled it out as a source of error. Selector is a checkerpiece object but its not part of the list.
Here's the actual findChecker and removeChecker code from Checkerboard class.
Checkerpiece* findChecker(int x, int y, CheckerpieceNode* list_head)
{
if(list_head== NULL) return NULL; // do nothing
else
{
CheckerpieceNode* node = new CheckerpieceNode;
node = list_head;
while(node != NULL && node->getNode() != NULL)
{
if()// comparison check here, but removed for space
{
return node->getNode();
delete node;
node = NULL;
}
else // traversing
node = node->getNext();
}
return NULL;
}
}
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode* list_head)
{
if(list_head== NULL) // throw exception
else
{
CheckerpieceNode *temp = NULL, *previous = NULL;
Checkerpiece* c_checker= new Checkerpiece;
temp = list_head;
while(temp != NULL && temp->getNode() != NULL)
{
c_checker= temp->getNode();
if(d_checker!= c_checker)
{
previous = temp;
temp = temp->getNext();
}
else
{
if(temp != list_head)
{
previous->setNext(temp->getNext());
delete temp;
temp = NULL;
}
else if(temp == list_head) // this is where head should get deleted
{
temp = list_head;
list_head= list_head->getNext();
delete temp;
temp = NULL;
}
return;
}
}
}
}
Oh my, you're complicating it. Lots of redundant checks, assignments and unnecessary variables (like c_checker which leaks memory too).
// Write down the various scenarios you can expect first:
// (a) null inputs
// (b) can't find d_checker
// (c) d_checker is in head
// (d) d_checker is elsewhere in the list
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode* list_head) {
// first sanitize your inputs
if (d_checker == nullptr || list_head == nullptr) // use nullptr instead of NULL. its a keyword literal of type nullptr_t
throw exception;
// You understand that there is a special case for deleting head. Good.
// Just take care of it once and for all so that you don't check every time in the loop.
CheckerpieceNode *curr = list_head;
// take care of deleting head before traversal
if (d_checker == curr->getNode()) {
list_head = list_head->next; // update list head
delete curr; // delete previous head
return; // we're done
}
CheckerpieceNode *prev = curr;
curr = curr->next;
// traverse through the list - keep track of previous
while (curr != nullptr) {
if (d_checker == curr->getNode()) {
prev->next = curr->next;
delete curr;
break; // we're done!
}
prev = curr;
curr = curr->next;
}
}
I hope that helps. Take the time to break down the problem into smaller pieces, figure out the scenarios possible, how you'll handle them and only then start writing code.
Based on this edit by the question author, the solution he used was to:
I modified the code to show the address passing in the checker delete
function.
void delete_checker(Checker* d_checker, CheckerNode* &list_head) // pass by address
{
if(list_head== NULL) // throw exception
else
{
CheckerNode*temp = NULL, *previous = NULL;
Checker* c_checker= new Checker;
temp = list_head;
while(temp != NULL && temp->node!= NULL)
{
c_checker= temp->node;
if(d_checker!= c_checker)
{
previous = temp;
temp = temp->next;
}
else
{
if(temp != list_head)
{
previous->next = temp->next;
delete temp;
temp = NULL;
}
else if(temp == list_head) // this is where head should get deleted
{
temp = list_head;
list_head= list_head->next;
delete temp;
temp = NULL;
}
delete c_checker;
c_checker = nullptr;
return;
}
}
}
}
removeChecker cannot modify the value of list_head as it is past by value. The method signature should be:
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode** list_head)
// You will need to call this function with &list_head
or
void removeChecker(Checkerpiece* d_checker, CheckerpieceNode* &list_head)
// Calling code does not need to change