I'm developing a sample code to get information about the Windows Updates Monitoring.
I bumped into Windows Update Agent APIs. link: http://msdn.microsoft.com/en-us/library/windows/desktop/aa387099(v=vs.85).aspx
But I'm not able to find any APIs for win32. I find only C#/.NET Interfaces.
Are there any corresponding win32 APIs?
Specifically I want to find out the "release date" of a windows update/patch.
Look forward to any suggestions and guidance.
Srivathsa
The WUA API includes a set of COM interfaces which can be used from C++ Apps, so try these IUpdateSearcher, IUpdateSession and IUpdate.
Check this sample c++ application which retrieve the updates and the date of release.
#include "stdafx.h"
#include <wuapi.h>
#include <iostream>
#include <ATLComTime.h>
#include <wuerror.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr;
hr = CoInitialize(NULL);
IUpdateSession* iUpdate;
IUpdateSearcher* searcher;
ISearchResult* results;
BSTR criteria = SysAllocString(L"IsInstalled=1 or IsHidden=1 or IsPresent=1");
hr = CoCreateInstance(CLSID_UpdateSession, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (LPVOID*)&iUpdate);
hr = iUpdate->CreateUpdateSearcher(&searcher);
wcout << L"Searching for updates ..."<<endl;
hr = searcher->Search(criteria, &results);
SysFreeString(criteria);
switch(hr)
{
case S_OK:
wcout<<L"List of applicable items on the machine:"<<endl;
break;
case WU_E_LEGACYSERVER:
wcout<<L"No server selection enabled"<<endl;
return 0;
case WU_E_INVALID_CRITERIA:
wcout<<L"Invalid search criteria"<<endl;
return 0;
}
IUpdateCollection *updateList;
IUpdate *updateItem;
LONG updateSize;
BSTR updateName;
DATE retdate;
results->get_Updates(&updateList);
updateList->get_Count(&updateSize);
if (updateSize == 0)
{
wcout << L"No updates found"<<endl;
}
for (LONG i = 0; i < updateSize; i++)
{
updateList->get_Item(i,&updateItem);
updateItem->get_Title(&updateName);
updateItem->get_LastDeploymentChangeTime(&retdate);
COleDateTime odt;
odt.m_dt=retdate;
wcout<<i+1<<" - "<<updateName<<" Release Date "<< (LPCTSTR)odt.Format(_T("%A, %B %d, %Y"))<<endl;
}
::CoUninitialize();
wcin.get();
return 0;
}
Related
I am working on active directory and was reading this https://learn.microsoft.com/en-us/windows/win32/adsi/setting-up-c---for-adsi-development and just used the mentioned code to connect to it but its not connecting to the server and says proccess exited with status code zero
i used the below code.
#include "stdafx.h"
#include "activeds.h"
int main(int argc, char* argv[])
{
HRESULT hr;
IADsContainer *pCont;
IDispatch *pDisp=NULL;
IADs *pUser;
// Initialize COM before calling any ADSI functions or interfaces.
CoInitialize(NULL);
hr = ADsGetObject( L"LDAP://CN=users,DC=fabrikam,DC=com",
IID_IADsContainer,
(void**) &pCont );
if ( !SUCCEEDED(hr) )
{
return 0;
}
}
what am i doing wrong i did exactly as the documentation told
ADsGetObject is for non authenticated connection that is the code should be performed inside the server. if you are trying to connect to a server hosted on seperate machine you should be using ADsOpenObject you may have the reference in the following link
https://learn.microsoft.com/en-us/windows/win32/api/adshlp/nf-adshlp-adsopenobject
ADsOpenObject binds using explicit username and password you can try the following
int login(LPCWSTR uname, LPCWSTR pass)
{
HRESULT hr;
IADsContainer* pCont;
IDispatch* pDisp = NULL;
IADs* pUser;
// Initialize COM before calling any ADSI functions or interfaces.
CoInitialize(NULL);
hr = ADsOpenObject(L"LDAP://machinename.domaincontroller.domain/CN=Users,DC=domaincontrollername,DC=domainname", uname, pass,
ADS_SECURE_AUTHENTICATION, // For secure authentication
IID_IADsContainer,
(void**)&pCont);
std::cout << hr << std::endl;
std::string message = std::system_category().message(hr);
std::cout << message << std::endl;
if (!SUCCEEDED(hr)) {
return 0;
}
else {
return 1;
}
}
instead of the variable you can type your username and password like L"usernameexample"
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();
}
I have an installer that tries to (re)start my application in the current user context after the installation is done.
The installer runs in the SYSTEM context and before launching the application it attempts (and theoretically succeeds) to impersonate the current user. However, when I look in the task manager, I see that my application is running in the SYSTEM context.
This is (a snippet from) my code:
TCHAR szUsername[128] = _T("");
DWORD dwUsernameSize = 128;
GetUserName(szUsername, &dwUsernameSize);
// Lets the calling process impersonate the security context of a logged-on user.
if (!ImpersonateLoggedOnUser(hToken))
{
throw Win32Exception(GetLastError(), _T("Failed to impersonate current user"));
}
TCHAR szUsername2[128] = _T("");
DWORD dwUsernameSize2 = 128;
GetUserName(szUsername2, &dwUsernameSize2);
MLOGD(_T("ProcessUtils::StartProcessInCurrentUserContext: Successfully impersonated %s"), szUsername2);
ProcessUtils::StartProcess(sExeName, lstParams, sWorkingDir, bWaitToFinish, errCode);
ProcessUtils::StartProcess is a wrapper around CreateProcess.
szUsername contains SYSTEM and szUsername2 contains the current user. So ImpersonateLoggedOnUser is successful.
However, as mentioned above, the process is started in the SYSTEM context, not the current user one.
I'm not sure how helpful this might be, but my installer is written in NSIS and it's calling the function that contains the code from above via a plugin written in C/C++.
Does anyone know why my application doesn't start in the current user context?
Win32 CreateProcess creates a process in the same security context as the caller which is SYSTEM (even though you are impersonating).
Think you need to be calling CreateProcessAsUser.
I had a very similar problem a couple of years ago when I was also
working on an installer application. After A LOT of frustration, caused
by failed attempts to start an application in the context of the current
user using CreateProcessAsUser, I've finally given up. After a thorough
search on the web, I've found a briliant implementation that uses
IShellDispatch2 interface. Here is an example:
#include <Windows.h>
#include <exdisp.h>
#include <Shobjidl.h>
#include <Shlwapi.h>
#include <comutil.h>
#include <SHLGUID.h>
#include <cstdlib>
#include <iostream>
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "comsuppw.lib")
bool ShellExecuteAsCurrentUser(const TCHAR *pcOperation, const TCHAR *pcFileName, const TCHAR *pcParameters,
const TCHAR *pcsDirectory, const DWORD dwShow)
{
bool bSuccess = false;
IShellWindows *psw = NULL;
HRESULT hr = CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw));
if(SUCCEEDED(hr))
{
HWND hwnd = 0;
IDispatch* pdisp = NULL;
_variant_t vEmpty;
if(S_OK == psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, reinterpret_cast<long*>(&hwnd), SWFO_NEEDDISPATCH, &pdisp))
{
if((hwnd != NULL) && (hwnd != INVALID_HANDLE_VALUE))
{
IShellBrowser *psb;
hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb));
if(SUCCEEDED(hr))
{
IShellView *psv = NULL;
hr = psb->QueryActiveShellView(&psv);
if(SUCCEEDED(hr))
{
IDispatch *pdispBackground = NULL;
HRESULT hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground));
if(SUCCEEDED(hr))
{
IShellFolderViewDual *psfvd = NULL;
hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd));
if(SUCCEEDED(hr))
{
IDispatch *pdisp = NULL;
hr = psfvd->get_Application(&pdisp);
if(SUCCEEDED(hr))
{
IShellDispatch2 *psd;
hr = pdisp->QueryInterface(IID_PPV_ARGS(&psd));
if(SUCCEEDED(hr))
{
_variant_t verb(pcOperation);
_variant_t file(pcFileName);
_variant_t para(pcParameters);
_variant_t dir(pcsDirectory);
_variant_t show(dwShow);
if(SUCCEEDED(psd->ShellExecute(file.bstrVal, para, vEmpty, verb, show)))
bSuccess = true;
psd->Release();
psd = NULL;
}
pdisp->Release();
pdisp = NULL;
}
}
pdispBackground->Release();
pdispBackground = NULL;
}
psv->Release();
psv = NULL;
}
psb->Release();
psb = NULL;
}
}
pdisp->Release();
pdisp = NULL;
}
psw->Release();
psw = NULL;
}
return bSuccess;
}
int main(int argc, char *argv[])
{
CoInitialize(NULL);
if(ShellExecuteAsCurrentUser(L"open", L"notepad", nullptr, nullptr, SW_SHOWNORMAL))
std::cout << "SUCCESS" << std::endl;
CoUninitialize();
return 0;
}
This is just a quick demo, the implementation of ShellExecuteAsCurrentUser can be
improved by using smart pointers for COM interfaces and some refactoring. This method
worked for me on versions WinXP SP3 - Win 8.1, not sure if it works on Windows 10. For
more details, check the authors github page:
https://github.com/lordmulder/stdutils/tree/master/Contrib/StdUtils
If you had read the documentation for CreateProcess, you would have found the answer to your question in the first three sentences:
Creates a new process and its primary thread. The new process runs in the security context of the calling process.
If the calling process is impersonating another user, the new process uses the token for the calling process, not the impersonation token.
There really isn't much else to say; the behaviour you describe is as documented. If you want to create a process as another user, you must use CreateProcessAsUser or one of the related functions.
I'm trying to run a SAPI sample from a Microsoft sample page.
When I run the application (with VS2010), this line fails:
hr = cpVoice.CoCreateInstance( CLSID_SpVoice );
hr return an error code and all other code is not executed.
I don't know why I'm wrong,, because I think to use correctly the sample code in that page and I've never use this API before.
This is my complete main.cpp file. What I'm missing?
#include "stdafx.h"
#include <sapi.h>
#include <sphelper.h>
#include <atlcomcli.h>
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = S_OK;
CComPtr <ISpVoice> cpVoice;
CComPtr <ISpStream> cpStream;
CSpStreamFormat cAudioFmt;
//Create a SAPI Voice
hr = cpVoice.CoCreateInstance( CLSID_SpVoice );
//Set the audio format
if(SUCCEEDED(hr))
{
hr = cAudioFmt.AssignFormat(SPSF_22kHz16BitMono);
}
//Call SPBindToFile, a SAPI helper method, to bind the audio stream to the file
if(SUCCEEDED(hr))
{
hr = SPBindToFile( L"c:\\ttstemp.wav", SPFM_CREATE_ALWAYS,
&cpStream, & cAudioFmt.FormatId(),cAudioFmt.WaveFormatExPtr() );
}
//set the output to cpStream so that the output audio data will be stored in cpStream
if(SUCCEEDED(hr))
{
hr = cpVoice->SetOutput( cpStream, TRUE );
}
//Speak the text "hello world" synchronously
if(SUCCEEDED(hr))
{
hr = cpVoice->Speak( L"Hello World", SPF_DEFAULT, NULL );
}
//close the stream
if(SUCCEEDED(hr))
{
hr = cpStream->Close();
}
//Release the stream and voice object
cpStream.Release ();
cpVoice.Release();
return 0;
}
You have to initialize the thread using CoInitialize[Ex] prior to using CoCreateInstance API. The error code you are getting should explicitly suggest that: CO_E_NOTINITIALIZED (you should have posted it on your question!).
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.