/indra/newview/llappviewerwin32.cpp

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