Before I get into to my question,let me explain what I am exactly doing.I have a main Process say ProcessA,I have hooked ProcessA and also injected dll(say myDll.dll) into the process space of ProcessA.Now at one point ProcessA kicks on another process which is ProcessB.Both the process A and B are in totally different Process memory space.I want to share the myDll.dll(which is inserted in processA sapce) in ProcessB(actually ProcessB's processSpace).Can it be done using pipe line method or any other suitable method.
thanks in advance.
The code of DLL will be automatically shared by different processes. For optimization only you should choose a good base address of the DLL (see http://msdn.microsoft.com/en-US/library/f7f5138s.aspx).
To share data between processes you can for example use Shared Memory objects or just place some variables which you need to share to a section which you mark as shared (see http://support.microsoft.com/kb/100634 and http://msdn.microsoft.com/en-us/library/h90dkhs0.aspx for details). To mark section ".SHAREDSECTIONNAME" as shared you can use
#pragma comment(linker, "/section:.SHAREDSECTIONNAME,RWS")
To have no conflicts in writing/reading from the shared memory you should use a named Event or Mutex exactly like in all other cases of multiprocess communication.
UPDATED based on the comment: If you create the child process yourself you receive the handle to the child process with full rights. So you have enough rights to make DLL infection with respect of CreateRemoteThread API. Here is a working code in C which start CMD.EXE and inject a MyTest.dll in the address space:
#include <Windows.h>
int main()
{
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi = {0};
TCHAR szCommandLine[4096] = TEXT("CMD.EXE");
BOOL bIsSuccess;
DWORD dwStatus;
LPCTSTR pszLibFile = TEXT("C:\\Oleg\\MyTest\\Release\\MyTest.dll");
PTSTR pszLibFileRemote = NULL;
HANDLE hThread = NULL;
int cb;
HMODULE hModule = NULL;
bIsSuccess = CreateProcess (NULL, szCommandLine, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
// Calculate the number of bytes needed for the DLL's pathname
cb = (1 + lstrlen(pszLibFile)) * sizeof(TCHAR);
__try {
PTHREAD_START_ROUTINE pfnThreadRtn;
// Allocate space in the remote process for the pathname
pszLibFileRemote = (PTSTR) VirtualAllocEx (pi.hProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);
if (pszLibFileRemote == NULL) __leave; // error
// Copy the DLL's pathname to the remote process's address space
if (!WriteProcessMemory (pi.hProcess, pszLibFileRemote, (PVOID) pszLibFile, cb, NULL)) __leave;
// Get the real address of LoadLibraryW in Kernel32.dll
// Real address of Kernel32.dll in dwProcessId and in our Process MUST be the same !!!
// Remote Process MUST have Kernel32.dll loaded (SMSSS.EXE and System havn't)!!!
#ifdef UNICODE
pfnThreadRtn = (PTHREAD_START_ROUTINE) GetProcAddress (GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
#else
pfnThreadRtn = (PTHREAD_START_ROUTINE) GetProcAddress (GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");
#endif
if (pfnThreadRtn == NULL) __leave;
// Create a remote thread that calls LoadLibraryW(DLLPathname)
hThread = CreateRemoteThread (pi.hProcess, NULL, 0, pfnThreadRtn, (LPVOID)pszLibFileRemote, 0, NULL);
if (hThread == NULL) __leave;
dwStatus = ResumeThread (pi.hThread);
// Wait for the remote thread to terminate
if (WaitForSingleObject (hThread, INFINITE) != WAIT_OBJECT_0) __leave;
GetExitCodeThread (hThread, (PDWORD)&hModule);
// hModule is the address in the destination process (CMD.EXE)
// of the injected DLL
// You can verify that it is really loaded for example with respect of
// Process Explorer (http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx)
}
__finally {
// Free the remote memory that contained the DLL's pathname
if (pszLibFileRemote != NULL)
bIsSuccess = VirtualFreeEx (pi.hProcess, pszLibFileRemote, 0, MEM_RELEASE);
if (hThread != NULL)
bIsSuccess = CloseHandle (hThread);
if (pi.hProcess != NULL)
bIsSuccess = CloseHandle (pi.hProcess);
if (pi.hThread != NULL)
bIsSuccess = CloseHandle (pi.hThread);
}
return 0;
}
Related
I'm using a Nucleo L496ZG, X-NUCLEO-IKS01A2 and the Quectel BG96 module to send sensor data (temperature, humidity etc..) to Azure IoT Central over HTTP.
I've been using the example implementation provided by Avnet here, which works fine but it's not power optimized and with a 6700mAh battery pack it only lasts around 30 hours sending telemetry ever ~10 seconds. Goal is for it to last around a week. I'm open to increasing the time between messages but I also want to save power in between sending.
I've gone over the Quectel BG96 manuals and I've tried two things:
1) powering off the device by driving the PWRKEY and turning it back on when I need to send a message
I've gotten this to work, kinda… until I get a hardfault exception which happens seemingly randomly anywhere from within ~5 minutes of running to 2 hours (messages successfully sending prior to the exception). Output of crash log parser is the same every time:
Crash location = strncmp [0x08038DF8] (based on PC value)
Caller location = _findenv_r [0x0804119D] (based on LR value)
Stack Pointer at the time of crash = [20008128]
Target and Fault Info:
Processor Arch: ARM-V7M or above
Processor Variant: C24
Forced exception, a fault with configurable priority has been escalated to HardFault
A precise data access error has occurred. Faulting address: 03060B30
The caller location traces back to my .map file and I don't know what to make of it.
My code:
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//#define USE_MQTT
#include <stdlib.h>
#include "mbed.h"
#include "iothubtransporthttp.h"
#include "iothub_client_core_common.h"
#include "iothub_client_ll.h"
#include "azure_c_shared_utility/platform.h"
#include "azure_c_shared_utility/agenttime.h"
#include "jsondecoder.h"
#include "bg96gps.hpp"
#include "azure_message_helper.h"
#define IOT_AGENT_OK CODEFIRST_OK
#include "azure_certs.h"
/* initialize the expansion board && sensors */
#include "XNucleoIKS01A2.h"
static HTS221Sensor *hum_temp;
static LSM6DSLSensor *acc_gyro;
static LPS22HBSensor *pressure;
static const char* connectionString = "xxx";
// to report F uncomment this #define CTOF(x) (((double)(x)*9/5)+32)
#define CTOF(x) (x)
Thread azure_client_thread(osPriorityNormal, 10*1024, NULL, "azure_client_thread");
static void azure_task(void);
EventFlags deleteOK;
size_t g_message_count_send_confirmations;
/* create the GPS elements for example program */
BG96Interface* bg96Interface;
//static int tilt_event;
// void mems_int1(void)
// {
// tilt_event++;
// }
void mems_init(void)
{
//acc_gyro->attach_int1_irq(&mems_int1); // Attach callback to LSM6DSL INT1
hum_temp->enable(); // Enable HTS221 enviromental sensor
pressure->enable(); // Enable barametric pressure sensor
acc_gyro->enable_x(); // Enable LSM6DSL accelerometer
//acc_gyro->enable_tilt_detection(); // Enable Tilt Detection
}
void powerUp(void) {
if (platform_init() != 0) {
printf("Error initializing the platform\r\n");
return;
}
bg96Interface = (BG96Interface*) easy_get_netif(true);
}
void BG96_Modem_PowerOFF(void)
{
DigitalOut BG96_RESET(D7);
DigitalOut BG96_PWRKEY(D10);
DigitalOut BG97_WAKE(D11);
BG96_RESET = 0;
BG96_PWRKEY = 0;
BG97_WAKE = 0;
wait_ms(300);
}
void powerDown(){
platform_deinit();
BG96_Modem_PowerOFF();
}
//
// The main routine simply prints a banner, initializes the system
// starts the worker threads and waits for a termination (join)
int main(void)
{
//printStartMessage();
XNucleoIKS01A2 *mems_expansion_board = XNucleoIKS01A2::instance(I2C_SDA, I2C_SCL, D4, D5);
hum_temp = mems_expansion_board->ht_sensor;
acc_gyro = mems_expansion_board->acc_gyro;
pressure = mems_expansion_board->pt_sensor;
azure_client_thread.start(azure_task);
azure_client_thread.join();
platform_deinit();
printf(" - - - - - - - ALL DONE - - - - - - - \n");
return 0;
}
static void send_confirm_callback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback)
{
//userContextCallback;
// When a message is sent this callback will get envoked
g_message_count_send_confirmations++;
deleteOK.set(0x1);
}
void sendMessage(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, char* buffer, size_t size)
{
IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray((const unsigned char*)buffer, size);
if (messageHandle == NULL) {
printf("unable to create a new IoTHubMessage\r\n");
return;
}
if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, send_confirm_callback, NULL) != IOTHUB_CLIENT_OK)
printf("FAILED to send! [RSSI=%d]\n", platform_RSSI());
else
printf("OK. [RSSI=%d]\n",platform_RSSI());
IoTHubMessage_Destroy(messageHandle);
}
void azure_task(void)
{
//bool tilt_detection_enabled=true;
float gtemp, ghumid, gpress;
int k;
int msg_sent=1;
while (true) {
powerUp();
mems_init();
/* Setup IoTHub client configuration */
IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, HTTP_Protocol);
if (iotHubClientHandle == NULL) {
printf("Failed on IoTHubClient_Create\r\n");
return;
}
// add the certificate information
if (IoTHubClient_LL_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK)
printf("failure to set option \"TrustedCerts\"\r\n");
#if MBED_CONF_APP_TELUSKIT == 1
if (IoTHubClient_LL_SetOption(iotHubClientHandle, "product_info", "TELUSIOTKIT") != IOTHUB_CLIENT_OK)
printf("failure to set option \"product_info\"\r\n");
#endif
// polls will happen effectively at ~10 seconds. The default value of minimumPollingTime is 25 minutes.
// For more information, see:
// https://azure.microsoft.com/documentation/articles/iot-hub-devguide/#messaging
unsigned int minimumPollingTime = 9;
if (IoTHubClient_LL_SetOption(iotHubClientHandle, "MinimumPollingTime", &minimumPollingTime) != IOTHUB_CLIENT_OK)
printf("failure to set option \"MinimumPollingTime\"\r\n");
IoTDevice* iotDev = (IoTDevice*)malloc(sizeof(IoTDevice));
if (iotDev == NULL) {
return;
}
setUpIotStruct(iotDev);
char* msg;
size_t msgSize;
hum_temp->get_temperature(>emp); // get Temp
hum_temp->get_humidity(&ghumid); // get Humidity
pressure->get_pressure(&gpress); // get pressure
iotDev->Temperature = CTOF(gtemp);
iotDev->Humidity = (int)ghumid;
iotDev->Pressure = (int)gpress;
printf("(%04d)",msg_sent++);
msg = makeMessage(iotDev);
msgSize = strlen(msg);
sendMessage(iotHubClientHandle, msg, msgSize);
free(msg);
iotDev->Tilt &= 0x2;
/* schedule IoTHubClient to send events/receive commands */
IOTHUB_CLIENT_STATUS status;
while ((IoTHubClient_LL_GetSendStatus(iotHubClientHandle, &status) == IOTHUB_CLIENT_OK) && (status == IOTHUB_CLIENT_SEND_STATUS_BUSY))
{
IoTHubClient_LL_DoWork(iotHubClientHandle);
ThisThread::sleep_for(100);
}
deleteOK.wait_all(0x1);
free(iotDev);
IoTHubClient_LL_Destroy(iotHubClientHandle);
powerDown();
ThisThread::sleep_for(300000);
}
return;
}
I know PSM is probably the way to go since powering on/off the device draws a lot of power but it would be useful if someone had an idea of what is happening here.
2) putting the device to PSM between sending messages
The BG96 library in the example code I'm using doesn't have a method to turn on PSM so I tried to implement my own. When I tried to run it, it basically runs into an exception right away so I know it's wrong (I'm very new to embedded development and have no prior experience with AT commands).
/** ----------------------------------------------------------
* this is a method provided by current library
* #brief Tx a string to the BG96 and wait for an OK response
* #param none
* #retval true if OK received, false otherwise
*/
bool BG96::tx2bg96(char* cmd) {
bool ok=false;
_bg96_mutex.lock();
ok=_parser.send(cmd) && _parser.recv("OK");
_bg96_mutex.unlock();
return ok;
}
/**
* method I created in an attempt to use PSM
*/
bool BG96::psm(void) {
return tx2bg96((char*)"AT+CPSMS=1,,,”00000100”,”00000001”");
}
Can someone tell me what I'm doing wrong and provide any guidance on how I can achieve my goal of having my device run on battery for longer?
Thank you!!
I got Power Saving Mode working by using Mbed's ATCmdParser and the AT+QPSMS commands as per Quectel's docs. The modem doesn't always go into power saving mode right away so that should be noted. I also found that I have to restart the modem afterwards or else I get weird behaviour. My code looks something like this:
bool BG96::psm(char* T3412, char* T3324) {
_bg96_mutex.lock();
if(_parser.send("AT+QPSMS=1,,,\"%s\",\"%s\"", T3412, T3324) && _parser.recv("OK")) {
_bg96_mutex.unlock();
}else {
_bg96_mutex.unlock();
return false;
}
return BG96Ready(); }//restarts modem
To send a message to Azure, the modem will need to be manually woken up by driving the PWRKEY to start bi-directional communication, and a new client handle needs to be created and torn down every time since Azure connection uses keepAlive and the modem will be unreachable when it's in PSM.
In my Application I need to open, read and write data to a text file using the calls f_open, f_read, and f_write.
It is failing to open the .txt file
res = f_open(&f_header.file, file_path, FA_OPEN_EXISTING | FA_WRITE | FA__WRITTEN | FA_READ | FA_CREATE_NEW );
printf("res value after f open %d \n\r",res);
if (res != FR_OK) {
printf("Failed to open %s, error %d\n\r", file_path, res);
}
This is giving error:
FR_NOT_ENABLED, /* (12) The volume has no work area */
For solving this error application program needs to perform f_mount function after each media change to force cleared the filesystem object.
How to use f_mount() call in this application to solve this issue?
I'm not clear about the 2nd parameter.
I added this f_mount(&fs0, "0://", 1); to solve this issue.
Before the f_open call. It is not taking f_mount() call also.
res=f_mount(&fs0,"0://", 1);
res = f_open(&f_header.file, file_path, FA_OPEN_EXISTING | FA_WRITE | FA__WRITTEN | FA_READ | FA_CREATE_NEW );
The code is stopping while run time before the f_mount()
Here is the source code for f_mount which I'm using:
FRESULT f_mount (
FATFS* fs, /* Pointer to the file system object (NULL:unmount)*/
const TCHAR* path, /* Logical drive number to be mounted/unmounted */
BYTE opt /* 0:Do not mount (delayed mount), 1:Mount immediately */
)
{
FATFS *cfs;
int vol;
FRESULT res;
const TCHAR *rp = path;
vol = get_ldnumber(&rp);
if (vol < 0) return FR_INVALID_DRIVE;
cfs = FatFs[vol]; /* Pointer to fs object */
if (cfs) {
#if _FS_LOCK
clear_lock(cfs);
#endif
#if _FS_REENTRANT /* Discard sync object of the current volume */
if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;
#endif
cfs->fs_type = 0; /* Clear old fs object */
}
if (fs) {
fs->fs_type = 0; /* Clear new fs object */
#if _FS_REENTRANT /* Create sync object for the new volume */
if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;
#endif
}
FatFs[vol] = fs; /* Register new fs object */
if (!fs || opt != 1) return FR_OK; /* Do not mount now, it will be mounted later */
res = find_volume(&fs, &path, 0); /* Force mounted the volume */
LEAVE_FF(fs, res);
}
The code is not showing any error/warnings at the time of make file.
I'm sure there is no problem with the code.
There is nothing wrong with the code.
Is this some problem related to the memory allocation or out of memory in emmc. What are the possible reason for this behaviour.
According to http://elm-chan.org/fsw/ff/doc/mount.html:
FRESULT f_mount (
FATFS* fs, /* [IN] Filesystem object */
const TCHAR* path, /* [IN] Logical drive number */
BYTE opt /* [IN] Initialization option */
);
Parameters
fs
Pointer to the filesystem object to be registered and cleared. Null pointer unregisters the registered filesystem object.
path
Pointer to the null-terminated string that specifies the logical drive. The string without drive number means the default drive.
opt
Mounting option. 0: Do not mount now (to be mounted on the first access to the volume), 1: Force mounted the volume to check if it is ready to work.
In other words, the second parameter is how you want to refer to this particular filesystem when later working with it.
For example, mounting it like so:
f_mount(&fs0, "0://", 1);
you would then be able to open files like this:
f_open(fp, "0://path/to/file", FA_CREATE_ALWAYS);
I have a redirected printer port that use redmon (redirect port monitor) with a postscript printer driver to convert postscript to pdf and apply some other effects like watermarks, overlays, etc.
In win 7 all work fine but in windows 10 the process run under system user account.
In the configuration window of the printer port there is a flag called "Run as user" and in win7, checking this flag let the job running under the user account.
In Windows 10 it seems not working.
Any suggestion will be very appreciated.
Thank you.
Roy
I had a similar problem. I needed the user that printed the document to select the type of document and a patient ID. Then print the document to our EHR system as a PDF. Works in Windows 7 when "Run as User" is checked, but not on Windows 10. Redmon always runs the program as "SYSTEM". So I added a bit to the beginning of the program to check the user name. If it is "SYSTEM" the program looks for the an interactive user on the system by finding an instance of explorer.exe. If more than one interactive user is logged onto the system this will fail. Not a problem for my task. The program then starts another instance of itself running as the same user as explorer.exe, passing the same command line. A pipe is used so that stdin from the first instance can be piped to stdin on the second instance. Another limitation is that on a 64 bit OS, a 64 bit version of the program must be used. Otherwise explorer.exe may not be found.
The following code is what I placed at the beginning of my program. Don't be fooled by the program starting at main(). I am using a GUII toolkit that has WinMain() in it and then calls main(). I have only tested the code on ASCII programs. I tried to use the ASCII version of calls so that it would work with non-ASCII programs, but I am not sure I got all of them.
The LogInfoSys("Hello World"); function just writes to a log file.
Good luck.
#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <time.h>
#include <direct.h>
#include <process.h>
#include <sqlext.h>
#include <Psapi.h>
#include <tlhelp32.h>
int main(int argc, char *argv[])
{
int error;
char msg[1024];
DWORD *processIDs;
int processCount;
HANDLE hProcess = NULL;
HANDLE hToken;
char userName[64];
char progName[1024];
int i, j;
char nameMe[256];
char domainMe[256];
PTOKEN_USER ptuMe = NULL;
PROCESS_INFORMATION procInfo;
STARTUPINFO startUpInfo;
HMODULE *hMod;
DWORD cbNeeded;
SECURITY_ATTRIBUTES saAttr;
HANDLE hChildStd_IN_Rd = NULL;
HANDLE hChildStd_IN_Wr = NULL;
i = 64; // Get user name, if it is "SYSTEM" redirect input to output to a new instance of the program
GetUserNameA(userName, &i);
if (_stricmp(userName, "system") == 0)
{
LogInfoSys("Running as SYSTEM");
processIDs = (DWORD *)calloc(16384, sizeof(DWORD)); // Look for explorer.exe running. If found that should be the user we want to run as.
EnumProcesses(processIDs, sizeof(DWORD) * 16384, &i); // If there is more than one that is OK as long as they are both being run by the same
processCount = i / sizeof(DWORD); // user. If more than one user is logged on, this will be a problem.
hMod = (HMODULE *)calloc(4096, sizeof(HMODULE));
hProcess = NULL;
for (i = 0; (i < processCount) && (hProcess == NULL); i++)
{
if (processIDs[i] == 11276)
Sleep(0);
if (processIDs[i] != 0)
{
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIDs[i]);
if (hProcess != NULL)
{
cbNeeded = 0;
error = EnumProcessModules(hProcess, hMod, sizeof(HMODULE) * 4096, &cbNeeded);
if (error == 0)
{
error = GetLastError();
Sleep(0);
}
progName[0] = 0;
error = GetModuleBaseNameA(hProcess, hMod[0], progName, 1024);
if (error == 0)
{
error = GetLastError();
Sleep(0);
}
if (_stricmp(progName, "explorer.exe") != 0)
{
CloseHandle(hProcess);
hProcess = NULL;
}
else
{
LogInfoSys("Found explorer.exe");
}
}
}
}
LogInfoSys("After looking for processes.");
nameMe[0] = domainMe[0] = 0;
if (hProcess != NULL)
{
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&hChildStd_IN_Rd, &hChildStd_IN_Wr, &saAttr, 0)) // Create a pipe for the child process's STDIN.
LogInfoSys("Stdin CreatePipe error");
if (!SetHandleInformation(hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) // Ensure the write handle to the pipe for STDIN is not inherited.
LogInfoSys("Stdin SetHandleInformation errir");
if (OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken) != 0)
{
GetStartupInfo(&startUpInfo);
startUpInfo.cb = sizeof(STARTUPINFO);
startUpInfo.lpReserved = NULL;
startUpInfo.lpDesktop = NULL;
startUpInfo.lpTitle = NULL;
startUpInfo.dwX = startUpInfo.dwY = 0;
startUpInfo.dwXSize = 0;
startUpInfo.dwYSize = 0;
startUpInfo.dwXCountChars = 0;
startUpInfo.dwYCountChars = 0;
startUpInfo.dwFillAttribute = 0;
startUpInfo.dwFlags |= STARTF_USESTDHANDLES;
startUpInfo.wShowWindow = 0;
startUpInfo.cbReserved2 = 0;
startUpInfo.lpReserved = NULL;
startUpInfo.hStdInput = hChildStd_IN_Rd;
startUpInfo.hStdOutput = NULL;
startUpInfo.hStdError = NULL;
GetModuleFileName(NULL, progName, 1024);
i = CreateProcessAsUserA(hToken, progName, GetCommandLine(), NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startUpInfo, &procInfo);
if (i == 0)
{
i = GetLastError();
}
do
{
i = (int)fread(msg, 1, 1024, stdin);
if (i > 0)
WriteFile(hChildStd_IN_Wr, msg, i, &j, NULL);
} while (i > 0);
}
}
LogInfoSys("End of running as SYSTEM.");
exit(0);
}
/**********************************************************************************************************
*
* End of running as SYSTEM and start of running as the user that printed the document (I hope).
*
**********************************************************************************************************/
exit(0);
}
First of all I need to say I don't know much about DLLs.
I am trying to send data from one program to another, using functions from kernel32.dll. My programs are coded in MQL4.
This is the code I use for the server part, which is supposed to save the data:
#define INVALID_HANDLE_VALUE -1
#define BUF_SIZE 256
#define PAGE_READWRITE 0x0004
#define FILE_MAP_ALL_ACCESS 0xf001F
#import "kernel32.dll"
int CreateFileMappingA(int hFile, int lpAttributes, int flProtect, int dwMaximumSizeHigh, int dwMaximumSizeLow, string lpName);
int MapViewOfFile(int hFileMappingObject, int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow, int dwNumberOfBytesToMap);
int UnmapViewOfFile(int lpBaseAddress);
int RtlMoveMemory(int DestPtr, string s, int Length);
int CloseHandle(int hwnd);
int CreateMutexA(int attr, int owner, string mutexName);
int ReleaseMutex(int hnd);
int WaitForSingleObject(int hnd, int dwMilliseconds);
bool started = False;
int hMapFile = 0;
int pBuf=0;
int hMutex;
int OnInit()
{
if(!started) {
started = true;
string szName="Global\\Value1";
int hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE,0,PAGE_READWRITE,0,BUF_SIZE,szName);
if(hMapFile==0) {
Alert("CreateFileMapping failed!");
return;
}
pBuf = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);
if(pBuf==0) {
Alert("Map View failed!");
return;
}
hMutex = CreateMutexA(0,0,"PriceMapMutex");
}
}
void OnTick()
{
WaitForSingleObject(hMutex,1000);
if(pBuf==0) return;
string szMsg = DoubleToStr(Bid,Digits);
Comment("Data: ",szMsg);
RtlMoveMemory(pBuf, szMsg, StringLen(szMsg)+1);
ReleaseMutex(hMutex);
return(0);
}
int deinit()
{
switch(UninitializeReason()) {
case REASON_CHARTCLOSE:
case REASON_REMOVE:
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
break;
}
return(0);
}
This is what I use for my client part, which is supposed to pick up the data:
#define INVALID_HANDLE_VALUE -1
#define BUF_SIZE 1024
#define FILE_MAP_READ 4
extern int BufferSize = 1024;
#import "kernel32.dll"
int OpenFileMappingA(int dwDesiredAccess, bool bInheritHandle, string lpName);
string MapViewOfFile(int hFileMappingObject, int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow, int dwNumberOfBytesToMap);
int UnmapViewOfFile(string lpBaseAddress);
int CloseHandle(int hwnd);
int CreateMutexA(int attr, int owner, string mutexName);
int ReleaseMutex(int hnd);
int WaitForSingleObject(int hnd, int dwMilliseconds);
string szName;
int hMapFile;
string obj;
string data;
int hMutex;
double dd;
int OnInit()
{
szName="Global\\Value1";
hMapFile = OpenFileMappingA(FILE_MAP_READ,False,szName);
if(hMapFile==0) {
Alert("CreateFile Failed!");
return;
}
obj="data";
ObjectCreate(obj,OBJ_HLINE,0,0,0);
ObjectSet(obj,OBJPROP_COLOR,Gold);
hMutex = CreateMutexA(0,0,"PriceMapMutex");
}
void OnDeinit(const int reason)
{
CloseHandle(hMapFile);
Comment("");
ObjectDelete(obj);
return(0);
}
void start()
{
getsignal();
Comment("Data: ",DoubleToStr(dd,Digits));
Sleep(50);
}
void getsignal() {
WaitForSingleObject(hMutex,333);
data = MapViewOfFile(hMapFile,FILE_MAP_READ,0,0,BUF_SIZE);
dd = StrToDouble(data);
ReleaseMutex(hMutex);
UnmapViewOfFile(data);
ObjectMove(obj,0,Time[0],dd);
}
The code basically works. However I am facing 2 major problems with it.
Problem number 1:
I want to exchange multiple values ( value1, value2, value3, ... ). For some reason it seems to be irrelevant which name I use for szName="Global\\Value1". The server saves the value and the client picks it up no matter what names I use szName="Global\\Value1", szName="Global\\Value2" or szName="Global\\Value3". So for example in the server code I use szName="Global\\Value1" and in my client code I use szName="Global\\Value3" the client still picks up the value which the server writes to szName="Global\\Value1".
Problem number 2:
my client is only stable for about 5 hours. After that I get a message in the client programme saying
"There is a problem and the program needs to be closed...".
Then I close and restart my client and its again working for the next 5 hours.
Has anyone any idea?
FILE MEDIUM
I agree that Kernel32 is not a good option if you need to do MT4-to-MT4 interfacing. The reason is that Kernel32 is Windows specific. The EA won't work on Mac. Also, messing around with Kernel32 DLL might cause memory leaks (eg, your 5hr live). Plus, it requires user to know how to enable DLL (you'd be surprise how many users have no idea how to enable it).
Recommendation:
If you only need SAME MT4 exchange (EAs between charts), then use the GlobalVariableGet(), GlobalVariableSet(), etc.
If you need exchange between 2 different MT4s (on the same PC) --even if it is across different broker MT4, then use the FILE system: Files\FilePipe.mqh which allows you to write to the common MT4 folder:
#include <Files\FilePipe.mqh>
CFilePipe voPipeOut;
voPipeOut.Open("yourFileName.txt", FILE_WRITE|FILE_COMMON|FILE_BIN);
voPipeOut.WriteString("WhatEverMessage, probably some CSV value here");
voPipeOut.Close();
and subsequently
CFilePipe voPipeFile;
string vsInString = "";
voPipeFile.Open("yourFileName.txt", FILE_SHARE_READ|FILE_COMMON|FILE_BIN);
voPipeFile.Seek(0,SEEK_SET);
voPipeFile.ReadString( vsInString );
voPipeFile.Close();
This way, your EA won't depend on DLLs and also works in a wide range of environments. It is very fast (under 2ms for a 1Mb pipe). It works even for cross-broker interfacing (exchanging info [feed?] between 2 different brokers).
The best idea?
The best thing I can advise you is to stop trying to tweak KERNEL32.DLL published API to make it use with MetaTrader Terminal 4 code-execution ecosystem and to rather start designing a professional distributed system, independently of injecting objects into the O/S pagefile and hassling with semaphores and MUTEX-es.
Besides the best next step:
MQL4 code ought NEVER block. A MUTEX-signalling turned into a non-blocking state is a must
MQL4 code / API mapper ought respect the data-types and their actual memory sizes in MQL4
MQL4 code ought conform to the recent New-MQL4 rules ( sections are in "old"-MQL4 )
MQL4 declared string is not a C-lang string, but rather a struct! Handle with care!
MQL4 code violated in several places syntax rules, just test with #property strict
MQL4 code is "shooting itself in leg" when ignoring namespace boundaries / scopes of declaration
MQL4 code ignores potential error states, not inspecting any GetLastError() to handle such collision(s)
MQL4 code does not gracefully return resources ( forgets to clear 'em )
MQL4 code proposed exposes itself into an immense risk of KERNEL32.DLL API usage unlocked stealth security flaw / enabling a run-time hijacking hack
better use separation of concerns, using ZeroMQ or nanomsg messaging to "exchange values between ( not only ) MQL4 programs"
I run MSDN example for createprocess parent and child but it doesn't work correctly,
becouse it writes the information to child input (what I've checked by printing it to the cmd), but after
printf( "\n->Contents of child process STDOUT:\n\n");
when it calls
ReadFromPipe();
in the cmd from which the application was called I receive nothing but it waits, waits. and only if I do ctrl+c it ends.
what might be the reason?
#include "stdafx.h"
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#include <iostream> //only for cout
#define BUFSIZE 4096
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
void CreateChildProcess(void);
void WriteToPipe(void);
void ReadFromPipe(void);
void ErrorExit(PTSTR);
int _tmain(int argc, TCHAR *argv[])
{
SECURITY_ATTRIBUTES saAttr;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
ErrorExit(TEXT("StdoutRd CreatePipe"));
// Ensure the read handle to the pipe for STDOUT is not inherited.
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdin SetHandleInformation"));
// Create the child process.
CreateChildProcess();
// Get a handle to an input file for the parent.
// This example assumes a plain text file and uses string output to verify data flow.
if (argc == 1)
ErrorExit(TEXT("Please specify an input file.\n"));
g_hInputFile = CreateFile(
argv[1],
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
if ( g_hInputFile == INVALID_HANDLE_VALUE )
ErrorExit(TEXT("CreateFile"));
// Write to the pipe that is the standard input for a child process.
// Data is written to the pipe's buffers, so it is not necessary to wait
// until the child process is running before writing data.
WriteToPipe();
printf( "\n->Contents of %s written to child STDIN pipe.\n", argv[1]);
// Read from pipe that is the standard output for child process.
printf( "\n->Contents of child process STDOUT:\n\n");
ReadFromPipe();
printf("\n->End of parent execution.\n");
// The remaining open handles are cleaned up when this process terminates.
// To avoid resource leaks in a larger application, close handles explicitly.
return 0;
}
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[]=TEXT("C:\\Windows\\notepad.exe");//TEXT("C:\\Windows\\System32\\cmd.exe");
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
std::cout<<"\nCreateChildProcess.\n";
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application.
if ( ! bSuccess )
ErrorExit(TEXT("CreateProcess"));
else
{
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
}
}
void WriteToPipe(void)
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
for (;;)
{
bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
if ( ! bSuccess || dwRead == 0 ) break;
bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
if ( ! bSuccess ) break;
}
std::cout<<"\nchBuf: "<<chBuf<<std::endl;
// Close the pipe handle so the child process stops reading.
if ( ! CloseHandle(g_hChildStd_IN_Wr) )
ErrorExit(TEXT("StdInWr CloseHandle"));
}
void ReadFromPipe(void)
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, 4, &dwRead, NULL);
std::cout<<"\nchBuf: "<<chBuf<<std::endl;
/*for (int i=0;i<2;i++) //read twice
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;
std::cout<<"\nchBuf: "<<chBuf<<std::endl;
bSuccess = WriteFile(hParentStdOut, chBuf,
dwRead, &dwWritten, NULL);
if (! bSuccess ) break;
} */
}
void ErrorExit(PTSTR lpszFunction)
// Format a readable error message, display a message box,
// and exit from the application.
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
The reason is that you are starting a GUI application, notepad.exe, which will never write to stdout. So stdout is sitting open and has nothing written to it so the call to ReadFile blocks waiting for something to show up. Switch back to cmd.exe which you have commented out and you'll get output when from your ReadFromPipe call.
Also your ReadFromPipe function is just going to mostly output junk. To test it I changed it to:
void ReadFromPipe(void)
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
{
DWORD dwRead;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = TRUE;
do
{
dwRead = 0;
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE-1, &dwRead, NULL);
chBuf[dwRead] = '\0';
std::cout<<"\nchBuf: "<<chBuf<<std::endl;
} while (bSuccess && dwRead > 0);
}