user13947194
user13947194

Reputation: 402

Win32 communicating with MariaDb

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

Answers (2)

secuman
secuman

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

secuman
secuman

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

Related Questions