Reputation: 402
Greetings StackOverflow comrades. Last time I inquired about environment variables. Thanks to Remy for informing me.
Thanks to him I completed my Process class. Now the real problem was connecting to and communicating with MariaDb. I successfully launched MariaDb; but for some reason, reading from MariaDb deadlocks my program. I know before hand that, once connected to MariaDb using, mysql --user=root
, MariaDb writes MariaDb[NONE]>
to the console. And expects an SQL query input. But I my application deadlocks when trying to read.
I am wondering if MariaDb is using the handles I passed it in CreateProcess StartUpInfo. I did some search on google and found a library on MariaDb website which allows C/C++ programs to connect to MariaDb. So probably they are coercing us to use there library to connect to MariaDb.
Edit:
@Secumen I am trying to communicate with MariaDb via win32 CreateProcess
; you know that popular database program? I am using the one shipped with Xampp software.
I want to be able to automate the tasks of adding tables, data, users, etc.
I created the pipes with CreatePipe(...)
. Then I launched MariaDb using CreateProcess(...)
. The second argument to CreateProcess
was the command line, mysql --user=root
. Note that Xampp calls MariaDb MySql. Now I am connected to MariaDb and expect it to write MariaDb[NONE]>
to the console. Which means that I should have data to read via ReadFile(...)
. However ReadFile deadlocks and PeekNamedFile
shows that there was zero bytes available to be read.
How the heck then would I communicate with MariaDb if it is not writing to the handles I passed it in CreateProcess?
Edit - Minimal Example
SECURITY_ATTRIBUTES sa = {};
sa.bInheritHandle = true;
sa.lpSecurityDescriptor =NULL;
sa.nLength = sizeof(sa);
HANDLE r,w;
HANDLE r1,w1;
if(!CreatePipe(&r,&w,&sa,0)) throw "Failed to create pipe\n";
if(!CreatePipe(&r1,&w1,&sa,0)) throw "Failed to create pipe\n";
auto cmd = "MYSQL --user=root";
auto current_dir = R"(C:\Program Files\xampp\mysql\bin)";
SetCurrentDirectoryA(current_dir);
STARTUPINFOA si = {sizeof(si)};
PROCESS_INFORMATION pi;
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdError = w;
si.hStdOutput = w;
si.hStdInput = r1;
if(!CreateProcessA(NULL,cmd,NULL,NULL,true,0,NULL,NULL,&si,&pi))
throw "Failed to create process";
CloseHandle(w);
CloseHandle(r1);
{
DWORD sz, avail;
char *buf = new char[1024];
PeekNamedPipe(r,NULL,0,NULL,&avail,NULL);
printf("available %i",avail);
ReadFile(r,buf,1023,&sz,NULL);
buf[sz] = 0;
printf("%s",buf);
delete[] buf;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
Upvotes: 0
Views: 224
Reputation: 539
Then, you can do this asynchronously.
I referred to RbMm
's answer at this article.
#include <malloc.h>
#include <windows.h>
#include <winternl.h>
#include <array>
#include <string>
#include <iostream>
typedef ULONG(__stdcall *RTLNTSTATUSTODOSERROR)(NTSTATUS);
RTLNTSTATUSTODOSERROR pRtlNtStatusToDosError = NULL;
struct IO_COUNT
{
HANDLE _hFile;
HANDLE _hEvent;
LONG _dwIoCount;
IO_COUNT()
{
_dwIoCount = 1;
_hEvent = 0;
}
~IO_COUNT()
{
if (_hEvent)
{
CloseHandle(_hEvent);
}
}
void BeginIo()
{
InterlockedIncrement(&_dwIoCount);
}
void EndIo()
{
if (!InterlockedDecrement(&_dwIoCount))
{
SetEvent(_hEvent);
}
}
void Wait()
{
WaitForSingleObject(_hEvent, INFINITE);
}
ULONG Create(HANDLE hFile);
};
struct U_IRP : OVERLAPPED
{
enum { read, write };
IO_COUNT* _pIoObject;
ULONG _code;
LONG _dwRef;
char _buffer[256];
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
U_IRP(IO_COUNT* pIoObject) : _pIoObject(pIoObject)
{
_dwRef = 1;
pIoObject->BeginIo();
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
}
~U_IRP()
{
_pIoObject->EndIo();
}
ULONG CheckIoResult(BOOL is_ok)
{
if (is_ok)
{
OnIoComplete(NOERROR, InternalHigh);
return NOERROR;
}
ULONG dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_IO_PENDING)
{
OnIoComplete(dwErrorCode, 0);
}
return dwErrorCode;
}
ULONG Read()
{
_code = read;
AddRef();
return CheckIoResult(ReadFile(_pIoObject->_hFile, _buffer, sizeof(_buffer) - 1, 0, this));
}
ULONG Write(const void* pvBuffer, ULONG cbBuffer)
{
_code = write;
AddRef();
return CheckIoResult(WriteFile(_pIoObject->_hFile, pvBuffer, cbBuffer, 0, this));
}
VOID OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
{
switch (_code)
{
case read:
if (dwErrorCode == NOERROR)
{
if (dwNumberOfBytesTransfered)
{
_buffer[dwNumberOfBytesTransfered] = 0;
std::cout << _buffer;
}
Read();
}
break;
case write:
break;
}
Release();
}
static VOID WINAPI _OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(pRtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
}
};
ULONG IO_COUNT::Create(HANDLE hFile)
{
_hFile = hFile;
return BindIoCompletionCallback(hFile, (LPOVERLAPPED_COMPLETION_ROUTINE)U_IRP::_OnIoComplete, 0) &&
SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) &&
(_hEvent = CreateEvent(0, TRUE, FALSE, 0)) ? NOERROR : GetLastError();
}
int main()
{
static const WCHAR name[] = L"\\\\?\\pipe\\somename";
pRtlNtStatusToDosError = (RTLNTSTATUSTODOSERROR)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlNtStatusToDosError");
HANDLE hFile = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX | FILE_READ_DATA | FILE_WRITE_DATA | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 0, 0, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return -1;
}
IO_COUNT obj;
if (obj.Create(hFile) != NOERROR)
{
CloseHandle(hFile);
return -2;
}
PROCESS_INFORMATION pi;
STARTUPINFOW si = { sizeof(si) };
SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = CreateFile(name, FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);
if (si.hStdError == INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
return -3;
}
si.hStdInput = si.hStdOutput = si.hStdError;
WCHAR param[] = L" -uroot -ppassword";
if (!CreateProcess(L"C:\\xampp\\mysql\\bin\\mysql.exe", param, 0, 0, TRUE, 0, 0, 0, &si, &pi))
{
CloseHandle(hFile);
return -4;
}
//. Close unneeded handles.
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(si.hStdError);
U_IRP* p;
if (p = new U_IRP(&obj))
{
p->Read();
p->Release();
}
obj.EndIo();
std::array<std::string, 5> commands = {
"show databases;\n",
"use test_db;\n",
"select count(*) from test_table;\n",
"select * from test_table;\n",
"exit\n"
};
for (auto & iter : commands)
{
if (p = new U_IRP(&obj))
{
p->Write(iter.c_str(), iter.length());
p->Release();
}
}
obj.Wait();
CloseHandle(hFile);
DisconnectNamedPipe(hFile);
return 0;
}
Upvotes: 1
Reputation: 539
I have written the following code by referring to MSDN. I am using visual studio 2017 and test with win32 application.
I have passed several SQL statements through PIPE
for testing, and confirmed that the results were exactly obtained through PIPE
.
#include <string>
#include <iostream>
#include <windows.h>
using namespace std;
HANDLE hChildOutRd = NULL;
HANDLE hChildOutWr = NULL;
HANDLE hChildInRd = NULL;
HANDLE hChildInWr = NULL;
//. Internal functions.
int CreatePipes();
int CreateChildProcess();
int PipeIO(string & request, string & response);
int main()
{
if (CreatePipes() != ERROR_SUCCESS)
{
cout << "Failed to create pipe. error: " << GetLastError() << endl;
return -1;
}
//. Create the child process.
if (CreateChildProcess() != ERROR_SUCCESS)
{
cout << "Failed to create child process. error: " << GetLastError() << endl;
return -2;
}
//. Write and Read.
string request, response;
request = "use test_db; select count(*) from test_table;";
PipeIO(request, response);
cout << "[Request]: " << request << "\n[Response]: \n" << response << endl << endl;
return 0;
}
int CreatePipes()
{
SECURITY_ATTRIBUTES sa{ sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
//. Create a pipe for the child process's output.
if (!CreatePipe(&hChildOutRd, &hChildOutWr, &sa, 0))
{
return -1;
}
if (!SetHandleInformation(hChildOutRd, HANDLE_FLAG_INHERIT, 0))
{
return -2;
}
//. Create a pipe for the child process's input.
if (!CreatePipe(&hChildInRd, &hChildInWr, &sa, 0))
{
return -3;
}
if (!SetHandleInformation(hChildInWr, HANDLE_FLAG_INHERIT, 0))
{
return -4;
}
return ERROR_SUCCESS;
}
int CreateChildProcess()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.hStdError = hChildOutWr;
si.hStdOutput = hChildOutWr;
si.hStdInput = hChildInRd;
si.dwFlags |= STARTF_USESTDHANDLES;
wchar_t cmd[] = L" -uroot -ppassword";
BOOL bRet = CreateProcess(L"C:\\xampp\\mysql\\bin\\mysql.exe", cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if (!bRet)
{
return -5;
}
else
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(hChildInRd);
CloseHandle(hChildOutWr);
}
return ERROR_SUCCESS;
}
int PipeIO(string & request, string & response)
{
int nRet = ERROR_SUCCESS;
DWORD dwRead = 0, dwWrite = 0;
response.clear();
if (!WriteFile(hChildInWr, request.c_str(), request.length(), &dwWrite, NULL))
{
cout << "ERROR: failed to write pipe. error: " << GetLastError() << endl;
return -1;
}
CloseHandle(hChildInWr);
while (true)
{
char buffer[1024] = { 0 };
if (!ReadFile(hChildOutRd, buffer, 1024, &dwRead, NULL) || dwRead == 0)
{
break;
}
response += buffer;
}
CloseHandle(hChildOutRd);
return ERROR_SUCCESS;
}
Upvotes: 1