GetIDsOfNames always fails with RPC_E_SERVERCALL_RETRYLATER for getting a property ID in Photoshop - c++

I wrote the following C++ code, trying to perform some OLE automation tasks
for currently-running Photoshop:
IUnknown* unk = NULL;
IDispatch* disp = NULL;
CLSID clsId;
if (FAILED(CLSIDFromProgID(L"Photoshop.Application", &clsId)))
goto err;
puts("CLSIDFromProgID succeeded");
if (FAILED(GetActiveObject(&clsId, NULL, &unk)))
goto err;
puts("GetActiveObject succeeded");
if (FAILED(IUnknown_QueryInterface(unk, &IID_IDispatch, (void**)&disp)))
goto err;
puts("QueryInterface succeeded");
DISPID dispId;
HRESULT resultOfGettingId = IDispatch_GetIDsOfNames(disp, &IID_NULL,
(LPOLESTR[1]){L"ActiveDocument"}, 1,
LOCALE_USER_DEFAULT, &dispId);
if (FAILED(resultOfGettingId))
{
//The code always goes here.
//The result code is printed as 0X8001010A,
//which is RPC_E_SERVERCALL_RETRYLATER.
printf("GetIDsOfNames for <ActiveDocument> failed. Result code: %#lX.\n",
resultOfGettingId);
goto err;
}
puts("GetIDsOfNames for Selection succeeded");
The above C++ code always prints the following text (whenever I run it):
GetIDsOfNames for <ActiveDocument> failed. Result code: 0X8001010A.
After searching for RPC_E_SERVERCALL_RETRYLATER, I found that the error code
seems to mean that the application is busy. But Photoshop is just opened, and
not being manipulated at all, and just contains only a single empty document.
How can it always be busy?
And why does the equivalent C# code such as
try
{
activeDocument.activeLayer.textItem.font = "FZCYJW--GB1-0";
}
catch(e)
{
...
}
works fine?
It doesn't make sense that Photoshop just happens to be busy everytime
when the C++ program is trying to connect.
Something problematic other than the so-called busyness must be hiden
in the details but I just have no clue.
Edit:
Issued resloved!
I've found the culprit of "busyness". If Photoshop is in the state of selecting some text, then it fails, otherwise it succeeds. So when some text is selected, Photoshop is in a state where it thinks handleing COM call-ins out of process isn't a good idea.

I've found the culprit of "busyness". If Photoshop is in the state of selecting some text, then it fails, otherwise it succeeds. So when some text is selected, Photoshop is in a state where it thinks handling COM call-ins out of process isn't a good idea.

Related

Visual Studio Debugger silently continues when stepping over a specific line of code

I'm using the windows API to get a handle to a IAudioEndpointVolume and register a IAudioEndpointVolumeCallback. I've also got a IMMDeviceEnumerator with a registered callback for IMMNotificationClient so that when the default device changes I release the IAudioEndpointVolumeCallback and IAudioEndpointVolume and store new ones.
However, my program seems to silently crash on the callback thread when releasing IAudioEndpointVolume with no exception or output that I can see. Even when breaking on the line and trying to step over it will silently continue without getting to the next statement but also without the debugger coming down.
if(_volumeEndPoint)
{
if(_callback)
{
HRESULT hrUnReg = _volumeEndPoint->UnregisterControlChangeNotify(this);
if(hrUnReg != S_OK)
{
std::cout << hrUnReg; // this reports successful
}
}
_volumeEndPoint->Release(); // <--- cannot step over this line
_volumeEndPoint = nullptr;
}
if(deviceEndPoint == nullptr)
{
return;
}
Things I've tried:
Enabling all the exceptions in Debug->Windows->Exception Settings
Adding a try/catch all exceptions
Feels like it's probably related to it being a callback on a different thread? I'm not that well versed in C++ nor the WinAPI and the COM stuff so almost certain I'm doing something stupid but it would be good if it could tell me an error of some kind.

Converting Visual C++ Media Foundation Capture application to C++ Builder

I am trying to convert the Microsoft "CaptureEngine video capture sample" code from Visual C++ to Embarcadero C++ Builder.
https://code.msdn.microsoft.com/windowsdesktop/Media-Foundation-Capture-78504c83
The code runs fine with Visual C++, but I need to include in a C++ Builder application. I basically have the code working, but there are a couple of issues I need help with.
I can select the video source, preview the video source and even start capture to file. However the video capture file just contains the one frame repeated for the length of the video, even though Audio is correctly recorded.
I am wondering if this is due to events not being handled properly.
The events from the media foundation capture engine are passed to the main thread using windows messaging which then calls the media engine event handler. However I have noticed the event handler to stop the recording and to stop the preview uses wait for result
void WaitForResult()
{
WaitForSingleObject(m_hEvent, INFINITE);
}
HRESULT CaptureManager::StopPreview()
{
HRESULT hr = S_OK;
if (m_pEngine == NULL)
{
return MF_E_NOT_INITIALIZED;
}
if (!m_bPreviewing)
{
return S_OK;
}
hr = m_pEngine->StopPreview();
if (FAILED(hr))
{
goto done;
}
WaitForResult();
if (m_fPowerRequestSet && m_hpwrRequest != INVALID_HANDLE_VALUE)
{
PowerClearRequest(m_hpwrRequest, PowerRequestExecutionRequired);
m_fPowerRequestSet = false;
}
done:
return hr;
}
The trouble is, this m_hEvent is triggered from the C++ Builder event handler which is part of the same main thread which is waiting for the event to be handled, so I get a thread lock when trying to stop the video recording. If I comment out the line, I don't lock but I also don't get valid recorded video file.
I am not sure how the Visual C++ separates the events from the Capture engine code, any suggestion as to how I can do this for C++ Builder?
Capture engine event callback is called on a worker thread and it is not "a part of same main thread".
// Callback method to receive events from the capture engine.
STDMETHODIMP CaptureManager::CaptureEngineCB::OnEvent( _In_ IMFMediaEvent* pEvent)
{
...
if (guidType == MF_CAPTURE_ENGINE_PREVIEW_STOPPED)
{
m_pManager->OnPreviewStopped(hrStatus);
SetEvent(m_pManager->m_hEvent);
This essentially changes the behavior of the application. The controlling thread stops preview and blocks until worker thread delivers a notification which sets the event as I quoted above. From there controlling thread wakes up from wait operation and continues with preview stopped.
If this is not what you are seeing in your application I would suggest setting a breakpoint at the first line of callback function to make sure you receive the notification. If you receive, you can step the code and make sure you reach the event setting line. If you don't receive, something else is blocking and you will have to figure that out, such as for example, by breaking in and examining thread states of the application.
I have found the cause of my issue. The OnEvent routine in the Capture engine example is definitely in its own thread. The problem is that it then posts a message to the main application thread, rather than handling it itself. This means that the main thread is frozen as it waiting on the mutex.
// Callback method to receive events from the capture engine.
STDMETHODIMP CaptureManager::CaptureEngineCB::OnEvent( _In_ IMFMediaEvent* pEvent)
{
// Post a message to the application window, so the event is handled
// on the application's main thread.
if (m_fSleeping && m_pManager != NULL)
{
// We're about to fall asleep, that means we've just asked the CE to stop the preview
// and record. We need to handle it here since our message pump may be gone.
GUID guidType;
HRESULT hrStatus;
HRESULT hr = pEvent->GetStatus(&hrStatus);
if (FAILED(hr))
{
hrStatus = hr;
}
hr = pEvent->GetExtendedType(&guidType);
if (SUCCEEDED(hr))
{
if (guidType == MF_CAPTURE_ENGINE_PREVIEW_STOPPED)
{
m_pManager->OnPreviewStopped(hrStatus);
SetEvent(m_pManager->m_hEvent);
}
else if (guidType == MF_CAPTURE_ENGINE_RECORD_STOPPED)
{
m_pManager->OnRecordStopped(hrStatus);
SetEvent(m_pManager->m_hEvent);
}
else
{
// This is an event we don't know about, we don't really care and there's
// no clean way to report the error so just set the event and fall through.
SetEvent(m_pManager->m_hEvent);
}
}
return S_OK;
}
else
{
pEvent->AddRef(); // The application will release the pointer when it handles the message.
PostMessage(m_hwnd, WM_APP_CAPTURE_EVENT, (WPARAM)pEvent, 0L);
}
return S_OK;
}

Access denied on setting property after clearing same property

I have been trying to edit the metadata of some audio files using Windows' IPropertyStore methods, and came across this strange problem. After setting an empty PROPVARIANT value to a property store's key via IPropertyStore::SetValue(), following attempts to set that key's value fail with a return value of 0x80030005, which Visual Studio informs me is Access Denied..
This is the smallest example with which I could produce this behaviour:
#include <atlbase.h>
#include <filesystem>
#include <PropIdl.h>
#include <Propsys.h>
#include <propkey.h>
#include <propvarutil.h>
#include <ShObjIdl.h>
#include <Windows.h>
namespace fs = std::experimental::filesystem;
int main() {
HRESULT hr;
if (FAILED(hr = CoInitializeEx(NULL, COINIT_MULTITHREADED))) {
// Handle error...
}
fs::path const test_path(fs::current_path() / "test.mp3");
CComPtr<IPropertyStore> property_store;
if (FAILED(hr = SHGetPropertyStoreFromParsingName(test_path.wstring().c_str(), NULL, GPS_READWRITE, IID_PPV_ARGS(&property_store)))) {
// Handle error...
}
// Set an empty value to the key
{
PROPVARIANT property{};
PropVariantInit(&property);
if (FAILED(hr = property_store->SetValue(PKEY_Title, property))) {
// Handle error...
}
if (FAILED(hr = PropVariantClear(&property))) {
// Handle error...
}
}
// Write a new value to the same key
{
PROPVARIANT property{};
if (FAILED(hr = InitPropVariantFromString(L"test file", &property))) {
// Handle error...
}
if (FAILED(hr = property_store->SetValue(PKEY_Title, property))) {
// Always fails here with hr == 0x80030005 "Access Denied."
}
if (FAILED(hr = PropVariantClear(&property))) {
// Handle error...
}
}
if (FAILED(hr = property_store->Commit())) {
// Handle error...
}
CoUninitialize();
}
This only seems to occur when setting an empty value first; any other value results in the program running as expected, with both changes written successfully.
As far as I can tell from the documentation, it is fine to write an empty value to a key - I can successfully write the empty value by itself and the changes are reflected when I view the file's properties in Explorer.
Furthermore, I don't understand how the error could possibly be "access denied" - the user the program is running under (my standard account) definitely has permissions to change the key (I can go into Properties in Explorer and manually change the key's value just fine), and how could it possibly be access denied for only one of the two key accesses?
So why can't I write an empty value to a key and then overwrite it later?
For those wondering why I need to clear a key and then immediately write a new value to it - I don't actually. That sequence of events just happens to occur in a larger program where all the properties are cleared and then later on certain keys are re-populated.
Update:
I've since experimented more with how the IPropertyStore object handles file access.
When the IPropertyStore object is initialised with a file (in this case through SHGetPropertyStoreFromParsingName()), it seems to open the file exclusively - as if through calling CreateFile() with 0 as the sharing mode. Once opened, none of my various attempts to open the file again via CreateFile() succeeded; all failed with ERROR_SHARING_VIOLATION. I believe this rules out another process (or even my program's process) from "stealing" the file access rights after the first property change (even though, as I discussed in the comments, property changes are not written until IPropertyStore::Commit() is called).
Even once the IPropertyStore::Commit() method is called, which writes all pending property changes to the file, the file remains open, exclusively. Reopening the file at this point is still not possible as far as I observed. This is peculiar, since the documentation states "[b]efore it returns, Commit releases the file stream or path with which the handler was initialized". What I found was until the IPropertyStore object was released (IUnknown::Release()), the associated file remained opened.
Committing and releasing the IPropertyStore object after clearing the key, then recreating it and writing the new value, seems to work perfectly. The key is successfully cleared, and then successfully rewritten. However this still leaves the original question open: why can't I do the clear and rewrite in one open/write/commit cycle of the property store?
When you set the property to VT_EMPTY, you don't "set" its value, you don't "write an empty value", you don't "clear" it, you remove it. Note that the running property store's count is decremented (GetCount), and you can't use GetAt either anymore.
Official documentation for IPropertyStore::SetValue method is very clear:
Removing a property values from a
property store is not supported and could lead to unexpected results.

IE BHO - DISPID_FILEDOWNLOAD being called for page loads?

I am implementing an Internet Explorer Browser Helper Object that should catch the DISPID_FILEDOWNLOAD event.
I first implemented this in C# which worked great except that I also need the cookies that go with the URL so need to call InternetGetCookiesEx. As .NET runs in it's own process it does not return me the session cookies so is no good.
I then wrote a quick test DLL in C++ so that it is loaded into the same process as IE which works great for the cookies but I now have a new problem:
I am getting calls to DISPID_FILEDOWNLOAD in my Invoke function for each page load when I only want them for an actual download.
In the C# version I only got a call to WebBrowser.FileDownload for an actual download but it seems that the C++ version is sending a DISPID_FILEDOWNLOAD even for each page load.
STDMETHODIMP CIEHlprObj::Invoke(
DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pvarResult,
EXCEPINFO* pExcepInfo,
UINT* puArgErr
)
{
USES_CONVERSION;
if (!pDispParams)
return E_INVALIDARG;
LPOLESTR lpURL = NULL;
m_spWebBrowser2->get_LocationURL(&lpURL);
int i = 0;
switch (dispidMember)
{
case DISPID_BEFORENAVIGATE2:
case DISPID_BEFORENAVIGATE:
sCurrentFile=NULL;
if (pDispParams->cArgs >= 5 && pDispParams->rgvarg[5].vt == (VT_BYREF | VT_VARIANT))
{
CComVariant varURL(*pDispParams->rgvarg[5].pvarVal);
varURL.ChangeType(VT_BSTR);
char* myStr = OLE2T(varURL.bstrVal);
if (myStr)
{
sCurrentFile = AllocateString(myStr);
sCurrentFileW = varURL.bstrVal;
}
}
break;
case DISPID_FILEDOWNLOAD:
// CALLED FOR EACH PAGE LOAD!
if(sCurrentFile)
{
TCHAR cookies[8192];
DWORD size = 8192;
BOOL ret = InternetGetCookieEx(sCurrentFile,
0,
cookies,
&size,
INTERNET_COOKIE_HTTPONLY,
0);
::MessageBox(0, sCurrentFile, "Downloading called multiple times!!", MB_OK);
}
break;
default:
break;
}
return S_OK;
}
Is there some filter that needs checking somewhere to know if a DISPID_FILEDOWNLOAD event relates to a file load or an actual file download?
Many thanks
UPDATE:
On closer inspection it seems that the C# managed code version is actually doing the same, I just didn't notice it the firs time around.
It seems that the FileDownload event is being called in the following circumstances:
New window / tab opened
New domain connected to (maybe a new keep-alive connection?)
An actual download
Obviously I only want the even on the actual download event.
As a possible solution to this I notice MS provide a ActiveDocument (BOOL) argument along with the event.. according to the Microsoft documentation the ActiveDocument argument means:
A Boolean that specifies whether the file is an Active Document
http://msdn.microsoft.com/en-us/library/bb268220(v=vs.85).aspx
Not very helpful but if I log the DISPID_FILEDOWNLOAD events to a text file and look at them later it appears that I should ignore all events where ActiveDocument = true
Getting the params is quite easy:
BOOL cancel = *pDispParams->rgvarg[0].pboolVal;
BOOL active = pDispParams->rgvarg[1].boolVal;
So the code would just need updating to something like:
if (active)
return S_OK;
I can only assume that this is some event the IE is sending telling the BHO that a page is downloading.. not a file, although that should mean that I get a DISPID_FILEDOWNLOAD event for each page load, which I don't.. only new tabs/browser instances and some new connections.
Hopefully someone else will be able to contribute to this and clarify what is the best way of handling this as it feels like a bit of a hack.

QueryInterface method for the IID_IPersistStreamInit quit working

I have an application that I've been using to parse data from an HTML document. The application has been working for a few years until this week when the QueryInterface method for the IID_IPersistStreamInit started failing. The call to QueryInterface is returning -2147467262 which fails the SUCCEEDED(hr) test. Any ideas why this quit working?
Thanks,
Wade
if (!myIE->IsValid())
return;
HRESULT hr;
LPDISPATCH lpDispatch = NULL;
LPOLECOMMANDTARGET lpOleCommandTarget = NULL;
LPPERSISTSTREAM lpPersistStream = NULL;
lpDispatch = myIE->GetHtmlDocument();
ASSERT(lpDispatch);
if (lpDispatch == NULL)
AfxMessageBox("Couldn't get IHTMLDocument2 interface!");
else
{
hr = lpDispatch->QueryInterface(IID_IPersistStreamInit, (void**) &lpPersistStream);
if (SUCCEEDED(hr) && lpPersistStream != NULL)
At what point are you executing the code above? In case it's not done, you should execute it only after the followings:
Navigating to about:blank to have mshtml loaded properly
Make sure the DocumentComplete event is called, meaning the navigation has completed, before you move on.
Only then is it safe to ask for the stream interface. For more, see Loading HTML content from a Stream.
Now, if all this is known and taken care of, you might pursue the solution from the other direction. The error code means "No such interface supported". I'd try finding out what is the component that contains that interface, and then re-register it. But given this is IE stuff you're dealing with, I kind of doubt it's installation got screwed.