WPD API Detect if Device is a Phone? - c++

EDIT: Full source code was requested. Below is a barebones implementation in order to replicate the bug. Content enumeration is removed, however the crash ocurrs on the first object call anyway. In this case, the WPD_DEVICE_OBJECT_ID object.
LINK TO CPP (Bug begins at line 103)
LINK TO QMAKE.PRO (I'm using Qt)
In my project I use the WPD API to read the contents of a mobile device. I followed the API to a tee and have successfully implemented content enumeration.
However, if a USB drive is connected, the WPD API will also sometimes detect that as a device. My program will go ahead and begin content enumeration anyway. I don't want that. I only want to enumerate mobile devices.
The problem is that during content enumeration, when my program attempts to retrieve a property of an object on the USB drive, it crashes. Here are the crash details:
Problem Event Name: BEX
Application Name: UniversalMC.exe
Application Version: 0.0.0.0
Application Timestamp: 5906a8a3
Fault Module Name: MSVCR100.dll
Fault Module Version: 10.0.40219.325
Fault Module Timestamp: 4df2be1e
Exception Offset: 0008af3e
Exception Code: c0000417
Exception Data: 00000000
OS Version: 6.1.7601.2.1.0.768.3
Locale ID: 1033
Additional Information 1: 185e
Additional Information 2: 185ef2beb7eb77a8e39d1dada57d0d11
Additional Information 3: a852
Additional Information 4: a85222a7fc0721be22726bd2ca6bc946
The crash occurs on this call:
hr = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &objectName);
hr returns FAILED and then my program crashes.
After some research I've found that exception code c0000417 means a buffer overflow occurred? Correct me if I'm wrong but, is this a vulnerability in the WPD API? If so, how could I detect ahead of time that this device is not a mobile device?
Thanks for your time!

I ended up paying someone to help me pinpoint the issue.
The problem was that the root object (WPD_DEVICE_OBJECT_ID) would not return an object name no matter what (Not true for all devices).
The solution was to simply begin content enumeration FROM the root object and only check the names of its children. In my original implementation, I assumed every object has a name, but apparently that is not the case. The root object is the exception.
Here is a snippet:
CComPtr<IEnumPortableDeviceObjectIDs> pEnumObjectIDs;
// Print the object identifier being used as the parent during enumeration.
//qDebug("%ws\n",pszObjectID);
// Get an IEnumPortableDeviceObjectIDs interface by calling EnumObjects with the
// specified parent object identifier.
hr = pContent->EnumObjects(0, // Flags are unused
WPD_DEVICE_OBJECT_ID, // Starting from the passed in object
NULL, // Filter is unused
&pEnumObjectIDs);
// Enumerate content starting from the "DEVICE" object.
if (SUCCEEDED(hr))
{
// Loop calling Next() while S_OK is being returned.
while(hr == S_OK)
{
DWORD cFetched = 0;
PWSTR szObjectIDArray[NUM_OBJECTS_TO_REQUEST] = {0};
hr = pEnumObjectIDs->Next(NUM_OBJECTS_TO_REQUEST, // Number of objects to request on each NEXT call
szObjectIDArray, // Array of PWSTR array which will be populated on each NEXT call
&cFetched); // Number of objects written to the PWSTR array
if (SUCCEEDED(hr))
{
// Traverse the results of the Next() operation and recursively enumerate
// Remember to free all returned object identifiers using CoTaskMemFree()
for (DWORD dwIndex = 0; dwIndex < cFetched; dwIndex++)
{
//RECURSIVE CONTENT ENUMERATION CONTINUES HERE
//OBJECT NAME CHECKING CONTINUES IN THE RECURSIVE FUNCTION
// Free allocated PWSTRs after the recursive enumeration call has completed.
CoTaskMemFree(szObjectIDArray[dwIndex]);
szObjectIDArray[dwIndex] = NULL;
}
}
}
}
The solution is exactly what the sample project shows to do, however, I made the mistake of checking the name of the root object. So don't do that.

Get the object name if there is no "original file name"
hr = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &objectName);
if(FAILED(hr)) {
hr = pObjectProperties->GetStringValue(WPD_OBJECT_NAME, &objectName);
}

Related

Why CoCreateInstance can return two different HRESULT in the exact same context?

I want to use DSound Audio Render in one of my application so I load it with CoCreateInstance. According to my previous question, CoCreateInstance can return REGDB_E_CLASSNOTREG if I have no audio hardware installed. But if I call CoCreateInstance a second time, I doesn't have the same HRESULT. Here is a little example (to reproduce it, you should disable all of your audio devices in the device manager of Windows):
#include <iostream>
#include <strmif.h>
#include <uuids.h>
int main()
{
std::cout << "Start" << std::endl;
HRESULT hr = CoInitialize(NULL);
printf("CoInitialize = 0x%x\n", hr);
IBaseFilter* ptr = NULL;
hr = CoCreateInstance(CLSID_DSoundRender, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void**)&ptr);
printf("CoCreateInstance = 0x%x\n", hr);
if(ptr)
ptr->Release();
hr = CoCreateInstance(CLSID_DSoundRender, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void**)&ptr);
printf("CoCreateInstance = 0x%x\n", hr);
if(ptr)
ptr->Release();
CoUninitialize();
std::cout << "End" << std::endl;
std::cin.get();
}
I get this result:
Start
CoInitialize = 0x0
CoCreateInstance = 0x80040154
CoCreateInstance = 0x80040256
End
The first error code 0x80040154 corresponds to REGDB_E_CLASSNOTREG so it is coherent with the answer I got in my previous question but the second error code 0x80040256 corresponds to VFW_E_NO_AUDIO_HARDWARE. According to MSDN:
VFW_E_NO_AUDIO_HARDWARE
0x80040256
Cannot play back the audio stream:
no audio hardware is available, or the hardware is not supported.
So why do I have the meaningful error only the second time I call CoCreateInstance? What can I change to have this error at the first call?
If you want documented and consistent behavior then you should not cut corners and go along suggested lines, which are: Using the System Device Enumerator.
Enumeration of devices in audio renderer category will get you a moniker object for "Default DirectSound Device" and then your IMoniker::BindToObject would result in meaningful VFW_E_NO_AUDIO_HARDWARE (both on the first call and the following ones).
You can also see this in action without writing code: you can use GraphStudioNext app, menu Graph, Insert Filter, "Audio Renderers" category in the combo box, then double click on "Default DirectSound Device" entry trying to instantiate the filter.
As for the tagline question about different codes, here is my educated guess. The error code eventually comes from software. You had a chance to see that COM server is present in the system, so COM subsystem does start instantiation, then fails in the middle and forwards you the failure code back. It is likely that COM server has a singleton or otherwise a cached enumeration of hardware. In your first attempt it hits "no devices" condition during initialization and aborts instantiation at an earlier step, so COM reports inability to create an instance. Next time the server already sees its cached enumeration (that is, it is not "exactly the same context" as you assumed) and skips the initial failure point, but then again stumbles on a next check.
There is nothing you can do about this, especially that you were supposed to instantiate CLSID_DSoundRender differently in first place.

c++ win32 DLL - need to dynamically load a base64 string (very strange!)

First of all, sorry if the title isn't really accurate, I have no idea how I can put my problem into a single sentence.
The problem I'm facing is that I have a win32 DLL which needs to dynamically load a binary file and do something with it (the binary file is found in a base64 string, which the DLL then decodes and writes to disk).
Pretty simple, in theory. However, here come the problems:
I tried putting the string into the resources by an external program. That worked and it does appear in the resources (according to reshack), BUT when I try to access it from inside the DLL it doesn't work. And yes, I do know that you need the hInstance of the DLL itself, not from the executable file that contains it, it didn't work either though.
I also tried to load the string from another source (I tried file, URL and even the registry), but whenever I save it in a variable, the program crashes ("X stopped working" message), I'm assuming that the program which loaded the DLL didn't clear enough RAM to store that extra variable.
And last but not least an extra note: I do not have access to the source code of the program containing the DLL (I'm writing a plugin more or less), so I couldn't pass a parameter either.
I really hope someone can help me out of this dilemma.
Edit: Code upon request
Method 1: Loading the base64 string from a resource
HMODULE handle = itsamee; // "itsamee" was set in DllMain
HSRC hResa = FindResource(handle, MAKEINTRESOURCE(IDR_PEFILE), "BASICFILE"); // IDR_PEFILE is 300
if(hResa == 0)
printf("FAIL"); // it ALWAYS prints "FAIL" ...
.rc file:
#include "resource.h" // there it just defines IDR_PEFILE and includes <windows.h>
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
IDR_PEFILE BASICFILE "app.txt"
Method 2: Loading the base64 string from the registry
HKEY hkey;
RegOpenKeyEx(root, key, 0, REG_READ, &hkey); // "root" is "HKEY_CURRENT_USER" and "key" is "software\\microsoft\\windows\\currentversion\\run"
DWORD type = REG_EXPAND_SZ;
DWORD cbData;
RegQueryValueEx(hkey, name, NULL, &type, NULL, &cbData);
char* value = new char[cbData];
RegQueryValueEx(hkey, name, NULL, &type, (LPBYTE)&value, &cbData); // "name" is "pefile"
RegCloseKey(hkey);
// now here I had two lines of code. the first one is:
printf("Okay"); // it would always print "Okay"
// this is the second version:
printf("Okay, value is %s", value); // it wouldn't print this, instead I'd get the "X stopped working" error
std::vector<char> dec = base64_decode(value); // this would never happen, "stopped working", regardless of which printf was called before
The mistake was that (LPBYTE)&value made the function write to the pointer and not the buffer itself. It had to be changed to (LPBYTE)value. Thanks to Mark Ransom for this answer!

How to trigger "running state" in Flash Player?

If I use windowed activation (giving a valid HWND to the Flash PLayer in the GetWindow function is enough to trigger this), the player will run the loaded swf file. However, if I use windowless activation, the loaded file does not run, only the very first frame is displayed. This article claims that I'm supposed to call
DoVerb(OLEIVERB_SHOW, NULL, (IOleClientSite *)this, 0, NULL, NULL);
However, this does not have any effect. What am I doing wrong?
Edit: Since I made this post, I found out that it returns -2147467259, which is not a known HRESULT, but certainly not 0. What does it mean?
After some more digging I found out that the return value is OLE_E_NOTRUNNING.
I have found the problem. Before this call, I was doing:
hr = _shockwaveFlash->put_WMode(BSTR("opaque"));
BUT I blatantly ignored the HRESULT there. It made the Flash Player confused, because it is not a valid way to insert a BSTR constant in the code (which is a wide string). Correctly:
hr = _shockwaveFlash->put_WMode(L"opaque");
Now it works as it should. Check you HRESULTs, kids :)

How do I get a string description of a Win32 crash while in Top level filter (I am looking for the address of the instruction at the top of the stack)

If I use a class/method like the one described here how can I get the description/address of the call at the top of the stack?
Basically I want some value I can use in a call to our bug tracking system. I want to "uniquely" identify based on the address of the instruction that caused the exception.
(It is usually something of the form of mydll.dll!1234ABDC())
EDIT:
Some background information:
I am creating a minidump to email to a defect tracking system (fogbugz). In order to reduce duplicates I am trying to come up with a reasonable "signature" for the crash. I know there is an xml PI for FB, but it requires a user logon and we are not sure yet that we can afford to have people sniffing our traffic and getting user information. Emailing is also simpler for now to implement. Later on we will use the XML API to submit minidumps.
You need to put the code to do this in your exception filter, by the time you get to the exception handler much of the context information for the exception has been lost.
try
{
// whatever
}
except (MyExceptionFilter(GetExceptionInformation()))
{
}
Your filter will look something like this
LONG WINAPI MyExceptionFilter (
EXCEPTION_POINTERS * pExcept,
BOOL fPassOn)
{
EXCEPTION_RECORD * pER = pExcept->ExceptionRecord;
DWORD dwExceptionCode = pER->ExceptionCode;
TCHAR szOut[MAX_PATH*4]; // exception output goes here.
szOut[0] = 0;
MEMORY_BASIC_INFORMATION mbi;
DWORD cb = VirtualQuery (pER->ExceptionAddress, &mbi, sizeof(mbi));
if (cb == sizeof(mbi))
{
TCHAR szModule[MAX_PATH];
if (GetModuleFileName ((HMODULE)mbi.AllocationBase, szModule, MAX_PATH))
{
wsprintf(szOut, "Exception at '%s' + 0x%X", szModule,
(ULONG_PTR)pER->ExceptionAddress - (ULONG_PTR)mbi.AllocationBase);
}
}
return EXCEPTION_EXECUTE_HANDLER;
}
Of course, you will need to adjust your output a bit for 64 bit architectures, since the ExceptionAddress and AllocationBase will be 64 bit quantities in that case.
The EXCEPTION_POINTERS struct which is sent to TopLevelFilter() contains an EXCEPTION_RECORD struct which contains the ExceptionAddress. Which this address you can figure out in which DLL the offending opcode is by enumerating the modules with CreateToolhelp32Snapshot. You can also use the functions in dbghelp.dll to find the symbol which correspond to the address (the function it is in)
GetExceptionInformation will return the EXCEPTION_POINTERS struct which contains information about the exception. The ExceptionRecord member contains an ExceptionAddress member, which is the address of the exception.
You'll need to map this address to a module relative location in your code to be useful. You can use GetModuleHandleEx with the GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS to get the HMODULE (which is also the base address of the module). GetModuleInformation can then be used to get the actual name of the module that the exception occurred in.
This may not be that helpful to you if the fault is actually inside of a system DLL. A more sophisticated scheme would be to generate a stack trace (using Stackwalk64 in dbghelp), and ignoring the topmost frames that are not in your code.
You can avoid the pain of printing a string for the exception (what happens if you can save the minidump but can't format a string without crashing?) by saving the minidump instead and using cdb.exe or windbg.exe to extract the exception information.

How can I determine why a call to IXMLDOMDocument::load() fails?

I am trying to debug what appears to be an XML parsing issue in my code. I have isolated it down to the following code snippet:
HRESULT
CXmlDocument::Load(IStream* Stream)
{
CComVariant xmlSource(static_cast<IUnknown*>(Stream));
VARIANT_BOOL isSuccessful;
* HRESULT hr = m_pXmlDoc->load(xmlSource, &isSuccessful);
return (hr == S_FALSE) ? E_FAIL : hr;
}
Note: m_pXmlDoc is of the type CComPtr<IXMLDOMDocument>.
It appears that the call to IXMLDOMDocument::load() (marked with the *) is failing - IOW, it is returning S_FALSE.
I am not able to step into load() to determine why it is failing, as it is a COM call.
The MSDN page for this method doesn't seem to be giving a lot of insight.
I have a few hunches:
The XML is not well-formed
The XML file is too large (approximately 120MB)
It is a memory-related issue (the process size gets to > 2GB at the time of failure)
NB: A registry key has been set to allow the process size to be this large, as the largest valid process size for WinXP, AFAIK, is 2GB).
Any ideas as to why this call could be failing?
The following code will fetch the specific parser error from the DOM and it's location in the source XML.
CComPtr<IXMLDOMParseError> pError;
CComBSTR sReason, sSource;
long nLine = 0, nColumn = 0;
m_pXmlDoc->get_parseError(&pError);
if(pError)
{
pError->get_reason(&sReason);
pError->get_srcText(&sSource);
pError->get_line(&nLine);
pError->get_linepos(&nColumn);
}
sReason will be filled with the error message. sSource will contain the errorneous source line in the XML. nLine and nColumn should get set to the line number and column of the error, although in practice these two aren't always set reliably (iirc, this is especially true of validation errors, rather than parser/well-formedness ones).
If the XML file is opened by another Task or Proccess the Load() method
can't load the file but it doesn't state that the loading has failed.
I consider this behaviour as a bug.
So you have to check the Property documentElement - if it is null, load() has failed, too.