Control other programs from Python (communication between Python and exe)

1. Purpose

The purpose is to run other programs in Python. Specifically, I wondered if I could use the SDK published in C language in combination with Python.

2. Try & error

2.1 When it can be controlled as it is by Python subprocess

I adopted this because it is possible to run other executable programs from Python by using the subprocess of the Python module. Communication with other processes includes an interprocess communication method called PIPE, and it feels like it can be done quickly. Actually, it works well with the following combination of Python (control) and C (program to run).

OK.cpp


#include <stdio.h>

#define MAX_BUFFER 256

int main()
{
    char buf[MAX_BUFFER];

    fgets(buf, MAX_BUFFER, stdin);
    printf("your input is :%s", buf);

    return 0;
}

control.py


import subprocess as sp

if __name__ == "__main__":

    #Launch the exe file as a process
    #Connect stdin and stdout as PIPE to this Python
    cmd = ".\\OK.exe"
    p = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE)

    #Send instructions to an instance of Popen with communicate
    #The return value is(stdout, stderr)Because of the tuple of
    #Because b before the character string is converted to byte
    out, err = p.communicate(input=b"Hello World")

    print(out)
    exit()

(Compile OK.cpp and put it as OK.exe in the same folder as control.py below)

2.2 If you can't control it as it is with Python subprocess (I was addicted to it)

However, the SDK I wanted to handle changed state through the input of stdin, and required input many times. In this case, even if you use the same control.py as before, the operation will stop at the communicate part. For example, such a C program is NG.

NG.cpp


#include <stdio.h>
#include <string.h>

#define MAX_BUFFER 256

int main()
{
    char buf[MAX_BUFFER], *ret;
    //Continue to receive input until there is an end character in the input string
    while(1)
    {
        fgets(buf, MAX_BUFFER, stdin);
        printf("your input is :%s", buf);
        //Determine if there is an end in the input string
        ret = strstr(buf, "end");
        //If not, NULL is returned in ret, so the while statement is exited.
        if(ret!=NULL)
        {
            break;
        }
    }
    return 0;
}

I couldn't find an implementation on the Python side to solve this ... so this time I solved it with the implementation on the C side. An implementation example on the C side is shown below, but if anyone knows how to do this on the Python side, I would appreciate it if you could tell me. (I found that pyexpect is a pretty good line, so I actually tried it, but I gave up because I couldn't use the full functionality unless it was Linux. If I do it on Linux, it may be a little less difficult. not)

3. Adopted method: Socket communication

The method adopted this time is to receive commands via TCP server / client communication and start the client process each time on the Python side. An example is shown below.

main.cpp


#include "stdafx.h"

char *getCharTCP(char*);

int main()
{
    char buf[DEFAULT_BUFLEN];
    char *ret;
    //Continue to receive input until there is an end character in the input string
    while(1)
    {
        //fgets(buf, DEFAULT_BUFLEN, stdin);
        printf("waiting new input :\n");
        ret = getCharTCP(buf);
        printf("your input is :%s", buf);
        //Determine if there is an end in the input string
        ret = strstr(buf, "end");
        //If not, NULL is returned in ret, so the while statement is exited.
        if(ret!=NULL)
        {
            break;
        }
    }
    return 0;
}

getCharTCP.cpp


#include "stdafx.h"

char *getCharTCP(char *out)
{
    WSADATA wsaData;
    int iResult, i;

    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET ClientSocket = INVALID_SOCKET;

    struct addrinfo *result = NULL;
    struct addrinfo hints;

    int iSendResult;
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;
    char errorcode[DEFAULT_BUFLEN];
    strcpy(errorcode, "error");

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return errorcode;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return errorcode;
    }

    // Create a SOCKET for connecting to server
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
        printf("socket failed with error: %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return errorcode;
    }

    // Setup the TCP listening socket
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
        return errorcode;
    }

    freeaddrinfo(result);

    iResult = listen(ListenSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) {
        printf("listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return errorcode;
    }

    // Accept a client socket
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return errorcode;
    }

    // No longer need server socket
    closesocket(ListenSocket);

    // Receive until the peer shuts down the connection
    do {

        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);

            // Echo the buffer back to the sender
            iSendResult = send(ClientSocket, recvbuf, iResult, 0);
            if (iSendResult == SOCKET_ERROR) {
                printf("send failed with error: %d\n", WSAGetLastError());
                closesocket(ClientSocket);
                WSACleanup();
                return errorcode;
            }
            printf("Bytes sent: %d\n", iSendResult);
        }
        else if (iResult == 0)
            printf("Connection closing...\n");
        else {
            printf("recv failed with error: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return errorcode;
        }

    } while (iResult > 0);

    // shutdown the connection since we're done
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return errorcode;
    }

    // cleanup
    closesocket(ClientSocket);
    WSACleanup();
    for (i = strlen(recvbuf) - 1; i >= 0; i--)
    {
        strncpy(out, recvbuf, i + 1);
        out[i + 1] = '\0';
        break;
    }
    printf("receive end\n");
    return out;
}

stdafx.cpp


#include "stdafx.h"

stdafx.h


#pragma once
//Turn off Visual Studio warning
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <string.h>



//include for getCharTCP
#undef UNICODE

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>

// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"

main.cpp


#include "stdafx.h"


int main(int argc, char **argv)
{
    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo *result = NULL,
        *ptr = NULL,
        hints;
    char sendbuf[DEFAULT_BUFLEN];
    char recvbuf[DEFAULT_BUFLEN];
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;

    printf("please write letters to control program :\n");
    fgets(sendbuf, DEFAULT_BUFLEN, stdin);

    // Validate the parameters
    if (argc != 2) {
        printf("usage: %s server-name\n", argv[0]);
        return 1;
    }

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address and port
    iResult = getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    // Attempt to connect to an address until one succeeds
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        // Create a SOCKET for connecting to server
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
            ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            return 1;
        }

        // Connect to server.
        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }

    // Send an initial buffer
    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
        printf("send failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    printf("Bytes Sent: %ld\n", iResult);

    // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    // Receive until the peer closes the connection
    do {

        iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0)
            printf("Bytes received: %d\n", iResult);
        else if (iResult == 0)
            printf("Connection closed\n");
        else
            printf("recv failed with error: %d\n", WSAGetLastError());

    } while (iResult > 0);

    // cleanup
    closesocket(ConnectSocket);
    WSACleanup();

    return 0;
}

stdafx.h


#pragma once

#include <stdio.h>

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>


// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")


#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"

(I put the compiled server program and client program in the same folder as Python)

control.py


import subprocess as sp

if __name__ == "__main__":

    #Launch the exe file on the server side
    #This file is the SDK
    cmd = ".\\server.exe"
    pserve = sp.Popen(cmd)

    #Client-side program
    cmd = ".\\client.exe localhost"
    pclient = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE)
    out, err = pclient.communicate(input=b"Hello World 1st\n")
    pclient.kill()
    #You need to launch an instance every time you send a command
    #I think there is a better implementation
    cmd = ".\\client.exe localhost"
    pclient = sp.Popen(cmd, stdin=sp.PIPE, stdout=sp.PIPE)
    out, err = pclient.communicate(input=b"Hello World 2nd\n")
    pclient.kill()
    exit()

4. Miscellaneous feelings

I started thinking that the Python module would be able to handle it immediately, but I was addicted to it unexpectedly, so I wrote it down. I feel that there is a better solution (such as interprocess communication with PIPE), so I may take the opportunity to investigate more. However, since we implemented TCP communication, I think it was a good part that it could be executed from other PCs as a by-product. There may be another good control method using Python's socket module.

a1. Environment

OS:Win10 IDE: Visual Studio 2017 (both C and Python were created in VS2017) Python : Anaconda 5.0.1

a2. URL of reference site

Python official: subprocess [Caution] when you want to return a character string in a function (https://fa11enprince.hatenablog.com/entry/2014/06/10/023712) Winsock2 implementation example (TCP communication): Getting Started with Winsock2

Recommended Posts

Control other programs from Python (communication between Python and exe)
Control other programs from Python (communication between Python and exe)
Interprocess communication between Ruby and Python (POSIX message queue)
Serial communication control with python and I2C communication (using USBGPIO8 device)
Serial communication control with python and SPI communication (using USBGPIO8 device)
Difference between Ruby and Python split
Difference between list () and [] in Python
Difference between == and is in python
Python, yield, return, and sometimes yield from
Read and use Python files from Python
Study from Python Hour2: Control statements
Cooperation between python module and API
Differences between Python, stftime and strptime
About Python, from and import, as
Difference between python2 series and python3 series dict.keys ()
Python3 socket module and socket communication flow
[Python] Difference between function and method
Python --Difference between exec and eval
[Python] Difference between randrange () and randint ()
[Python] Difference between sorted and sorted (Colaboratory)
Communicate between Elixir and Python with gRPC
Differences in authenticity between Python and JavaScript
From Python to using MeCab (and CaboCha)
Differences between Ruby and Python in scope
Socket communication and multi-thread processing by Python
difference between statements (statements) and expressions (expressions) in Python
Differences in syntax between Python and Java
Difference between PHP and Python finally and exit
Difference between @classmethod and @staticmethod in Python
Difference between append and + = in Python list
Difference between nonlocal and global in Python
[Python] Difference between class method and static method
Porting and modifying doublet-solver from python2 to python3.
[Beginner] Installing Python and running programs (Windows)
[Python3] Switch between Shift_JIS, UTF-8 and ASCII
[Python Iroha] Difference between List and Tuple
Socket communication by C language and Python
[python] Difference between rand and randn output
Differences in multithreading between Python and Jython
Differences between Ruby and Python (basic syntax)
Correspondence between Python built-in functions and Rust
Exchange encrypted data between Python and C #
Study from Python Reading and writing Hour9 files
The answer of "1/2" is different between python2 and 3
[python] Difference between variables and self. Variables in class
[Python] How to read data from CIFAR-10 and CIFAR-100
[Python] Find coordinates from two angles and distance
[Python] Conversion memo between time data and numerical data
About the difference between "==" and "is" in python
Python hand play (interoperability between CSV and PostgreSQL)
Load and execute command from yml in python
[Python] Chapter 02-01 Basics of Python programs (operations and variables)
PHP and Python integration from scratch on Laravel
Let's control EV3 motors and sensors with Python
Serial communication between Raspberry pi --Arduino Uno (Python)
Generate and output plantuml object diagram from Python object