Accessing Clipboard Using IStream Interface - c++

#include <Windows.h>
#include <iostream>
#include <vector>
#include <string>
//
// Desired Output: Text in clipboard should be displayed to the screen.
//
int main( void )
{
//
// OLE COM Interface
//
HRESULT hr;
// Get Clipeboard
IDataObject* pcb = 0;
OleGetClipboard(&pcb);
// Get Clipeboard Data Interface
FORMATETC format;
format.cfFormat = CF_TEXT;
format.ptd = NULL;
format.dwAspect = DVASPECT_CONTENT;
format.lindex = -1;
format.tymed = TYMED_ISTREAM;
STGMEDIUM medium;
hr = pcb->GetData(&format, &medium);
if( FAILED(hr) )
return hr;
// Get Stat of returned IStream
IStream* pis = medium.pstm;
STATSTG stat;
ULONG cb = 0;
hr = pis->Stat(&stat,STATFLAG_DEFAULT);
if( SUCCEEDED(hr) )
{
if( stat.pwcsName )
std::wcout << L"Name: " << stat.pwcsName << std::endl;
std::cout << "DataSize: " << stat.cbSize.QuadPart << std::endl;
std::cout << "Type: " << stat.type << std::endl;
cb = stat.cbSize.QuadPart;
}
// Read Data from IStream
std::vector<char> v;
v.resize(cb);
ULONG ret;
hr = pis->Read(v.data(), cb, &ret);
if( FAILED( hr ) )
{
std::cout << "Failed to Read" << std::endl;
}
else
{
std::string out(v.begin(),v.end());
std::cout << "Read " << ret << "chars. Content: {" << out << "}" << std::endl;
}
pis->Release();
//
// My Output when I have 40 characters in Clipboard
//
// DataSize: 40
// Type: 2
// Read 0chars. Content: { }
// The number of characters are correct, but content always appear empty.
}
Hello.
I'm trying to gain access to clipboard through IStream interface.
IStream::Stat seems like giving me correct status of the IStream, but IStream::Read is not really giving me any data.
I have almost zero experience in using COM object and IStream interface.
Please, point out if there is obvious error.
Thank you.

My condolences for having to use COM and C++. It's been 4 or 5 years since I've touched the stuff, but looking at what you have, I'd guess one of two things is a problem:
The IStream pointer starts out at the end of the data. In this case you have to call pis->Seek(0, STREAM_SEEK_SET, NULL) to reset it at the beginning. It may be that the call to pis->Read() returns S_FALSE rather than S_OK; the MSDN docs on Read() say that this can occur if the stream pointer is at the end of the stream.
The Clipboard just doesn't support using IStream. Actually I've never heard of doing this; I thought the usual method was to access Clipboard data as a global memory block. (See this example which is a lot simpler than your code) IStreams are necessary when you get into icky subjects like structured storage which was the old way that MS Office applications stored hierarchical data in one file.
A side note: if you don't have to use C++, and are familiar with other languages that have bindings for Windows clipboard access (C#, VB for "native" .NET access; Java has portable Clipboard access with a subset of the native Windows features, I think Python does also), you don't have to mess with any of those ugly COM features.

Related

Can't figure out how to call IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES IOCTL on Windows - INVALID_PARAMETERS

Greetings!
I have come today to ask a question about invoking a very specific IOCTL on Windows. I have some amount of driver development experience, but my experience with file system drivers is relatively limited.
The Goal
I am developing a tool that manages volumes/physical disks/partitions. For the purpose I am attempting to learn to invoke many of the Windows file system data set management (DSM) IOCTLs. Currently I am learning how to use IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES which is documented at https://learn.microsoft.com/en-us/windows/win32/api/winioctl/ni-winioctl-ioctl_storage_manage_data_set_attributes?redirectedfrom=MSDN.
However, I have had to intuit how to set up the call to the IOCTL myself. The MSDN article does not give fully detailed instructions on how to set up the input buffer, and specifically what values of the inputs are strictly required. I have uncertainty about how to call the IOCTL that has lead to a bug I cannot debug easily.
In order to reduce my uncertainty about proper invocation of the IOCTL I worked off a tool MS released a few years ago and copied some of their code: https://github.com/microsoft/StorScore/blob/7cbe261a7cad74f3a4f758c2b8a35ca552ba8dde/src/StorageTool/src/_backup.c
My Code
At first I tried:
#include <windows.h>
#include <stdio.h>
#include <string>
#include <iostream>
#include <winnt.h>
#include <winternl.h>
#include <ntddstor.h>
int main(int argc, const char* argv[]) {
//My understanding is for this IOCTL I need to open the drive, not the object PartmgrControl device that the driver registers.
HANDLE hDevice = CreateFile(L"\\\\.\\Physicaldrive0",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING,
NULL);
int cf_error = 0;
cf_error = GetLastError();
if (hDevice == INVALID_HANDLE_VALUE) {
std::cout << "COULDN'T GET HANDLE";
return -1;
}
std::cout << "Device Handle error: " << cf_error << "\n";
std::cout << "Handle value: " << hDevice << "\n";
_DEVICE_MANAGE_DATA_SET_ATTRIBUTES attributes_struct;
LPDWORD BytesReturned = 0;
int inputbufferlength = 0;
inputbufferlength = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES) + sizeof(_DEVICE_DSM_OFFLOAD_WRITE_PARAMETERS) + sizeof(DEVICE_DATA_SET_RANGE);
PUCHAR inputbuffer = (PUCHAR)malloc(inputbufferlength);
PUCHAR outputbuffer = (PUCHAR)malloc(inputbufferlength);
//RtlZeroMemory(inputbuffer, inputBufferLength);
PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes = (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES)inputbuffer;
PDEVICE_DSM_OFFLOAD_WRITE_PARAMETERS offload_write_parameters = NULL;
dsmAttributes->Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
dsmAttributes->Action = DeviceDsmAction_OffloadWrite;
dsmAttributes->Flags = 0;
dsmAttributes->ParameterBlockOffset = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
dsmAttributes->ParameterBlockLength = sizeof(DEVICE_DSM_OFFLOAD_WRITE_PARAMETERS);
offload_write_parameters = (PDEVICE_DSM_OFFLOAD_WRITE_PARAMETERS)((PUCHAR)dsmAttributes + dsmAttributes->ParameterBlockOffset);
offload_write_parameters->Flags = 0;
offload_write_parameters->TokenOffset = 0;
dsmAttributes->DataSetRangesOffset = dsmAttributes->ParameterBlockOffset + dsmAttributes->ParameterBlockLength;
dsmAttributes->DataSetRangesLength = sizeof(DEVICE_DATA_SET_RANGE);
PDEVICE_DATA_SET_RANGE lbaRange = NULL;
lbaRange = (PDEVICE_DATA_SET_RANGE)((PUCHAR)dsmAttributes + dsmAttributes->DataSetRangesOffset);
lbaRange->StartingOffset = 0; // not sure about this one for now
lbaRange->LengthInBytes = 256 * 1024 * 1024;
int status = DeviceIoControl(
hDevice, // handle to device
IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES, // dwIoControlCode
inputbuffer, // input buffer
inputbufferlength, // size of the input buffer
outputbuffer, // output buffer
inputbufferlength, // size of the input buffer - modified to be too small!
BytesReturned, // number of bytes returned
0 //(LPOVERLAPPED) &overlapped_struct // OVERLAPPED structure
);
DWORD error_num = GetLastError();
CloseHandle(hDevice);
std::cout << "STATUS IS: " << status << "\n";
std::cout << "ERROR IS: " << error_num;
return 0;
}
But this returned error 87 ERROR_INVALID_PARAMETER when attempting to call it.
My instinct was to debug the IOCTL by placing a breakpoint on partmgr!PartitionIoctlDsm - I was under the impression the targeted IOCTL was throwing the error. However my breakpoint was not being hit. So, then I moved on to placing a breakpoint on the IOCTL dispatch table itself
bp partmgr!PartitionDeviceControl
But that BP is never hit either. So, something else before my driver is throwing the error.
The Question(s)
How should I go about debugging this? How do I figure which driver is throwing the error?
What is the correct way to invoke this driver without throwing errors?
Why Am I getting this error?
Additional information
To be absolutely clear, I am dead set on using this particular IOCTL function. This is a learning exercise, and I am not interested in using alternative/easier to use functionality to implement the same effect. My curiosity lies in figuring out why the IO manager wont let me call the function.
I am running this code as admin.
I am running this code in a virtual machine.
I am debugging with windbg preview over a COM port.
Through some sleuthing I believe this is a filter driver, and that other drivers can intercept and handle this request.
Let me know if there is any other information I can provide.

IPortableDeviceManager::GetDevices returning 0 Devices

I'm currently writing a simple application to retrieve a list of the PnP devices of my computer. To do this, I'm making use of the Windows PortableDeviceApi Library.
So far I have the following code:
#include <iostream>
#include <PortableDeviceApi.h>
#include <wrl.h>
inline void getDeviceHWIDs() {
// Initialize
CoInitialize(nullptr);
// create portable device manager object
Microsoft::WRL::ComPtr<IPortableDeviceManager> device_manager;
HRESULT hr = CoCreateInstance(CLSID_PortableDeviceManager,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&device_manager));
if (FAILED(hr)) {
std::cout << "! Failed to CoCreateInstance CLSID_PortableDeviceManager, hr = " << std::hex << hr << std::endl;
}
// obtain amount of devices
DWORD pnp_device_id_count = 0;
if (SUCCEEDED(hr)) {
hr = device_manager->GetDevices(nullptr, &pnp_device_id_count);
if (FAILED(hr)) {
std::cout << "! Failed to get number of devices on the system, hr = " << std::hex << hr << std::endl;
}
}
std::cout << "Devices found: " << pnp_device_id_count << std::endl;
// Uninitialize
CoUninitialize();
}
The code compiles and runs successfully, however pnp_device_id_count is returning 0, indicating that there are no PnP devices connected to my computer. This is obviously an incorrect result, since Get-PnpDevice in PowerShell returns a large list of devices.
Any help would be much appreciated, as I'm a bit stumped over this ':(
Thank you :)
This is expected, Windows Portable Devices provides only a way to communicate with music players, storage devices, mobile phones, cameras, and many other types of connected devices.
It will not enumerate all devices on your system. Connect an IPhone and you will see pnp_device_id_count become 1.
To enumerate all devices you can use WinRT's Windows.Devices.Enumeration Namespace or the ancient Setup API.
Here is a (C# but you can do the same with C++) sample for using WinRT's one https://github.com/smourier/DeviceExplorer and a C++ sample for using Setup API: https://www.codeproject.com/Articles/14412/Enumerating-windows-device

How to get a human readable process name (like in the task manager) via Win32 API? [duplicate]

I want to get the description of a process (the description that is seen in task manager) in Windows using C++.
You most likely want to get the FileDesription field from the version resources of the main .exe file of the program, using the VerQueryValue() API call. Here is an example from that document:
The following example shows how to enumerate the available version languages and retrieve the FileDescription string-value for each language.
Be sure to call the GetFileVersionInfoSize and GetFileVersionInfo functions before calling VerQueryValue to properly initialize the pBlock buffer.
// Structure used to store enumerated languages and code pages.
HRESULT hr;
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *lpTranslate;
// Read the list of languages and code pages.
VerQueryValue(pBlock,
TEXT("\\VarFileInfo\\Translation"),
(LPVOID*)&lpTranslate,
&cbTranslate);
// Read the file description for each language and code page.
for( i=0; i < (cbTranslate/sizeof(struct LANGANDCODEPAGE)); i++ )
{
hr = StringCchPrintf(SubBlock, 50,
TEXT("\\StringFileInfo\\%04x%04x\\FileDescription"),
lpTranslate[i].wLanguage,
lpTranslate[i].wCodePage);
if (FAILED(hr))
{
// TODO: write error handler.
}
// Retrieve file description for language and code page "i".
VerQueryValue(pBlock,
SubBlock,
&lpBuffer,
&dwBytes);
}
Although I read this question, the accepted answer, and the documentation for VerQueryValue, I spent quite a while to understand how to use that VerQueryValue function (since the code example in the docs has no declarations of variables and isn't clear for me at all).
So I decided to put here the code that can be easily run as a working example of the usage of VerQueryValue to get a process description.
#pragma comment(lib,"Version.lib")
#include <iostream>
#include <windows.h>
#include <winver.h>
using namespace std;
int printFileDescriptions(const wchar_t* filename)
{
int versionInfoSize = GetFileVersionInfoSize(filename, NULL);
if (!versionInfoSize) {
return 0;
}
auto versionInfo = new BYTE[versionInfoSize];
std::unique_ptr<BYTE[]> versionInfo_automatic_cleanup(versionInfo);
if (!GetFileVersionInfo(filename, NULL, versionInfoSize, versionInfo)) {
return 0;
}
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *translationArray;
UINT translationArrayByteLength = 0;
if (!VerQueryValue(versionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayByteLength)) {
return 0;
}
// You may check GetSystemDefaultUILanguage() == translationArray[i].wLanguage
// if only the system language required
for (unsigned int i = 0; i < (translationArrayByteLength / sizeof(LANGANDCODEPAGE)); i++) {
wchar_t fileDescriptionKey[256];
wsprintf(
fileDescriptionKey,
L"\\StringFileInfo\\%04x%04x\\FileDescription",
translationArray[i].wLanguage,
translationArray[i].wCodePage
);
wchar_t* fileDescription = NULL;
UINT fileDescriptionSize;
if (VerQueryValue(versionInfo, fileDescriptionKey, (LPVOID*)&fileDescription, &fileDescriptionSize)) {
wcout << endl << fileDescription << endl;
}
}
return TRUE;
}
int main()
{
// Set locale of the console to print non-ASCII symbols
SetConsoleOutputCP(GetACP());
SetConsoleCP(GetACP());
wcout.imbue(std::locale("")); // set default global locale
// ----------------------------------------------------
auto path = L"C:\\Windows\\explorer.exe";
printFileDescriptions(path);
wcin.get(); // to prevent console close
}
It's supposed that all WinAPI functions on your system use wchar_t, that is VerQueryValueW is actually used.
The initial version of the code I took here Retrieve File Description an Application VerQueryValue

CLR ICLRControl::GetCLRManager fails with error HOST_E_INVALIDOPERATION

I have a native process which uses .NET component via COM interop. Once the process is up, I am trying to get interface pointers to the various CLR managers such as ICLRDebugManager, ICLRGCManager, etc. using the ICLRControl interface pointer. I'm able to reach up to acquiring ICLRControl interface without a glitch. ICLRRuntimeInfo also correctly tells me that it is 4.0.x .net version when I call GetVersionString on it.
It is only that the ICLRControl::GetCLRManager keeps failing with error 0x80130122, which error code stands for HOST_E_INVALIDOPERATION. Searched the internet, but could not get information as to why this might be failing. Any help is much appreciated.
TIA.
WinCPP
Edit 1. Adding code snippet.
// ICLRRuntimeInfo interface pointer
CComQIPtr<ICLRRuntimeInfo> pCLRRuntimeInfo = pUnk;
if (!pCLRRuntimeInfo)
{
cout << "failed to get run time info interface pointer" << endl;
return;
}
TCHAR version[128];
memset(version, 0, 128);
DWORD count = 127;
pCLRRuntimeInfo->GetVersionString(version, &count);
cout << version << endl;
// ICLRRuntimeHost
CComPtr<ICLRRuntimeHost> pCLRRuntimeHost = 0;
hr = pCLRRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID *)&pCLRRuntimeHost);
if (FAILED(hr))
{
cout << "failed to get run time host interface pointer" << endl;
return;
}
// ICLRControl
CComPtr<ICLRControl> pCLRControl = 0;
hr = pCLRRuntimeHost->GetCLRControl(&pCLRControl);
if (FAILED(hr))
{
cout << "failed to get clr control interface pointer" << endl;
return;
}
///////////////////////////////////////////
// Everything is successful upto this point
///////////////////////////////////////////
// ICLRGCManager
CComPtr<ICLRGCManager> pCLRGCManager = 0;
hr = pCLRControl->GetCLRManager(IID_ICLRGCManager, (LPVOID *)&pCLRGCManager);
if (FAILED(hr))
{
cout << "failed to get GC manager interface pointer" << endl;
return;
}
// Above call fails with the error 0x81031022, though everything is as per the MSDN documentation for the API
Looking at this source code on GitHub, it looks like CCorCLRControl::GetCLRManager will return HOST_E_INVALIDOPERATION if the runtime has already been started.
This explains why you are receiving that error code when attempting to "latch on to already loaded CLR instance," but that you meet with success when you create a CLR instance explicitly.

How to get process description?

I want to get the description of a process (the description that is seen in task manager) in Windows using C++.
You most likely want to get the FileDesription field from the version resources of the main .exe file of the program, using the VerQueryValue() API call. Here is an example from that document:
The following example shows how to enumerate the available version languages and retrieve the FileDescription string-value for each language.
Be sure to call the GetFileVersionInfoSize and GetFileVersionInfo functions before calling VerQueryValue to properly initialize the pBlock buffer.
// Structure used to store enumerated languages and code pages.
HRESULT hr;
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *lpTranslate;
// Read the list of languages and code pages.
VerQueryValue(pBlock,
TEXT("\\VarFileInfo\\Translation"),
(LPVOID*)&lpTranslate,
&cbTranslate);
// Read the file description for each language and code page.
for( i=0; i < (cbTranslate/sizeof(struct LANGANDCODEPAGE)); i++ )
{
hr = StringCchPrintf(SubBlock, 50,
TEXT("\\StringFileInfo\\%04x%04x\\FileDescription"),
lpTranslate[i].wLanguage,
lpTranslate[i].wCodePage);
if (FAILED(hr))
{
// TODO: write error handler.
}
// Retrieve file description for language and code page "i".
VerQueryValue(pBlock,
SubBlock,
&lpBuffer,
&dwBytes);
}
Although I read this question, the accepted answer, and the documentation for VerQueryValue, I spent quite a while to understand how to use that VerQueryValue function (since the code example in the docs has no declarations of variables and isn't clear for me at all).
So I decided to put here the code that can be easily run as a working example of the usage of VerQueryValue to get a process description.
#pragma comment(lib,"Version.lib")
#include <iostream>
#include <windows.h>
#include <winver.h>
using namespace std;
int printFileDescriptions(const wchar_t* filename)
{
int versionInfoSize = GetFileVersionInfoSize(filename, NULL);
if (!versionInfoSize) {
return 0;
}
auto versionInfo = new BYTE[versionInfoSize];
std::unique_ptr<BYTE[]> versionInfo_automatic_cleanup(versionInfo);
if (!GetFileVersionInfo(filename, NULL, versionInfoSize, versionInfo)) {
return 0;
}
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *translationArray;
UINT translationArrayByteLength = 0;
if (!VerQueryValue(versionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayByteLength)) {
return 0;
}
// You may check GetSystemDefaultUILanguage() == translationArray[i].wLanguage
// if only the system language required
for (unsigned int i = 0; i < (translationArrayByteLength / sizeof(LANGANDCODEPAGE)); i++) {
wchar_t fileDescriptionKey[256];
wsprintf(
fileDescriptionKey,
L"\\StringFileInfo\\%04x%04x\\FileDescription",
translationArray[i].wLanguage,
translationArray[i].wCodePage
);
wchar_t* fileDescription = NULL;
UINT fileDescriptionSize;
if (VerQueryValue(versionInfo, fileDescriptionKey, (LPVOID*)&fileDescription, &fileDescriptionSize)) {
wcout << endl << fileDescription << endl;
}
}
return TRUE;
}
int main()
{
// Set locale of the console to print non-ASCII symbols
SetConsoleOutputCP(GetACP());
SetConsoleCP(GetACP());
wcout.imbue(std::locale("")); // set default global locale
// ----------------------------------------------------
auto path = L"C:\\Windows\\explorer.exe";
printFileDescriptions(path);
wcin.get(); // to prevent console close
}
It's supposed that all WinAPI functions on your system use wchar_t, that is VerQueryValueW is actually used.
The initial version of the code I took here Retrieve File Description an Application VerQueryValue