I have shared memory communication successful between two processes on Windows. I write to it like this:
Don't mind my comments to myself. I thought I'd have some fun with the diagram.
How can I do this faster? In-other words, how can I do this using a Mutex or CreateEvent? I tried hard to understand Mutexes and CreateEvent but it confuses me on MSDN as it uses on application and a thread. An example would be helpful but isn't required.
The way I do current do it is (Very SLOW!):
//Create SharedMemory Using Filename + KnownProcessID so I can have multiple clients and servers with unique mappings. I already have this working and communication successful.
bool SharedMemoryBusy()
{
double* Data = static_cast<double*>(pData); //Pointer to the mapped memory.
return static_cast<int>(Data[1]) > 0 ? true : false;
}
void* RequestSharedMemory()
{
for (int Success = 0; Success < 50; ++Success)
{
if (SharedMemoryBusy())
Sleep(10);
else
break;
}
return pData;
}
bool SharedMemoryReturned()
{
double* Data = static_cast<double*>(pData);
return static_cast<int>(Data[1]) == 2 ? true : false;
}
bool SharedDataFetched()
{
for (int Success = 0; Success < 50; ++Success)
{
if (SharedMemoryReturned())
return true;
if (!SharedMemoryBusy())
return false;
Sleep(10);
}
return false;
}
The reading and writing part (Just an example of on request.. There can be many different types.. Models, Points, Locations, etc):
void* GLHGetModels()
{
double* Data = static_cast<double*>(RequestSharedMemory());
Data[0] = MODELS;
Data[1] = StatusSent;
if (SharedDataFetched())
{
if (static_cast<int>(Data[2]) != 0)
{
unsigned char* SerializedData = reinterpret_cast<unsigned char*>(&Data[3]);
MemDeSerialize(ListOfModels, SerializedData, static_cast<int>(Data[2]));
return Models.data();
}
return NULL;
}
return NULL;
}
void WriteModels()
{
If (pData[0] == MODELS)
{
//Write The Request..
Data[1] = StatusReturned;
Data[2] = ListOfModels.size();
unsigned char* Destination = reinterpret_cast<unsigned char*>(&Data[3]);
MemSerialize(Destination, ListOfModels); //Uses MEMCOPY To copy to Destination.
}
}
You can use events and mutexes between processes. This works fine. You only need to duplicate the handle of the object before passing it to other process:
BOOL WINAPI DuplicateHandle(
_In_ HANDLE hSourceProcessHandle,
_In_ HANDLE hSourceHandle,
_In_ HANDLE hTargetProcessHandle,
_Out_ LPHANDLE lpTargetHandle,
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwOptions
);
Each process works with its own handle while they both refer to the same kernel object.
What you need to do is to create 2 events. First party will signal that the data is ready. Other side shoud take the data, place its own data, reset first event and signal the second. After that the first party is doing the same.
Event functions are: CreateEvent, SetEvent, ... CloseHandle.
Related
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.
Using CM_Register_Notification function for receiving device change event.
returning #define CR_INVALID_DATA (0x0000001F)
I do not want to use RegisterDeviceNotification function for receiving device change event. So please do not suggest the above function.
int main()
{
CM_NOTIFY_FILTER cmNotifyFilter = { 0 };
cmNotifyFilter.cbSize = sizeof(cmNotifyFilter);
cmNotifyFilter.Flags = CM_NOTIFY_FILTER_FLAG_ALL_DEVICE_INSTANCES;
cmNotifyFilter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE;
cmNotifyFilter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_COMPORT;
HCMNOTIFICATION hcm;
char *test = new char[1024]();
CONFIGRET configRet = ::CM_Register_Notification(&cmNotifyFilter, (PVOID)test, (PCM_NOTIFY_CALLBACK)&MyCMInterfaceNotification, &hcm);
if (configRet != CR_SUCCESS) {
printf("CM_Register_Notification failed, error %d\n", (DWORD)configRet);
}
return 0;
}
DWORD MyCMInterfaceNotification( HCMNOTIFICATION hNotify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData,DWORD EventDataSize )
{
switch (Action) {
case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL:
wprintf(("MyCmInterfaceNotification: Arrival of %S\n",
EventData->u.DeviceInterface.SymbolicLink));
//
// Enqueue a work item to open target
//
break;
case CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL:
wprintf(("MyCmInterfaceNotification: removal of %S\n",
EventData->u.DeviceInterface.SymbolicLink));
break;
default:
printf(("MyCmInterfaceNotification: Arrival unknown action\n"));
break;
}
}
Thanks in advance
CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE filter type is used for notification of device removals. When the device represented by the interface receives a remove query request, the system notifies your component.
You should call CM_Register_Notification with DeviceHandle set (handle of already opened device via CreateFile call) if you want to use this filter type:
CM_NOTIFY_FILTER NotifyFilter = {0};
NotifyFilter.cbSize = sizeof(NotifyFilter);
NotifyFilter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE;
NotifyFilter.u.DeviceHandle.hTarget = Context->DeviceHandle;
But I guess you want to register for device interface arrival instead:
CM_NOTIFY_FILTER NotifyFilter = {0};
NotifyFilter.cbSize = sizeof(NotifyFilter);
NotifyFilter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
NotifyFilter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_COMPORT;
Here is example code for CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE callback:
DWORD
WINAPI
InterfaceCallback(
_In_ HCMNOTIFICATION hNotify,
_In_ PVOID hContext,
_In_ CM_NOTIFY_ACTION Action,
_In_ PCM_NOTIFY_EVENT_DATA EventData,
_In_ DWORD EventDataSize
)
{
switch (Action)
{
case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL:
//
// Register for device notifications
//
RegisterDeviceNotifications(EventData->u.DeviceInterface.SymbolicLink);
break;
case CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL:
...
break;
default:
break;
}
return ERROR_SUCCESS;
}
See docs from Microsoft for proper algorithm. And official example code.
Please understand my lack of writing skills.
I am testing to make a custom credential provider.
I want to create a CommandLink that does the same thing with the submit button.
I want to log on through the CommandLink separately from the Submit button.
Currently, only the custom credential provider is exposed through the providerFilter::Filter(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, DWORD dwFlags, GUID* rgclsidProviders, BOOL* rgbAllow, DWORD cProviders).
Click [anathor longon button] to log on.
This is my sample code:
HRESULT CSampleCredential::CommandLinkClicked(DWORD dwFieldID)
{
HRESULT hr = S_OK;
DWORD dwResult = 0;
if (dwFieldID < ARRAYSIZE(_rgCredProvFieldDescriptors) &&
(CPFT_COMMAND_LINK == _rgCredProvFieldDescriptors[dwFieldID].cpft))
{
HWND hwndOwner = nullptr;
switch (dwFieldID)
{
case SFI_ANATHOR_SUBMIT_LINK:
dwResult = function_foo();
if(dwResult == 1) {
Call GetSerialization()...?
Run the logon.
}
break;
// ...
}
}
}
Because you are writing credential provider, than you are already implementing ICredentialProvider interface and its Advise method:
virtual HRESULT STDMETHODCALLTYPE Advise(
/* [annotation][in] */
_In_ ICredentialProviderEvents *pcpe,
/* [annotation][in] */
_In_ UINT_PTR upAdviseContext) = 0;
The first argument is pointer to events interface ICredentialProviderEvents which have only one method: CredentialsChanged.
Your task is to grab credentials from user (login/password) store them inside your internals and call this method.
On the next turn your provider will be called this method:
virtual HRESULT STDMETHODCALLTYPE GetCredentialCount(
/* [annotation][out] */
_Out_ DWORD *pdwCount,
/* [annotation][out] */
_Out_ DWORD *pdwDefault,
/* [annotation][out] */
_Out_ BOOL *pbAutoLogonWithDefault) = 0;
Your task is to return the correct values in pdwDefault and pbAutoLogonWithDefault parameters (my suggest is 0 and TRUE).
Than your class that implementing ICredentialProviderCredential interface will be immediately called for GetSerialization method.
Here you can return already stored credentials.
Newbie here,
For the people who don't understand #Alexander 's explanation (like me several days ago), I recommend to read the Sample Harware Event Credential Provider. You need the 3rd class that is not inherited from ICredentialProvider or ICredentialProviderCredential. This is an independent class which is not related to both of them. I use this class to call the CredentialsChanged function.
So, TL;DR this is how I implement it:
Make a condition inside the ICredentialProvider's GetCredentialCount function like this
HRESULT CSampleCredentialProvider::GetCredentialCount(
DWORD* pdwCount,
DWORD* pdwDefault,
BOOL* pbAutoLogonWithDefault
)
{
//SavedCredentialInfo is just a simple Bool variable
if (_pCredential->SavedCredentialInfo == true)
{
*pdwCount = 1;
*pdwDefault = 0;
*pbAutoLogonWithDefault = TRUE;
}else{
*pdwCount = 1;
*pdwDefault = 0;
*pbAutoLogonWithDefault = FALSE;
}
return S_OK;
}
Save the credential (I use username & password) as a variable (string) inside my custom class that inherit ICredentialProviderCredential
HRESULT CGuardianCredential::CommandLinkClicked(DWORD dwFieldID)
{
if (dwFieldID < ARRAYSIZE(_rgCredProvFieldDescriptors) &&
(CPFT_COMMAND_LINK == _rgCredProvFieldDescriptors[dwFieldID].cpft)) {
this._username = "Ramon";
this._domain = "DESKTOP-937SDJ";
this._password = "MyPassword";
}
. . .
}
Call the 3rd class's function that call the ICredentialProvider's CredentialsChanged function
HRESULT CGuardianCredential::CommandLinkClicked(DWORD dwFieldID)
{
. . .
_commandWindow->callOnChange();
}
Then inside the commandWindow's class
BOOL CCommandWindow::callOnChange()
{
_pProvider->OnConnectStatusChanged();
return true;
}
Then you have it
I've started learing an asynchronous aproach, and
encountered a problem, help me with it.
The purpose is: get from somewhere a char data, and after that do something with it(using as text on the button, in my case). The code, that is pinned below is very slow. The most slowiest moment is a data getting: the fact is that the get(int id) function loads data from internet via WinInet(synchronously), sending the Post methods, and returning the answer.
void some_func()
{
for(int i(0);i<10;i++)
for(int q(0);q<5;q++)
{
char data[100];
strcpy(data, get(i,q)); // i, q - just some identifier data
button[5*i+(q+1)]=new Button(data);
}
}
The first question:
How should it be solved(generaly, I mean, if get has nothing to do with the internet, but runs slow)? I have only one, stupid idea: run get in every separate thread. If it's the right way - how should I do that? Cause, it's wrong to, created 50 threads call from each the get function. 50 get functions?
Second Question
How to realize it with WinInet? Have red MSDN, but it too hardly for me, as for newer, maybe you explain it more simlier?
Thanks
for asynchronous programming you need create some object which will be maintain state - in current case i and q must be not local variables of function but members of object, mandatory reference count to object. and usual file(socket) handle , etc.
function some_func() must have another pattern. it must be member function of object. and it must not call asynchronous get in loop. after call get it must just exit. when asynchronous operation, initiated by get, will be finished - some your callback must be called (if failed initiate asynchronous operation you need yourself just call this callback with error code). in callback you will be have pointer to your object and using it - call some_func(). so some_func() must at begin handle result of previous get call - check for error, handle received data, if no error. than adjust object state (in your case i and q) and if need - call get again. and for initiated all this - need first time call get direct:
begin -> get() -> .. callback .. -> some_func() -> exit
^ ┬
└─────────────────────────────┘
some demo example (with asynchronous read file)
struct SOME_OBJECT
{
LARGE_INTEGER _ByteOffset;
HANDLE _hFile;
LONG _dwRef;
int _i, _q;
SOME_OBJECT()
{
_i = 0, _q = 0;
_dwRef = 1;
_ByteOffset.QuadPart = 0;
_hFile = 0;
}
void beginGet();
void DoSomething(PVOID pvData, DWORD_PTR cbData)
{
DbgPrint("DoSomething<%u,%u>(%x, %p)\n", _i, _q, cbData, pvData);
}
// some_func
void OnComplete(DWORD dwErrorCode, PVOID pvData, DWORD_PTR cbData)
{
if (dwErrorCode == NOERROR)
{
DoSomething(pvData, cbData);
if (++_q == 5)
{
_q = 0;
if (++_i == 10)
{
return ;
}
}
_ByteOffset.QuadPart += cbData;
beginGet();
}
else
{
DbgPrint("OnComplete - error=%u\n", dwErrorCode);
}
}
~SOME_OBJECT()
{
if (_hFile) CloseHandle(_hFile);
}
void AddRef() { InterlockedIncrement(&_dwRef); }
void Release() { if (!InterlockedDecrement(&_dwRef)) delete this; }
ULONG Create(PCWSTR FileName);
};
struct OPERATION_CTX : OVERLAPPED
{
SOME_OBJECT* _pObj;
BYTE _buf[];
OPERATION_CTX(SOME_OBJECT* pObj) : _pObj(pObj)
{
pObj->AddRef();
hEvent = 0;
}
~OPERATION_CTX()
{
_pObj->Release();
}
VOID CALLBACK CompletionRoutine(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
{
_pObj->OnComplete(dwErrorCode, _buf, dwNumberOfBytesTransfered);
delete this;
}
static VOID CALLBACK _CompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, OVERLAPPED* lpOverlapped)
{
static_cast<OPERATION_CTX*>(lpOverlapped)->CompletionRoutine(RtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
}
void CheckResult(BOOL fOk)
{
if (!fOk)
{
ULONG dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_IO_PENDING)
{
CompletionRoutine(dwErrorCode, 0);
}
}
}
void* operator new(size_t cb, size_t ex)
{
return ::operator new(cb + ex);
}
void operator delete(PVOID pv)
{
::operator delete(pv);
}
};
ULONG SOME_OBJECT::Create(PCWSTR FileName)
{
HANDLE hFile = CreateFile(FileName, FILE_READ_DATA, FILE_SHARE_READ, 0,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
_hFile = hFile;
if (BindIoCompletionCallback(hFile, OPERATION_CTX::_CompletionRoutine, 0))
{
return NOERROR;
}
}
return GetLastError();
}
void SOME_OBJECT::beginGet()
{
const ULONG cbRead = 0x1000;
if (OPERATION_CTX* ctx = new(cbRead) OPERATION_CTX(this))
{
ctx->Offset = _ByteOffset.LowPart;
ctx->OffsetHigh = _ByteOffset.HighPart;
ctx->CheckResult(ReadFile(_hFile, ctx->_buf, cbRead, 0, ctx));
}
}
void ADemo(PCWSTR FileName)
{
if (SOME_OBJECT* pObj = new SOME_OBJECT)
{
if (!pObj->Create(FileName))
{
pObj->beginGet();
}
pObj->Release();
}
}
I'm working on an application that creates a new desktop when launched and using a key combo I can move back and forth between the original and the new desktop. At creation time, in the new desktop a new explorer.exe process is started, so the user can start whatever applications he desires.
When the key combo that sends the exit command is detected, the new desktop is closed, and we return to the original one, but all the applications that the user started in the new desktop are still running.
Is there a way to get a handle on all of this processes opened in the new desktop, having a HANDLE for the Window Station and a HDESK handle for the new Desktop?
Thanks to David Heffernan's idea, I was able to find the following solution. Having a HDESK handle for the desktop, I compare it using GetThreadDesktop function with every thread from the system. I'm not sure that it's the most performant solution, I'm open towards suggestions for improvements, but this works just fine:
int main(void)
{
// Desktop handles
HDESK currentDesktop = GetsecondDesktop(GetCurrentThreadId());
HDESK secondDesktop = CreateDesktop(L"secondDesktop", NULL, NULL, 0, GENERIC_ALL, NULL);
// Start processes in secondDesktop ...
// Process enumeration
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;
EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded);
cProcesses = cbNeeded / sizeof(DWORD);
for (i = 0; i < cProcesses; i++)
{
if (aProcesses[i] != 0)
{
DWORD pThreadId = ListProcessThreads(aProcesses[i]);
if (GetsecondDesktop(pThreadId) == secondDesktop)
{
TerminateProcess(aProcesses[i]);
}
}
}
return 0;
}
DWORD ListProcessThreads(DWORD dwOwnerPID)
{
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
// Take a snapshot of all running threads
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE)
return(FALSE);
// Fill in the size of the structure before using it.
te32.dwSize = sizeof(THREADENTRY32);
// Retrieve information about the first thread,
// and exit if unsuccessful
if (!Thread32First(hThreadSnap, &te32))
{
CloseHandle(hThreadSnap); // Must clean up the snapshot object!
return(FALSE);
}
// Now walk the thread list of the system,
// and display information about each thread
// associated with the specified process
do
{
if (te32.th32OwnerProcessID == dwOwnerPID)
{
return te32.th32ThreadID;
}
} while (Thread32Next(hThreadSnap, &te32));
// Don't forget to clean up the snapshot object.
CloseHandle(hThreadSnap);
return 0;
}
BOOL TerminateProcess(DWORD dwProcessId)
{
DWORD dwDesiredAccess = PROCESS_TERMINATE;
BOOL bInheritHandle = FALSE;
HANDLE hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
if (hProcess == NULL)
return FALSE;
UINT uExitCode = 0;
BOOL result = TerminateProcess(hProcess, uExitCode);
CloseHandle(hProcess);
return result;
}