There is an implementation of remote file transfer via dragging virtual files using IStream/IDataObject (based on Raymond Chen's blog topic: What a drag: Dragging a virtual file (IStream edition)).
Basically, it works good. But if the application is run under the SYSTEM account, the IDataObject::GetData() is called only once - requests a FILEDESCRIPTOR, but doesn't return with a requests for FILECONTENTS.
How I use IDataObject:
if (SUCCEEDED(OleInitialize(NULL))) {
IDataObject* dtob = new MyDataObject(/*some files info here*/);
if (dtob) {
OleSetClipboard(dtob);
dtob->Release();
}
//simulate Ctrl-V
...
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
OleUninitialize();
}
The solution is to set security for the process using CoInitializeSecurity:
HRESULT res = CoInitialize(NULL);
if (SUCCEEDED(res))
CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE,
RPC_C_IMP_LEVEL_IDENTIFY, NULL, NULL, NULL);
// your code here....
if (SUCCEEDED(res))
CoUninitialize();
Related
I am trying to control the size and position of a UWP APP (Windows Mixed Reality Portal) via a sepate app. In my case, I am using a console app for simplicity. A Command script would also work for what I want to achieve.
I have tried Windows api such as MoveWindow,SetWindowPos but they do not work as expected and GetWindowRect returns a 0,0,0,0 rect. I can get the window handle but not change the size/position.
My reason for doing this is to send virtual mouse keys to the app in order to initialise the front position of the Windows Mixed Reality system. Sending the virtual keys are fine but I am having trouble automating shifting of the position of the uwp app itself.
#include <iostream>
#include <ShObjIdl.h>
#include <atlbase.h>
#include <tlhelp32.h>
BOOL CALLBACK EnumWindowsProcBack(HWND windowHandle, LPARAM lParam) {
DWORD searchedProcessId = (DWORD)lParam; // This is the process ID we search for (passed from BringToForeground as lParam)
DWORD windowProcessId = 0;
GetWindowThreadProcessId(windowHandle, &windowProcessId); // Get process ID of the window we just found
if (searchedProcessId == windowProcessId) { // Is it the process we care about?
//std::cout << "moving window..\n";
//bool s=MoveWindow(windowHandle, 0, 0, 1920, 1080, true);
SetWindowPos(
windowHandle,
HWND_TOP,
0,
0,
600,
600,
SWP_NOSIZE
);
return FALSE; // Stop enumerating windows
}
return TRUE; // Continue enumerating
}
void MoveWindowToFixedLocation(DWORD processId) {
EnumWindows(&EnumWindowsProcBack, (LPARAM)processId);
}
HRESULT LaunchApp(LPCWSTR AUMID, DWORD &pid)
{
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (FAILED(hr))
{
wprintf(L"LaunchApp %s: Failed to init COM. hr = 0x%08lx \n", AUMID, hr);
}
{
CComPtr<IApplicationActivationManager> AppActivationMgr = nullptr;
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_ApplicationActivationManager, nullptr,
CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&AppActivationMgr));
if (FAILED(hr))
{
wprintf(L"LaunchApp %s: Failed to create Application Activation Manager.hr = 0x%08lx \n", AUMID, hr);
}
}
if (SUCCEEDED(hr))
{
//DWORD pid = 0;
hr = AppActivationMgr->ActivateApplication(AUMID, nullptr, AO_NONE,
&pid);
if (FAILED(hr))
{
wprintf(L"LaunchApp %s: Failed to Activate App. hr = 0x%08lx \n", AUMID, hr);
}
}
}
CoUninitialize();
return hr;
}
int main() {
DWORD pid = 0;
LaunchApp(L"Microsoft.Windows.HolographicFirstRun_cw5n1h2txyewy!App", pid);
//cout << pid;
MoveWindowToFixedLocation(pid);
}
It's impossible. UWP app runs in own closed environment. A desktop application cannot sent it any signal.
I'm trying to open Mozilla Firefox using CreateProcess(). However, If Firefox is auto updating while I try to open it, I get the following error message:
Cannot load XPCOM
And I need to restart the application.
Here is the code I'm using:
path = MozillaExePath.c_str();
STARTUPINFO info = { sizeof(STARTUPINFO), NULL, NULL, "FireFox", 0,0,800, 600, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL };
PROCESS_INFORMATION processInfo;
if (CreateProcess(path, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo))
{
WaitForSingleObject(processInfo.hProcess, 3000);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
else
{
WriteLogFile("May be error with mozilla firefox...\n");
exit(1);
}
So, how can I handle that error message using C++?
Here is a different way to do this that is working for me:
#include <shellapi.h>
[...]
if (ShellExecute(NULL, TEXT("open"), TEXT("firefox.exe"), NULL, NULL, 0) <= HINSTANCE(32))
{
WriteLogFile("Could not open Mozilla Firefox...\n");
}
Reference:
https://learn.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecutea
So, I want to display a CDialog to the user:
void CMeetingScheduleAssistantDlg::OnOptionsOutlookCalendarOptions()
{
COutlookCalendarSettingsDlg dlgSettings(this);
dlgSettings.DoModal();
}
Now, the popup dialogue (in OnInitDialog) runs a console application behind the scenes. This console application is communicating with Microsoft Graph.
As a result, it can take a few seconds for the dialog to display.
I execute the console application with this method:
bool CMeetingScheduleAssistantApp::ExecuteProgram(CString strCommand, DWORD& rExitCode)
{
PROCESS_INFORMATION processInformation = { nullptr };
STARTUPINFO startupInfo = { 0 };
int nStrBuffer;
BOOL bProcessResult, bExitCodeProcess;
bool bOK = false;
CWaitCursor wait;
SetProgramExecuting(true);
rExitCode = -1;
startupInfo.cb = sizeof(startupInfo);
nStrBuffer = strCommand.GetLength() + 50;
bProcessResult = CreateProcess(nullptr, strCommand.GetBuffer(nStrBuffer),
nullptr, nullptr, FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
nullptr, nullptr, &startupInfo, &processInformation);
strCommand.ReleaseBuffer();
if (!bProcessResult)
{
// CreateProcess() failed
// Get the error from the system
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, nullptr);
// Display the error
CString strError = (LPTSTR)lpMsgBuf;
TRACE(_T("Authenticate failed at CreateProcess()\nCommand=%s\nMessage=%s\n\n"), strCommand, strError);
// Free resources created by the system
LocalFree(lpMsgBuf);
SetProgramExecuting(false);
// We failed.
return false;
}
else
{
// Successfully created the process. Wait for it to finish.
DWORD WaitResult;
do
{
WaitResult = MsgWaitForMultipleObjects(1,
// only 1 wait object
&processInformation.hProcess, // worker thread
FALSE, // stop if any
INFINITE, // no timeout
QS_ALLINPUT);
if (WaitResult == WAIT_OBJECT_0 + 1)
{
// Handle windows message
MSG Msg;
while (PeekMessage(&Msg, nullptr, 0, (UINT)-1, PM_REMOVE))
{
TRACE3("%d %d %d\n", Msg.message, Msg.wParam, Msg.lParam);
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
} while (WaitResult != WAIT_OBJECT_0);
ASSERT(WaitResult == WAIT_OBJECT_0);
// Get the exit code.
bExitCodeProcess = GetExitCodeProcess(processInformation.hProcess, &rExitCode);
// Close the handles.
CloseHandle(processInformation.hProcess);
CloseHandle(processInformation.hThread);
if (!bExitCodeProcess)
{
// Could not get exit code.
TRACE(_T("Executed command but couldn't get exit code.\nCommand=%s\n"), strCommand);
SetProgramExecuting(false);
return false;
}
SetProgramExecuting(false);
return true;
}
}
Inside OnInitDialog, just before the ExecuteProgram is called, I tried using:
CWaitCursor wait;
But it makes no difference. So how can I show a wait cursor from the moment I invoke the popup dialog until the dialog is visible to the user?
One solution could be to use Modeless Dialog. You can create a dialog which looks similar to wait cursor dialog.
You show that Modeless Dialog just before dlgSettings.DoModal(); statement in your code. Please use TOP_MOST while showing Modeless Dialog.
Finally, hide/close Modeless Dialog from OnInitDialog() once processing is over.
Another approach could be:
Add a public member of asCWaitCursor* m_pWaitCursor in COutlookCalendarSettingsDlg class. Now modify code as
void CMeetingScheduleAssistantDlg::OnOptionsOutlookCalendarOptions()
{
COutlookCalendarSettingsDlg dlgSettings(this);
dlgSettings->m_pWaitCursor = new CWaitCursor();
dlgSettings.DoModal();
}
Then modify OnInitDialog of COutlookCalendarSettingsDlg to delete instance of CWaitCursor before returning from it.
delete m_pWaitCursor;
Update
I thought I would add an update to this answer that applies in other situations. What you do is use a CPersistantWaitCursor instead. The article provides a little example:
#include "PersistentWaitCursor.h"
void CMyWnd::DoSomeLengthyOperation()
{
// Create and show the wait cursor
CPersistentWaitCursor waitCursor;
// Do some lengthy operation
...
// waitCursor goes out of scope and cursor is restored
}
BOOL CMyWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (CPersistentWaitCursor::WaitCursorShown())
{
// We are showing the wait cursor
RestoreWaitCursor();
return TRUE;
}
// Let the base class deal with this one
return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
Look at the article for full details about how it works. But I can confirm that for some of my other lengthy actions this enhanced CPersistantWaitCursor did the trick.
I am creating a DLL which gets called from a Windows application. It calls my function which then spawns a thread running PttThread shown below. I then register a message only window to handle raw input events. None of the calls return any errors but I do not get raw input events. However, if I add a delay after creating the window but before registering the raw input it suddenly works.
Since Windows does not tell me anything is wrong, how can I change my code so initialisation works properly?
DWORD WINAPI PttThread(LPVOID lpParameter) {
if (!hInstance) {
hInstance = GetModuleHandle(NULL);
}
//Create message only window
WNDCLASSEX wx = {};
wx.cbSize = sizeof(WNDCLASSEX);
wx.lpfnWndProc = messageProc;
wx.hInstance = hInstance;
wx.lpszClassName = class_name;
if (!RegisterClassEx(&wx)) {
printf("could not register window class: ");
printLastError();
}
windowHandle = CreateWindowEx(0, class_name, L"R4nd0m_dummy_name", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
if (!windowHandle) {
printf("Could not create window\n");
printLastError();
}
printf("hinstance %u\nhwnd %u\n", hInstance, windowHandle);
// HERE
Sleep(1000); //Then raw input works
// Setup raw input
RAWINPUTDEVICE rid[1];
rid[0].usUsagePage = 0x01;
rid[0].usUsage = 0x06;
rid[0].dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK;
rid[0].hwndTarget = windowHandle;
if (RegisterRawInputDevices(rid, 1, sizeof(rid[0])) == FALSE) {
printf("!!! Could not setup keyboard input\n");
printLastError();
}
printf("Finish setup of PTT\n");
// Raw input does NOT work if Sleep(1000) is move to here
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
printf("Message received\n"); //Does not print if early registration of raw input.
if (msg.message == MSG_SHUTDOWN) {
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
threadShutdown();
return 0;
}
The MSDN says that using ReadDirectoryChangesW implies the calling process having the Backup and Restore privileges.
Does this mean that only process launched under administrator account will work correctly?
I've tried the following code, it fails to enable the required privileges when running as a restricted user.
void enablePrivileges()
{
enablePrivilege(SE_BACKUP_NAME);
enablePrivilege(SE_RESTORE_NAME);
}
void enablePrivilege(LPCTSTR name)
{
HANDLE hToken;
DWORD status;
if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp = { 1 };
if( ::LookupPrivilegeValue(NULL, name, &tp.Privileges[0].Luid) )
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
BOOL result = ::AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
verify (result != FALSE);
status = ::GetLastError();
}
::CloseHandle(hToken);
}
}
Am I doing something wrong? Is there any workaround for using ReadDirectoryChangesW from a non-administrator user account? It seems that the .NET's FileSystemWatcher can do this. Thanks!
Update: Here is the full code of the class:
class DirectoryChangesWatcher
{
public:
DirectoryChangesWatcher(wstring directory)
{
enablePrivileges();
hDir = ::CreateFile(directory.c_str(),
FILE_LIST_DIRECTORY | FILE_FLAG_OVERLAPPED,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
ensure (hDir != INVALID_HANDLE_VALUE, err::SystemException);
::ZeroMemory(&overlapped, sizeof(OVERLAPPED));
overlapped.hEvent = dirChangedEvent.getHandle();
}
~DirectoryChangesWatcher() { ::CloseHandle(hDir); }
public:
Event& getEvent() { return dirChangedEvent; }
FILE_NOTIFY_INFORMATION* getBuffer() { return buffer; }
public:
void startAsyncWatch()
{
DWORD bytesReturned;
const BOOL res = ::ReadDirectoryChangesW(
hDir,
&buffer,
sizeof(buffer),
TRUE,
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE,
&bytesReturned,
&overlapped,
NULL);
ensure(res != FALSE, err::SystemException);
}
private:
void enablePrivileges()
{
enablePrivilege(SE_BACKUP_NAME);
enablePrivilege(SE_RESTORE_NAME);
}
void enablePrivilege(LPCTSTR name)
{
HANDLE hToken;
DWORD status;
if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp = { 1 };
if( ::LookupPrivilegeValue(NULL, name, &tp.Privileges[0].Luid) )
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
BOOL result = ::AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
verify (result != FALSE);
status = ::GetLastError();
}
::CloseHandle(hToken);
}
}
private:
HANDLE hDir;
OVERLAPPED overlapped;
Event dirChangedEvent;
FILE_NOTIFY_INFORMATION buffer[1024];
};
}
Update: Good news! It turned out the problem really was in the FILE_SHARE_WRITE flag in the call to CreateFile. The notifications did not come unless I was an admin. When I removed this flag, everything is now working ona non-admin account too.
I have used ReadDirectoryChangesW without requiring administrator rights, at least on Vista. I don't think you need to manually elevate the process in order to use it on a folder the user already has permissions to see.
It would be more helpful to see the actual code you are using to call ReadDirectoryChangesW, including how you create the handle you pass in.
I don't see where MSDN says you need either backup or restore privileges. It instructs you to call CreateFile with the File_Flag_Backup_Semantics flag set, and in that flag's description, MSDN says this:
The system ensures that the calling process overrides file security checks when the process has SE_BACKUP_NAME and SE_RESTORE_NAME privileges.
The way I read it, if you have those privileges, then the system will override the file security checks for you. So if you don't have those privileges, then the program will simply continue to be bound by whatever file security checks would ordinarily be in effect.
Alex, in your CreateFile() call you put FILE_FLAG_OVERLAPPED into wrong position. It should be moved from 2nd to 6th parameter.