In my project, I'm using the Windows Portable Device (WPD) API to enumerate the contents of a mobile device. WPD API Enumeration Guide. I'm able to enumerate over each object and view their properties as shown in the API programming guide. WPD API Properties Guide
However when I try to get an object's name that has a . within the name, the returned value is truncated at that .
HRESULT hr = objectProperties->GetStringValue(WPD_OBJECT_NAME, &strOriginalFileName);
if(FAILED(hr))
return false;
PWSTR wideStr = strOriginalFileName;
char buffer[20];
wcstombs(buffer, wideStr, 20);
qDebug() << buffer;
So for example, an object (folder on the device) with the name of com.example is returned as com. This becomes an obvious issue when I'm trying to locate a specific filepath on the device.
I can't seem to figure out what's wrong. Am I misunderstanding how the filename actually is? Is example another property or something within the com object? I'm very confused.
EDIT:
So I used the WPD API sample software to retrieve all the object properties of the com.example object and you can see that WPD itself cannot get the full folder name.
Thanks for your time!
The WPD Application Programming Reference refers following 3 NAMEs.
WPD_OBJECT_HINT_LOCATION_DISPLAY_NAME: A friendlier name, mostly intended for display
WPD_OBJECT_NAME: The name of the object on device.
WPD_OBJECT_ORIGINAL_FILE_NAME: The original filename of the object on device.
The MS code sample in C++ uses WPD_OBJECT_ORIGINAL_FILE_NAME to get to the actual file name (underneath the object) while transferring files from device to PC.
I modified the MS code sample (to enumerate object properties) and it showed me the actual file name (nothing truncated from the filename com.ef1.first.second)
I used:
Windows Windows 7 Ultimate (without SP1)
Visual Studio 2013
Android 4.4.4 (Moto-E)
Connection type: MTP
Memory type: Internal Memory as well as External (SD Card)
I wouldn't be surprised if it doesn't work on some combination of Windows versions, Windows SDK versions, android versions, Connection types (MTP, PTP, USB Mass Storage).
Here is the part of code that I modified (and that is how it worked).
// Reads properties for the user specified object.
void ReadContentProperties(_In_ IPortableDevice* device)
{
//.... Edited for brevity
tempHr = propertiesToRead->Add(WPD_OBJECT_NAME);
if (FAILED(tempHr))
{
wprintf(L"! Failed to add WPD_OBJECT_NAME to IPortableDeviceKeyCollection, hr= 0x%lx\n", tempHr);
}
// Here is the added code
tempHr = propertiesToRead->Add(WPD_OBJECT_ORIGINAL_FILE_NAME);
if (FAILED(tempHr))
{
wprintf(L"! Failed to add WPD_OBJECT_ORIGINAL_FILE_NAME to IPortableDeviceKeyCollection, hr= 0x%lx\n", tempHr);
}
//.... Edited for brevity
}
Related
For the past couple of days, I've been trying to learn WinRT, working with the manifests, loading the C++ or C# DLLs, etc. The next logical step for me was to try to build a plugin system that loads my WinRT DLLs that can be written in C++ or C# into my Win32 apps. This seems to be a bit more of a challenge because of unclear documentation. What makes this even more unclear to me is the underlying usage of COM and WRL which I don't have experience with. I've prepared a minimal viable example on GitHub for anyone that could help me figure this out.
A bit of elaboration on why C++/WinRT is only being used from step 6. I'm aiming at building a plugin system around dynamically/runtime loaded DLLs. I don't want to have any explicit references to DLLs or WinMD files in my projects. The goal is to basically have a Win32 application that has a statically linked abstraction project that contains the WinRT generated header files and a set of DLLs that also make use of the same abstraction project and are dynamically loaded during the runtime of the Win32 application. I've built these kind of systems before for C++ and C# but I feel lost with the WinRT layers and which methods to call when to utilize the WinRT factories correctly.
Everything seems to be working fine until step 6 when I'm not sure what would come next.
typedef HRESULT(WINAPI *dllAbstractionFactoryFp)(HSTRING activableClassId, IActivationFactory **factory);
...
// #### 0. Prep
HRESULT hRes;
// #### 1. Load DLL into memory
HMODULE hLibrary;
hLibrary = LoadLibrary(L"TicketMachine.dll");
//hLibrary = ::LoadLibraryEx(L"TicketMachine.dll", NULL, NULL);
if (!hLibrary) return EXIT_FAILURE;
// #### 2. Get the pointer to the DllGetActivationFactory of the loaded DLL
dllAbstractionFactoryFp abstractionFactoryFp;
abstractionFactoryFp = (dllAbstractionFactoryFp)GetProcAddress(hLibrary, "DllGetActivationFactory");
if (!abstractionFactoryFp) return EXIT_FAILURE;
// #### 3. Define the class name to activate
HSTRING activableClassName;
WindowsCreateString(L"TicketMachine.Ticket", 20, &activableClassName);
if (!activableClassName) return EXIT_FAILURE;
// #### 4. Set the activation factory
IActivationFactory* activationFactoryP;
hRes = (abstractionFactoryFp)(activableClassName, &activationFactoryP);
if (!activationFactoryP || hRes != S_OK) return EXIT_FAILURE;
// #### 5. Get the runtime class name to check if everything is going fine
HSTRING className;
activationFactoryP->GetRuntimeClassName(&className);
if (!className) return EXIT_FAILURE;
// #### 6. The identification GUID
IID iid = winrt::guid_of<winrt::TicketMachine::ITicketFactory>();
winrt::TicketMachine::ITicketFactory* iTicketFactory;
hRes = activationFactoryP->QueryInterface(iid, (void**) & iTicketFactory); // iTicketFactory stays uninitialized
if (!iTicketFactory || hRes != S_OK) return EXIT_FAILURE;
// ### ?. ...
;
// #### ?. Profit???
;
With an explicitly linked WinMD file I can just call the class constructor or even the ::impl::call_factory method that will instantiatie the object for me. This of course won't work if the DLL is loaded dynamically on runtime.
winrt::TicketMachine::Ticket staTicket;
// or
winrt::Windows::Foundation::IInspectable baseInterface, innerInterface;
winrt::TicketMachine::Ticket ticket = winrt::impl::call_factory<winrt::TicketMachine::Ticket, winrt::TicketMachine::ITicketFactory>([&](winrt::TicketMachine::ITicketFactory const& f) { return f.CreateInstance(baseInterface, innerInterface); });
This is the GitHub repository where I've prepared this code and which should compile and run out of the box, https://github.com/zborowa/winrt-dynamic-dll-loading
FYI, I've already seen the following questions and also found the following resources:
Loading WinRT component without referencing the DLL/assembly
Consuming a DLL(Universal Apps) from a WinRT Component
Creating WinRT component with static methods in C++ / WRL
https://devblogs.microsoft.com/oldnewthing/20201118-00/?p=104459
https://gist.github.com/clarkezone/43e984fb9bdcd2cfcd9a4f41c208a02f
These Aren't the COM Objects
You're Looking For
And many more...
I am currently trying to piece together a shader-based music visualizer. The plan is to read data from the current MMDevice, which I'm trying to follow the documentation for, but I must be doing something wrong because I had to jump through all sorts of hoops to get even just the MMDeviceEnumerator to compile.
In order for the uuids of MMDeviceEnumerator and IMMDeviceEnumerator to be defined, I had to set #define WINAPI_FAMILY WINAPI_FAMILY_GAMES. This was also required for EDataFlow and ERole enumerations to be defined. My first question is if I've missed some configuration somewhere, or if this is the intended method of enabling these things.
Currently, I have the following code in an AudioStream class:
class AudioStream {
public:
AudioStream() {
//SUCCEEDING(CoInitializeEx(nullptr, COINIT_MULTITHREADED));
SUCCEEDING(CoCreateInstance(
__uuidof(IMMDeviceEnumerator),
NULL,
CLSCTX_ALL,
__uuidof(MMDeviceEnumerator),
(void**)&this->mmDeviceEnumerator));
SUCCEEDING(this->mmDeviceEnumerator->GetDefaultAudioEndpoint(
eRender,
eConsole,
&this->mmDevice));
}
private:
IAudioClient* audioClient = NULL;
IAudioCaptureClient* captureClient = NULL;
IMMDeviceEnumerator* mmDeviceEnumerator = NULL;
IMMDevice* mmDevice = NULL;
};
If you're familiar with what the DirectX 12 project template looks like, this object is being instantiated in the Sample3DSceneRenderer constructor. The main issue I'm having right now is the following two errors which are immediately raised during startup:
onecore\com\combase\dcomrem\resolver.cxx(2299)\combase.dll!75AA0DFF: (caller: 75B1CF2C) ReturnHr(1) tid(42a8) 80040154 Class not registered
onecore\com\combase\dcomrem\resolver.cxx(2507)\combase.dll!75B1CF4D: (caller: 75AA29E4) ReturnHr(2) tid(42a8) 80040154 Class not registered
This causes the entire app to hang, and the project template visualization to never appear (the succeeding macro exits). Does anyone have any idea why this is failing? It must have to be something with the CoCreateInstance call :(
You are writing a Universal Windows Platform (UWP) app because that's what the "built-in" DirectX 12 App project template creates in Visual Studio. UWPs do not have access to all the same APIs and IMMDevice is not part of the UWP API surface area.
The fact that you defined WINAPI_FAMILY_GAMES means you hacked the API Family Partition macros which will define the API in a UWP context, but it doesn't mean that API actually works from the AppContainer process that all UWPs run in.
You really have two options:
(1) If you want to write a UWP, then you will need to enumerate audio devices via the proper Windows Runtime APIs which are in the Windows::Devices::Enumeration namespace.
Assuming you are using C++/CX language extensions (instead of the more modern C++/WinRT projections), then this code works:
auto operation = DeviceInformation::FindAllAsync(DeviceClass::AudioRender);
while (operation->Status == Windows::Foundation::AsyncStatus::Started)
{
Sleep(100);
}
if (operation->Status != Windows::Foundation::AsyncStatus::Completed)
{
throw std::runtime_error("FindAllAsync");
}
DeviceInformationCollection^ devices = operation->GetResults();
for (unsigned i = 0; i < devices->Size; ++i)
{
using Windows::Devices::Enumeration::DeviceInformation;
DeviceInformation^ d = devices->GetAt(i);
// d->Id->Data();
// d->Name->Data();
}
Also, if you want to get access to the audio capture device from a UWP, you must add a capability to your manifest to request it via <DeviceCapability Name="microphone"/>. See Microsoft Docs.
You should take the time to read the Microsoft Docs on UWPs so you have a better idea of what's supported and what's not.
(2) If you want to write a Win32 desktop app, use the directx-vs-templates instead which include DirectX 12 starting templates for Win32 desktop apps (plus alternative DirectX templates for UWP if that's your thing).
Whichever appmodel you use, you may want to take a look at DirectX Tool Kit for Audio.
BTW, WINAPI_FAMILY_GAMES is used by the Microsoft GDK for Xbox which is for writing titles for Xbox One and Xbox Series X|S. It uses Win32 APIs and doesn't use Windows Runtime APIs, so it has the IMMDevice interface in it's API surface. See Microsoft Docs.
I am working on my outlook plugin using C++ MAPI.
On one particular button click I want to open New Mail window for which I am following below approach. But to my surprise, IMAPIFormMgr::ResolveMessageClass() function fails. That too randomly. At times it works fine and at other times it fails. Couldn't figure out what could be the cause for the same.
Any pointer, idea?
My code flow is something like below
Note: It's not full code, just little bit of overview.
I have printed all the variables and poiters in my log fine and non of them is NULL or garbage or like.
Still it fails at ResolveMessageClass() function only. That too strangely it fails randomly. many a time it works like champ and suddenly it shows some error in ResolveMessageClass()
Code
CComPtr<IMessage> mapiMessage;
hRes = spOutboxFolder->CreateMessage(&IID_IMessage, 0, &mapiMessage);
CComPtr<IMAPIFormInfo> pFormInfo;
std::wstring szMessageClass(L"IPM.Note");
hRes = pFormMgr->ResolveMessageClass(
wstringTostring(szMessageClass).c_str(), // Message class is ALWAYS ANSI --> Never Unicode
0,
spOutboxFolder.get(),
&pFormInfo);
CComPtr<IPersistMessage> pPersistMessage;
hRes = pFormMgr->CreateForm(NULL, 0L, pFormInfo, IID_IPersistMessage, (LPVOID*)&pPersistMessage);
ULONG_PTR ul = 0;
hRes = spSession->PrepareForm(NULL, mapiMessage, &ul);
hRes = spSession->ShowForm(
NULL, //(ULONG)parent->GetSafeHwnd(),
msgStore,
spInboxFolder.get(),
NULL,
ul,
NULL,
MAPI_POST_MESSAGE,
pPropsMsg[MSG_STATUS].Value.l,
pPropsMsg[MSG_FLAGS].Value.l,
pPropsMsg[MSG_ACCESS].Value.l,
pPropsMsg[MSG_CLASS].Value.lpszA);
Thanks in advance
If your code is in an Outlook addin, why not use OOM to create and display message? In this particular case, using MAPI does not buy you anything.
Use the Outlook object model instead of Extended MAPI. Creating a COM add-in involves two major steps:
Implement the IDTExtensibility2 interface in a class module of a dynamic link library (DLL).
Register the COM add-in.
The key to understanding COM add-in development is the IDTExensibility2 interface. This interface is used by all Office applications to communicate with a COM add-in. This ensures a common initialization mechanism and an ability to pass in the application's object model so that the COM add-in can communicate with the Office application. Listing 23-1 shows the IDTExtensibility2 interface.
public interface IDTExtensibility2
{
void OnAddInsUpdate(ref System.Array custom);
void OnBeginShutdown(ref System.Array custom);
void OnConnection(object Application,
Extensibility.ext_ConnectMode ConnectMode,
object AddInInst, ref System.Array custom);
void OnDisconnection(Extensibility.ext_DisconnectMode RemoveMode,
ref System.Array custom);
void OnStartupComplete(ref System.Array custom);
}
As you can see the OnConnection method gets an instance of the host application instance. So, that is where you could start from developing your add-in.
Read more about the OOM in the Outlook object model overview section of MSDN.
I have created a 64-bit executable using Visual Studio 2015, intended to be run on Windows 7. The executable is a C++ wrapper that calls the main method in a Java application via JNI. The application runs as expected, but in Windows Task Manager, on the "Process" tab, my application name has been prepended with 16 hex digits. So even though my application compiles to "someapp.exe", it is listed as "80005b29594d4a91someapp.exe" in the process list when I run it. Does anyone know why this is happening, and how to make it show up as just "someapp.exe" in the Task Manager?
EDIT 1:
I should note that the hex string is always the same when it appears in the name. However, there is a small percentage of the time I run my application when it actually has the expected name of "someapp.exe". I have not been able to figure out the pattern of when the hex string is prepended and when it is not, but I estimate the hex string appears 98% of the time it is executed.
EDIT 2:
This appears to be related somehow to the use of JNI. When I remove the JNI calls, this stops occuring altogether. The following represents the entirety of the C++ code making up the "someapp" application:
#include <jni.h>
#include <Windows.h>
#define STRING_CLASS "java/lang/String"
int main(size_t const argc, char const *const argv[]) {
// Modify the DLL search path
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 |
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_USER_DIRS);
SetDllDirectoryA(R"(C:\Program Files\Java\jdk1.8.0_112\jre\bin\server)");
// Create and populate the JVM input arguments
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_8;
vm_args.ignoreUnrecognized = JNI_FALSE;
vm_args.nOptions = 2;
vm_args.options = new JavaVMOption[vm_args.nOptions];
// Set command-line options
vm_args.options[0].optionString = "-Dfile.encoding=UTF-8";
vm_args.options[1].optionString = "-Djava.class.path=someapp.jar";
// Create the JVM instance
JavaVM *jvm;
JNIEnv *env;
JNI_CreateJavaVM(&jvm, reinterpret_cast<void**>(&env), &vm_args);
// Get the main entry point of the Java application
jclass mainClass = env->FindClass("myNamespace/MainClass");
jmethodID mainMethod = env->GetStaticMethodID(
mainClass, "main", "([L" STRING_CLASS ";)V");
// Create the arguments passed to the JVM
jclass stringClass = env->FindClass(STRING_CLASS);
jobjectArray mainArgs = env->NewObjectArray(
static_cast<jsize>(argc - 1), stringClass, NULL);
for (size_t i(1); i < argc; ++i) {
env->SetObjectArrayElement(mainArgs,
static_cast<jsize>(i - 1), env->NewStringUTF(argv[i]));
}
env->CallStaticVoidMethod(mainClass, mainMethod, mainArgs);
// Free the JVM, and return
jvm->DestroyJavaVM();
delete[] vm_args.options;
return 0;
}
I have tried to remove the arguments passed to the Java main method, but that had no affect on the outcome.
EDIT 3:
Thanks to the suggestion from 1201ProgramAlarm, I realized that this was actually related to running from a dynamic ClearCase view. The "Image Path Name" column in the Task Manager was one of the following values, which directly correlates with the incorrect "Image Name" symptom that I was observing:
\view\view-name\someapp-path\someapp.exe
\view-server\views\domain\username\view-name.vws.s\00035\80005b29594d4a91someapp.exe
I would still like to know why this is happening, but since this only affects our development environment, fixing it has become low priority. For anyone else experiencing this problem, the following represents the relevant software installed in my environment:
Windows 7 Enterprise x64 SP1
Rational ClearCase Explorer 7.1.2.8
Visual Studio 2015 Update 3
Java x64 JDK 8u112
Run your application from a drive that isn't a ClearCase dynamic view.
The Image Name of the running process reference a file in a ClearCase view Storage (\\view\view-name\someapp-path\someapp.exe =>
\\view-server\views\domain\username\view-name.vws\.s\00035\80005b29594d4a91someapp.exe), the .vws meaning view storage.
See "About dynamic view storage directories":
Every view has a view storage directory. For dynamic views, this directory is used to keep track of which versions are checked out to your view and to store view-private objects
So a view storage exists both for snapshot and dynamic view.
But for dynamic view, that storage is also used to keep a local copy of the file you want to read/execute (all the other visible files are accessed through the network with MVFS: MultiVersion File System)
That is why you see \\view-server\views\domain\username\view-name.vws\.s\00035\80005b29594d4a91someapp.exe when you execute that file: you see the local copy done through MVFS by ClearCase.
Would you have used a snapshot view, you would not have seen such a complex path, since a snapshot view by its very nature does copy all files locally.
It appears as though the path is "correct" when I have not accessed the MVFS mount recently using Windows Explorer
That means the executable executed by Windows is still the correct one, while MVFS will be busy downloading that same executable from the Vob onto the inner folder of the view storage.
But once you re-execute it, that executable is already there (in the view storage), so MVFS will communicate its full path (again, in the view storage) to Windows (as seen in the Process Explorer)
I'm currently working on porting the Boost 'filesystem' library to Windows Phone 8.1. I succeeded in porting about half of the problematic functions by substituting 'banned' Win32 API functions with other, newer non-banned functions.
I now need to tackle those Boost functions for which there is no alternative Win32 API. Based on Steve Gates's excellent port of other Boost libraries to WP8.1, and in a private communication with him, I have decided to use WRL within the Boost code, rather than C++/CX.
To learn WRL and get my bearings, I wrote a minimal WP8.1 app consisting of a C++/CX client app that calls down into a Windows Runtime Component, the latter written in C++. In the runtime component I have a function that attempts to determine the file system path of the Picture Library. The problem I'm encountering is that the final path I get (i.e., pszPath) is an empty string.
Here is the runtime component code:
void Class1::Test1()
{
HRESULT hr;
HString hstrKnownFolders;
hstrKnownFolders.Set(RuntimeClass_Windows_Storage_KnownFolders);
// Get the Activation Factory
ComPtr<IActivationFactory> pKnownFoldersActivationFactory;
hr = ABI::Windows::Foundation::GetActivationFactory(hstrKnownFolders.Get(),
&pKnownFoldersActivationFactory);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
// QI for the IKnownFoldersStatics
ComPtr<IKnownFoldersStatics> pKnownFolders;
hr = pKnownFoldersActivationFactory.As(&pKnownFolders);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
// Get the Pictures library folder
ComPtr<IStorageFolder> pStorageFolder;
hr = pKnownFolders->get_PicturesLibrary(&pStorageFolder);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
// QI for the IStorageItem interface (from which IStorageFolder is derived)
ComPtr<IStorageItem> pItem;
hr = pStorageFolder.As(&pItem);
// Get the path corresponding to the folder
HSTRING hsPath;
pItem->get_Path(&hsPath);
PCWSTR pszPath = WindowsGetStringRawBuffer(hsPath, 0);
}
At the end of the function, the function get_Path() returns an empty string. Can anyone shed light one what I'm doing wrong, and how it should be done?
Thanks in advance!
No path is the right result: the Pictures library is a shell folder which compiles data from several locations (such as the public Pictures directory and the user's picture directory). The Pictures library itself doesn't have a path.
Individual items within the library probably have paths, but they may not be paths in the same file system directory.
StorageFiles are not limited to "files" from the file system. They can also include objects from elsewhere in the shell and objects from other apps. All of these are represented as file streams, but don't necessarily have file system paths.