locked
WinMain argc and argv - can't get back argv RRS feed

  • Question

  • In my program I am running a Windows service using:

    extern

    "C"intWINAPI_tWinMain(HINSTANCE, HINSTANCE, LPTSTR, intnShowCmd) //hInstance, hPrevInstance, lpCmdLine


    {.....

    at the start of my code and have decided to try to use __argc and __argv rather than

    GetCommandLine. I decided that my code might read better if I used __argc and __argv. However, although __argc comes out fine as 4, __argv seems to give me problems. I try to do the following:

    string configurationInputFile =__argv[1];

    and then write configurationInputFile out to a log file and the program crashes. Any ideas would be welcome. Snippet of code is:

    extern

    "C"intWINAPI_tWinMain(HINSTANCE, HINSTANCE, LPTSTR, intnShowCmd) //hInstance, hPrevInstance, lpCmdLine

    LOG_INFO("<=============================== START OF PROGRAM ===============================>"); 

    LOG_INFO("ServiceControllerService: Main: Entry, Log level: LOG_INFO"); 

    try

      {  

          //Read in command line arguments   

          int         argc = 0; 

          char**      argv = NULL;

          argc = __argc;

          argv = __argv;

          ostringstreamss, ss2;

          ss <<"ServiceControllerService: Main: argc = "<<argc <<", Log level: LOG_INFO";

          LOG_INFO(ss.str()); 

          LOG_INFO("Before config file read name");

          string configurationInputFile =argv[1];

          stringstrMessage = "ServiceControllerService: Main: config file="+configurationInputFile +", Log level: LOG_INFO";

        LOG_INFO(strMessage);

    Doesn't write out last LOG_INFO message and program stops.

    Monday, October 2, 2017 2:40 PM

All replies

  • Try using CommandLineToArgvW() in your code.

    Something like

    LPWSTR *szArglist;
    int nArgs;
    szArglist = CommandLineToArgvW(GetCommandLine(), &nArgs);

    //          Now nArgs is your argc and szarglist is your argv

       LocalFree(szArglist);

    Thanks


    Rupesh Shukla

    Monday, October 2, 2017 3:22 PM
  • Thanks. Looks much neater than what I had originally
    Monday, October 2, 2017 4:02 PM
  • If you want to capture command line arguments directly they are passed to the ServiceMain function. See ServiceMain callback function

    The following debugging output shows the difference between what is available in WinMain versus ServiceMain (Unicode build).

    [3064] WinMain lpCmdLine is 00FB147A
    [3064] WinMain __argc is 1, __argv is 00000000 __wargv is 00FB8A98
    [3064] WinMain argv[0] is C:\Users\RLWA32\Documents\Visual Studio 2015\Projects\ATLProject1\Debug\ATLProject1.exe
    [3064] ServiceMain __argc is 1, __argv is 00000000 __wargv is 00FB8A98
    [3064] ServiceMain dwArgc is 3
    [3064] ServiceMain lpszArgv is 00FBCA80
    [3064] ServiceMain lpszArgv[0] is ATLProject1
    [3064] ServiceMain lpszArgv[1] is parm1
    [3064] ServiceMain lpszArgv[2] is parm2

    • Edited by RLWA32 Monday, October 2, 2017 4:35 PM added info
    Monday, October 2, 2017 4:30 PM
  • are you saying you can only get the application name in WinMain and no other arguments?
    Monday, October 2, 2017 4:46 PM
  • are you saying you can only get the application name in WinMain and no other arguments?

    That seems to be the result of my test.
    Monday, October 2, 2017 4:50 PM
  • In my program I am running a Windows service using:

    extern

    "C"intWINAPI_tWinMain(HINSTANCE, HINSTANCE, LPTSTR, intnShowCmd) //hInstance, hPrevInstance, lpCmdLine


    {.....

    at the start of my code and have decided to try to use __argc and __argv rather than

    GetCommandLine. I decided that my code might read better if I used __argc and __argv. However, although __argc comes out fine as 4, __argv seems to give me problems. I try to do the following:

    string configurationInputFile =__argv[1];

    and then write configurationInputFile out to a log file and the program crashes. Any ideas would be welcome. Snippet of code is:

    extern

    "C"intWINAPI_tWinMain(HINSTANCE, HINSTANCE, LPTSTR, intnShowCmd) //hInstance, hPrevInstance, lpCmdLine

    LOG_INFO("<=============================== START OF PROGRAM ===============================>"); 

    LOG_INFO("ServiceControllerService: Main: Entry, Log level: LOG_INFO"); 

    try

      {  

          //Read in command line arguments   

          int         argc = 0; 

          char**      argv = NULL;

          argc = __argc;

          argv = __argv;

          ostringstreamss, ss2;

          ss <<"ServiceControllerService: Main: argc = "<<argc <<", Log level: LOG_INFO";

          LOG_INFO(ss.str()); 

          LOG_INFO("Before config file read name");

          string configurationInputFile =argv[1];


    In a Unicode build, this appears to work for me:

    int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { int argc = 0; //char** argv = NULL; argc = __argc; //argv = __argv; wchar_t** wargv = NULL; wargv = __wargv; //wstring configurationInputFile =__wargv[1]; // OK wstring configurationInputFile = wargv[1]; // OK

        wstring  arg2 = wargv[2]; // OK
        wstring  arg3 = wargv[3]; // OK



    - Wayne

    Tuesday, October 3, 2017 12:49 AM
  • >In a Unicode build, this appears to work for me:

    And the OP's code of:

        int argc = 0; 
    
        char** argv = NULL;
    
        argc = __argc;
    
        argv = __argv;
    
       string configurationInputFile = argv[1];
    

    works for me in a non-Unicode build.

    - Wayne

    Tuesday, October 3, 2017 1:04 AM
  • I ran the test again (Unicode build) and received exactly the same results.

    Service start -  "sc start atlproject1 parm1 parm2"

    Results -

    2892] WinMain __argc is 1, __argv is 00000000, __wargv is 00D48A98
    [2892] WinMain __wargv[0] is C:\Users\RLWA32\Documents\Visual Studio 2015\Projects\ATLProject1\Debug\ATLProject1.exe
    [2892] ServiceMain dwArgc is 3, lpszArgv is 00D4CEC0
    [2892] ServiceMain lpszArgv[0] is ATLProject1
    [2892] ServiceMain lpszArgv[1] is parm1
    [2892] ServiceMain lpszArgv[2] is parm2
    

    The service was created with VS 2015 Community Update3.  The __argc variable does not reflect the actual number of parameters.  Perhaps this is a VS2015 bug?

    Tuesday, October 3, 2017 3:56 AM

  • The service was created with VS 2015 Community Update3.  The __argc variable does not reflect the actual number of parameters.  Perhaps this is a VS2015 bug?

    <shrug> The results I described above were from VS2012 Express with 
    Update 4. I notice that the documentation says:

    "These global variables provide the arguments to main or wmain."

    No mention of their use with WinMain or wWinMain, so I wonder if that has 
    ever been officially supported. Then again, as these variables are provided
    by the C Runtime Library, maybe they got broken during the Great Migration
    to the Universal C RTL.

    - Wayne

    Tuesday, October 3, 2017 7:18 AM

  •  I notice that the documentation says:

    "These global variables provide the arguments to main or wmain."

    No mention of their use with WinMain or wWinMain, so I wonder if that has 
    ever been officially supported. Then again, as these variables are provided
    by the C Runtime Library, maybe they got broken during the Great Migration
    to the Universal C RTL.

    I noticed that qualification in the documentation also.  But when indications were that __argc and __argv contained meaningful information it makes one wonder if the docs might be inaccurate.

    Interestingly, the variables do appear to contain the right values when the service application was invoked from the command line for registration as opposed to being started by the service controller.


    • Edited by RLWA32 Tuesday, October 3, 2017 8:47 AM
    Tuesday, October 3, 2017 8:46 AM
  • I was experimenting with VS 2013 and the following code:

    	szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
    	if (NULL == szArglist)
    	{
    		wprintf(L"CommandLineToArgvW failed\n");
    		return -1;
    	}
    	else
    	{
    		for (i = 0; i < nArgs; i++)
    		{
    			{wchar_t  buffer[BUFSIZ];
    			swprintf_s(buffer, BUFSIZ, L"_tWinMain <<< i: %d argList ==>%s<==\n", i, szArglist[i]);
    			OutputDebugStringW(buffer); }
    		}
    	}
    
    	// **** free memory allocated for CommandLineToArgvW arguments ****
    	LocalFree(szArglist);

    I would only get back the name of the application (e.g., *.exe).

    Switched to VS 2017 and I got back the name of the app (*.exe) and the 4 arguments.

    _tWinMain <<< lpCmdLine ==>C:\Temp\CoverPhotos cropped_cover.jpg rotated_cover.jpg 45<==
    _tWinMain <<< cmdLine ==>"C:\MyProjects\ImageRotation\Debug\ImageRotation.exe" C:\Temp\CoverPhotos cropped_cover.jpg rotated_cover.jpg 45<==
    _tWinMain <<< i: 0 argList ==>C:\MyProjects\ImageRotation\Debug\ImageRotation.exe<==
    _tWinMain <<< i: 1 argList ==>C:\Temp\CoverPhotos<==
    _tWinMain <<< i: 2 argList ==>cropped_cover.jpg<==
    _tWinMain <<< i: 3 argList ==>rotated_cover.jpg<==
    _tWinMain <<< i: 4 argList ==>45<==

    I am pretty confident it will also work with VS 2019 :o)

    Thursday, December 19, 2019 2:27 PM