views:

119

answers:

4

Hi!

very basic question. i try to write a program that outputs the filenames of the files dragged onto the exe.

i followed this guide for main arguments: http://publications.gbdirect.co.uk/c_book/chapter10/arguments_to_main.html

this is my code:

#include "stdafx.h"
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    cout<<"num arguments: "<<argc<<"\n";

    while(argc--)
        printf("%s\n", *argv++);

    cout<<"press any key";
    getchar();
    return 0;
}

but all it output's is:

if i run it without dragging&dropping files onto the exe:

num arguments: 1

G

press any key

if i drag&drop 3 files it will output:

num arguments: 4

G

G

G

G

press any key

neither the application name, nor the name of any of the files starts with "G"

what's the problem?

thanks!

A: 

Use this:

while(argc--)
    printf("%s\n", argv[argc]);
Pablo Santa Cruz
doesn't solve the problem, still the same
Mat
+5  A: 

You're only getting the first letter because you're mixing Unicode and not. I can't explain the Gs sorry (unless all your files are on drive G:?) but you'll see the full filenames with

while(argc--)
    _tprintf(_T("%s\n"), *argv++);

instead.

Afterthought, and beaten to it by sbi's answer: I can't find a _tcout to use as

_tcout << *argv++;

instead if you wanted to stay C++ - I guess you'd have to define one, e.g.

#ifdef _UNICODE
#define _tcin wcin
#define _tcout wcout
#define _tcerr wcerr
#define _tclog wclog
#else
#define _tcin cin
#define _tcout cout
#define _tcerr cerr
#define _tclog clog
#endif

although this being C++ they probably shouldn't be defines but something else, and I'm not 100% sure what.

Rup
+1  A: 

argv[0] is always the executable's name. (Which means that argc > 0 is always true.) If you want to output wide characters (I think _TCHAR maps to wchar_t, BICBWT), you must not use narrow output. In C++, outputting is done using output streams. The wide console output stream is std::wcout.

#include <iostream>

//Beware, brain-compiled code ahead!
template< typename InpIt >
void output(InpIt begin, InpIt end)
{
  while(begin != end)
    std::wcout << *begin++ << L'\n';
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::wcout << L"num arguments: " << std::argc << L'\n';

    output(argv+1, argv+argc)

    return 0;
}

As Rup mentions, _TCHAR changes its value (char or wchar_t) depending on some definition.

First and foremost: Do you really need this switching? In practice, when you need wide characters, then most often you really need them and the program won't work correctly with narrow characters.
So it's very likely you can just settle to pure wide characters, get rid of the switching, and use the above code as written.

However, if you really need to switch, you need to switch between narrow and wide console streams yourself. (This is only true for the console stream objects, BTW. For your own streams, say, for example, file streams, you can just use _TCHAR and let the compiler figure out the rest: std::basic_ofstream<_TCHAR>.) One way to do this would be a traits class:

//Beware, brain-compiled code ahead!
template< typename Char >
struct console_stream_traits; // leave undefined

template<>
struct console_stream_traits<char> {
  typedef std::basic_ostream<char> ostream;
  typedef std::basic_istream<char> istream;
  std::basic_ostream<char>& cout = std::cout;
  std::basic_ostream<char>& cerr = std::cerr;
  std::basic_ostream<char>& clog = std::clog;
  std::basic_istream<char>& cin  = std::cin;
};

template<>
struct console_stream_traits<wchar_t> {
  typedef std::basic_ostream<wchar_>  ostream;
  typedef std::basic_istream<wchar_>  istream;
  std::basic_ostream<wchar_t>& cout = std::wcout;
  std::basic_ostream<wchar_t>& cerr = std::wcerr;
  std::basic_ostream<wchar_t>& clog = std::wclog;
  std::basic_istream<wchar_t>& cin  = std::wcin;
};

typedef console_stream_traits<_TCHAR> my_ostream;
typedef my_console_stream_traits::ostream    my_ostream;
typedef my_console_stream_traits::ostream    my_ostream;

my_ostream& my_cout = my_console_stream_traits::cout;
my_ostream& my_cerr = my_console_stream_traits::cerr;
my_ostream& my_clog = my_console_stream_traits::clog;
my_istream& my_cin  = my_console_stream_traits::cin;

With that, the loop in the output() function above would become:

while(begin != end)
  my_cout << *begin++ << _T('\n');
sbi
I was just writing something about that. TCHAR, _tmain etc. compile as char or wchar_t depending on `-D_UNICODE`, so you'd really need a `_tcout` conditionally defined to `cout` or `wcout` as appropriate. But I can't find one, and that surprises me.
Rup
@Rup: Thanks for clarifying this. I'll change my answer accordingly.
sbi
A: 

Just use ASCII 'main' function and everything will be OK. Change

int _tmain(int argc, _TCHAR* argv[])

to

int main(int argc, char *argv[])
adf88
You would also have to change Character Set in Configuration, General to 'Use MCBS' not 'Use Unicode'. But you should really always be using Unicode on Windows, and that goes double for filename handling.
Rup
"you should always..." - there are no such things in programming. The author is learning basics so it is OK now to leave the Unicode and work only with ASCII, for simplicity.@Rup: are you sure it won't work without changing the option? I have no VS right now to check...
adf88