mirror of
https://github.com/cookiengineer/audacity
synced 2025-04-30 15:49:41 +02:00
Fixes detection problems on Mac and possibly Windows (Bug #290)
This should fix the detection problems on the Mac and may even help with issues on Windows and Linux. On any of the platforms, the main issue is the search path for libraries and how absolute path names are handled. The Mac seems to be especially susceptible since there isn't one concrete location where libraries are stored. Windows handles absolute paths differently and allows runtime updates to the environment variables (and if push comes to shove, provides the SetDllDirectory() function), so if problems still exist, they should be easy to circumvent. This patch does three things: 1) It adds a shell script on OSX that takes care of starting Audacity after reassigning and clearing the DYLD_LIBRARY_PATH environment variable. This will allow loading of libraries from their absolute path rather than searching DYLD_LIBRARY_PATH first. This script should be transparent to the user, but it will affect people running Audacity with gdb as they will have to specifically target Audacity.app/Contents/MacOS/Audacity instead of the Audacity.app bundle. Not big deal really. If ppl no enough to use gdb from the command line, they should be able to figure it out. 2) It corrects detection of a monolithic FFmpeg library. This is one where avformat, avcodec, and avutil have all been linked into one large library. The main issue here was that under OSX and Linux, looking for symbols that should reside in avutil and avcodec, would always succeed since dlsym() on these platforms not only scans the requested library, but any dependent libraries as well. And when avformat was loaded, it would pull in it's dependent libraries as well. Another issue here was the if it was determined that the library was not monolithic, the library was never unloaded, so those dependent libraries may have come from the wrong location since they would have been loaded via the normal search paths and not by absolute path name. 3) It adds a method to FileNames which returns the full path name of a loaded module that contains an address. In the case of FFmpeg, it is used to verify that a routine from a specific library is actually being used from that library or not. It is also used to show (in Help->Show log) the directory from which a library was actually loaded.
This commit is contained in:
parent
6c147de4ab
commit
07661c186f
21
mac/Audacity.sh
Executable file
21
mac/Audacity.sh
Executable file
@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# The purpose of this script is to give the user more control over where libraries
|
||||
# such as Lame and FFmpeg get loaded from.
|
||||
#
|
||||
# Since absolute pathnames are used when loading these libraries, the normal search
|
||||
# path would be DYLD_LIBRARY_PATH, absolute path, DYLD_FALLBACK_LIBRARY_PATH. This
|
||||
# means that DYLD_LIBRARY_PATH can override what the user actually wants.
|
||||
#
|
||||
# So, we simply clear DYLD_LIBRARY_PATH to allow the users choice to be the first
|
||||
# one tried.
|
||||
#
|
||||
|
||||
DYLD_FALLBACK_LIBRARY_PATH="$DYLD_LIBRARY_PATH:$DYLD_FALLBACK_LIBRARY_PATH:$HOME/lib:/usr/local/lib:/usr/lib"
|
||||
export DYLD_FALLBACK_LIBRARY_PATH
|
||||
|
||||
DYLD_LIBRARY_PATH=""
|
||||
export DYLD_LIBRARY_PATH
|
||||
|
||||
dir=$(dirname "$0")
|
||||
"$dir/Audacity"
|
@ -1065,6 +1065,8 @@
|
||||
28BD8AB2101DF4C700686679 /* CommandDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28BD8AAA101DF4C600686679 /* CommandDirectory.cpp */; };
|
||||
28BD8AB3101DF4C700686679 /* ExecMenuCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28BD8AAC101DF4C600686679 /* ExecMenuCommand.cpp */; };
|
||||
28BD8AB4101DF4C700686679 /* GetAllMenuCommands.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28BD8AAE101DF4C600686679 /* GetAllMenuCommands.cpp */; };
|
||||
28C816B91312555B0035BB10 /* Audacity.sh in Resources */ = {isa = PBXBuildFile; fileRef = 28C816B81312555B0035BB10 /* Audacity.sh */; };
|
||||
28C816BA131255790035BB10 /* Audacity.sh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 28C816B81312555B0035BB10 /* Audacity.sh */; };
|
||||
28CCDD000F939FD70081F2FC /* FileHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28CCDCFF0F939FD70081F2FC /* FileHistory.cpp */; };
|
||||
28D540050FD1912A00FA7C75 /* AppCommandEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28D53FFA0FD1912A00FA7C75 /* AppCommandEvent.cpp */; };
|
||||
28D540060FD1912A00FA7C75 /* CommandBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28D53FFE0FD1912A00FA7C75 /* CommandBuilder.cpp */; };
|
||||
@ -1535,6 +1537,19 @@
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
28C816B7131255550035BB10 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 6;
|
||||
files = (
|
||||
28C816BA131255790035BB10 /* Audacity.sh in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
17048267098C0F5F00824C66 /* mad.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = mad.h; sourceTree = "<group>"; };
|
||||
17072F580988D6A9008541CC /* xmlparse.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = xmlparse.c; sourceTree = "<group>"; };
|
||||
@ -2862,6 +2877,7 @@
|
||||
28BD8AAE101DF4C600686679 /* GetAllMenuCommands.cpp */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.cpp.cpp; path = GetAllMenuCommands.cpp; sourceTree = "<group>"; };
|
||||
28BD8AAF101DF4C600686679 /* GetAllMenuCommands.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = GetAllMenuCommands.h; sourceTree = "<group>"; };
|
||||
28BD8AB0101DF4C600686679 /* Validators.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = Validators.h; sourceTree = "<group>"; };
|
||||
28C816B81312555B0035BB10 /* Audacity.sh */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; name = Audacity.sh; path = mac/Audacity.sh; sourceTree = "<group>"; };
|
||||
28CCDCFF0F939FD70081F2FC /* FileHistory.cpp */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.cpp.cpp; path = FileHistory.cpp; sourceTree = "<group>"; };
|
||||
28CCDD040F93A0B20081F2FC /* FileHistory.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = FileHistory.h; sourceTree = "<group>"; };
|
||||
28D53FFA0FD1912A00FA7C75 /* AppCommandEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.cpp.cpp; path = AppCommandEvent.cpp; sourceTree = "<group>"; };
|
||||
@ -3929,6 +3945,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
174F73C8098D516D009CD6E0 /* Resources */,
|
||||
28C816B81312555B0035BB10 /* Audacity.sh */,
|
||||
17FC86F30994637C0061DC94 /* compile.txt */,
|
||||
178CF4FF0989541C0056CE58 /* configmac.h */,
|
||||
174F73D7098D51AA009CD6E0 /* Info.plist */,
|
||||
@ -6693,6 +6710,7 @@
|
||||
1790AFAE09883B6D008A330A /* Frameworks */,
|
||||
2878AF000F340F9E00396567 /* Copy nyquist */,
|
||||
2878AF020F340FE700396567 /* Copy plug-ins */,
|
||||
28C816B7131255550035BB10 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
286D31370F264C38004E6E4A /* PBXBuildRule */,
|
||||
@ -6985,6 +7003,7 @@
|
||||
2806D5BF11108FC800BAF530 /* hi.po in Resources */,
|
||||
2806D5C011108FC800BAF530 /* my.po in Resources */,
|
||||
8406A93812D0F2510011EA01 /* EQDefaultCurves.xml in Resources */,
|
||||
28C816B91312555B0035BB10 /* Audacity.sh in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -7014,7 +7033,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if [ -z \"$WX_CONFIG\" ]\nthen\n echo \"WX_CONFIG environment variable not set...aborting\"\n exit 1\nfi\n\n#\n# Here we create empty xcconfig files if they do not exist. Otherwise,\n# the build will fail.\n#\nconfigs='Debug_Shared Debug_Static Release_Shared Release_Static'\nfor config in $configs\ndo\n\t[ ! -f $TOPLEVEL/mac/$config.xcconfig ] && touch $TOPLEVEL/mac/$config.xcconfig\ndone\n\t\ncd $TOPLEVEL\nsh configure --enable-static=$CONFIG_STATIC \\\n --enable-unicode=$CONFIG_UNICODE \\\n --enable-debug=$CONFIG_DEBUG \\\n --with-lib-preference=local,system \\\n --with-wx-version=$WX_VER \\\n $CONFIG_FLAGS || exit 1\n\n#\n# Libsndfile's endianness detection is a bit unfriendly to universal binary\n# builds, so we coerce it into working our way.\n#\nFILE=lib-src/libsndfile/src/config.h\ngrep -q '__BIG_ENDIAN__' $FILE || cat <<EOF >>$FILE\n#undef CPU_IS_BIG_ENDIAN\n#undef CPU_IS_LITTLE_ENDIAN\n#if defined(__BIG_ENDIAN__)\n#define CPU_IS_BIG_ENDIAN 1\n#define CPU_IS_LITTLE_ENDIAN 0\n#else\n#define CPU_IS_BIG_ENDIAN 0\n#define CPU_IS_LITTLE_ENDIAN 1\n#endif\nEOF\n\n#\n# Ditto for libflac.\n#\nFILE=lib-src/libflac/config.h\ngrep -q '__BIG_ENDIAN__' $FILE || cat <<EOF >>$FILE\n#ifdef WORDS_BIGENDIAN\n#undef WORDS_BIGENDIAN\n#endif\n#if defined(__BIG_ENDIAN__)\n#define WORDS_BIGENDIAN 1\n#endif\nEOF\n\n#\n# And libmad.\n#\nFILE=lib-src/libmad/config.h\ngrep -q '__BIG_ENDIAN__' $FILE || cat <<EOF >>$FILE\n#ifdef WORDS_BIGENDIAN\n#undef WORDS_BIGENDIAN\n#endif\n#if defined(__BIG_ENDIAN__)\n#define WORDS_BIGENDIAN 1\n#endif\nEOF\n\n#\n# Generate the header file for preprocessing the Info.plist\n#\ngcc -E -dM src/Audacity.h | grep AUDACITY >mac/Info.plist.h\n\nexit 0\n";
|
||||
shellScript = "if [ -z \"$WX_CONFIG\" ]\nthen\n echo \"WX_CONFIG environment variable not set...aborting\"\n exit 1\nfi\n\n#\n# Here we create empty xcconfig files if they do not exist. Otherwise,\n# the build will fail.\n#\nconfigs='Debug_Shared Debug_Static Release_Shared Release_Static'\nfor config in $configs\ndo\n\t[ ! -f $TOPLEVEL/mac/$config.xcconfig ] && touch $TOPLEVEL/mac/$config.xcconfig\ndone\n\n#\n# Get to a known location\n#\ncd $TOPLEVEL\n\n#\n# Libsndfile seems to have some date/time issues, so placate him.\n#\n# Run aclocal to make libsndfile happy\n#\ntouch lib-src/libsndfile/configure.ac\n\n#\n# Configure the world\n#\nsh configure --enable-static=$CONFIG_STATIC \\\n --enable-unicode=$CONFIG_UNICODE \\\n --enable-debug=$CONFIG_DEBUG \\\n --with-lib-preference=local,system \\\n --with-wx-version=$WX_VER \\\n $CONFIG_FLAGS || exit 1\n\n#\n# Libsndfile's endianness detection is a bit unfriendly to universal binary\n# builds, so we coerce it into working our way.\n#\nFILE=lib-src/libsndfile/src/config.h\ngrep -q '__BIG_ENDIAN__' $FILE || cat <<EOF >>$FILE\n#undef CPU_IS_BIG_ENDIAN\n#undef CPU_IS_LITTLE_ENDIAN\n#if defined(__BIG_ENDIAN__)\n#define CPU_IS_BIG_ENDIAN 1\n#define CPU_IS_LITTLE_ENDIAN 0\n#else\n#define CPU_IS_BIG_ENDIAN 0\n#define CPU_IS_LITTLE_ENDIAN 1\n#endif\nEOF\n\n#\n# Ditto for libflac.\n#\nFILE=lib-src/libflac/config.h\ngrep -q '__BIG_ENDIAN__' $FILE || cat <<EOF >>$FILE\n#ifdef WORDS_BIGENDIAN\n#undef WORDS_BIGENDIAN\n#endif\n#if defined(__BIG_ENDIAN__)\n#define WORDS_BIGENDIAN 1\n#endif\nEOF\n\n#\n# And libmad.\n#\nFILE=lib-src/libmad/config.h\ngrep -q '__BIG_ENDIAN__' $FILE || cat <<EOF >>$FILE\n#ifdef WORDS_BIGENDIAN\n#undef WORDS_BIGENDIAN\n#endif\n#if defined(__BIG_ENDIAN__)\n#define WORDS_BIGENDIAN 1\n#endif\nEOF\n\n#\n# Generate the header file for preprocessing the Info.plist\n#\ngcc -E -dM src/Audacity.h | grep AUDACITY >mac/Info.plist.h\n\nexit 0\n";
|
||||
};
|
||||
17073FEB0988E6C9008541CC /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
|
@ -217,7 +217,7 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Audacity</string>
|
||||
<string>Audacity.sh</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>Audacity.icns</string>
|
||||
<key>CFBundleName</key>
|
||||
|
@ -19,6 +19,7 @@ License: GPL v2. See License.txt.
|
||||
#include "Audacity.h" // for config*.h
|
||||
#include "FFmpeg.h"
|
||||
#include "AudacityApp.h"
|
||||
#include "FileNames.h"
|
||||
|
||||
#include <wx/file.h>
|
||||
|
||||
@ -674,12 +675,12 @@ bool FFmpegLibs::ValidLibsLoaded()
|
||||
|
||||
bool FFmpegLibs::InitLibs(wxString libpath_format, bool showerr)
|
||||
{
|
||||
wxString syspath;
|
||||
bool pathfix = false;
|
||||
|
||||
FreeLibs();
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
wxString syspath;
|
||||
bool pathfix = false;
|
||||
|
||||
wxLogMessage(wxT("Looking up PATH environment variable..."));
|
||||
// First take PATH environment variable and store its content.
|
||||
if (wxGetEnv(wxT("PATH"),&syspath))
|
||||
@ -728,55 +729,57 @@ bool FFmpegLibs::InitLibs(wxString libpath_format, bool showerr)
|
||||
wxFileName name(libpath_format);
|
||||
bool gotError = false;
|
||||
|
||||
// Check for a monolithic avformat
|
||||
avformat = new wxDynamicLibrary();
|
||||
wxLogDebug(wxT("Loading avformat from '%s'."), libpath_format.c_str());
|
||||
// Vaughan, 2010-08-17: No explanation why logging was turned off, so commented these out.
|
||||
//wxLogWindow* pLogger = wxGetApp().mLogger;
|
||||
//if (showerr)
|
||||
// pLogger->SetActiveTarget(NULL);
|
||||
gotError = !avformat->Load(libpath_format, wxDL_LAZY);
|
||||
//if (showerr)
|
||||
// pLogger->SetActiveTarget(pLogger);
|
||||
wxLogDebug(wxT("Checking for monolithic avformat from '%s'."), name.GetFullPath().c_str());
|
||||
gotError = !avformat->Load(name.GetFullPath(), wxDL_LAZY);
|
||||
|
||||
// Verify it really is monolithic
|
||||
if (!gotError) {
|
||||
if (avformat->HasSymbol(wxT("av_free"))) {
|
||||
util = avformat;
|
||||
wxFileName actual;
|
||||
|
||||
actual = FileNames::PathFromAddr(avformat->GetSymbol(wxT("avutil_version")));
|
||||
if (actual.GetPath().IsSameAs(name.GetPath())) {
|
||||
|
||||
actual = FileNames::PathFromAddr(avformat->GetSymbol(wxT("avcodec_version")));
|
||||
if (actual.GetPath().IsSameAs(name.GetPath())) {
|
||||
util = avformat;
|
||||
codec = avformat;
|
||||
}
|
||||
}
|
||||
if (avformat->HasSymbol(wxT("avcodec_init"))) {
|
||||
codec = avformat;
|
||||
|
||||
if (util == NULL || codec == NULL) {
|
||||
wxLogDebug(wxT("avformat not monolithic"));
|
||||
avformat->Unload();
|
||||
util = NULL;
|
||||
codec = NULL;
|
||||
}
|
||||
else {
|
||||
wxLogDebug(wxT("avformat is monolithic"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!util) {
|
||||
name.SetFullName(GetLibAVUtilName());
|
||||
avutil = util = new wxDynamicLibrary();
|
||||
avutil = util = new wxDynamicLibrary();
|
||||
wxLogDebug(wxT("Loading avutil from '%s'."), name.GetFullPath().c_str());
|
||||
//if (showerr)
|
||||
// pLogger->SetActiveTarget(NULL);
|
||||
util->Load(name.GetFullPath(), wxDL_LAZY);
|
||||
//if (showerr)
|
||||
// pLogger->SetActiveTarget(pLogger);
|
||||
}
|
||||
|
||||
if (!codec) {
|
||||
name.SetFullName(GetLibAVCodecName());
|
||||
avcodec = codec = new wxDynamicLibrary();
|
||||
wxLogDebug(wxT("Loading avcodec from '%s'."), name.GetFullPath().c_str());
|
||||
//if (showerr)
|
||||
// pLogger->SetActiveTarget(NULL);
|
||||
codec->Load(name.GetFullPath(), wxDL_LAZY);
|
||||
//if (showerr)
|
||||
// pLogger->SetActiveTarget(pLogger);
|
||||
}
|
||||
|
||||
if (!avformat->IsLoaded()) {
|
||||
//if (showerr)
|
||||
// pLogger->SetActiveTarget(NULL);
|
||||
gotError = !avformat->Load(libpath_format, wxDL_LAZY);
|
||||
//if (showerr)
|
||||
// pLogger->SetActiveTarget(pLogger);
|
||||
name.SetFullName(libpath_format);
|
||||
wxLogDebug(wxT("Loading avformat from '%s'."), name.GetFullPath().c_str());
|
||||
gotError = !avformat->Load(name.GetFullPath(), wxDL_LAZY);
|
||||
}
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
//Return PATH to normal
|
||||
if ( pathfix )
|
||||
{
|
||||
@ -784,6 +787,7 @@ bool FFmpegLibs::InitLibs(wxString libpath_format, bool showerr)
|
||||
wxLogMessage(wxT("Returning PATH to previous setting..."));
|
||||
wxSetEnv(wxT("PATH"),oldpath.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gotError) {
|
||||
wxLogError(wxT("Failed to load FFmpeg libraries."));
|
||||
@ -791,6 +795,20 @@ bool FFmpegLibs::InitLibs(wxString libpath_format, bool showerr)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Show the actual libraries loaded
|
||||
if (avutil) {
|
||||
wxLogDebug(wxT("Actual avutil path %s"),
|
||||
FileNames::PathFromAddr(avutil->GetSymbol(wxT("avutil_version"))).c_str());
|
||||
}
|
||||
if (avcodec) {
|
||||
wxLogDebug(wxT("Actual avcodec path %s"),
|
||||
FileNames::PathFromAddr(avcodec->GetSymbol(wxT("avcodec_version"))).c_str());
|
||||
}
|
||||
if (avformat) {
|
||||
wxLogDebug(wxT("Actual avformat path %s"),
|
||||
FileNames::PathFromAddr(avformat->GetSymbol(wxT("avformat_version"))).c_str());
|
||||
}
|
||||
|
||||
wxLogDebug(wxT("Importing symbols..."));
|
||||
INITDYN(avformat,av_register_all);
|
||||
INITDYN(avformat,av_open_input_file);
|
||||
|
@ -28,8 +28,13 @@ used throughout Audacity into this one place.
|
||||
#include <wx/stdpaths.h>
|
||||
#include "Prefs.h"
|
||||
#include "FileNames.h"
|
||||
#include "Internat.h"
|
||||
#include "PlatformCompatibility.h"
|
||||
|
||||
#if defined(__WXMAC__) || defined(__WXGTK__)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
static wxString gDataDir;
|
||||
|
||||
wxString FileNames::MkDir(const wxString &Str)
|
||||
@ -205,3 +210,33 @@ wxString FileNames::ThemeComponent(const wxString &Str)
|
||||
{
|
||||
return wxFileName( ThemeComponentsDir(), Str, wxT("png") ).GetFullPath();
|
||||
}
|
||||
|
||||
//
|
||||
// Returns the full path of program module (.exe, .dll, .so, .dylib) containing address
|
||||
//
|
||||
wxString FileNames::PathFromAddr(void *addr)
|
||||
{
|
||||
wxFileName name;
|
||||
|
||||
#if defined(__WXMAC__) || defined(__WXGTK__)
|
||||
Dl_info info;
|
||||
if (dladdr(addr, &info)) {
|
||||
name = LAT1CTOWX(info.dli_fname);
|
||||
}
|
||||
#elif defined(__WXMSW__)
|
||||
HMODULE module;
|
||||
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(LPCWSTR) addr,
|
||||
&module)) {
|
||||
TCHAR path[MAX_PATH];
|
||||
DWORD nSize;
|
||||
|
||||
nSize = GetModuleFileName(module, path, MAX_PATH);
|
||||
if (nSize && nSize < MAX_PATH) {
|
||||
name = LAT1CTOWX(path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return name.GetFullPath();
|
||||
}
|
||||
|
@ -55,6 +55,9 @@ public:
|
||||
static wxString ThemeCacheHtm();
|
||||
static wxString ThemeImageDefsAsCee();
|
||||
|
||||
// Obtain name of loaded module that contains address
|
||||
static wxString PathFromAddr(void *addr);
|
||||
|
||||
private:
|
||||
// Private constructors: No one is ever going to instantiate it.
|
||||
//
|
||||
|
Loading…
x
Reference in New Issue
Block a user