From 216211343ff4ea3183823939c977146aeced0914 Mon Sep 17 00:00:00 2001 From: David Rose Date: Mon, 5 Oct 2009 22:35:50 +0000 Subject: [PATCH] panda3dw.exe, and file-associations for windows --- .../src/plugin_installer/FileAssociation.nsh | 190 ++++++++++++++++++ direct/src/plugin_installer/make_installer.py | 78 ++++--- direct/src/plugin_installer/p3d_installer.nsi | 25 ++- direct/src/plugin_standalone/Sources.pp | 26 +++ direct/src/plugin_standalone/panda3d.cxx | 75 +++++++ 5 files changed, 365 insertions(+), 29 deletions(-) create mode 100755 direct/src/plugin_installer/FileAssociation.nsh diff --git a/direct/src/plugin_installer/FileAssociation.nsh b/direct/src/plugin_installer/FileAssociation.nsh new file mode 100755 index 0000000000..71a9162efc --- /dev/null +++ b/direct/src/plugin_installer/FileAssociation.nsh @@ -0,0 +1,190 @@ +/* +_____________________________________________________________________________ + + File Association +_____________________________________________________________________________ + + Based on code taken from http://nsis.sourceforge.net/File_Association + + Usage in script: + 1. !include "FileAssociation.nsh" + 2. [Section|Function] + ${FileAssociationFunction} "Param1" "Param2" "..." $var + [SectionEnd|FunctionEnd] + + FileAssociationFunction=[RegisterExtension|UnRegisterExtension] + +_____________________________________________________________________________ + + ${RegisterExtension} "[executable]" "[extension]" "[description]" + +"[executable]" ; executable which opens the file format + ; +"[extension]" ; extension, which represents the file format to open + ; +"[description]" ; description for the extension. This will be display in Windows Explorer. + ; + + + ${UnRegisterExtension} "[extension]" "[description]" + +"[extension]" ; extension, which represents the file format to open + ; +"[description]" ; description for the extension. This will be display in Windows Explorer. + ; + +_____________________________________________________________________________ + + Macros +_____________________________________________________________________________ + + Change log window verbosity (default: 3=no script) + + Example: + !include "FileAssociation.nsh" + !insertmacro RegisterExtension + ${FileAssociation_VERBOSE} 4 # all verbosity + !insertmacro UnRegisterExtension + ${FileAssociation_VERBOSE} 3 # no script +*/ + + +!ifndef FileAssociation_INCLUDED +!define FileAssociation_INCLUDED + +!include Util.nsh + +!verbose push +!verbose 3 +!ifndef _FileAssociation_VERBOSE + !define _FileAssociation_VERBOSE 3 +!endif +!verbose ${_FileAssociation_VERBOSE} +!define FileAssociation_VERBOSE `!insertmacro FileAssociation_VERBOSE` +!verbose pop + +!macro FileAssociation_VERBOSE _VERBOSE + !verbose push + !verbose 3 + !undef _FileAssociation_VERBOSE + !define _FileAssociation_VERBOSE ${_VERBOSE} + !verbose pop +!macroend + + + +!macro RegisterExtensionCall _EXECUTABLE _EXTENSION _DESCRIPTION + !verbose push + !verbose ${_FileAssociation_VERBOSE} + Push `${_DESCRIPTION}` + Push `${_EXTENSION}` + Push `${_EXECUTABLE}` + ${CallArtificialFunction} RegisterExtension_ + !verbose pop +!macroend + +!macro UnRegisterExtensionCall _EXTENSION _DESCRIPTION + !verbose push + !verbose ${_FileAssociation_VERBOSE} + Push `${_EXTENSION}` + Push `${_DESCRIPTION}` + ${CallArtificialFunction} UnRegisterExtension_ + !verbose pop +!macroend + + + +!define RegisterExtension `!insertmacro RegisterExtensionCall` +!define un.RegisterExtension `!insertmacro RegisterExtensionCall` + +!macro RegisterExtension +!macroend + +!macro un.RegisterExtension +!macroend + +!macro RegisterExtension_ + !verbose push + !verbose ${_FileAssociation_VERBOSE} + + Exch $R2 ;exe + Exch + Exch $R1 ;ext + Exch + Exch 2 + Exch $R0 ;desc + Exch 2 + Push $0 + Push $1 + + ReadRegStr $1 HKCR $R1 "" ; read current file association + StrCmp "$1" "" NoBackup ; is it empty + StrCmp "$1" "$R0" NoBackup ; is it our own + WriteRegStr HKCR $R1 "backup_val" "$1" ; backup current value +NoBackup: + WriteRegStr HKCR $R1 "" "$R0" ; set our file association + + ReadRegStr $0 HKCR $R0 "" + StrCmp $0 "" 0 Skip + WriteRegStr HKCR "$R0" "" "$R0" + WriteRegStr HKCR "$R0\shell" "" "open" + WriteRegStr HKCR "$R0\DefaultIcon" "" "$R2,0" +Skip: + WriteRegStr HKCR "$R0\shell\open\command" "" '"$R2" "%1"' + WriteRegStr HKCR "$R0\shell\edit" "" "Edit $R0" + WriteRegStr HKCR "$R0\shell\edit\command" "" '"$R2" "%1"' + + Pop $1 + Pop $0 + Pop $R2 + Pop $R1 + Pop $R0 + + !verbose pop +!macroend + + + +!define UnRegisterExtension `!insertmacro UnRegisterExtensionCall` +!define un.UnRegisterExtension `!insertmacro UnRegisterExtensionCall` + +!macro UnRegisterExtension +!macroend + +!macro un.UnRegisterExtension +!macroend + +!macro UnRegisterExtension_ + !verbose push + !verbose ${_FileAssociation_VERBOSE} + + Exch $R1 ;desc + Exch + Exch $R0 ;ext + Exch + Push $0 + Push $1 + + ReadRegStr $1 HKCR $R0 "" + StrCmp $1 $R1 0 NoOwn ; only do this if we own it + ReadRegStr $1 HKCR $R0 "backup_val" + StrCmp $1 "" 0 Restore ; if backup="" then delete the whole key + DeleteRegKey HKCR $R0 + Goto NoOwn + +Restore: + WriteRegStr HKCR $R0 "" $1 + DeleteRegValue HKCR $R0 "backup_val" + DeleteRegKey HKCR $R1 ;Delete key with association name settings + +NoOwn: + + Pop $1 + Pop $0 + Pop $R1 + Pop $R0 + + !verbose pop +!macroend + +!endif # !FileAssociation_INCLUDED diff --git a/direct/src/plugin_installer/make_installer.py b/direct/src/plugin_installer/make_installer.py index 43515a8f7c..ce96ab6fa4 100755 --- a/direct/src/plugin_installer/make_installer.py +++ b/direct/src/plugin_installer/make_installer.py @@ -125,21 +125,66 @@ def parseDependenciesWindows(tempFile): # At least we got some data. return filenames +def addDependencies(path, pathname, file, pluginDependencies, dependentFiles): + """ Checks the named file for DLL dependencies, and adds any + appropriate dependencies found into pluginDependencies and + dependentFiles. """ + + tempFile = Filename.temporary('', 'p3d_', '.txt') + command = 'dumpbin /dependents "%s" >"%s"' % ( + pathname.toOsSpecific(), + tempFile.toOsSpecific()) + try: + os.system(command) + except: + pass + filenames = None + + if tempFile.exists(): + filenames = parseDependenciesWindows(tempFile) + tempFile.unlink() + if filenames is None: + sys.exit("Unable to determine dependencies from %s" % (pathname)) + + # Look for MSVC[RP]*.dll, and MFC*.dll. These dependent files + # have to be included too. Also, any Panda-based libraries, or + # the Python DLL, should be included, in case panda3d.exe wasn't + # built static. The Panda-based libraries begin with "lib" and + # are all lowercase. + for dfile in filenames: + dfilelower = dfile.lower() + if dfilelower not in dependentFiles: + if dfilelower.startswith('msvc') or \ + dfilelower.startswith('mfc') or \ + (dfile.startswith('lib') and dfile == dfilelower) or \ + dfilelower.startswith('python'): + pathname = path.findFile(dfile) + if not pathname: + sys.exit("Couldn't find %s." % (dfile)) + dependentFiles[dfilelower] = pathname.toOsSpecific() + + # Also recurse. + addDependencies(path, pathname, file, pluginDependencies, dependentFiles) + + if dfilelower in dependentFiles: + pluginDependencies[file].append(dfilelower) + def makeInstaller(): # Locate the plugin(s). pluginFiles = {} pluginDependencies = {} dependentFiles = {} - # These are the three primary files that make up the + # These are the four primary files that make up the # plugin/runtime. ocx = 'p3dactivex.ocx' npapi = 'nppanda3d.dll' panda3d = 'panda3d.exe' + panda3dw = 'panda3dw.exe' path = DSearchPath() path.appendPath(os.environ['PATH']) - for file in [ocx, npapi, panda3d]: + for file in [ocx, npapi, panda3d, panda3dw]: pathname = path.findFile(file) if not pathname: sys.exit("Couldn't find %s." % (file)) @@ -148,32 +193,7 @@ def makeInstaller(): pluginDependencies[file] = [] # Also look for the dll's that these plugins reference. - tempFile = Filename.temporary('', 'p3d_', '.txt') - command = 'dumpbin /dependents "%s" >"%s"' % ( - pathname.toOsSpecific(), - tempFile.toOsSpecific()) - try: - os.system(command) - except: - pass - filenames = None - - if tempFile.exists(): - filenames = parseDependenciesWindows(tempFile) - tempFile.unlink() - if filenames is None: - sys.exit("Unable to determine dependencies from %s" % (pathname)) - - # Look for MSVC[RP]*.dll, and MFC*.dll. These dependent files - # have to be included too. - for dfile in filenames: - dfile = dfile.lower() - if dfile.startswith('msvc') or dfile.startswith('mfc'): - pathname = path.findFile(dfile) - if not pathname: - sys.exit("Couldn't find %s." % (dfile)) - pluginDependencies[file].append(dfile) - dependentFiles[dfile] = pathname.toOsSpecific() + addDependencies(path, pathname, file, pluginDependencies, dependentFiles) welcomeBitmap = None if options.welcome_image: @@ -216,6 +236,8 @@ def makeInstaller(): CMD += '/DNPAPI_PATH="' + pluginFiles[npapi] + '" ' CMD += '/DPANDA3D="' + panda3d + '" ' CMD += '/DPANDA3D_PATH="' + pluginFiles[panda3d] + '" ' + CMD += '/DPANDA3DW="' + panda3dw + '" ' + CMD += '/DPANDA3DW_PATH="' + pluginFiles[panda3dw] + '" ' dependencies = dependentFiles.items() for i in range(len(dependencies)): diff --git a/direct/src/plugin_installer/p3d_installer.nsi b/direct/src/plugin_installer/p3d_installer.nsi index 956bac06bd..e2e2fd5a62 100755 --- a/direct/src/plugin_installer/p3d_installer.nsi +++ b/direct/src/plugin_installer/p3d_installer.nsi @@ -1,6 +1,7 @@ !include "MUI.nsh" !include LogicLib.nsh !include FileFunc.nsh +!include FileAssociation.nsh ; Several variables are assumed to be pre-defined by the caller. See ; make_installer.py in this directory. @@ -73,6 +74,7 @@ Section "MainSection" SEC01 File "${OCX_PATH}" File "${NPAPI_PATH}" File "${PANDA3D_PATH}" + File "${PANDA3DW_PATH}" ; Auto-detected dependencies on the above executables. Python ; computes these values for us. @@ -91,7 +93,15 @@ Section "MainSection" SEC01 !ifdef DEP4P File "${DEP4P}" !endif +!ifdef DEP5P + File "${DEP5P}" +!endif +!ifdef DEP6P + File "${DEP6P}" +!endif + ${registerExtension} "$INSTDIR\${PANDA3DW}" ".p3d" "Panda3D applet" + !ifdef ADD_START_MENU ; Start->Programs links CreateDirectory "$SMPROGRAMS\${PROG_GROUPNAME}" @@ -166,6 +176,12 @@ Mozilla-Install-Loop: !endif !ifdef NPAPI_DEP4 CopyFiles $INSTDIR\${NPAPI_DEP4} "$2" +!endif +!ifdef NPAPI_DEP5 + CopyFiles $INSTDIR\${NPAPI_DEP5} "$2" +!endif +!ifdef NPAPI_DEP6 + CopyFiles $INSTDIR\${NPAPI_DEP6} "$2" !endif ${EndIf} @@ -183,6 +199,7 @@ Section Uninstall Delete "$INSTDIR\${OCX}" Delete "$INSTDIR\${NPAPI}" Delete "$INSTDIR\${PANDA3D}" + Delete "$INSTDIR\${PANDA3DW}" !ifdef DEP0 Delete "$INSTDIR\${DEP0}" !endif @@ -198,6 +215,12 @@ Section Uninstall !ifdef DEP4 Delete "$INSTDIR\${DEP4}" !endif +!ifdef DEP5 + Delete "$INSTDIR\${DEP5}" +!endif +!ifdef DEP6 + Delete "$INSTDIR\${DEP6}" +!endif StrCpy $1 "0" Mozilla-Uninstall-Loop: @@ -213,7 +236,7 @@ Mozilla-Uninstall-Loop: goto Mozilla-Uninstall-Loop Mozilla-Uninstall-End: - ReadRegDWORD $0 HKLM SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System EnableLUA + ${unregisterExtension} ".p3d" "Panda3D applet" # Remove the user's "Panda3D" directory, where all of the downloaded # contents are installed. Too bad we can't do this for every system diff --git a/direct/src/plugin_standalone/Sources.pp b/direct/src/plugin_standalone/Sources.pp index e2673d12c3..bdcae15607 100644 --- a/direct/src/plugin_standalone/Sources.pp +++ b/direct/src/plugin_standalone/Sources.pp @@ -26,3 +26,29 @@ #endif #end bin_target + +#begin bin_target + // On Windows, we also need to build panda3dw.exe, the non-console + // version of panda3d.exe. + + #define BUILD_TARGET $[WINDOWS_PLATFORM] + #define USE_PACKAGES openssl zlib + #define TARGET panda3dw + #define EXTRA_CDEFS NON_CONSOLE + + #define LOCAL_LIBS plugin_common + + #define OTHER_LIBS \ + prc:c dtoolutil:c dtoolbase:c dtool:m \ + interrogatedb:c dconfig:c dtoolconfig:m \ + express:c downloader:c pandaexpress:m \ + pystub + + #define OSX_SYS_FRAMEWORKS Foundation AppKit Carbon + + #define SOURCES \ + panda3d.cxx panda3d.h panda3d.I + + #define WIN_SYS_LIBS user32.lib gdi32.lib shell32.lib ole32.lib + +#end bin_target diff --git a/direct/src/plugin_standalone/panda3d.cxx b/direct/src/plugin_standalone/panda3d.cxx index 2f83ddeb00..fa365f13c4 100644 --- a/direct/src/plugin_standalone/panda3d.cxx +++ b/direct/src/plugin_standalone/panda3d.cxx @@ -52,6 +52,12 @@ Panda3D() { _root_dir = find_root_dir(); _reporting_download = false; _enable_security = false; + +#ifdef NON_CONSOLE + // For the desktop version of this program, let's always assume -S + // is in effect. + _enable_security = true; +#endif } //////////////////////////////////////////////////////////////////// @@ -1126,8 +1132,77 @@ run() { } +#if defined(_WIN32) && defined(NON_CONSOLE) +// On Windows, we may need to build panda3dw.exe, a non-console +// version of this program. + +// Returns a newly-allocated string representing the quoted argument +// beginning at p. Advances p to the first character following the +// close quote. +static char * +parse_quoted_arg(char *&p) { + char quote = *p; + ++p; + string result; + + while (*p != '\0' && *p != quote) { + // TODO: handle escape characters? Not sure if we need to. + result += *p; + ++p; + } + if (*p == quote) { + ++p; + } + return strdup(result.c_str()); +} + +// Returns a newly-allocated string representing the unquoted argument +// beginning at p. Advances p to the first whitespace following the +// argument. +static char * +parse_unquoted_arg(char *&p) { + string result; + while (*p != '\0' && !isspace(*p)) { + result += *p; + ++p; + } + return strdup(result.c_str()); +} + +WINAPI +WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { + char *command_line = GetCommandLine(); + + vector argv; + + char *p = command_line; + while (*p != '\0') { + if (*p == '"') { + char *arg = parse_quoted_arg(p); + argv.push_back(arg); + } else { + char *arg = parse_unquoted_arg(p); + argv.push_back(arg); + } + + // Skip whitespace. + while (*p != '\0' && isspace(*p)) { + ++p; + } + } + + assert(!argv.empty()); + + Panda3D program; + return program.run(argv.size(), &argv[0]); +} + +#else // NON_CONSOLE + +// The normal, "console" program. int main(int argc, char *argv[]) { Panda3D program; return program.run(argc, argv); } +#endif // NON_CONSOLE