PageRenderTime 87ms CodeModel.GetById 20ms app.highlight 43ms RepoModel.GetById 18ms app.codeStats 0ms

/indra/newview/llappviewerwin32.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 578 lines | 410 code | 84 blank | 84 comment | 43 complexity | 497bb9b28eab65bb84c9181cbd130bf4 MD5 | raw file
  1/**
  2 * @file llappviewerwin32.cpp
  3 * @brief The LLAppViewerWin32 class definitions
  4 *
  5 * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */ 
 26
 27#include "llviewerprecompiledheaders.h"
 28
 29#include "llappviewerwin32.h"
 30
 31#include "llmemtype.h"
 32
 33#include "llwindowwin32.cpp" // *FIX: for setting gIconResource.
 34#include "res/resource.h" // *FIX: for setting gIconResource.
 35
 36#include <fcntl.h>		//_O_APPEND
 37#include <io.h>			//_open_osfhandle()
 38#include <errorrep.h>	// for AddERExcludedApplicationA()
 39#include <process.h>	// _spawnl()
 40#include <tchar.h>		// For TCHAR support
 41
 42#include "llviewercontrol.h"
 43#include "lldxhardware.h"
 44
 45#include "llweb.h"
 46#include "llsecondlifeurls.h"
 47
 48#include "llviewernetwork.h"
 49#include "llmd5.h"
 50#include "llfindlocale.h"
 51
 52#include "llcommandlineparser.h"
 53#include "lltrans.h"
 54
 55#ifndef LL_RELEASE_FOR_DOWNLOAD
 56#include "llwindebug.h"
 57#endif
 58
 59// *FIX:Mani - This hack is to fix a linker issue with libndofdev.lib
 60// The lib was compiled under VS2005 - in VS2003 we need to remap assert
 61#ifdef LL_DEBUG
 62#ifdef LL_MSVC7 
 63extern "C" {
 64    void _wassert(const wchar_t * _Message, const wchar_t *_File, unsigned _Line)
 65    {
 66        llerrs << _Message << llendl;
 67    }
 68}
 69#endif
 70#endif
 71
 72const std::string LLAppViewerWin32::sWindowClass = "Second Life";
 73
 74// Create app mutex creates a unique global windows object. 
 75// If the object can be created it returns true, otherwise
 76// it returns false. The false result can be used to determine 
 77// if another instance of a second life app (this vers. or later)
 78// is running.
 79// *NOTE: Do not use this method to run a single instance of the app.
 80// This is intended to help debug problems with the cross-platform 
 81// locked file method used for that purpose.
 82bool create_app_mutex()
 83{
 84	bool result = true;
 85	LPCWSTR unique_mutex_name = L"SecondLifeAppMutex";
 86	HANDLE hMutex;
 87	hMutex = CreateMutex(NULL, TRUE, unique_mutex_name); 
 88	if(GetLastError() == ERROR_ALREADY_EXISTS) 
 89	{     
 90		result = false;
 91	}
 92	return result;
 93}
 94
 95//#define DEBUGGING_SEH_FILTER 1
 96#if DEBUGGING_SEH_FILTER
 97#	define WINMAIN DebuggingWinMain
 98#else
 99#	define WINMAIN WinMain
100#endif
101
102int APIENTRY WINMAIN(HINSTANCE hInstance,
103                     HINSTANCE hPrevInstance,
104                     LPSTR     lpCmdLine,
105                     int       nCmdShow)
106{
107	LLMemType mt1(LLMemType::MTYPE_STARTUP);
108
109	const S32 MAX_HEAPS = 255;
110	DWORD heap_enable_lfh_error[MAX_HEAPS];
111	S32 num_heaps = 0;
112	
113#if WINDOWS_CRT_MEM_CHECKS && !INCLUDE_VLD
114	_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); // dump memory leaks on exit
115#elif 1
116	// Experimental - enable the low fragmentation heap
117	// This results in a 2-3x improvement in opening a new Inventory window (which uses a large numebr of allocations)
118	// Note: This won't work when running from the debugger unless the _NO_DEBUG_HEAP environment variable is set to 1
119
120	_CrtSetDbgFlag(0); // default, just making explicit
121	
122	ULONG ulEnableLFH = 2;
123	HANDLE* hHeaps = new HANDLE[MAX_HEAPS];
124	num_heaps = GetProcessHeaps(MAX_HEAPS, hHeaps);
125	for(S32 i = 0; i < num_heaps; i++)
126	{
127		bool success = HeapSetInformation(hHeaps[i], HeapCompatibilityInformation, &ulEnableLFH, sizeof(ulEnableLFH));
128		if (success)
129			heap_enable_lfh_error[i] = 0;
130		else
131			heap_enable_lfh_error[i] = GetLastError();
132	}
133#endif
134	
135	// *FIX: global
136	gIconResource = MAKEINTRESOURCE(IDI_LL_ICON);
137
138	LLAppViewerWin32* viewer_app_ptr = new LLAppViewerWin32(lpCmdLine);
139	
140	viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash);
141
142	// Set a debug info flag to indicate if multiple instances are running.
143	bool found_other_instance = !create_app_mutex();
144	gDebugInfo["FoundOtherInstanceAtStartup"] = LLSD::Boolean(found_other_instance);
145
146	bool ok = viewer_app_ptr->init();
147	if(!ok)
148	{
149		llwarns << "Application init failed." << llendl;
150		return -1;
151	}
152	
153	// Have to wait until after logging is initialized to display LFH info
154	if (num_heaps > 0)
155	{
156		llinfos << "Attempted to enable LFH for " << num_heaps << " heaps." << llendl;
157		for(S32 i = 0; i < num_heaps; i++)
158		{
159			if (heap_enable_lfh_error[i])
160			{
161				llinfos << "  Failed to enable LFH for heap: " << i << " Error: " << heap_enable_lfh_error[i] << llendl;
162			}
163		}
164	}
165	
166	// Run the application main loop
167	if(!LLApp::isQuitting()) 
168	{
169		viewer_app_ptr->mainLoop();
170	}
171
172	if (!LLApp::isError())
173	{
174		//
175		// We don't want to do cleanup here if the error handler got called -
176		// the assumption is that the error handler is responsible for doing
177		// app cleanup if there was a problem.
178		//
179#if WINDOWS_CRT_MEM_CHECKS
180    llinfos << "CRT Checking memory:" << llendflush;
181	if (!_CrtCheckMemory())
182	{
183		llwarns << "_CrtCheckMemory() failed at prior to cleanup!" << llendflush;
184	}
185	else
186	{
187		llinfos << " No corruption detected." << llendflush;
188	}
189#endif
190	
191	gGLActive = TRUE;
192
193	viewer_app_ptr->cleanup();
194	
195#if WINDOWS_CRT_MEM_CHECKS
196    llinfos << "CRT Checking memory:" << llendflush;
197	if (!_CrtCheckMemory())
198	{
199		llwarns << "_CrtCheckMemory() failed after cleanup!" << llendflush;
200	}
201	else
202	{
203		llinfos << " No corruption detected." << llendflush;
204	}
205#endif
206	 
207	}
208	delete viewer_app_ptr;
209	viewer_app_ptr = NULL;
210
211	//start updater
212	if(LLAppViewer::sUpdaterInfo)
213	{
214		_spawnl(_P_NOWAIT, LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), LLAppViewer::sUpdaterInfo->mParams.str().c_str(), NULL);
215
216		delete LLAppViewer::sUpdaterInfo ;
217		LLAppViewer::sUpdaterInfo = NULL ;
218	}
219
220	return 0;
221}
222
223#if DEBUGGING_SEH_FILTER
224// The compiler doesn't like it when you use __try/__except blocks
225// in a method that uses object destructors. Go figure.
226// This winmain just calls the real winmain inside __try.
227// The __except calls our exception filter function. For debugging purposes.
228int APIENTRY WinMain(HINSTANCE hInstance,
229                     HINSTANCE hPrevInstance,
230                     LPSTR     lpCmdLine,
231                     int       nCmdShow)
232{
233    __try
234    {
235        WINMAIN(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
236    }
237    __except( viewer_windows_exception_handler( GetExceptionInformation() ) )
238    {
239        _tprintf( _T("Exception handled.\n") );
240    }
241}
242#endif
243
244void LLAppViewerWin32::disableWinErrorReporting()
245{
246	const char win_xp_string[] = "Microsoft Windows XP";
247	BOOL is_win_xp = ( getOSInfo().getOSString().substr(0, strlen(win_xp_string) ) == win_xp_string );		/* Flawfinder: ignore*/
248	if( is_win_xp )
249	{
250		// Note: we need to use run-time dynamic linking, because load-time dynamic linking will fail
251		// on systems that don't have the library installed (all non-Windows XP systems)
252		HINSTANCE fault_rep_dll_handle = LoadLibrary(L"faultrep.dll");		/* Flawfinder: ignore */
253		if( fault_rep_dll_handle )
254		{
255			pfn_ADDEREXCLUDEDAPPLICATIONA pAddERExcludedApplicationA  = (pfn_ADDEREXCLUDEDAPPLICATIONA) GetProcAddress(fault_rep_dll_handle, "AddERExcludedApplicationA");
256			if( pAddERExcludedApplicationA )
257			{
258
259				// Strip the path off the name
260				const char* executable_name = gDirUtilp->getExecutableFilename().c_str();
261
262				if( 0 == pAddERExcludedApplicationA( executable_name ) )
263				{
264					U32 error_code = GetLastError();
265					llinfos << "AddERExcludedApplication() failed with error code " << error_code << llendl;
266				}
267				else
268				{
269					llinfos << "AddERExcludedApplication() success for " << executable_name << llendl;
270				}
271			}
272			FreeLibrary( fault_rep_dll_handle );
273		}
274	}
275}
276
277const S32 MAX_CONSOLE_LINES = 500;
278
279void create_console()
280{
281	int h_con_handle;
282	long l_std_handle;
283
284	CONSOLE_SCREEN_BUFFER_INFO coninfo;
285	FILE *fp;
286
287	// allocate a console for this app
288	AllocConsole();
289
290	// set the screen buffer to be big enough to let us scroll text
291	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
292	coninfo.dwSize.Y = MAX_CONSOLE_LINES;
293	SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
294
295	// redirect unbuffered STDOUT to the console
296	l_std_handle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
297	h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT);
298	if (h_con_handle == -1)
299	{
300		llwarns << "create_console() failed to open stdout handle" << llendl;
301	}
302	else
303	{
304		fp = _fdopen( h_con_handle, "w" );
305		*stdout = *fp;
306		setvbuf( stdout, NULL, _IONBF, 0 );
307	}
308
309	// redirect unbuffered STDIN to the console
310	l_std_handle = (long)GetStdHandle(STD_INPUT_HANDLE);
311	h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT);
312	if (h_con_handle == -1)
313	{
314		llwarns << "create_console() failed to open stdin handle" << llendl;
315	}
316	else
317	{
318		fp = _fdopen( h_con_handle, "r" );
319		*stdin = *fp;
320		setvbuf( stdin, NULL, _IONBF, 0 );
321	}
322
323	// redirect unbuffered STDERR to the console
324	l_std_handle = (long)GetStdHandle(STD_ERROR_HANDLE);
325	h_con_handle = _open_osfhandle(l_std_handle, _O_TEXT);
326	if (h_con_handle == -1)
327	{
328		llwarns << "create_console() failed to open stderr handle" << llendl;
329	}
330	else
331	{
332		fp = _fdopen( h_con_handle, "w" );
333		*stderr = *fp;
334		setvbuf( stderr, NULL, _IONBF, 0 );
335	}
336}
337
338LLAppViewerWin32::LLAppViewerWin32(const char* cmd_line) :
339    mCmdLine(cmd_line)
340{
341}
342
343LLAppViewerWin32::~LLAppViewerWin32()
344{
345}
346
347bool LLAppViewerWin32::init()
348{
349	// Platform specific initialization.
350	
351	// Turn off Windows XP Error Reporting
352	// (Don't send our data to Microsoft--at least until we are Logo approved and have a way
353	// of getting the data back from them.)
354	//
355	llinfos << "Turning off Windows error reporting." << llendl;
356	disableWinErrorReporting();
357
358#ifndef LL_RELEASE_FOR_DOWNLOAD
359	LLWinDebug::instance().init();
360#endif
361
362	return LLAppViewer::init();
363}
364
365bool LLAppViewerWin32::cleanup()
366{
367	bool result = LLAppViewer::cleanup();
368
369	gDXHardware.cleanup();
370
371	return result;
372}
373
374bool LLAppViewerWin32::initLogging()
375{
376	return LLAppViewer::initLogging();
377}
378
379void LLAppViewerWin32::initConsole()
380{
381	// pop up debug console
382	create_console();
383	return LLAppViewer::initConsole();
384}
385
386void write_debug_dx(const char* str)
387{
388	std::string value = gDebugInfo["DXInfo"].asString();
389	value += str;
390	gDebugInfo["DXInfo"] = value;
391}
392
393void write_debug_dx(const std::string& str)
394{
395	write_debug_dx(str.c_str());
396}
397
398bool LLAppViewerWin32::initHardwareTest()
399{
400	//
401	// Do driver verification and initialization based on DirectX
402	// hardware polling and driver versions
403	//
404	if (FALSE == gSavedSettings.getBOOL("NoHardwareProbe"))
405	{
406		// per DEV-11631 - disable hardware probing for everything
407		// but vram.
408		BOOL vram_only = TRUE;
409
410		LLSplashScreen::update(LLTrans::getString("StartupDetectingHardware"));
411
412		LL_DEBUGS("AppInit") << "Attempting to poll DirectX for hardware info" << LL_ENDL;
413		gDXHardware.setWriteDebugFunc(write_debug_dx);
414		BOOL probe_ok = gDXHardware.getInfo(vram_only);
415
416		if (!probe_ok
417			&& gWarningSettings.getBOOL("AboutDirectX9"))
418		{
419			LL_WARNS("AppInit") << "DirectX probe failed, alerting user." << LL_ENDL;
420
421			// Warn them that runnin without DirectX 9 will
422			// not allow us to tell them about driver issues
423			std::ostringstream msg;
424			msg << LLTrans::getString ("MBNoDirectX");
425			S32 button = OSMessageBox(
426				msg.str(),
427				LLTrans::getString("MBWarning"),
428				OSMB_YESNO);
429			if (OSBTN_NO== button)
430			{
431				LL_INFOS("AppInit") << "User quitting after failed DirectX 9 detection" << LL_ENDL;
432				LLWeb::loadURLExternal(DIRECTX_9_URL, false);
433				return false;
434			}
435			gWarningSettings.setBOOL("AboutDirectX9", FALSE);
436		}
437		LL_DEBUGS("AppInit") << "Done polling DirectX for hardware info" << LL_ENDL;
438
439		// Only probe once after installation
440		gSavedSettings.setBOOL("ProbeHardwareOnStartup", FALSE);
441
442		// Disable so debugger can work
443		std::string splash_msg;
444		LLStringUtil::format_map_t args;
445		args["[APP_NAME]"] = LLAppViewer::instance()->getSecondLifeTitle();
446		splash_msg = LLTrans::getString("StartupLoading", args);
447
448		LLSplashScreen::update(splash_msg);
449	}
450
451	if (!restoreErrorTrap())
452	{
453		LL_WARNS("AppInit") << " Someone took over my exception handler (post hardware probe)!" << LL_ENDL;
454	}
455
456	if (gGLManager.mVRAM == 0)
457	{
458		gGLManager.mVRAM = gDXHardware.getVRAM();
459	}
460
461	LL_INFOS("AppInit") << "Detected VRAM: " << gGLManager.mVRAM << LL_ENDL;
462
463	return true;
464}
465
466bool LLAppViewerWin32::initParseCommandLine(LLCommandLineParser& clp)
467{
468	if (!clp.parseCommandLineString(mCmdLine))
469	{
470		return false;
471	}
472
473	// Find the system language.
474	FL_Locale *locale = NULL;
475	FL_Success success = FL_FindLocale(&locale, FL_MESSAGES);
476	if (success != 0)
477	{
478		if (success >= 2 && locale->lang) // confident!
479		{
480			LL_INFOS("AppInit") << "Language: " << ll_safe_string(locale->lang) << LL_ENDL;
481			LL_INFOS("AppInit") << "Location: " << ll_safe_string(locale->country) << LL_ENDL;
482			LL_INFOS("AppInit") << "Variant: " << ll_safe_string(locale->variant) << LL_ENDL;
483			LLControlVariable* c = gSavedSettings.getControl("SystemLanguage");
484			if(c)
485			{
486				c->setValue(std::string(locale->lang), false);
487			}
488		}
489	}
490	FL_FreeLocale(&locale);
491
492	return true;
493}
494
495bool LLAppViewerWin32::restoreErrorTrap()
496{	
497	return true;
498	//return LLWinDebug::checkExceptionHandler();
499}
500
501void LLAppViewerWin32::handleCrashReporting(bool reportFreeze)
502{
503	const char* logger_name = "win_crash_logger.exe";
504	std::string exe_path = gDirUtilp->getExecutableDir();
505	exe_path += gDirUtilp->getDirDelimiter();
506	exe_path += logger_name;
507
508	const char* arg_str = logger_name;
509
510	// *NOTE:Mani - win_crash_logger.exe no longer parses command line options.
511	if(reportFreeze)
512	{
513		// Spawn crash logger.
514		// NEEDS to wait until completion, otherwise log files will get smashed.
515		_spawnl(_P_WAIT, exe_path.c_str(), arg_str, NULL);
516	}
517	else
518	{
519		_spawnl(_P_NOWAIT, exe_path.c_str(), arg_str, NULL);
520	}
521}
522
523//virtual
524bool LLAppViewerWin32::sendURLToOtherInstance(const std::string& url)
525{
526	wchar_t window_class[256]; /* Flawfinder: ignore */   // Assume max length < 255 chars.
527	mbstowcs(window_class, sWindowClass.c_str(), 255);
528	window_class[255] = 0;
529	// Use the class instead of the window name.
530	HWND other_window = FindWindow(window_class, NULL);
531
532	if (other_window != NULL)
533	{
534		lldebugs << "Found other window with the name '" << getWindowTitle() << "'" << llendl;
535		COPYDATASTRUCT cds;
536		const S32 SLURL_MESSAGE_TYPE = 0;
537		cds.dwData = SLURL_MESSAGE_TYPE;
538		cds.cbData = url.length() + 1;
539		cds.lpData = (void*)url.c_str();
540
541		LRESULT msg_result = SendMessage(other_window, WM_COPYDATA, NULL, (LPARAM)&cds);
542		lldebugs << "SendMessage(WM_COPYDATA) to other window '" 
543				 << getWindowTitle() << "' returned " << msg_result << llendl;
544		return true;
545	}
546	return false;
547}
548
549
550std::string LLAppViewerWin32::generateSerialNumber()
551{
552	char serial_md5[MD5HEX_STR_SIZE];		// Flawfinder: ignore
553	serial_md5[0] = 0;
554
555	DWORD serial = 0;
556	DWORD flags = 0;
557	BOOL success = GetVolumeInformation(
558			L"C:\\",
559			NULL,		// volume name buffer
560			0,			// volume name buffer size
561			&serial,	// volume serial
562			NULL,		// max component length
563			&flags,		// file system flags
564			NULL,		// file system name buffer
565			0);			// file system name buffer size
566	if (success)
567	{
568		LLMD5 md5;
569		md5.update( (unsigned char*)&serial, sizeof(DWORD));
570		md5.finalize();
571		md5.hex_digest(serial_md5);
572	}
573	else
574	{
575		llwarns << "GetVolumeInformation failed" << llendl;
576	}
577	return serial_md5;
578}