WINAPI Network Discovery without SMBv1 - c++

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();
}

Related

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;
}

TaskScheduler RegisterTaskDefinition fails with NULL path in Win10

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.

How to create shortcut for virtual folder in C++ on windows 7?

The platform I am using is windows 7. I need to create shortcut for the virtual folder on windows 7.
I use windows 7 SDK sample to create a virtual folder under Computer:
The sample project name is called ExplorerDataProvider, it defines the CLSID for the IShellFolder class:
// add classes supported by this module here
const CLASS_OBJECT_INIT c_rgClassObjectInit[] =
{
{ &CLSID_FolderViewImpl, CFolderViewImplFolder_CreateInstance },
{ &CLSID_FolderViewImplContextMenu,CFolderViewImplContextMenu_CreateInstance },
};
The definition for CFolderViewImplFolder_CreateInstance is:
HRESULT CFolderViewImplFolder_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CFolderViewImplFolder* pFolderViewImplShellFolder = new (std::nothrow) CFolderViewImplFolder(0);
HRESULT hr = pFolderViewImplShellFolder ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pFolderViewImplShellFolder->QueryInterface(riid, ppv);
pFolderViewImplShellFolder->Release();
}
return hr;
}
And the CFolderViewImplFolder implement IShellFolder2 amd IPersistFolder2.
I found a similar code which is used to create shortcut for printer here:
https://www.codeproject.com/Articles/596642/Creating-a-shortcut-programmatically-in-Cplusplus
and in
https://msdn.microsoft.com/en-us/library/aa969393.aspx#Shellink_Item_Identifiers
Once you have the class identifier for IShellFolder, you can call the CoCreateInstance function to retrieve the address of the interface. Then you can call the interface to enumerate the objects in the folder and retrieve the address of the item identifier for the object that you are searching for. Finally, you can use the address in a call to the IShellLink::SetIDList member function to create a shortcut to the object.
I revised
hr = SHGetMalloc(&pMalloc);
hr = SHGetDesktopFolder( &pDesktopFolder );
hr = SHGetSpecialFolderLocation( NULL, CSIDL_PRINTERS, &netItemIdLst );
hr = pDesktopFolder->BindToObject( netItemIdLst, NULL, IID_IShellFolder, (void **)&pPrinterFolder );
to
// testFolder is the CLSID for the virtual folder implementation
hr = CoCreateInstance(testFolder, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (LPVOID*)&pVirtualFolder);
or
hr = CoCreateInstance(testFolder, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder2, (LPVOID*)&pVirtualFolder);
But the pVirtualFolder is still NULL, and it prints that "cannot find the corresponding interface".
Is there anything wrong with CoCreateInstance when I use it ? Or I shouldn't use this solution ? Any sample code for it ?
To create a shortcut, you can use the official documentation. Here is a sample code that creates a shortcut for a children of "This PC" (aka: ComputerFolder)
int main()
{
CoInitialize(NULL);
// I've used my Apple's iCloud as an example (name is in french)
// it's a virtual folder, a shell namespace extension
HRESULT hr = CreateComputerChildShortCut(L"Photos iCloud", L"c:\\temp\\my icloud");
printf("hr:0x%08X\n", hr);
CoUninitialize();
return 0;
}
HRESULT CreateComputerChildShortCut(LPWSTR childDisplayName, LPWSTR path)
{
// get My Computer folder's ShellItem (there are other ways for this...)
CComPtr<IShellItem> folder;
HRESULT hr = SHCreateItemInKnownFolder(FOLDERID_ComputerFolder, 0, NULL, IID_PPV_ARGS(&folder));
if (FAILED(hr)) return hr;
// enumerate children
CComPtr<IEnumShellItems> items;
hr = folder->BindToHandler(NULL, BHID_EnumItems, IID_PPV_ARGS(&items));
if (FAILED(hr)) return hr;
for (CComPtr<IShellItem> item; items->Next(1, &item, NULL) == S_OK; item.Release())
{
// get parsing path (if's often useful)
CComHeapPtr<wchar_t> parsingPath;
item->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &parsingPath);
wprintf(L"Path: %s\n", parsingPath);
// get display name
CComHeapPtr<wchar_t> displayName;
item->GetDisplayName(SIGDN_NORMALDISPLAY, &displayName);
wprintf(L" Name: %s\n", displayName);
if (!lstrcmpi(childDisplayName, displayName))
{
// get pidl
// it's the unambiguous way of referencing a shell thing
CComHeapPtr<ITEMIDLIST> pidl;
hr = SHGetIDListFromObject(item, &pidl);
if (FAILED(hr)) return hr;
// create an instance of the standard Shell's folder shortcut creator
CComPtr<IShellLink> link;
hr = link.CoCreateInstance(CLSID_FolderShortcut);
if (FAILED(hr)) return hr;
// just use the pidl
hr = link->SetIDList(pidl);
if (FAILED(hr)) return hr;
CComPtr<IPersistFile> file;
hr = link->QueryInterface(&file);
if (FAILED(hr)) return hr;
// save the shortcut (we could also change other IShellLink parameters)
hr = file->Save(path, FALSE);
if (FAILED(hr)) return hr;
break;
}
}
return S_OK;
}
Of course, if you know an absolute parsing path or an absolute pidl, you don't have to enumerate anything, this was just for demonstration purposes.

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.