[objective]
Basic C++ console application needs to be able to send HTML emails through Outlook 2007 (which runs minimized) - attachments are not necessary. This method works with plain text and I'm not sure if it can be modified to fit the requirements of HTML. I read somewhere that perhaps you could reference an attachment and it would become the body. I'm just not sure what to do next. Suggestions?
[what I have so far]
BOOL SendMail(CHAR *lpszFrom, CHAR *lpszTo, CHAR *lpszSubject, CHAR *lpszMessage)
{
BOOL bSent = FALSE;
HINSTANCE hMAPI = ::LoadLibrary(_T("mapi32.dll"));
if(0==hMAPI) return bSent;
typedef ULONG (FAR PASCAL *PFN_MAPILogon)(ULONG,LPTSTR,LPTSTR,FLAGS,ULONG,LPLHANDLE);
typedef ULONG (FAR PASCAL *PFN_MAPISendMail)(LHANDLE,ULONG,lpMapiMessage,FLAGS,ULONG);
typedef ULONG (FAR PASCAL *PFN_MAPILogoff)(LHANDLE,ULONG,FLAGS,ULONG);
PFN_MAPILogon MAPILogon = (PFN_MAPILogon)::GetProcAddress(hMAPI,"MAPILogon");
PFN_MAPISendMail MAPISendMail = (PFN_MAPISendMail)::GetProcAddress(hMAPI,"MAPISendMail");
PFN_MAPILogoff MAPILogoff = (PFN_MAPILogoff)::GetProcAddress(hMAPI,"MAPILogoff");
const BOOL bFunctionsLoaded = (0!=MAPILogon)&&(0!=MAPISendMail)&&(0!=MAPILogoff);
ASSERT(bFunctionsLoaded);
if(bFunctionsLoaded)
{
LHANDLE session = 0;
VERIFY(SUCCESS_SUCCESS==MAPILogon(0,0,0,MAPI_NEW_SESSION,0,&session));
ASSERT(0!=session);
MapiRecipDesc recipient;
::ZeroMemory(&recipient,sizeof(recipient));
recipient.ulRecipClass = MAPI_TO;
recipient.lpszName = lpszTo;
MapiMessage message;
::ZeroMemory(&message,sizeof(message));
message.lpszSubject = lpszSubject;
message.lpszNoteText = lpszMessage;
message.nRecipCount = 1;
message.lpRecips = &recipient;
bSent = SUCCESS_SUCCESS == MAPISendMail(session,0,&message,0,0);
VERIFY(SUCCESS_SUCCESS==MAPILogoff(session,0,0,0));
}
::FreeLibrary(hMAPI);
return bSent;
}
Called by...
SendMail("from","to","subject","body");
It looks like MAPI isn't suited for HTML emails.
http://support.microsoft.com/kb/268440
I've sent HTML emails through MFC but that isn't MAPI.
I was sending an requiring the same thing - i found that attaching an html file would result in the html file being used in the body of the email.
the code below was found on this site ( somewhere ) and it works great.
code
bool SendMail(HWND hWndParent, std::string strAttachmentFileName, std::string strSubject,std::string& err)
{
// The attachment must exist as a file on the system
// or MAPISendMail will fail, so......
if (strAttachmentFileName.empty())
return false;
// You may want to remove this check, but if a valid
// HWND is passed in, the mail dialog will be made
// modal to it's parent.
//if (!hWndParent || !::IsWindow(hWndParent))
// return false;
HINSTANCE hMAPI = ::LoadLibraryA("MAPI32.DLL");
if (!hMAPI)
return false;
// Grab the exported entry point for the MAPISendMail function
ULONG (PASCAL *SendMail)(ULONG, ULONG_PTR,
MapiMessage*, FLAGS, ULONG);
(FARPROC&)SendMail = GetProcAddress(hMAPI,
"MAPISendMail");
if (!SendMail)
return false;
// TCHAR szFileName[_MAX_PATH];
// TCHAR szPath[_MAX_PATH];
// TCHAR szSubject[_MAX_PATH];
// ::strcpy(&szFileName[0], strAttachmentFileName.c_str());
// ::strcpy(&szPath[0], strAttachmentFileName.c_str());
// ::strcpy(&szSubject[0], strSubject.c_str());
MapiFileDesc fileDesc;
::ZeroMemory(&fileDesc, sizeof(fileDesc));
fileDesc.nPosition = (ULONG)-1;
fileDesc.lpszPathName = (char *)strAttachmentFileName.c_str();
fileDesc.lpszFileName = (char *)strAttachmentFileName.c_str();
MapiMessage message;
::ZeroMemory(&message, sizeof(message));
message.lpszSubject = (char *)strSubject.c_str();//&szSubject[0];//szSubject;
message.nFileCount = 1;
message.lpFiles = &fileDesc;
// Ok to send
int nError = SendMail(0, (ULONG_PTR)hWndParent,
&message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
if (nError != SUCCESS_SUCCESS &&
nError != MAPI_USER_ABORT &&
nError != MAPI_E_LOGIN_FAILURE)
err = "error";
return false;
err="ok";
return true;
}
Related
Basically, all I'm interested in is to grab the password field in plain text from the data returned, to validate it later on in code. Currently, I'm using CREDUIWIN_GENERIC, but I hate that the user can mess with the username field, so I guess filling it with something by default would be good. I tried CREDUIWIN_ENUMERATE_CURRENT_USER, but it has been returning the password in an unknown encrypted format.
I know that it has something to do with filling the pulAuthPackage, but I have no clue how to do it.
Here is my code:
BOOL save = false;
DWORD authPackage = 0;
LPVOID authBuffer;
ULONG authBufferSize = 0;
CREDUI_INFO credUiInfo;
static WCHAR username[CREDUI_MAX_USERNAME_LENGTH * sizeof(WCHAR)] = { 0 };
static WCHAR password[CREDUI_MAX_PASSWORD_LENGTH * sizeof(WCHAR)] = { 0 };
DWORD uLen = CREDUI_MAX_USERNAME_LENGTH;
DWORD pLen = CREDUI_MAX_PASSWORD_LENGTH;
credUiInfo.pszCaptionText = TEXT("Authentication");
credUiInfo.pszMessageText = TEXT("Please enter your Key in \"Password\".");
credUiInfo.cbSize = sizeof(credUiInfo);
credUiInfo.hbmBanner = NULL;
credUiInfo.hwndParent = NULL;
LPVOID inBuffer = NULL;
ULONG inBufferSize = 0;
HRESULT rc = CredUIPromptForWindowsCredentials(&(credUiInfo), 0, &(authPackage), inBuffer, inBufferSize, &authBuffer, &authBufferSize, &(save), CREDUIWIN_GENERIC);
if (rc == ERROR_SUCCESS)
{
CredUnPackAuthenticationBufferW(0, authBuffer, authBufferSize, username, &uLen, NULL, 0, password, &pLen);
wstring ws(password);
string res(ws.begin(), ws.end());
return res;
}
I'm making a Cloud Sync Engines Supports Placeholder based on CloudMirror. And got problem on CF_CALLBACK_TYPE_FETCH_DATA
When i double click file (placeholder) in window explorer, app trigger FILE_ATTRIBUTE_PINNED and Hydrating file. And then cfapi call FETCH_DATA and read asynchronous file (my app work look same with CloudMirror).
But i got HRESULT return from CfExecute is 0x8007017c the cloud operation is invalid. Debug look all value is true
Then how to resolve it, thank.
#define CHUNKSIZE 4096
#define FIELD_SIZE( type, field ) ( sizeof( ( (type*)0 )->field ) )
#define CF_SIZE_OF_OP_PARAM( field )( FIELD_OFFSET( CF_OPERATION_PARAMETERS, field ) + FIELD_SIZE( CF_OPERATION_PARAMETERS, field ) )
struct READ_COMPLETION_CONTEXT
{
OVERLAPPED Overlapped;
LARGE_INTEGER CallbackInfo_FileSize;
CF_CONNECTION_KEY CallbackInfo_ConnectionKey;
CF_TRANSFER_KEY CallbackInfo_TransferKey;
HANDLE PipeHandle{ 0 };
LARGE_INTEGER StartOffset;
LARGE_INTEGER RemainingLength;
ULONG BufferSize;
WCHAR* FullPath{ nullptr };
BYTE* Buffer{ nullptr };
~READ_COMPLETION_CONTEXT()
{
if (FullPath) delete FullPath;
if (Buffer) delete Buffer;
if (PipeHandle) CloseHandle(PipeHandle);
}
void Cancel()
{
TransferData(
CallbackInfo_ConnectionKey,
CallbackInfo_TransferKey,
NULL,
StartOffset,
RemainingLength,
STATUS_UNSUCCESSFUL);
}
};
void CALLBACK FETCH_DATA(_In_ CONST CF_CALLBACK_INFO* callbackInfo, _In_ CONST CF_CALLBACK_PARAMETERS* callbackParameters)
{
try
{
//...
if (DownloadItem(/*call to c++\cli for stream download and copy async to pipe server*/))
{
std::wstring pipename(L"\\\\.\\pipe\\");
pipename.append(ci->Id);
HANDLE hpipe = CreateFile(pipename.c_str(),
GENERIC_READ,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL); // no template file
if (hpipe != INVALID_HANDLE_VALUE)
{
if (GetLastError() != ERROR_PIPE_BUSY)
{
READ_COMPLETION_CONTEXT* readContext = new READ_COMPLETION_CONTEXT();
DWORD chunkBufferSize = (ULONG)min(callbackParameters->FetchData.RequiredLength.QuadPart, CHUNKSIZE);
std::wstring fullClientPath(callbackInfo->VolumeDosName);
fullClientPath.append(callbackInfo->NormalizedPath);
readContext->Overlapped.Offset = callbackParameters->FetchData.RequiredFileOffset.LowPart;
readContext->Overlapped.OffsetHigh = callbackParameters->FetchData.RequiredFileOffset.HighPart;
readContext->CallbackInfo_FileSize = callbackInfo->FileSize;
readContext->CallbackInfo_ConnectionKey = callbackInfo->ConnectionKey;
readContext->CallbackInfo_TransferKey = callbackInfo->TransferKey;
readContext->PipeHandle = hpipe;
readContext->StartOffset = callbackParameters->FetchData.RequiredFileOffset;
readContext->RemainingLength = callbackParameters->FetchData.RequiredLength;
readContext->BufferSize = chunkBufferSize;
readContext->FullPath = Utilities::WStringToWCHARP(fullClientPath);
readContext->Buffer = new BYTE[chunkBufferSize];
if (ReadFileEx(hpipe, readContext->Buffer, chunkBufferSize, &readContext->Overlapped, OverlappedCompletionRoutine))
if (GetLastError() == S_OK) return;
delete readContext;
}
else CloseHandle(hpipe);
}
}
}
catch (...)
{
}
TransferData(
callbackInfo->ConnectionKey,
callbackInfo->TransferKey,
NULL,
callbackParameters->FetchData.RequiredFileOffset,
callbackParameters->FetchData.RequiredLength,
STATUS_UNSUCCESSFUL);
}
void CALLBACK CANCEL_FETCH_DATA(_In_ CONST CF_CALLBACK_INFO* callbackInfo,_In_ CONST CF_CALLBACK_PARAMETERS* callbackParameters)
{
}
HRESULT TransferData(
_In_ CF_CONNECTION_KEY connectionKey,
_In_ LARGE_INTEGER transferKey,
_In_reads_bytes_opt_(length.QuadPart) LPCVOID transferData,
_In_ LARGE_INTEGER startingOffset,
_In_ LARGE_INTEGER length,
_In_ NTSTATUS completionStatus)
{
CF_OPERATION_INFO opInfo = { 0 };
CF_OPERATION_PARAMETERS opParams = { 0 };
opInfo.StructSize = sizeof(opInfo);
opInfo.Type = CF_OPERATION_TYPE_TRANSFER_DATA;
opInfo.ConnectionKey = connectionKey;
opInfo.TransferKey = transferKey;
opParams.ParamSize = CF_SIZE_OF_OP_PARAM(TransferData);
opParams.TransferData.CompletionStatus = completionStatus;
opParams.TransferData.Buffer = transferData;
opParams.TransferData.Offset = startingOffset;
opParams.TransferData.Length = length;
HRESULT hresult = CfExecute(&opInfo, &opParams);
return hresult;
}
void WINAPI OverlappedCompletionRoutine(
_In_ DWORD errorCode,
_In_ DWORD numberOfBytesTransfered,
_Inout_ LPOVERLAPPED overlapped)
{
READ_COMPLETION_CONTEXT* readContext = (READ_COMPLETION_CONTEXT*)overlapped;
if (errorCode == 0 && !GetOverlappedResult(readContext->PipeHandle, overlapped, &numberOfBytesTransfered, TRUE)) errorCode = GetLastError();
if (errorCode != 0)
{
readContext->Cancel();
delete readContext;
return;
}
assert(numberOfBytesTransfered != 0);
LONGLONG total = readContext->CallbackInfo_FileSize.QuadPart;
LONGLONG completed = readContext->StartOffset.QuadPart + numberOfBytesTransfered;
Utilities::ApplyTransferStateToFile(readContext->FullPath,
readContext->CallbackInfo_ConnectionKey,
readContext->CallbackInfo_TransferKey,
total,
completed);
HRESULT hresult = TransferData(
readContext->CallbackInfo_ConnectionKey,
readContext->CallbackInfo_TransferKey,
errorCode == 0 ? readContext->Buffer : NULL,
readContext->StartOffset,
Utilities::LongLongToLargeInteger(numberOfBytesTransfered),
errorCode);
if (hresult != S_OK)
{
readContext->Cancel();
delete readContext;
winrt::check_hresult(hresult);
return;
}
readContext->StartOffset.QuadPart += numberOfBytesTransfered;
readContext->RemainingLength.QuadPart -= numberOfBytesTransfered;
if (readContext->RemainingLength.QuadPart > 0)
{
DWORD bytesToRead = (DWORD)(min(readContext->RemainingLength.QuadPart, readContext->BufferSize));
readContext->Overlapped.Offset = readContext->StartOffset.LowPart;
readContext->Overlapped.OffsetHigh = readContext->StartOffset.HighPart;
if (!ReadFileEx(readContext->PipeHandle, readContext->Buffer, bytesToRead, &readContext->Overlapped, OverlappedCompletionRoutine))
{
readContext->Cancel();
delete readContext;
}
}
else delete readContext;//done
}
Edit: After test, fake byteread = 4096 it running successful.
Then, how much the min limit of data transfer?
My question in another forum
The chuck size seems required a multiple of 4096. Define this size to
(4096*N) will solve the 0x8007017c error.
This is suspected to the issue of offset/length alignment during transfer data operation. This is what the API spec says about these parameters.
OpParams.TransferData.Offset and OpParams.TransferData.Length describe a range in the placeholder to which the sync provider is transferring the data. There is no requirement that the sync provider return all data as requested in one shot. It is also OK for a sync provider to return more data than requested. As an example, the sync provider can decide to over-read, for performance or other reasons. The sync provider can also perform multiple TRANSFER_DATA operations repeatedly as a response to the same FETCH_DATA callback. The only requirement is that both offset and length are 4KB aligned unless the range described ends on the logical file size (EoF), in which case, the length is not required to be 4KB aligned as long as the resulting range ends on or beyond the logical file size.
I have C++ code to show dialog chooser file. I want to the user can only chooser type file specified. My dialog can show type file specified but the user can input other type file in File name like my picture
So, how can I make the user only input File name and search type file specified in lpstrFilter? OR Can i disable File name box?
This is my code:
const wchar_t* ChooserFile(const char* typeFile)
{
try
{
ZeroMemory( &sfn , sizeof( sfn));
sfn.lStructSize = sizeof ( sfn );
sfn.hwndOwner = NULL ;
wchar_t w_syFile[MAX_PATH];
//mbstowcs(w_syFile, syFile, strlen(syFile)+1);//Plus null
size_t convertedChars = 0;
mbstowcs_s(&convertedChars, w_syFile, MAX_PATH, syFile, _TRUNCATE);
sfn.lpstrFile = w_syFile ;
sfn.lpstrFile[0] = _T('\0');
sfn.nMaxFile = sizeof( syFile );
//TypeFile
sfn.lpstrFilter = TEXT("Microsoft Office Word Documents (*.xlsx)\0*.XLSX\0");
sfn.nFilterIndex =1;
sfn.lpstrFileTitle = NULL ;
sfn.nMaxFileTitle = 0 ;
sfn.lpstrInitialDir=NULL;
//sfn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT|OFN_EXPLORER | OFN_ENABLEHOOK ;
sfn.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST|OFN_NOVALIDATE|OFN_HIDEREADONLY ;
if (GetOpenFileName( &sfn ) != TRUE)
{
wstrPathFile = TEXT("");
return wstrPathFile.c_str();
}
DWORD retval=0;
//BOOL success;
TCHAR buffer[BUFSIZE]=TEXT("");
TCHAR buf[BUFSIZE]=TEXT("");
TCHAR** lppPart={NULL};
wchar_t wstrPath[BUFSIZE];
retval = GetFullPathNameW(sfn.lpstrFile,sfn.nMaxFile,wstrPath,lppPart);
if (retval==0)
{
wstrPathFile = TEXT("");
return wstrPathFile.c_str();
}
std::wstring s(wstrPath);
wstrPathFile = s;
wcout<<wstrPathFile<<endl;
return wstrPathFile.c_str();
}
catch (...)
{
PrintToFile("ChooserFile","Error");
wstrPathFile = TEXT("");
return wstrPathFile.c_str();
}
}
I want to the user can only chooser type file specified.
You can't stop the user choosing whatever file they like by typing into the file name edit control. So, you should simply let them do that and instead validate that the file name matches your requirements.
You have a couple of options to do that:
Let the dialog return and if the file name does not meet your requirements, present the user with an error dialog letting them know what went wrong.
Supply a hook procedure in the lpfnHook member of the OPENFILENAME struct. That will get sent a CDN_FILEOK notification message when the user attempts to accept a file. Perform your validation in response to that message. If the file name does not meet requirements, show a message to that effect and return a non-zero value to force the dialog to remain open.
Your code is commenting out the OFN_EXPLORER and OFN_ENABLEHOOK flags, so you must already know about the existence of Explorer-style hooking. As others have told you, you can use that hook to catch the CDN_FILEOK notification to accept/reject a selected filename. For example:
UINT_PTR CALLBACK MyOFNHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if (uiMsg == WM_NOTIFY)
{
LPOFNOTIFY ofn = (LPOFNOTIFY) lParam;
if (ofn->hdr.code == CDN_FILEOK)
{
LPOPENFILENAMEW lpOFN = (LPOPENFILENAMEW) ofn->lpOFN;
LPWSTR lpExt = PathFindExtensionW(lpOFN->lpstrFile);
if (lstrcmpiW(lpExt, L".XLSX") != 0)
{
SetWindowLongPtr(hdlg, DWL_MSGRESULT, 1);
return 1;
}
}
}
return 0;
}
std::wstring ChooserFile(const char* typeFile)
{
OPENFILEAMEW sfn = {0};
wchar_t w_syFile[MAX_PATH+1] = {0};
size_t convertedChars = 0;
sfn.lStructSize = sizeof(sfn);
sfn.hwndOwner = NULL;
mbstowcs_s(&convertedChars, w_syFile, MAX_PATH, syFile, _TRUNCATE);
sfn.lpstrFile = w_syFile;
sfn.nMaxFile = MAX_PATH;
//TypeFile
sfn.lpstrFilter = L"Microsoft Office Word Documents (*.xlsx)\0*.XLSX\0";
sfn.nFilterIndex = 1;
sfn.lpstrFileTitle = NULL;
sfn.nMaxFileTitle = 0;
sfn.lpstrInitialDir = NULL;
sfn.lpfnHook = &MyOFNHookProc;
sfn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOVALIDATE | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLEHOOK;
if (!GetOpenFileNameW(&sfn))
return L"";
WCHAR szPath[MAX_PATH+1] = {0};
DWORD retval = GetFullPathNameW(sfn.lpstrFile, MAX_PATH, szPath, NULL);
if ((retval == 0) || (retval > MAX_PATH))
return L"";
std::wstring wstrPath(szPath, retval);
std::wcout << wstrPath << endl;
return wstrPath;
}
I'm trying to figure out whether the computer is locked.
I've looked at LockWorkStation function, but the function that I'm hoping to find is IsWorkStationLocked.
I need to support all windows version >= XP
For windows 7 and abowe WTS API can be used:
bool IsSessionLocked() {
typedef BOOL (PASCAL * WTSQuerySessionInformation)(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR* ppBuffer, DWORD* pBytesReturned);
typedef void (PASCAL * WTSFreeMemory)( PVOID pMemory);
WTSINFOEXW * pInfo = NULL;
WTS_INFO_CLASS wtsic = DW_WTSSessionInfoEx;
bool bRet = false;
LPTSTR ppBuffer = NULL;
DWORD dwBytesReturned = 0;
LONG dwFlags = 0;
WTSQuerySessionInformation pWTSQuerySessionInformation = NULL;
WTSFreeMemory pWTSFreeMemory = NULL;
HMODULE hLib = LoadLibrary( _T("wtsapi32.dll") );
if (!hLib) {
return false;
}
pWTSQuerySessionInformation = (WTSQuerySessionInformation)GetProcAddress(hLib, "WTSQuerySessionInformationW" );
if (!pWTSQuerySessionInformation) {
goto EXIT;
}
pWTSFreeMemory = (WTSFreeMemory)GetProcAddress(hLib, "WTSFreeMemory" );
if (pWTSFreeMemory == NULL) {
goto EXIT;
}
if(pWTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, g_dwSessionID, wtsic, &ppBuffer, &dwBytesReturned)) {
if(dwBytesReturned > 0) {
pInfo = (WTSINFOEXW*)ppBuffer;
if (pInfo->Level == 1) {
dwFlags = pInfo->Data.WTSInfoExLevel1.SessionFlags;
}
if (dwFlags == WTS_SESSIONSTATE_LOCK) {
bRet = true;
}
}
pWTSFreeMemory(ppBuffer);
ppBuffer = NULL;
}
EXIT:
if (hLib != NULL) {
FreeLibrary(hLib);
}
return bRet;
}
Please check following article for supported platforms for WTSINFOEX structure:
https://technet.microsoft.com/ru-ru/sysinternals/ee621017
From the same MSDN link you gave, the third paragraph of "Remarks" says:
This function has the same result as pressing Ctrl+Alt+Del and clicking Lock Workstation. To unlock the workstation, the user must log in. There is no function you can call to determine whether the workstation is locked. To receive notification when the user logs in, use the WTSRegisterSessionNotification function to receive WM_WTSSESSION_CHANGE messages. You can use session notifications to track the desktop state so you know whether it is possible to interact with the user.
Alex Vershynin's version works well, with a few code changes that I had to make.
I had to: change DW_WTSSessionInfoEx to WTSSessionInfoEx (Answering user586399), define "g_dwSessionID" to WTSGetActiveConsoleSessionId(), include Wtsapi32.h. I think that's it ...
Since I can't comment, I'll paste the whole code here.
#include "Wtsapi32.h"
bool IsSessionLocked()
{
typedef BOOL( PASCAL * WTSQuerySessionInformation )( HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR* ppBuffer, DWORD* pBytesReturned );
typedef void ( PASCAL * WTSFreeMemory )( PVOID pMemory );
WTSINFOEXW * pInfo = NULL;
WTS_INFO_CLASS wtsic = WTSSessionInfoEx;
bool bRet = false;
LPTSTR ppBuffer = NULL;
DWORD dwBytesReturned = 0;
LONG dwFlags = 0;
WTSQuerySessionInformation pWTSQuerySessionInformation = NULL;
WTSFreeMemory pWTSFreeMemory = NULL;
HMODULE hLib = LoadLibrary( "wtsapi32.dll" );
if( !hLib )
{
return false;
}
pWTSQuerySessionInformation = (WTSQuerySessionInformation) GetProcAddress( hLib, "WTSQuerySessionInformationW" );
if( pWTSQuerySessionInformation )
{
pWTSFreeMemory = (WTSFreeMemory) GetProcAddress( hLib, "WTSFreeMemory" );
if( pWTSFreeMemory != NULL )
{
DWORD dwSessionID = WTSGetActiveConsoleSessionId();
if( pWTSQuerySessionInformation( WTS_CURRENT_SERVER_HANDLE, dwSessionID, wtsic, &ppBuffer, &dwBytesReturned ) )
{
if( dwBytesReturned > 0 )
{
pInfo = (WTSINFOEXW*) ppBuffer;
if( pInfo->Level == 1 )
{
dwFlags = pInfo->Data.WTSInfoExLevel1.SessionFlags;
}
if( dwFlags == WTS_SESSIONSTATE_LOCK )
{
bRet = true;
}
}
pWTSFreeMemory( ppBuffer );
ppBuffer = NULL;
}
}
}
if( hLib != NULL )
{
FreeLibrary( hLib );
}
return bRet;
}
Just as a refinement to MGamsby's post: if you are targeting the Win8.1 SDK or Win10 SDK, then it's not necessary to mess around with GetProcAddress(), LoadLibrary(), and WTSGetActiveConsoleSessionId(), and the code becomes much more compact:
bool isSessionLocked()
{
WTSINFOEXW* pInfo = NULL;
WTS_INFO_CLASS wtsic = WTSSessionInfoEx;
LPTSTR ppBuffer = NULL;
DWORD dwBytesReturned = 0;
LONG sessionFlags = WTS_SESSIONSTATE_UNKNOWN; // until we know otherwise. Prevents a false positive since WTS_SESSIONSTATE_LOCK == 0
DWORD dwSessionID = WTSGetActiveConsoleSessionId();
if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, dwSessionID, wtsic, &ppBuffer, &dwBytesReturned))
{
if (dwBytesReturned > 0)
{
pInfo = (WTSINFOEXW*)ppBuffer;
if (pInfo->Level == 1)
{
sessionFlags = pInfo->Data.WTSInfoExLevel1.SessionFlags;
}
}
WTSFreeMemory(ppBuffer);
ppBuffer = NULL;
}
return (sessionFlags == WTS_SESSIONSTATE_LOCK);
}
NOTE:
I realise that the question is about Windows 7 but you can still target Win7 using the Win8.1 SDK, and it is available for use in VS2015 onwards.
Edit: Oops. I just realised the OP asked for WinXP onwards in which case, yes, you do need to mess around with LoadLibrary() etc. Sorry about that.
I'll leave my code here in case it is useful for anyone.
Apparently there is a difference between screen lock and session lock. Here're my investigations results.
A session becomes locked when a password box is shown on the lock screen. In Windows 7 after pressing Win+L lockscreen appears with password box, so a session is locked immediately. But in Win10/11 lockscreen initially appears without password box. And nothing happens. After pressing a key or clicking a mouse button, password box appears and session becomes locked.
WTSRegisterSessionNotification(): WM_WTSSESSION_CHANGE message arrives after password box is shown.
WTSQuerySessionInformation(): sessionFlags == WTS_SESSIONSTATE_LOCK after password box is shown
SetWinEventHook() with EVENT_SYSTEM_DESKTOPSWITCH: multiple events arrives when (un)locking. Looks like two desktops are created when locking: for initial lock screen and the one with password box.
SwitchDesktop() succeeds until password box is shown.
Checking for running LogonUI.exe works, but it remains running for a while after unlock. Theoretically in the future versions it may remain running permanently. So one can't be absolutely sure the session is locked if LogonUI.exe is running.
So far I haven't found a reliable way to check if computer is locked and unlocked. But I'll try to continue my research.
I wanted to programatically show the below Windows Security dialog from C++. Wondering which Windows API is to be used to prompt this dialog to get the proxy credentials instead of building a custom dialog box.
I am able to show WindowsSecurity dialog to ask for Proxy credentials. Here is the code. Will have to fine tune it bit but it is working.
void getCredentials(const char * proxyIp, int proxyPort, char * proxType, QString &user, QString &password)
{
Log.info("Credentials", L"About to read credentials for [%hs] [%d] [%hs]",proxyIp,proxyPort,proxType);
HRESULT hr = S_OK;
DWORD dwResult;
PVOID pvInAuthBlob = NULL;
ULONG cbInAuthBlob = 0;
PVOID pvAuthBlob = NULL;
ULONG cbAuthBlob = 0;
CREDUI_INFOW ui;
ULONG ulAuthPackage = 0;
BOOL fSave = FALSE;
TCHAR pszName[CREDUI_MAX_USERNAME_LENGTH+1];
TCHAR pszPwd[CREDUI_MAX_PASSWORD_LENGTH+1];
TCHAR domain[CREDUI_MAX_DOMAIN_TARGET_LENGTH+1];
DWORD maxLenName = CREDUI_MAX_USERNAME_LENGTH+1;
DWORD maxLenPassword = CREDUI_MAX_PASSWORD_LENGTH+1;
DWORD maxLenDomain = CREDUI_MAX_DOMAIN_TARGET_LENGTH+1;
// Display a dialog box to request credentials.
ui.cbSize = sizeof(ui);
ui.hwndParent = GetConsoleWindow();
ui.pszMessageText = L"The Proxy Server requires user name and password";
ui.pszCaptionText = L"Proxy Authentication";
ui.hbmBanner = NULL;
dwResult = CredUIPromptForWindowsCredentialsW(
&ui, // Customizing information
0, // Error code to display
&ulAuthPackage, // Authorization package
pvInAuthBlob, // Credential byte array
cbInAuthBlob, // Size of credential input buffer
&pvAuthBlob, // Output credential byte array
&cbAuthBlob, // Size of credential byte array
&fSave, // Select the save check box.
CREDUIWIN_GENERIC
);
if (dwResult == NO_ERROR)
{
Log.info("Credentials", L"cred read success");
CredUnPackAuthenticationBufferW(0,
pvAuthBlob,
cbAuthBlob,
pszName,
&maxLenName,
domain,
&maxLenDomain,
pszPwd,
&maxLenPassword);
SecureZeroMemory(pvAuthBlob, cbAuthBlob);
CoTaskMemFree(pvAuthBlob);
pvAuthBlob = NULL;
Log.info("Credentials", L"u [%ls] p [%ls] d[%ls]",QString::fromWCharArray(pszName).toStdWString().c_str(),
QString::fromWCharArray(pszPwd).toStdWString().c_str(),
QString::fromWCharArray(domain).toStdWString().c_str());
user = QString::fromWCharArray(pszName).toStdWString();
password = QString::fromWCharArray(pszPwd).toStdWString();
SecureZeroMemory(pszName, sizeof(pszName));
SecureZeroMemory(pszPwd, sizeof(pszPwd));
}else
{
Log.info("Credentials", L"cred read fail");
hr = HRESULT_FROM_WIN32(dwResult);
if (pvInAuthBlob)
{
SecureZeroMemory(pvInAuthBlob, cbInAuthBlob);
CoTaskMemFree(pvInAuthBlob);
pvInAuthBlob = NULL;
}
}
}
You could try with CredUIPromptForCredentials ( small example of usage here ) or CredUIPromptForWindowsCredentials