Link to home
Start Free TrialLog in
Avatar of StMike38
StMike38Flag for United States of America

asked on

Losing latter half of command line in Visual Studio C++ online program

At http://www.MarpX.com/framistat.asp there is an explanation of the loss of the latter half of the command line within a program hosted on a Windows 2003 server. The setup, issue, question, and source code are on page Framistat.asp. The result of running the program is found by clicking on the "Launch Framistat" button.

The question: How does one get at the missing part of the command line?
Avatar of sarabande
sarabande
Flag of Luxembourg image

could you post a description of your problem here?

Sara
Avatar of StMike38

ASKER

Content of marpx.com/framistat.asp follows:

Test access to command line within server program Framistat.exe

Launch Framistat

        Settings: Framistat.cpp is a Visual Studio 15 console application with precompiled header using MFC in a static library. It's a radically reduced testing subset of a real program. The platform toolset is Visual Studio 2015 - Windows XP (v140_xpl). The target is a Windows 2003 server. The character set is inherited (i.e., not Unicode). The header files and stdafx.cpp are all auto-generated and unchanged.

         

        Issue: Clicking Launch Framistat above reveals the loss of the second half of the command line within the program.

         

        Question: How does one get at the lost portion of the command line?

         

        The source code:
        // Framistat.cpp : Defines the entry point for the console application.
        // May 12, 2016

        #include "stdafx.h"
        #include <WinBase.h>
        #include "Framistat.h"

        #ifdef _DEBUG
        #define new DEBUG_NEW
        #endif


        // The one and only application object

        CWinApp theApp;

        using namespace std;

        int main()
        {
        int nRetCode = 0;

        HMODULE hModule = ::GetModuleHandle(nullptr);
        if (hModule == NULL)
        {
        _tprintf(_T("Fatal Error: GetModuleHandle failed\n"));
        return(1);
        }

        // initialize MFC and print and error on failure
        if (!AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0))
        {
        // TODO: change error code to suit your needs
        wprintf(L"Fatal Error: MFC initialization failed\n");
        return(1);
        }

        // Initialize output page.
        printf("Content-type: text/html\n\n");
        printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 ");
        printf("Transitional//EN\"\n");
        printf("\"http://www.w3.org/TR/html4/loose.dtd\">\n");
        printf("<html><head>\n");
        printf("<meta http-equiv=\"Content-Type\"");
        printf(" content=\"text/html; charset=iso-8859-1\">\n");
        printf("<title>Framistat Command Line Test</title>\n");
        printf("\n</head>\n<body>\n");

        printf("<blockquote><blockquote>\n");
        printf("<p>Entered program.</p>\n");
        printf("<p>Command line sent to program: /cgi-bin/Framistat.exe?key=Once+upon+a+time&action=start&Submit=&</p>\n");

        // Try to fetch the command line.

        char *lpCmdLine = ::GetCommandLine();

        // Output the command line as found within the program, with a few
        // bytes beyond its end.

        printf("<p>Command line received via function GetCommandLine(). The ");
        printf("printout shows the first characters after the result, showing ");
        printf("that the remainder is not contiguous in memory.</p>\n");
        printf("<p>");
        int CtNulls = 0;
        unsigned char uc;
        int i = 0;
        while (CtNulls < 2)
        {
        uc = (unsigned char)*(lpCmdLine + i++);
        if (uc < 0x21 || uc > 0x7e)
        printf("\\%02x", uc);
        else
        putchar(uc);
        if (uc == '\0')
        CtNulls++;
        }
        printf("</p>\n<p>Question: What is the appropriate function to get access");
        printf(" to the rest of the command line?</p>\n</body></html>\n");

        return(0);
        }

Open in new window

Well, you could try changing line 67: while (CtNulls < 2)

At the moment, after it hits to null chars it stops.

Since this looks like it's supposed to be a CGI script I've no way of actually testing it other than hacking it to make it work as a command line program and that gives me the full output.
the problem probably is because you have 'UNICODE' character set enabled, what means that GetCommandLine returns a wide character string (where every second byte is zero).

you may try the following:

#include <string>
#include <iostream>

....
std::wstring strCommandLine = GetCommandLine(); 
std::wcout << strCommandLine.c_str() << L"\n";

return 0;

Open in new window


and set a breakpoint at the return statement. if that doesn't compile try std::string and std::cout instead.

alternatively, use the following code to parse the command line

 
 LPWSTR * pszArglist = NULL;
   int nArgs = 0;
   int i = 0;

  pszArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
   if (pszArglist != NULL)
   {
        for ( i=0; i<nArgs; ++i) 
               printf("%d: %ws\n", i, pszArglist[i]);
   }
   LocalFree(pszArglist) ;
   return 0;

Open in new window


Sara
Sorry, so far no joy.
[1] Evilrix: Extending the count simply demonstrates further that the arguments are separated in memory from the pre-question mark portion of the command line. The demo is now set to 40. Try it.

[2] Sarabande: There is no Unicode in the program. To be able to run the program on a Windows 2003 server, I used platform toolset Windows XP (V140-XP). That opens a character set option "inherit..." which maintains all strings in 8 bit characters. The rest of the real program (not this artificial snippet) is dependent on 8 bit character strings, hence the No-Unicode choice here. This is demonstrated in the memory dump of the first part of the command line and what follows, shown if you click "Launch Framistat" at www.MarpX.com/Framistat.asp.

[3] Sarabande: strCommandLine.c_str and arguments from CommandLineToArgW are similarly truncated after the .exe. Visual Studio 2015 is parking the portion of the command line after the question mark somewhere else in memory.

The problem remains: How to locate and access the rest of the command line.
May 14: I have revised the source code and www.marpx.com/Framistat.asp slightly in an attempt to make the problem even more clear. ExpertsExchange chooses not to allow upload of files with extension .vcxproj, so the full project cannot be attached here.  The revised source code shows in Framistat.asp. The result of launching the program is a browser page that demonstrates the result of three methods of trying to get at the command line.

Incidentally, I now get the same problem with Visual Studio 2010. That's not happened in the past. What the heck is Microsoft doing? Why can't the full command line be easily accessible?
not to allow upload of files with extension .vcxproj
you may rename a copy of the file to have a *.txt extension.


There is no Unicode in the program.

open the project properties in visual studio 10. look at configuration properties - general page. there is a property called 'character set'. it must be set to 'multi-byte character' in order to get single-byte strings from GetCommandLine.

did you try the second code snippet i posted? it is not dependent on the character set used of your project but calls the 'W'ide version  of GetCommandLine. that is because the parsing function CommandLineToArgvW which turns the commandline back to an array of strings only exists in the W variant.

to get the result array as an array of zero-terminated char strings you could do:

LPWSTR * pszArglist = NULL;
int nArgs = 0;
int i = 0;
pszArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
if (pszArglist != NULL)
{       
        int len = 0;
        char ** ppsz = new char*[nArgs];
        for ( i=0; i<nArgs; ++i) 
        {
               printf("%d: %ws\n", i, pszArglist[i]);
               len = (int)wcslen(pszArglist[i]);
               ppsz[i] = new wchar_t[len+10];  // add some extra bytes
               wcstombs(ppsz[i],  pszArglist[i], len+10);
               printf("%d: %s\n", i, ppsz[i]);
        }
        // the arguments are now available in ppsz array

        ...
        // free memory after use of ppsz
        for (i = 0; i < nArgs; ++i)
        {
               delete []ppsz[i];
        } 
        delete ppsz;

Open in new window


Sara
Sarabande -- Thanks for your efforts.

I took your code and injected it early in the source code.Framistat.zip The results were very much like before... still no access to the part of the command line that followed ".exe?".

To all who are interested: The zip file contains the source, project files, a resulting .htm, and a set of notes in a .doc file. The .txt must be dropped from all the .txt files except readme.txt in order to compile the project. Please look over the notes in the .doc file, especially those about settings used in the project. If someone could compile and put the executable up on a more modern server, that would be very revealing. Success there would suggest that the Windows 2003 server that I have been using has gone defective. It would need a test on a newer server to confirm this possibility.

Thanks.
ASKER CERTIFIED SOLUTION
Avatar of sarabande
sarabande
Flag of Luxembourg image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Well done. Thank you for your persistence, Sara.

For those who are puzzled by how to express the command line through an html link, the trick is to convert all punctuation characters to %xx hex codes. Example: <a href="/cgi-bin/Framistat.exe?key%3DOnce%2Bupon%2Ba%2Btime%26%20action%3Dstart%26%20Submit%3D%26">Launch Framistat</a>

OR eliminate the spaces (%20)  if the command line is to be parsed as a block:  <a href="/cgi-bin/Framistat.exe?key%3DOnce%2Bupon%2Ba%2Btime%26action%3Dstart%26Submit%3D%26">Without blanks</a>