Browse Source
split init and shutdown from ui.cpp into init.cpp, support wxUSE_GUI=0 -- version 0.2.70.8
s_nakamoto
15 years ago
13 changed files with 817 additions and 773 deletions
@ -0,0 +1,625 @@
@@ -0,0 +1,625 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "headers.h" |
||||
|
||||
|
||||
|
||||
|
||||
void ExitTimeout(void* parg) |
||||
{ |
||||
#ifdef __WXMSW__ |
||||
Sleep(5000); |
||||
ExitProcess(0); |
||||
#endif |
||||
} |
||||
|
||||
void Shutdown(void* parg) |
||||
{ |
||||
static CCriticalSection cs_Shutdown; |
||||
static bool fTaken; |
||||
bool fFirstThread; |
||||
CRITICAL_BLOCK(cs_Shutdown) |
||||
{ |
||||
fFirstThread = !fTaken; |
||||
fTaken = true; |
||||
} |
||||
static bool fExit; |
||||
if (fFirstThread) |
||||
{ |
||||
fShutdown = true; |
||||
nTransactionsUpdated++; |
||||
DBFlush(false); |
||||
StopNode(); |
||||
DBFlush(true); |
||||
CreateThread(ExitTimeout, NULL); |
||||
Sleep(50); |
||||
printf("Bitcoin exiting\n\n"); |
||||
fExit = true; |
||||
exit(0); |
||||
} |
||||
else |
||||
{ |
||||
while (!fExit) |
||||
Sleep(500); |
||||
Sleep(100); |
||||
ExitThread(0); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Startup folder
|
||||
//
|
||||
|
||||
#ifdef __WXMSW__ |
||||
typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate); |
||||
|
||||
string MyGetSpecialFolderPath(int nFolder, bool fCreate) |
||||
{ |
||||
char pszPath[MAX_PATH+100] = ""; |
||||
|
||||
// SHGetSpecialFolderPath is not usually available on NT 4.0
|
||||
HMODULE hShell32 = LoadLibraryA("shell32.dll"); |
||||
if (hShell32) |
||||
{ |
||||
PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath = |
||||
(PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA"); |
||||
if (pSHGetSpecialFolderPath) |
||||
(*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate); |
||||
FreeModule(hShell32); |
||||
} |
||||
|
||||
// Backup option
|
||||
if (pszPath[0] == '\0') |
||||
{ |
||||
if (nFolder == CSIDL_STARTUP) |
||||
{ |
||||
strcpy(pszPath, getenv("USERPROFILE")); |
||||
strcat(pszPath, "\\Start Menu\\Programs\\Startup"); |
||||
} |
||||
else if (nFolder == CSIDL_APPDATA) |
||||
{ |
||||
strcpy(pszPath, getenv("APPDATA")); |
||||
} |
||||
} |
||||
|
||||
return pszPath; |
||||
} |
||||
|
||||
string StartupShortcutPath() |
||||
{ |
||||
return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk"; |
||||
} |
||||
|
||||
bool GetStartOnSystemStartup() |
||||
{ |
||||
return wxFileExists(StartupShortcutPath()); |
||||
} |
||||
|
||||
void SetStartOnSystemStartup(bool fAutoStart) |
||||
{ |
||||
// If the shortcut exists already, remove it for updating
|
||||
remove(StartupShortcutPath().c_str()); |
||||
|
||||
if (fAutoStart) |
||||
{ |
||||
CoInitialize(NULL); |
||||
|
||||
// Get a pointer to the IShellLink interface.
|
||||
IShellLink* psl = NULL; |
||||
HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, |
||||
CLSCTX_INPROC_SERVER, IID_IShellLink, |
||||
reinterpret_cast<void**>(&psl)); |
||||
|
||||
if (SUCCEEDED(hres)) |
||||
{ |
||||
// Get the current executable path
|
||||
TCHAR pszExePath[MAX_PATH]; |
||||
GetModuleFileName(NULL, pszExePath, sizeof(pszExePath)); |
||||
|
||||
// Set the path to the shortcut target
|
||||
psl->SetPath(pszExePath); |
||||
PathRemoveFileSpec(pszExePath); |
||||
psl->SetWorkingDirectory(pszExePath); |
||||
psl->SetShowCmd(SW_SHOWMINNOACTIVE); |
||||
|
||||
// Query IShellLink for the IPersistFile interface for
|
||||
// saving the shortcut in persistent storage.
|
||||
IPersistFile* ppf = NULL; |
||||
hres = psl->QueryInterface(IID_IPersistFile, |
||||
reinterpret_cast<void**>(&ppf)); |
||||
if (SUCCEEDED(hres)) |
||||
{ |
||||
WCHAR pwsz[MAX_PATH]; |
||||
// Ensure that the string is ANSI.
|
||||
MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH); |
||||
// Save the link by calling IPersistFile::Save.
|
||||
hres = ppf->Save(pwsz, TRUE); |
||||
ppf->Release(); |
||||
} |
||||
psl->Release(); |
||||
} |
||||
CoUninitialize(); |
||||
} |
||||
} |
||||
#else |
||||
bool GetStartOnSystemStartup() { return false; } |
||||
void SetStartOnSystemStartup(bool fAutoStart) { } |
||||
#endif |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CMyApp
|
||||
//
|
||||
|
||||
// Define a new application
|
||||
class CMyApp: public wxApp |
||||
{ |
||||
public: |
||||
wxLocale m_locale; |
||||
|
||||
CMyApp(){}; |
||||
~CMyApp(){}; |
||||
bool OnInit(); |
||||
bool OnInit2(); |
||||
int OnExit(); |
||||
|
||||
// Hook Initialize so we can start without GUI
|
||||
virtual bool Initialize(int& argc, wxChar** argv); |
||||
|
||||
// 2nd-level exception handling: we get all the exceptions occurring in any
|
||||
// event handler here
|
||||
virtual bool OnExceptionInMainLoop(); |
||||
|
||||
// 3rd, and final, level exception handling: whenever an unhandled
|
||||
// exception is caught, this function is called
|
||||
virtual void OnUnhandledException(); |
||||
|
||||
// and now for something different: this function is called in case of a
|
||||
// crash (e.g. dereferencing null pointer, division by 0, ...)
|
||||
virtual void OnFatalException(); |
||||
}; |
||||
|
||||
IMPLEMENT_APP(CMyApp) |
||||
|
||||
bool CMyApp::Initialize(int& argc, wxChar** argv) |
||||
{ |
||||
if (argc > 1 && argv[1][0] != '-' && (!fWindows || argv[1][0] != '/') && |
||||
wxString(argv[1]) != "start") |
||||
{ |
||||
fCommandLine = true; |
||||
} |
||||
else if (!fGUI) |
||||
{ |
||||
fDaemon = true; |
||||
} |
||||
else |
||||
{ |
||||
// wxApp::Initialize will remove environment-specific parameters,
|
||||
// so it's too early to call ParseParameters yet
|
||||
for (int i = 1; i < argc; i++) |
||||
{ |
||||
wxString str = argv[i]; |
||||
#ifdef __WXMSW__ |
||||
if (str.size() >= 1 && str[0] == '/') |
||||
str[0] = '-'; |
||||
str = str.MakeLower(); |
||||
#endif |
||||
// haven't decided which argument to use for this yet
|
||||
if (str == "-daemon" || str == "-d" || str == "start") |
||||
fDaemon = true; |
||||
} |
||||
} |
||||
|
||||
if (fDaemon) |
||||
fprintf(stdout, "bitcoin server starting\n"); |
||||
|
||||
#ifdef __WXGTK__ |
||||
if (fDaemon || fCommandLine) |
||||
{ |
||||
// Call the original Initialize while suppressing error messages
|
||||
// and ignoring failure. If unable to initialize GTK, it fails
|
||||
// near the end so hopefully the last few things don't matter.
|
||||
{ |
||||
wxLogNull logNo; |
||||
wxApp::Initialize(argc, argv); |
||||
} |
||||
|
||||
if (fDaemon) |
||||
{ |
||||
// Daemonize
|
||||
pid_t pid = fork(); |
||||
if (pid < 0) |
||||
{ |
||||
fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno); |
||||
return false; |
||||
} |
||||
if (pid > 0) |
||||
pthread_exit((void*)0); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
#endif |
||||
|
||||
return wxApp::Initialize(argc, argv); |
||||
} |
||||
|
||||
bool CMyApp::OnInit() |
||||
{ |
||||
bool fRet = false; |
||||
try |
||||
{ |
||||
fRet = OnInit2(); |
||||
} |
||||
catch (std::exception& e) { |
||||
PrintException(&e, "OnInit()"); |
||||
} catch (...) { |
||||
PrintException(NULL, "OnInit()"); |
||||
} |
||||
if (!fRet) |
||||
Shutdown(NULL); |
||||
return fRet; |
||||
} |
||||
|
||||
extern int g_isPainting; |
||||
|
||||
bool CMyApp::OnInit2() |
||||
{ |
||||
#ifdef _MSC_VER |
||||
// Turn off microsoft heap dump noise
|
||||
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); |
||||
_CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); |
||||
#endif |
||||
#if defined(__WXMSW__) && defined(__WXDEBUG__) && wxUSE_GUI |
||||
// Disable malfunctioning wxWidgets debug assertion
|
||||
g_isPainting = 10000; |
||||
#endif |
||||
#if wxUSE_GUI |
||||
wxImage::AddHandler(new wxPNGHandler); |
||||
#endif |
||||
#ifdef __WXMSW__ |
||||
SetAppName("Bitcoin"); |
||||
#else |
||||
SetAppName("bitcoin"); |
||||
umask(077); |
||||
#endif |
||||
#ifdef __WXMSW__ |
||||
#if wxUSE_UNICODE |
||||
// Hack to set wxConvLibc codepage to UTF-8 on Windows,
|
||||
// may break if wxMBConv_win32 implementation in strconv.cpp changes.
|
||||
class wxMBConv_win32 : public wxMBConv |
||||
{ |
||||
public: |
||||
long m_CodePage; |
||||
size_t m_minMBCharWidth; |
||||
}; |
||||
if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP) |
||||
((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8; |
||||
#endif |
||||
#endif |
||||
|
||||
// Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
|
||||
m_locale.Init(wxLANGUAGE_DEFAULT, 0); |
||||
m_locale.AddCatalogLookupPathPrefix("locale"); |
||||
if (!fWindows) |
||||
{ |
||||
m_locale.AddCatalogLookupPathPrefix("/usr/share/locale"); |
||||
m_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale"); |
||||
} |
||||
m_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
|
||||
m_locale.AddCatalog("bitcoin"); |
||||
|
||||
//
|
||||
// Parameters
|
||||
//
|
||||
if (fCommandLine) |
||||
{ |
||||
int ret = CommandLineRPC(argc, argv); |
||||
exit(ret); |
||||
} |
||||
|
||||
ParseParameters(argc, argv); |
||||
if (mapArgs.count("-?") || mapArgs.count("--help")) |
||||
{ |
||||
wxString strUsage = string() + |
||||
_("Usage: bitcoin [options]") + "\t\t\t\t\t\t\n" + |
||||
_("Options:\n") + |
||||
" -gen \t\t " + _("Generate coins\n") + |
||||
" -gen=0 \t\t " + _("Don't generate coins\n") + |
||||
" -min \t\t " + _("Start minimized\n") + |
||||
" -datadir=<dir> \t " + _("Specify data directory\n") + |
||||
" -proxy=<ip:port>\t " + _("Connect through socks4 proxy\n") + |
||||
" -addnode=<ip> \t " + _("Add a node to connect to\n") + |
||||
" -connect=<ip> \t " + _("Connect only to the specified node\n") + |
||||
" -? \t\t " + _("This help message\n"); |
||||
|
||||
if (fWindows && fGUI) |
||||
{ |
||||
// Remove spaces, the tabs make the columns line up in the message box
|
||||
for (int i = 0; i < 50; i++) |
||||
strUsage.Replace(" \t", "\t"); |
||||
wxMessageBox(strUsage, "Bitcoin", wxOK); |
||||
} |
||||
else |
||||
{ |
||||
// Remove tabs
|
||||
strUsage.Replace("\t", ""); |
||||
fprintf(stderr, "%s", ((string)strUsage).c_str()); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
if (mapArgs.count("-datadir")) |
||||
strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir)); |
||||
|
||||
if (mapArgs.count("-debug")) |
||||
fDebug = true; |
||||
|
||||
if (mapArgs.count("-printtodebugger")) |
||||
fPrintToDebugger = true; |
||||
|
||||
if (!fDebug && !pszSetDataDir[0]) |
||||
ShrinkDebugFile(); |
||||
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); |
||||
printf("Bitcoin version 0.%d.%d%s beta, OS version %s\n", VERSION/100, VERSION%100, pszSubVer, ((string)wxGetOsDescription()).c_str()); |
||||
printf("System default language is %d %s\n", m_locale.GetSystemLanguage(), ((string)m_locale.GetSysName()).c_str()); |
||||
printf("Language file %s (%s)\n", (string("locale/") + (string)m_locale.GetCanonicalName() + "/LC_MESSAGES/bitcoin.mo").c_str(), ((string)m_locale.GetLocale()).c_str()); |
||||
|
||||
if (mapArgs.count("-loadblockindextest")) |
||||
{ |
||||
CTxDB txdb("r"); |
||||
txdb.LoadBlockIndex(); |
||||
PrintBlockTree(); |
||||
return false; |
||||
} |
||||
|
||||
//
|
||||
// Limit to single instance per user
|
||||
// Required to protect the database files if we're going to keep deleting log.*
|
||||
//
|
||||
#ifdef __WXMSW__ |
||||
// todo: wxSingleInstanceChecker wasn't working on Linux, never deleted its lock file
|
||||
// maybe should go by whether successfully bind port 8333 instead
|
||||
wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH"); |
||||
for (int i = 0; i < strMutexName.size(); i++) |
||||
if (!isalnum(strMutexName[i])) |
||||
strMutexName[i] = '.'; |
||||
wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName); |
||||
if (psingleinstancechecker->IsAnotherRunning()) |
||||
{ |
||||
printf("Existing instance found\n"); |
||||
unsigned int nStart = GetTime(); |
||||
loop |
||||
{ |
||||
// TODO: find out how to do this in Linux, or replace with wxWidgets commands
|
||||
// Show the previous instance and exit
|
||||
HWND hwndPrev = FindWindowA("wxWindowClassNR", "Bitcoin"); |
||||
if (hwndPrev) |
||||
{ |
||||
if (IsIconic(hwndPrev)) |
||||
ShowWindow(hwndPrev, SW_RESTORE); |
||||
SetForegroundWindow(hwndPrev); |
||||
return false; |
||||
} |
||||
|
||||
if (GetTime() > nStart + 60) |
||||
return false; |
||||
|
||||
// Resume this instance if the other exits
|
||||
delete psingleinstancechecker; |
||||
Sleep(1000); |
||||
psingleinstancechecker = new wxSingleInstanceChecker(strMutexName); |
||||
if (!psingleinstancechecker->IsAnotherRunning()) |
||||
break; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
// Bind to the port early so we can tell if another instance is already running.
|
||||
// This is a backup to wxSingleInstanceChecker, which doesn't work on Linux.
|
||||
string strErrors; |
||||
if (!BindListenPort(strErrors)) |
||||
{ |
||||
wxMessageBox(strErrors, "Bitcoin"); |
||||
return false; |
||||
} |
||||
|
||||
//
|
||||
// Load data files
|
||||
//
|
||||
bool fFirstRun; |
||||
strErrors = ""; |
||||
int64 nStart; |
||||
|
||||
printf("Loading addresses...\n"); |
||||
nStart = GetTimeMillis(); |
||||
if (!LoadAddresses()) |
||||
strErrors += _("Error loading addr.dat \n"); |
||||
printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart); |
||||
|
||||
printf("Loading block index...\n"); |
||||
nStart = GetTimeMillis(); |
||||
if (!LoadBlockIndex()) |
||||
strErrors += _("Error loading blkindex.dat \n"); |
||||
printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); |
||||
|
||||
printf("Loading wallet...\n"); |
||||
nStart = GetTimeMillis(); |
||||
if (!LoadWallet(fFirstRun)) |
||||
strErrors += _("Error loading wallet.dat \n"); |
||||
printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart); |
||||
|
||||
printf("Done loading\n"); |
||||
|
||||
//// debug print
|
||||
printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size()); |
||||
printf("nBestHeight = %d\n", nBestHeight); |
||||
printf("mapKeys.size() = %d\n", mapKeys.size()); |
||||
printf("mapPubKeys.size() = %d\n", mapPubKeys.size()); |
||||
printf("mapWallet.size() = %d\n", mapWallet.size()); |
||||
printf("mapAddressBook.size() = %d\n", mapAddressBook.size()); |
||||
|
||||
if (!strErrors.empty()) |
||||
{ |
||||
wxMessageBox(strErrors, "Bitcoin"); |
||||
return false; |
||||
} |
||||
|
||||
// Add wallet transactions that aren't already in a block to mapTransactions
|
||||
ReacceptWalletTransactions(); |
||||
|
||||
//
|
||||
// Parameters
|
||||
//
|
||||
if (mapArgs.count("-printblockindex") || mapArgs.count("-printblocktree")) |
||||
{ |
||||
PrintBlockTree(); |
||||
return false; |
||||
} |
||||
|
||||
if (mapArgs.count("-printblock")) |
||||
{ |
||||
string strMatch = mapArgs["-printblock"]; |
||||
int nFound = 0; |
||||
for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) |
||||
{ |
||||
uint256 hash = (*mi).first; |
||||
if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0) |
||||
{ |
||||
CBlockIndex* pindex = (*mi).second; |
||||
CBlock block; |
||||
block.ReadFromDisk(pindex); |
||||
block.BuildMerkleTree(); |
||||
block.print(); |
||||
printf("\n"); |
||||
nFound++; |
||||
} |
||||
} |
||||
if (nFound == 0) |
||||
printf("No blocks matching %s were found\n", strMatch.c_str()); |
||||
return false; |
||||
} |
||||
|
||||
if (mapArgs.count("-gen")) |
||||
{ |
||||
if (mapArgs["-gen"].empty()) |
||||
fGenerateBitcoins = true; |
||||
else |
||||
fGenerateBitcoins = (atoi(mapArgs["-gen"].c_str()) != 0); |
||||
} |
||||
|
||||
if (mapArgs.count("-proxy")) |
||||
{ |
||||
fUseProxy = true; |
||||
addrProxy = CAddress(mapArgs["-proxy"]); |
||||
if (!addrProxy.IsValid()) |
||||
{ |
||||
wxMessageBox(_("Invalid -proxy address"), "Bitcoin"); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
if (mapArgs.count("-addnode")) |
||||
{ |
||||
foreach(string strAddr, mapMultiArgs["-addnode"]) |
||||
{ |
||||
CAddress addr(strAddr, NODE_NETWORK); |
||||
addr.nTime = 0; // so it won't relay unless successfully connected
|
||||
if (addr.IsValid()) |
||||
AddAddress(addr); |
||||
} |
||||
} |
||||
|
||||
//
|
||||
// Create the main window and start the node
|
||||
//
|
||||
if (!fDaemon) |
||||
CreateMainWindow(); |
||||
|
||||
if (!CheckDiskSpace()) |
||||
return false; |
||||
|
||||
RandAddSeedPerfmon(); |
||||
|
||||
if (!CreateThread(StartNode, NULL)) |
||||
wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin"); |
||||
|
||||
if (mapArgs.count("-server") || fDaemon) |
||||
CreateThread(ThreadRPCServer, NULL); |
||||
|
||||
if (fFirstRun) |
||||
SetStartOnSystemStartup(true); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
int CMyApp::OnExit() |
||||
{ |
||||
Shutdown(NULL); |
||||
return wxApp::OnExit(); |
||||
} |
||||
|
||||
bool CMyApp::OnExceptionInMainLoop() |
||||
{ |
||||
try |
||||
{ |
||||
throw; |
||||
} |
||||
catch (std::exception& e) |
||||
{ |
||||
PrintException(&e, "CMyApp::OnExceptionInMainLoop()"); |
||||
wxLogWarning("Exception %s %s", typeid(e).name(), e.what()); |
||||
Sleep(1000); |
||||
throw; |
||||
} |
||||
catch (...) |
||||
{ |
||||
PrintException(NULL, "CMyApp::OnExceptionInMainLoop()"); |
||||
wxLogWarning("Unknown exception"); |
||||
Sleep(1000); |
||||
throw; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
void CMyApp::OnUnhandledException() |
||||
{ |
||||
// this shows how we may let some exception propagate uncaught
|
||||
try |
||||
{ |
||||
throw; |
||||
} |
||||
catch (std::exception& e) |
||||
{ |
||||
PrintException(&e, "CMyApp::OnUnhandledException()"); |
||||
wxLogWarning("Exception %s %s", typeid(e).name(), e.what()); |
||||
Sleep(1000); |
||||
throw; |
||||
} |
||||
catch (...) |
||||
{ |
||||
PrintException(NULL, "CMyApp::OnUnhandledException()"); |
||||
wxLogWarning("Unknown exception"); |
||||
Sleep(1000); |
||||
throw; |
||||
} |
||||
} |
||||
|
||||
void CMyApp::OnFatalException() |
||||
{ |
||||
wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR); |
||||
} |
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
void Shutdown(void* parg); |
||||
bool GetStartOnSystemStartup(); |
||||
void SetStartOnSystemStartup(bool fAutoStart); |
Loading…
Reference in new issue