winvlc.c 12 KB
Newer Older
1
/*****************************************************************************
2
 * winvlc.c: the Windows VLC media player
3
 *****************************************************************************
4
 * Copyright (C) 1998-2011 the VideoLAN team
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
 *          Samuel Hocevar <sam@zoy.org>
 *          Gildas Bazin <gbazin@videolan.org>
 *          Derk-Jan Hartman <hartman at videolan dot org>
 *          Lots of other people, see the libvlc AUTHORS file
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#define UNICODE
#include <vlc/vlc.h>
#include <windows.h>
34
#include <shellapi.h>
35

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
36 37 38 39 40 41 42 43 44 45
#ifndef _WIN32_IE
#  define  _WIN32_IE 0x501
#endif
#include <fcntl.h>
#include <io.h>
#include <shlobj.h>
#include <wininet.h>
#define PSAPI_VERSION 1
#include <psapi.h>
#define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1
46
static void check_crashdump(void);
47
LONG WINAPI vlc_exception_filter(struct _EXCEPTION_POINTERS *lpExceptionInfo);
48
static const wchar_t *crashdump_path;
49

50
static char *FromWide (const wchar_t *wide)
51
{
52 53
    size_t len;
    len = WideCharToMultiByte (CP_UTF8, 0, wide, -1, NULL, 0, NULL, NULL);
54

55 56 57 58
    char *out = (char *)malloc (len);
    if (out)
        WideCharToMultiByte (CP_UTF8, 0, wide, -1, out, len, NULL, NULL);
    return out;
59 60
}

61 62 63
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine,
                    int nCmdShow )
64
{
65
    int argc;
66

67 68 69 70 71 72 73
    /* VLC does not change the thread locale, so gettext/libintil will use the
     * user default locale as reference. */
    /* gettext versions 0.18-0.18.1 will use the Windows Vista locale name
     * if the GETTEXT_MUI environment variable is set. If not set or if running
     * on Windows 2000/XP/2003 an hard-coded language ID list is used. This
     * putenv() call may become redundant with later versions of gettext. */
    putenv("GETTEXT_MUI=1");
74 75
#ifdef TOP_BUILDDIR
    putenv("VLC_PLUGIN_PATH=Z:"TOP_BUILDDIR"/modules");
76
    putenv("VLC_DATA_PATH=Z:"TOP_SRCDIR"/share");
77 78
#endif

79
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
80

81
    /* SetProcessDEPPolicy */
82 83 84 85
    HINSTANCE h_Kernel32 = LoadLibraryW(L"kernel32.dll");
    if(h_Kernel32)
    {
        BOOL (WINAPI * mySetProcessDEPPolicy)( DWORD dwFlags);
86
        BOOL (WINAPI * mySetDllDirectoryA)(const char* lpPathName);
87 88 89 90 91 92
# define PROCESS_DEP_ENABLE 1

        mySetProcessDEPPolicy = (BOOL WINAPI (*)(DWORD))
                            GetProcAddress(h_Kernel32, "SetProcessDEPPolicy");
        if(mySetProcessDEPPolicy)
            mySetProcessDEPPolicy(PROCESS_DEP_ENABLE);
93 94

        /* Do NOT load any library from cwd. */
95 96
        mySetDllDirectoryA = (BOOL WINAPI (*)(const char*))
                            GetProcAddress(h_Kernel32, "SetDllDirectoryA");
97 98 99
        if(mySetDllDirectoryA)
            mySetDllDirectoryA("");

100 101 102
        FreeLibrary(h_Kernel32);
    }

103
    /* Args */
104 105 106
    wchar_t **wargv = CommandLineToArgvW (GetCommandLine (), &argc);
    if (wargv == NULL)
        return 1;
107

108
    char *argv[argc + 3];
109 110
    BOOL crash_handling = TRUE;
    int j = 0;
111

112
    argv[j++] = FromWide( L"--media-library" );
113 114
    argv[j++] = FromWide( L"--no-ignore-config" );
    for (int i = 1; i < argc; i++)
115 116 117 118
    {
        if(!wcscmp(wargv[i], L"--no-crashdump"))
        {
            crash_handling = FALSE;
Rafaël Carré's avatar
Rafaël Carré committed
119
            continue; /* don't give argument to libvlc */
120
        }
Rafaël Carré's avatar
Rafaël Carré committed
121 122

        argv[j++] = FromWide (wargv[i]);
123 124 125
    }

    argc = j;
126 127
    argv[argc] = NULL;
    LocalFree (wargv);
128 129 130

    if(crash_handling)
    {
131 132 133 134 135 136 137
        static wchar_t path[MAX_PATH];
        if( S_OK != SHGetFolderPathW( NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE,
                    NULL, SHGFP_TYPE_CURRENT, path ) )
            fprintf( stderr, "Can't open the vlc conf PATH\n" );
        swprintf( path+wcslen( path ), L"%s", L"\\vlc\\crashdump" );
        crashdump_path = &path[0];

138 139 140 141
        check_crashdump();
        SetUnhandledExceptionFilter(vlc_exception_filter);
    }

142
    _setmode( STDIN_FILENO, _O_BINARY ); /* Needed for pipes */
143

144
    /* Initialize libvlc */
145
    libvlc_instance_t *vlc;
146
    vlc = libvlc_new (argc, (const char **)argv);
147 148
    if (vlc != NULL)
    {
149
        libvlc_set_user_agent (vlc, "VLC media player", "VLC/"PACKAGE_VERSION);
150 151
        libvlc_add_intf (vlc, "globalhotkeys,none");
        libvlc_add_intf (vlc, NULL);
152
        libvlc_playlist_play (vlc, -1, 0, NULL);
153 154 155 156
        libvlc_wait (vlc);
        libvlc_release (vlc);
    }

157 158 159 160
    for (int i = 0; i < argc; i++)
        free (argv[i]);

    (void)hInstance; (void)hPrevInstance; (void)lpCmdLine; (void)nCmdShow;
161
    return 0;
162
}
163

164
/* Crashdumps handling */
165
static void check_crashdump(void)
166
{
167 168 169 170
    FILE * fd = _wfopen ( crashdump_path, L"r, ccs=UTF-8" );
    if( !fd )
        return;
    fclose( fd );
171

172 173 174
    int answer = MessageBox( NULL, L"VLC media player just crashed." \
    " Do you want to send a bug report to the developers team?",
    L"VLC crash reporting", MB_YESNO);
175

176
    if(answer == IDYES)
177
    {
178 179
        HINTERNET Hint = InternetOpen(L"VLC Crash Reporter",
                INTERNET_OPEN_TYPE_PRECONFIG, NULL,NULL,0);
180
        if(Hint)
181
        {
182
            HINTERNET ftp = InternetConnect(Hint, L"crash.videolan.org",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
183
                        INTERNET_DEFAULT_FTP_PORT, NULL, NULL,
184
                        INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0);
185
            if(ftp)
186
            {
187 188 189
                SYSTEMTIME now;
                GetSystemTime(&now);
                wchar_t remote_file[MAX_PATH];
190 191 192 193 194 195 196 197 198
                swprintf(remote_file,
                        L"/crashes-win32/%04d%02d%02d%02d%02d%02d",
                        now.wYear, now.wMonth, now.wDay, now.wHour,
                        now.wMinute, now.wSecond );

                if( FtpPutFile( ftp, crashdump_path, remote_file,
                            FTP_TRANSFER_TYPE_BINARY, 0) )
                    MessageBox( NULL, L"Report sent correctly. Thanks a lot \
                            for the help.", L"Report sent", MB_OK);
199
                else
200 201
                    MessageBox( NULL, L"There was an error while \
                            transferring to the FTP server. "\
202 203 204
                                "Thanks a lot for the help anyway.",
                                L"Report sending failed", MB_OK);
                InternetCloseHandle(ftp);
205
            }
206 207
            else
            {
208 209
                MessageBox( NULL, L"There was an error while connecting to \
                        the FTP server. "\
210 211 212 213
                                "Thanks a lot for the help anyway.",
                                L"Report sending failed", MB_OK);
                fprintf(stderr,"Can't connect to FTP server 0x%08lu\n",
                        (unsigned long)GetLastError());
214
            }
215 216 217 218
            InternetCloseHandle(Hint);
        }
        else
        {
219 220
              MessageBox( NULL, L"There was an error while connecting to the \
                      Internet. "\
221 222
                                "Thanks a lot for the help anyway.",
                                L"Report sending failed", MB_OK);
223 224
        }
    }
225 226

    _wremove(crashdump_path);
227 228
}

229 230 231 232 233
/*****************************************************************************
 * vlc_exception_filter: handles unhandled exceptions, like segfaults
 *****************************************************************************/
LONG WINAPI vlc_exception_filter(struct _EXCEPTION_POINTERS *lpExceptionInfo)
{
234
    if(IsDebuggerPresent())
235
    {
236 237
        //If a debugger is present, pass the exception to the debugger
        //with EXCEPTION_CONTINUE_SEARCH
238
        return EXCEPTION_CONTINUE_SEARCH;
239
    }
240
    else
241
    {
242
        fprintf( stderr, "unhandled vlc exception\n" );
243

244
        FILE * fd = _wfopen ( crashdump_path, L"w, ccs=UTF-8" );
245 246 247 248 249 250

        if( !fd )
        {
            fprintf( stderr, "\nerror while opening file" );
            exit( 1 );
        }
251

252 253 254 255 256
        OSVERSIONINFO osvi;
        ZeroMemory( &osvi, sizeof(OSVERSIONINFO) );
        osvi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
        GetVersionEx( &osvi );

257 258 259 260 261 262 263 264 265 266 267
        fwprintf( fd, L"[version]\nOS=%d.%d.%d.%d.%s\nVLC=" VERSION_MESSAGE,
                osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber,
                osvi.dwPlatformId, osvi.szCSDVersion);

        const CONTEXT *const pContext = (const CONTEXT *)
            lpExceptionInfo->ContextRecord;
        const EXCEPTION_RECORD *const pException = (const EXCEPTION_RECORD *)
            lpExceptionInfo->ExceptionRecord;
        /* No nested exceptions for now */
        fwprintf( fd, L"\n\n[exceptions]\n%08x at %px", 
                pException->ExceptionCode, pException->ExceptionAddress );
268

Rafaël Carré's avatar
Rafaël Carré committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
        for( unsigned int i = 0; i < pException->NumberParameters; i++ )
            fwprintf( fd, L" | %p", pException->ExceptionInformation[i] );

#ifdef WIN64
        fwprintf( fd, L"\n\n[context]\nRDI:%px\nRSI:%px\n" \
                    "RBX:%px\nRDX:%px\nRCX:%px\nRAX:%px\n" \
                    "RBP:%px\nRIP:%px\nRSP:%px\nR8:%px\n" \
                    "R9:%px\nR10:%px\nR11:%px\nR12:%px\n" \
                    "R13:%px\nR14:%px\nR15:%px\n",
                        pContext->Rdi,pContext->Rsi,pContext->Rbx,
                        pContext->Rdx,pContext->Rcx,pContext->Rax,
                        pContext->Rbp,pContext->Rip,pContext->Rsp,
                        pContext->R8,pContext->R9,pContext->R10,
                        pContext->R11,pContext->R12,pContext->R13,
                        pContext->R14,pContext->R15 );
#else
        fwprintf( fd, L"\n\n[context]\nEDI:%px\nESI:%px\n" \
                    "EBX:%px\nEDX:%px\nECX:%px\nEAX:%px\n" \
                    "EBP:%px\nEIP:%px\nESP:%px\n",
288 289 290
                        pContext->Edi,pContext->Esi,pContext->Ebx,
                        pContext->Edx,pContext->Ecx,pContext->Eax,
                        pContext->Ebp,pContext->Eip,pContext->Esp );
Rafaël Carré's avatar
Rafaël Carré committed
291
#endif
292

293
        fwprintf( fd, L"\n[stacktrace]\n#EIP|base|module\n" );
294

Rafaël Carré's avatar
Rafaël Carré committed
295 296 297 298 299 300 301 302
#ifdef WIN64
        LPCVOID caller = (LPCVOID)pContext->Rip;
        LPVOID *pBase  = (LPVOID*)pContext->Rbp;
#else
        LPVOID *pBase  = (LPVOID*)pContext->Ebp;
        LPCVOID caller = (LPCVOID)pContext->Eip;
#endif
        for( unsigned frame = 0; frame <= 100; frame++ )
303
        {
Rafaël Carré's avatar
Rafaël Carré committed
304 305 306 307 308 309 310 311 312 313 314 315
            MEMORY_BASIC_INFORMATION mbi;
            wchar_t module[ 256 ];
            VirtualQuery( caller, &mbi, sizeof( mbi ) ) ;
            GetModuleFileName( mbi.AllocationBase, module, 256 );
            fwprintf( fd, L"%p|%s\n", caller, module );

            /*The last BP points to NULL!*/
            caller = *(pBase + 1);
            if( !caller )
                break;
            pBase = *pBase;
        }
316

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
        HANDLE hpid = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
                                        FALSE, GetCurrentProcessId());
        if (hpid) {
            HMODULE mods[1024];
            DWORD size;
            if (EnumProcessModules(hpid, mods, sizeof(mods), &size)) {
                fwprintf( fd, L"\n\n[modules]\n" );
                for (unsigned int i = 0; i < size / sizeof(HMODULE); i++) {
                    wchar_t module[ 256 ];
                    GetModuleFileName(mods[i], module, 256);
                    fwprintf( fd, L"%p|%s\n", mods[i], module);
                }
            }
            CloseHandle(hpid);
        }

333 334 335 336
        fclose( fd );
        fflush( stderr );
        exit( 1 );
    }
337
}