После некоторых исследований и испытаний я нашел решение, приведенное ниже. Ключевым моментом здесь является использование boost::move
(или, возможно, std :: move, не знаю, будет ли здесь разница). Поскольку мы используем boost::move
, не будет копий объектов IPCString
и, следовательно, не нужно умножать на 2
. Помните, из-за того, как мы определили IPCString
, он будет размещен внутри сегмента разделяемой памяти.
Необходимы дополнительные издержки (возможно, из-за выравнивания или других добавленных накладных расходов, или обоих), но всегда остается некоторое пространство. Поскольку я мог посылать несколько мегабайт, служебные данные 500
байтов довольно малы.
Я использую метод std::string
size()
, потому что он возвращает размер строки в байтах .
Код выглядит следующим образом:
shared_memory.h:
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
namespace apl_shared_memory
{
typedef boost::interprocess::allocator CharAllocator;
typedef boost::interprocess::basic_string, CharAllocator> IPCString;
typedef boost::interprocess::allocator StringAllocator;
typedef boost::interprocess::vector ShmVector;
bool write_to_memory(std::string wsuid, std::string loop_val, std::string should_intercept, std::string post_data) ;
const std::string shm_prefix = "shm_";
const std::string mutex_prefix = "mtx_";
}
shared_memory.cpp:
#include "shared_memory.h"
#include "logger.h"
namespace apl_shared_memory
{
bool write_to_memory(std::string wsuid, std::string loop_val, std::string should_intercept, std::string post_data)
{
bool ret_val;
std::string shm_name = shm_prefix + wsuid;
std::string mtx_name = mutex_prefix + wsuid;
boost::interprocess::named_mutex named_mtx{ boost::interprocess::open_or_create, mtx_name.c_str() };
// Add the size of all the structures we're putting in the shared memory region and add some for the overhead.
size_t size = (3*sizeof(IPCString) + loop_val.size() + should_intercept.size() + post_data.size() + sizeof(ShmVector)) + 500;
try
{
named_mtx.lock();
boost::interprocess::shared_memory_object::remove(shm_name.c_str());
boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only, shm_name.c_str(), size);
CharAllocator charallocator(segment.get_segment_manager());
StringAllocator stringallocator(segment.get_segment_manager());
IPCString shm_loop_val(charallocator);
IPCString shm_should_intercept(charallocator);
IPCString shm_intercepted_data(charallocator);
shm_loop_val = loop_val.c_str();
shm_should_intercept = should_intercept.c_str();
shm_intercepted_data = post_data.c_str();
segment.destroy("ShmVector");
ShmVector *shmVector = segment.construct("ShmVector")(stringallocator);
shmVector->clear();
shmVector->reserve(3);
// push_back will call a copy-constructor. But, if we send a rvalue reference (i.e. if we move it), there will be no copying.
shmVector->push_back(boost::move(shm_loop_val));
shmVector->push_back(boost::move(shm_should_intercept));
shmVector->push_back(boost::move(shm_intercepted_data));
ret_val = true;
}
catch (const std::exception& ex)
{
ret_val = false;
boost::interprocess::shared_memory_object::remove(shm_name.c_str());
}
named_mtx.unlock();
return ret_val;
}
}
[1117 ] Если кто-то считает, что я сделал что-то не так, пожалуйста, прокомментируйте.
Если Вы хотите, чтобы Ваша программа не была помещена в сон, в то время как это работает, лучший способ состоит в том, чтобы создать функцию типа KeepAlive, которая называет SystemIdleTimerReset, SHIdleTimerReset и моделирует ключевое касание. Затем необходимо назвать его много, в основном везде.
#include <windows.h>
#include <commctrl.h>
extern "C"
{
void WINAPI SHIdleTimerReset();
};
void KeepAlive()
{
static DWORD LastCallTime = 0;
DWORD TickCount = GetTickCount();
if ((TickCount - LastCallTime) > 1000 || TickCount < LastCallTime) // watch for wraparound
{
SystemIdleTimerReset();
SHIdleTimerReset();
keybd_event(VK_LBUTTON, 0, KEYEVENTF_SILENT, 0);
keybd_event(VK_LBUTTON, 0, KEYEVENTF_KEYUP | KEYEVENTF_SILENT, 0);
LastCallTime = TickCount;
}
}
Этот метод только работает, когда пользователь запускает приложение вручную.
Если Ваше приложение запущено уведомлением (т.е. в то время как устройство приостановлено), то необходимо сделать больше, или иначе приложение будет приостановлено после очень короткого промежутка времени, пока пользователь не приводит в действие устройство из приостановленного режима. Для обработки этого, необходимо поместить устройство в необслуживаемый режим питания.
if(!::PowerPolicyNotify (PPN_UNATTENDEDMODE, TRUE))
{
// handle error
}
// do long running process
if(!::PowerPolicyNotify (PPN_UNATTENDEDMODE, FALSE))
{
// handle error
}
Во время необслуживаемого использования режима все еще необходимо назвать KeepAlive много, можно использовать отдельный поток, который спит для x миллисекунд и называет поддержание funcation.
Обратите внимание на то, что необслуживаемый режим не выводит его из режима ожидания, это помещает устройство в странное полусонное состояние.
Таким образом, если Вы запускаете необслуживаемый режим, в то время как устройство в приостановленном режиме, он не разбудит экран или что-либо. Весь необслуживаемый режим делает, мешают WM приостановить Ваше приложение. Также другая проблема состоит в том, что это не работает над всеми устройствами, некоторое управление питанием устройств не очень хорошо, и это временно отстранит Вас так или иначе независимо от того, что Вы делаете.
Вероятно, путем изменения "состояний Питания системы", как описано здесь (но в c#)
Та статья также описывает способ предотвратить мобильное устройство для сна (который не является точно, что можно хотеть), путем вызова собственного функционального SystemIdleTimerReset () периодически (чтобы препятствовать тому, чтобы устройство выключилось).
Измените реестр Диспетчера электропитания, устанавливающий, который влияет на определенное условие сна, Вы хотите (тайм-аут, жидкое тесто, питание переменным током, и т.д.) и SetEvent на именованном системном событии под названием "PowerManager/ReloadActivityTimeouts" сказать ОС перезагружать настройки.