TaskScheduler RegisterTaskDefinition fails with NULL path in Win10 - c++

According to this MSDN doc, we may pass NULL for the path argument:
path [in]
The name of the task. If this value is NULL, the task will be registered in the root task folder and the task name will be a GUID value created by the Task Scheduler service.
I have a code that use this behavior. The code works fine in Win7 and 8.1, but not in my Win10 box (ver 1709 64-bit, build 16299). In Win10, it will return 0x80070005 aka "Access Denied" when path is NULL. If I specify a name like "Foobar", it will work fine.
Test code:
// Link comsuppw.lib and taskschd.lib.
#include <stdio.h>
#include <windows.h>
#include <atlbase.h>
#include <atlstr.h>
#include <shlobj.h>
#include <taskschd.h>
#include <comutil.h>
class AutoHR {
HRESULT hr;
public:
void operator=(HRESULT hr)
{
this->hr = hr;
if (FAILED(hr)) {throw *this;}
}
HRESULT GetHR() const { return hr; }
};
static void TestTaskSched()
{
AutoHR hr;
CComPtr<ITaskService> taskSvc;
CComPtr<ITaskFolder> taskFol;
CComPtr<ITaskDefinition> taskDef;
CComPtr<IActionCollection> taskAC;
CComPtr<IAction> taskAction;
CComPtr<IExecAction> taskEA;
CComPtr<IRegisteredTask> registeredTask;
try {
hr = taskSvc.CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_ALL);
hr = taskSvc->Connect(CComVariant(),CComVariant(),CComVariant(),CComVariant());
hr = taskSvc->GetFolder(_bstr_t(L""), &taskFol);
hr = taskSvc->NewTask(0, &taskDef);
hr = taskDef->get_Actions(&taskAC);
hr = taskAC->Create(TASK_ACTION_EXEC, &taskAction);
hr = taskAction.QueryInterface<IExecAction>(&taskEA);
hr = taskEA->put_Path(_bstr_t(L"C:\\Windows\\System32\\cmd.exe"));
hr = taskEA->put_Arguments(_bstr_t(L"/k echo Testing"));
// Note that NULL is passed as the first argument.
hr = taskFol->RegisterTaskDefinition(nullptr, taskDef,
TASK_CREATE_OR_UPDATE, CComVariant(), CComVariant(),
TASK_LOGON_NONE, CComVariant(), &registeredTask);
MessageBoxW(nullptr, L"Succeeded!", L"OK", MB_ICONINFORMATION);
}
catch (AutoHR const &autohr) {
WCHAR buf[99] = {0};
wsprintfW(buf, L"HRESULT error 0x%.8X\n", autohr.GetHR());
MessageBoxW(nullptr, buf, nullptr, MB_ICONERROR);
}
}
int main()
{
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr))
{
TestTaskSched();
CoUninitialize();
}
return 0;
}
Test result:
Questions:
1) Is there a behavior change between Win10 and older Windows? I suspect there is, but I cannot find any doc that mentions it.
2) Any good alternative for this behavior? I hope I don't have to generate GUID by myself for temporary task creation.

Related

WINAPI Network Discovery without SMBv1

I need to get a list of available shared folders on the local network, the way they appear in the "Network" tab in File Explorer. Earlier, I used combination of NetServerEnum/NetShareEnum functions to obtain it, but they are using SMBv1 protocol, which is now disabled by default in windows, so now i'm getting error 1231 from NetServerEnum. But File Explorer still cat obtain this list. I tried use Process Monitor to determine, which API it use, but failed. So, is there any way to get list of available shared folders in local network without using API, that requires SMBv1?
You can use windows shell api and use FOLDERID_NetworkFolder to get the KNOWNFOLDERID of "network".
The following sample can get folders, nonfolders, and hidden items in the "network" folder.
#include <windows.h>
#include <Shobjidl.h>
#include <Shlobj.h>
#include <iostream>
void wmain(int argc, TCHAR* lpszArgv[])
{
IShellItem* pShellItem;
IEnumShellItems* pShellEnum = NULL;
HRESULT hr = S_OK;
hr = CoInitialize(NULL);
if (FAILED(hr))
{
printf("CoInitialize error, %x\n", hr);
return;
}
hr = SHGetKnownFolderItem(FOLDERID_NetworkFolder, KF_FLAG_DEFAULT, NULL, IID_PPV_ARGS(&pShellItem));
if (FAILED(hr))
{
printf("SHGetKnownFolderItem error, %x\n", hr);
return;
}
hr = pShellItem->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&pShellEnum));
if (FAILED(hr))
{
printf("BindToHandler error, %x\n", hr);
return;
}
do {
IShellItem* pItem;
LPWSTR szName = NULL;
hr = pShellEnum->Next(1, &pItem, nullptr);
if (hr == S_OK && pItem)
{
HRESULT hres = pItem->GetDisplayName(SIGDN_NORMALDISPLAY, &szName);
std::wcout << szName << std::endl;
CoTaskMemFree(szName);
}
} while (hr == S_OK);
CoUninitialize();
}

How Can I Remove System.Size Property from My Custom File Type On Windows Explorer?

I have been trying lately to get a grasp on the Windows Property System. I implemented a custom file type(Property Handler, Thumbnail Handler, Preview Handler) and I want to hide System.Size property for my file type on Windows Explorer(Details Tab on Properties dialogbox and Size Column on Details View Mode).I used the following code but no result. Is it possible to hide size property? Maybe I should write Shell Folder extension.
#include <shobjidl.h>
#include <propsys.h>
#include <propvarutil.h>
#include <propkey.h>
#include <strsafe.h>
#pragma comment(lib,"propsys.lib")
HRESULT SetPropertyValue(PCWSTR pszFilename, PCWSTR pszCanonicalName, PCWSTR pszValue)
{
// Convert the Canonical name of the property to PROPERTYKEY
PROPERTYKEY key;
HRESULT hr = PSGetPropertyKeyFromName(pszCanonicalName, &key);
if (SUCCEEDED(hr))
{
IPropertyStore* pps = NULL;
hr = SHGetPropertyStoreFromParsingName(pszFilename, NULL, gpsFlags, IID_PPV_ARGS(ppps));
if (SUCCEEDED(hr))
{
PROPVARIANT propvarValue = { 0 };
hr = InitPropVariantFromString(pszValue, &propvarValue);
if (SUCCEEDED(hr))
{
hr = PSCoerceToCanonicalValue(key, &propvarValue);
if (SUCCEEDED(hr))
{
// Set the value to the property store of the item.
hr = pps->SetValue(key, propvarValue);
if (SUCCEEDED(hr))
{
// Commit does the actual writing back to the file stream.
hr = pps->Commit();
if (SUCCEEDED(hr))
{
wprintf(L"Property %s value %s written successfully \n", pszCanonicalName, pszValue);
}
else
{
wprintf(L"Error %x: Commit to the propertystore failed.\n", hr);
}
}
else
{
wprintf(L"Error %x: Set value to the propertystore failed.\n", hr);
}
}
PropVariantClear(&propvarValue);
}
pps->Release();
}
else
{
wprintf(L"Error %x: getting the propertystore for the item.\n", hr);
}
}
else
{
wprintf(L"Invalid property specified: %s\n", pszCanonicalName);
}
return hr;
}
int wmain(int argc, wchar_t* argv[])
{
if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)))
{
SetPropertyValue(L"FullFilePath",L"System.Size", L"0");
CoUninitialize();
}
return 0;
}

How to Initialize HRESULT for SetMasterVolume?

#include <iostream>
#include <windows.h>
#include <Audioclient.h>
int main(){
ShellExecute(NULL, "open", "https://www.youtube.com/watch?v=zf2VYAtqRe0", NULL, NULL, SW_SHOWNORMAL);
HRESULT SetMasterVolume(1.0, NULL);
return();
}
Okay so I'm trying to code this program, that opens a YouTube song, and turns up the volume at the same time. I don´t understand the error I get.
ERROR : C2440 ´initializing´: cannot convert from ´initializer list´ to ´HRESULT´
So therefore my question is: how do I initialize HRESULT so SetMasterVolume works? Or, how to setup SetMasterVolume? And please, if possible, explain why I cant just write
SetMasterVolume(1.0,NULL);
When I have included audioclient.h
ISimpleAudioVolume::SetMasterVolume is a COM method, it is not a regular WinAPI. You get a compile error when you just type in the function. Adding HRESULT in front of it will cause a different C++ error.
Use this code instead, with SetMasterVolumeLevelScalar
Based on code from:
Change Master Volume in Visual C++
#include <Windows.h>
#include <Mmdeviceapi.h>
#include <Endpointvolume.h>
BOOL ChangeVolume(float nVolume)
{
HRESULT hr = NULL;
IMMDeviceEnumerator *deviceEnumerator = NULL;
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
if(FAILED(hr))
return FALSE;
IMMDevice *defaultDevice = NULL;
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
deviceEnumerator->Release();
if(FAILED(hr))
return FALSE;
IAudioEndpointVolume *endpointVolume = NULL;
hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume),
CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
defaultDevice->Release();
if(FAILED(hr))
return FALSE;
hr = endpointVolume->SetMasterVolumeLevelScalar(nVolume, NULL);
endpointVolume->Release();
return SUCCEEDED(hr);
}
int main()
{
CoInitialize(NULL);
ChangeVolume(0.5);
CoUninitialize();
return 0;
}
You need to give it a name and assign to it.
HRESULT hResult = SetMasterVolume(1.0, NULL);

Play Audio Stream Using Windows APIs in C++

To play an .mp3 file in Windows using (in this case) DirectShow you only need:
#include <dshow.h>
#include <cstdio>
// For IID_IGraphBuilder, IID_IMediaControl, IID_IMediaEvent
#pragma comment(lib, "strmiids.lib")
const wchar_t* filePath = L"C:/Users/Public/Music/Sample Music/Sleep Away.mp3";
int main()
{
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEvent *pEvent = NULL;
// Initialize the COM library.
HRESULT hr = ::CoInitialize(NULL);
if (FAILED(hr))
{
::printf("ERROR - Could not initialize COM library");
return 0;
}
// Create the filter graph manager and query for interfaces.
hr = ::CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph);
if (FAILED(hr))
{
::printf("ERROR - Could not create the Filter Graph Manager.");
return 0;
}
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
// Build the graph.
hr = pGraph->RenderFile(filePath, NULL);
if (SUCCEEDED(hr))
{
// Run the graph.
hr = pControl->Run();
if (SUCCEEDED(hr))
{
// Wait for completion.
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);
// Note: Do not use INFINITE in a real application, because it
// can block indefinitely.
}
}
// Clean up in reverse order.
pEvent->Release();
pControl->Release();
pGraph->Release();
::CoUninitialize();
}
I can't find a way to have something like this, but to be able to play an .asx instead, like for example: http://listen.radiotunes.com/public5/solopiano.asx
In MSDN I can only find ways to do this in C# making a Forms application and inserting a WindowsMediaPlayer control in a form.
Any ideas?
An .asx file is actually a playlist. See here some information about the format.
.asx is not supported by DirectShow. See here for the supported formats.
You might parse the file, as it is XML, and find the actual URL of the stream, and then play it, or you could use the Windows Media Player SDK. You can see some sample code for WM SDK here.
OK, got it to work with this example taken from here and adding this extra line: hr = spPlayer->put_URL(L"http://listen.radiotunes.com/public5/solopiano.asx");:
#include "atlbase.h"
#include "atlwin.h"
#include "wmp.h"
#include <cstdio>
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
HRESULT hr = S_OK;
CComBSTR bstrVersionInfo; // Contains the version string.
CComPtr<IWMPPlayer> spPlayer; // Smart pointer to IWMPPlayer interface.
hr = spPlayer.CoCreateInstance(__uuidof(WindowsMediaPlayer), 0, CLSCTX_INPROC_SERVER);
if (SUCCEEDED(hr))
{
hr = spPlayer->get_versionInfo(&bstrVersionInfo);
hr = spPlayer->put_URL(L"http://listen.radiotunes.com/public5/solopiano.asx");
}
if (SUCCEEDED(hr))
{
// Show the version in a message box.
COLE2T pStr(bstrVersionInfo);
MessageBox(NULL, (LPCSTR)pStr, _T("Windows Media Player Version"), MB_OK);
}
// Clean up.
spPlayer.Release();
CoUninitialize();
return 0;
}

Task scheduler created in C++ could not be started

I created an scheduler in C++.I have set all the parameters and the task is configured to run only when the user is logged on with the user name provided(Done by setting the TASK_FLAG_RUN_ONLY_IF_LOGGED_ON flag).
When I try to run the task I get a status "Could not start". Now suppose I manually edit any property in task property and click on OK the task runs fine.
Note:The manual edit specified may be anything, like just adding a space at the end of the excecutable name or the user name. What may be the problem?
Below is the code i am using:
#include <windows.h>
#include <initguid.h>
#include <ole2.h>
#include <mstask.h>
#include <msterr.h>
#include <wchar.h>
#include<stdio.h>
#include<conio.h>
#pragma comment(lib, "Mstask.lib")
#pragma comment(lib, "ole32.lib")
int main(int argc, char **argv)
{
HRESULT hr = S_OK;
ITaskScheduler *pITS;
///////////////////////////////////////////////////////////////////
// Call CoInitialize to initialize the COM library and then
// CoCreateInstance to get the Task Scheduler object.
///////////////////////////////////////////////////////////////////
hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_CTaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskScheduler,
(void **) &pITS);
if (FAILED(hr))
{
CoUninitialize();
return 1;
}
}
else
{
return 1;
}
LPCWSTR pwszTaskName;
ITask *pITask;
pwszTaskName = L"TestTask";
hr = pITS->NewWorkItem(pwszTaskName,
CLSID_CTask,
IID_ITask,
(IUnknown**)&pITask);
if (FAILED(hr))
{
wprintf(L"Failed calling ITaskScheduler::NewWorkItem: ");
wprintf(L"error = 0x%x\n",hr);
CoUninitialize();
return 1;
}
LPCWSTR pwszApplicationName = L"C:\\windows\\notepad.exe";
hr = pITask->SetApplicationName(pwszApplicationName);
if (FAILED(hr))
{
wprintf(L"Failed calling ITask::SetApplicationName: ");
wprintf(L"error = 0x%x\n",hr);
pITS->Release();
pITask->Release();
CoUninitialize();
return 1;
}
pITask->SetAccountInformation(L"USERNAME", NULL);
pITask->SetFlags(TASK_FLAG_RUN_ONLY_IF_LOGGED_ON);
pITask->SetWorkingDirectory(L"C:\\windows");
ITaskTrigger *pITaskTrigger;
WORD piNewTrigger;
hr = pITask->CreateTrigger(&piNewTrigger,
&pITaskTrigger);
if (FAILED(hr))
{
wprintf(L"Failed calling ITask::CreatTrigger: ");
wprintf(L"error = 0x%x\n",hr);
pITask->Release();
CoUninitialize();
return 1;
}
pITS->AddWorkItem(pwszTaskName, pITask);
pITS->Release(); // Release sceduler
hr = pITask->Run();
if (FAILED(hr))
{
wprintf(L"Failed calling ITask::Run, error = 0x%x\n",hr);
pITask->Release();
CoUninitialize();
return 1;
}
pITask->Release();
CoUninitialize();
_getch();
return 0;
}
I think you need to test all return values, that could be revealing. I'm mostly suspicious about:
pITask->SetAccountInformation(L"USERNAME", NULL);
pITask->SetFlags(TASK_FLAG_RUN_ONLY_IF_LOGGED_ON);
Readinig http://msdn.microsoft.com/en-us/library/aa381276(VS.85).aspx I got the impression that you need to call SetFlags first and then SetAccountInformation.
I've had the same problem on XP: status "Could not start" and all ok after manual editing.
Solution:
Go to Advanced -> View Log. And see the reason for the fail.
In my case there was "The attempt to retrieve account information for the specified task failed". So I've just needed to get user name via GetUserName and set it via SetAccountInformation. Note order of SetAccountInformation and SetFlags doesn't matter.
Take a look here for other reasons.
May be it will help someone someday.