4

If you try to change build type from Console (/SUBSYSTEM:CONSOLE) to Windows (/SUBSYSTEM:WINDOWS), you'll get an error complaining that entry point WinMain is missing:

MSVCRT.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16

I think the best way to get around this would be to call your normal int main(int, char**) from WinMain:

#ifdef _WINDOWS_
INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR lpCmdLine, INT nCmdShow)
{
    return main(0, NULL);
}
#endif

The problem is that ImageMagick is using the console parameters (and I plan to use them in future too):

Segmentation fault imagemagick init

So passing NULL and 0 is probably not a good idea. How can I convert WinMain arguments to main arugments?

Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • 1
    The `argv` array has to be terminated by a `NULL` pointer, so `argv[argc]` is always a valid expression and is a `NULL` pointer, which means you have to create an array of one entry, which is a `NULL` pointer and pass as the second argument to `main`. – Some programmer dude Dec 08 '14 at 17:55
  • This would solve the crash but still wouldn't pass the real arguments to my function. Thank you anyway. – Tomáš Zato Dec 08 '14 at 17:59
  • Possible duplicate of [Replacing WinMain() with main() function in Win32 programs](http://stackoverflow.com/questions/11785157/replacing-winmain-with-main-function-in-win32-programs) – R Sahu Dec 08 '14 at 17:59
  • @RSahu I have this already in open tab. But it doesn't really answer this question - or does it? – Tomáš Zato Dec 08 '14 at 18:01
  • @RSahu "*...unless you need the arguments..*" God can't you even read the damn **question title**? It says "**pass WinMain arguments**". So yes, I need to parse it (or convert or typecast or whatever is it called) - and this question is asking *how to do it*. – Tomáš Zato Dec 08 '14 at 22:42
  • Using the technique shown in the linked possible duplicate you can get the arguments without having to parse/convert them. Really up to you how to handle it, but even if you didn't end up using it right now, the accepted answer can be a really useful thing to know how to do. – Retired Ninja Dec 09 '14 at 00:39
  • @RetiredNinja Maybe the linked answer is even the correct way to do it. But I asked this question because I tried it *before asking* and failed. That's also why I was so upset when somebody proposed it's duplicate - I was already angry at that post for not helping me. – Tomáš Zato Dec 09 '14 at 00:43
  • 1
    It worked immediately for me, but mileage may vary and there's no particular "right" way to do anything, only what's right for you. Good luck. – Retired Ninja Dec 09 '14 at 03:14

2 Answers2

7

One way would be to use CommandLineToArgvW() to parse the result of GetCommandLineW() into an argv-style array of UTF-16 encoded strings, then use WideCharToMultiByte() to convert them to ANSI strings so you can then pass them to main() (assuming you can't use wmain() instead).

For example:

int w_argc = 0;
LPWSTR* w_argv = CommandLineToArgvW(GetCommandLineW(), &w_argc);
if (w_argv)
{
    char** my_argv = new char*[w_argc];
    int my_argc = 0;

    for (int i = 0; i < w_argc; ++i)
    {
        int w_len = lstrlenW(w_argv[i]);
        int len = WideCharToMultiByte(CP_ACP, 0, w_argv[i], w_len, NULL, 0, NULL, NULL);
        my_argv[my_argc] = new char[len+1];
        WideCharToMultiByte(CP_ACP, 0, wargv[i], w_len, my_argv[my_argc], len+1, NULL, NULL);
        ++my_argc;
    }

    main(my_argc, my_argv);

    for (int i = 0; i < my_argc; ++i)
        delete[] my_argv[i];
    delete[] my_argv;

    LocalFree(w_argv);
}

Alternatively:

int w_argc = 0;
LPWSTR* w_argv = CommandLineToArgvW(GetCommandLineW(), &w_argc);
if (w_argv)
{
    std vector<std::string> my_argv_buf;
    my_argv.reserve(w_argc);

    for (int i = 0; i < w_argc; ++i)
    {
        int w_len = lstrlenW(w_argv[i]);
        int len = WideCharToMultiByte(CP_ACP, 0, w_argv[i], w_len, NULL, 0, NULL, NULL);
        std::string s;
        s.resize(len);
        WideCharToMultiByte(CP_ACP, 0, wargv[i], w_len, &s[0], len, NULL, NULL);
        my_argv_buf.push_back(s);
    }

    std vector<char*> my_argv;
    my_argv.reserve(my_argv_buf.size());
    for (std vector<std::string>::iterator i = my_argv_buf.begin(); i != my_argv_buf.end(); ++i)
        my_argv.push_back(i->c_str());

    main(my_argv.size(), &my_argv[0]);

    LocalFree(w_argv);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
1

You can use CommandLineToArgvW to parse and convert the command line into the format expected by "normal" C main-style functions.

 int argc;
 LPWSTR *argvw = CommandLineToArgvW(GetCommandLineW(), &argc);

 CallCMainFunction(argvw, argc);
 LocalFree(argvw);

Note that this is in wide characters, as windows natively expects utf-16 command line arguments. If InitializeMagick only operates with narrow chars, you'll have to convert those as well (which can be done with wcstombs_s, with the caveats implied there

Logan Capaldo
  • 39,555
  • 5
  • 63
  • 78
  • Standard main is defined as `int main(int, char**)`. But `LPWSTR*` can't be converted to `char*` (and `&(LPWSTR*)` doesn't turn in `char**`). So I'm probably need to do that conversion somehow... – Tomáš Zato Dec 08 '14 at 23:07