├── .gitattributes
├── .github
├── CONTRIBUTING.md
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── Reference
└── CAB Format Notes.txt
├── appveyor.yml
├── art
├── lessmsiicon.png
├── lessmsiicon.pxm
├── lessmsiicon32x32.png
└── lessmsiicon32x32.pxm
├── contrib
└── python
│ ├── LessMsi.py
│ ├── setup.cfg
│ └── setup.py
├── lib
├── wix.dll
└── wixcab.dll
├── misc
├── CompoundDocuments
│ ├── CompoundDocumentViewer11.zip
│ └── SSView.zip
├── screenshot-explorerintegration.png
├── screenshot-filestab.png
├── screenshot-preferences.png
├── screenshot-summarytab.png
└── screenshot-tabletab.png
├── release.config.js
└── src
├── .build
├── MSBuild.Community.Tasks.dll
├── MSBuild.Community.Tasks.targets
├── VERIFICATION.txt
├── chocolateyInstall.ps1
├── lessmsi.msbuild
├── lessmsi.nuspec
├── nuget-restore.bat
├── semantic-release-prepare.cmd
├── semantic-release-publish.cmd
└── semantic-release-verify.cmd
├── .nuget
├── NuGet.Config
├── NuGet.exe
├── NuGet.mono.targets
├── NuGet.targets
└── packages.config
├── CommonAssemblyInfo.cs
├── ExplorerShortcutHelper
├── ExplorerShortcutHelper.csproj
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── RegistryTools.cs
├── app.config
└── app.manifest
├── LICENSE
├── LessMsi.Cli
├── App.config
├── ExtractCommand.cs
├── LessMsi.Cli.csproj
├── LessMsiCommand.cs
├── ListTableCommand.cs
├── OpenGuiCommand.cs
├── Options.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── ShowHelpCommand.cs
├── ShowVersionCommand.cs
└── packages.config
├── LessMsi.Core
├── LessMsi.Core.csproj
├── Msi
│ ├── ColumnInfo.cs
│ ├── ExternalCabNotFoundException.cs
│ ├── ExtractionMode.cs
│ ├── MsiDatabase.cs
│ ├── MsiDirectory.cs
│ ├── MsiFile.cs
│ ├── TableWrapper.cs
│ ├── ViewWrapper.cs
│ └── Wixtracts.cs
├── OleStorage
│ ├── NativeMethods.cs
│ ├── OleStorageFile.cs
│ └── README.md
├── Properties
│ └── AssemblyInfo.cs
└── packages.config
├── LessMsi.Gui
├── AboutBox.Designer.cs
├── AboutBox.cs
├── AboutBox.resx
├── ChangeLanguageForm.Designer.cs
├── ChangeLanguageForm.cs
├── ChangeLanguageForm.resx
├── Extensions
│ ├── DataGridViewExtensions.cs
│ ├── MsiNativeMethods.cs
│ └── SummaryInformationExtensions.cs
├── ExtractionProgressDialog.cs
├── IMainFormView.cs
├── LessMsi.Gui.csproj
├── MainForm.cs
├── MainForm.resx
├── MainFormPresenter.cs
├── Model
│ ├── CabContainedFileView.cs
│ ├── MsiFileItemView.cs
│ ├── MsiPropertyInfo.cs
│ └── StreamInfoView.cs
├── MruMenuStripManager.cs
├── PreferencesForm.Designer.cs
├── PreferencesForm.cs
├── PreferencesForm.resx
├── Program.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── Resources
│ ├── Languages
│ │ ├── Strings.Designer.cs
│ │ ├── Strings.de.resx
│ │ ├── Strings.fr.resx
│ │ ├── Strings.it.resx
│ │ ├── Strings.pt.resx
│ │ └── Strings.resx
│ └── LessmsiIcon.ico
├── Windows.Forms
│ ├── DisposableCursor.cs
│ ├── ElevationButton.cs
│ ├── ElevationNativeMethods.cs
│ ├── ErrorDialog.Designer.cs
│ ├── ErrorDialog.cs
│ ├── ErrorDialog.resx
│ ├── SearchPanel.Designer.cs
│ ├── SearchPanel.cs
│ ├── SearchPanel.resx
│ ├── SortableBindingList.cs
│ └── WinFormsHelper.cs
├── aboutbox.rtf
├── app.config
└── packages.config
├── LessMsi.sln
├── Lessmsi.Tests
├── CommandLineExtractTests.cs
├── CompareEntriesResult.cs
├── FileEntry.cs
├── FileEntryGraph.cs
├── GUITests.cs
├── LessMsi.Tests.csproj
├── MiscTests.cs
├── MiscTestsNUnit.cs
├── MspTests.cs
├── OleStorageTests.cs
├── Properties
│ └── AssemblyInfo.cs
├── TestBase.cs
├── TestFiles
│ ├── ExpectedOutput
│ │ ├── Extract1Arg.expected.csv
│ │ ├── Extract1ArgRelativePath.expected.csv
│ │ ├── Extract2Args.expected.csv
│ │ ├── Extract2ArgsRelativePath.expected.csv
│ │ ├── Extract3Args.expected.csv
│ │ ├── Extract3ArgsRelativePath.expected.csv
│ │ ├── ExtractCompatibility1Arg.expected.csv
│ │ ├── ExtractCompatibility2Args.expected.csv
│ │ ├── ExtractOnlySomeFiles.msi.expected.csv
│ │ ├── FlatOverwriteExtract1Arg.expected.csv
│ │ ├── FlatOverwriteExtract2Args.expected.csv
│ │ ├── FlatOverwriteExtract3Args.expected.csv
│ │ ├── FlatRenameExtract1Arg.expected.csv
│ │ ├── FlatRenameExtract2Args.expected.csv
│ │ ├── FlatRenameExtract3Args.expected.csv
│ │ ├── IviNetSharedComponents32_Fx20_1.3.0.msi.expected.csv
│ │ ├── NUnit-2.5.2.9222.msi.expected.csv
│ │ ├── OverwriteExtract1Arg.expected.csv
│ │ ├── OverwriteExtract2Args.expected.csv
│ │ ├── OverwriteExtract3Args.expected.csv
│ │ ├── Path With Spaces
│ │ │ └── spaces example.msi.expected.csv
│ │ ├── X86 Debuggers And Tools-x86_en-us.msi.expected.csv
│ │ ├── msi_with_external_cab.msi.expected.csv
│ │ ├── putty-0.68-installer.msi.expected.csv
│ │ ├── python-2.7.3.msi.expected.csv
│ │ └── vcredist.msi.expected.csv
│ ├── MsiInput
│ │ ├── AppleMobileDeviceSupport64.msi
│ │ ├── ExtractOnlySomeFiles.msi
│ │ ├── IviNetSharedComponents32_Fx20_1.3.0.msi
│ │ ├── NUnit-2.5.2.9222.msi
│ │ ├── Path With Spaces
│ │ │ └── spaces example.msi
│ │ ├── SQL2008_AS.msp
│ │ ├── Slik-Subversion-1.6.6-x64.msi
│ │ ├── VBRuntime.msi
│ │ ├── WPF2_32.msp
│ │ ├── X86 Debuggers And Tools-x86_en-us.msi
│ │ ├── long-directory-name
│ │ │ └── very
│ │ │ │ └── unusually
│ │ │ │ └── long
│ │ │ │ └── directory
│ │ │ │ └── name
│ │ │ │ └── with
│ │ │ │ └── cream
│ │ │ │ └── sugar
│ │ │ │ └── and
│ │ │ │ └── chocolate
│ │ │ │ └── topping
│ │ │ │ └── python-2.7.3.msi
│ │ ├── msi_with_external_cab.cab
│ │ ├── msi_with_external_cab.msi
│ │ ├── msxml5.msp
│ │ ├── putty-0.68-installer.msi
│ │ ├── python-2.7.3.msi
│ │ ├── vcredis1.cab
│ │ └── vcredist.msi
│ ├── create_msi_with_external_cab.cmd
│ └── create_msi_with_external_cab.wxs
└── packages.config
├── build.bat
├── build.sh
├── clean.bat
├── packages
└── repositories.config
└── test.bat
/.gitattributes:
--------------------------------------------------------------------------------
1 | # We use this to keep .sh (like build.sh) with lf only line endings to keep bash from puking when it's edited on someone's windows box. See http://git-scm.com/docs/gitattributes#_checking-out_and_checking-in
2 |
3 | *.sh eol=lf
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
4 |
5 | The following is a set of guidelines for contributing to LessMSI. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
6 |
7 | # Questions, Suggestions, Bugs
8 | Feel free to submit issues at https://github.com/activescott/lessmsi/issues to report bugs, request enhancements or just ask a question.
9 |
10 |
11 | # Your First Code Contribution
12 |
13 | Unsure where to begin contributing? You can start by looking through these `beginner` and `help-wanted` issues:
14 |
15 | * [Beginner issues][beginner] - issues which should only require a few lines of code, and a test or two.
16 | * [Help wanted issues][help-wanted] - issues which should be a bit more involved than `beginner` issues.
17 |
18 | Both issue lists are sorted by total number of comments. While not perfect, number of comments is a reasonable proxy for impact a given change will have.
19 |
20 | ## Pull Requests ##
21 | To get your changes into a Lessmsi release, please fork the repository, commit your changes to your fork, then submit a pull request. If you're not super confident about pull requests see Github's great documentation about how to do so at https://help.github.com/articles/about-pull-requests/
22 |
23 |
24 | Based off of https://github.com/atom/atom/blob/a7ad7478b7cd386ff377615841fcd91fa241640b/CONTRIBUTING.md
25 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: activescott/lessmsi # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: ['https://tip4commit.com/github/activescott/lessmsi'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Desktop (please complete the following information):**
24 | - OS: [e.g. iOS]
25 | - Browser [e.g. chrome, safari]
26 | - Version [e.g. 22]
27 |
28 | **Smartphone (please complete the following information):**
29 | - Device: [e.g. iPhone6]
30 | - OS: [e.g. iOS8.1]
31 | - Browser [e.g. stock browser, safari]
32 | - Version [e.g. 22]
33 |
34 | **Additional context**
35 | Add any other context about the problem here.
36 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
3 |
4 | Please fill out the following checklist:
5 |
6 | - [ ] Added tests for any bug fixes that changed existing code
7 | - [ ] Added tests for any new behavior
8 | - [ ] All unit tests are passing (please make sure all tests are passing for your branch/PR at https://ci.appveyor.com/project/activescott/lessmsi/history )
9 |
10 |
11 | If you need any help at all, feel free to submit the PR and @-mention activescott and I'll be happy to assist!
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | [Bb]in/
3 | [Oo]bj/
4 | *.suo
5 | *.user
6 | *.csproj.user
7 | *.dtbcache.json
8 | *.userprefs
9 | *.wixpdb
10 | *.wixobj
11 | # VIM
12 | *~
13 | *.swp
14 | /src/.deploy
15 | _Resharper*/
16 |
17 | # Nuget: http://docs.nuget.org/consume/package-restore#omitting-packages-from-source-control
18 | ## Ignore NuGet Packages
19 | *.nupkg
20 | ## Ignore the packages folder
21 | **/packages/*
22 | ## except build/, which is used as an MSBuild target.
23 | !**/packages/build/
24 | ## Uncomment if necessary however generally it will be regenerated when needed
25 | !**/packages/repositories.config
26 |
27 | # Misc
28 | src/TestResult.xml
29 | src/.build/nuget.exe
30 | src/msbuild.log
31 | src/.vs/
32 | src/.temp/
33 | .vs/
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at lessmsi@willeke.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2004 Scott Willeke (http://scott.willeke.com)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Reference/CAB Format Notes.txt:
--------------------------------------------------------------------------------
1 | CAB Format Notes
2 | http://www.cabextract.org.uk/wince_cab_format/
3 | http://msdn.microsoft.com/en-us/library/bb267310(VS.85).aspx
4 | http://www.ddj.com/184410186;jsessionid=30Q2P0IQ3EA4XQE1GHPSKH4ATMY32JVN?_requestid=420442
5 | http://msdn.microsoft.com/en-us/library/aa367841%28VS.85%29.aspx
6 | http://en.wikipedia.org/wiki/Cabinet_%28file_format%29
7 |
8 | InstallShield has their own Proprietary CAB format. SynCE Project has a decompressor for it at: http://sourceforge.net/projects/synce/files/Unshield/0.6/
9 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # see http://www.appveyor.com/docs/appveyor-yml
2 | # environment info: https://www.appveyor.com/docs/windows-images-software/#visual-studio-2015
3 | image: Visual Studio 2022
4 |
5 | environment:
6 | # from https://github.com/settings/tokens encrypted at https://ci.appveyor.com/tools/encrypt
7 | GH_TOKEN:
8 | secure: MJhg1HrP+chjNOM6GAdrQzMWMTHNwmzD57kWT7mhWwnOOJiag1EZHHgVtrTUmGJ1syJncgmKTVofdOND+9QmqbpbMji6V0eN9EPXN6t+Os78H+wuCj58GLWjel0x+nM9
9 | CHOCO_KEY:
10 | secure: /Ie5xuB5GTDwElbSN0V+mCyYtYNhfQJRjdoWFUsNsVJW9bq32LNzSlI1cn4OUxIu
11 |
12 | #---------------------------------#
13 | # build configuration #
14 | #---------------------------------#
15 | build_script:
16 | - cmd: msbuild .\src\.build\lessmsi.msbuild /p:TheVersion=%APPVEYOR_BUILD_VERSION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
17 |
18 | #---------------------------------#
19 | # tests configuration #
20 | #---------------------------------#
21 | test:
22 | assemblies:
23 | - .\src\.deploy\LessMsi.Tests.dll
24 |
25 | artifacts:
26 | - path: 'src\.deploy\chocolateypackage\*.nupkg'
27 | - path: 'src\.deploy\*.zip'
28 |
29 | # NOTE: Deployments, before_deploy and after_deploy scripts are disabled by default on Pull Requests.
30 | # https://www.appveyor.com/docs/deployment/#pull-requests
31 | deploy_script:
32 | # install (select) node 10 per semantic-release requirements: https://www.appveyor.com/docs/lang/nodejs-iojs/#selecting-nodejs-or-iojs-version
33 | - ps: Install-Product node 10
34 | # install semantic-release and reequirement plugins:
35 | - cmd: npm i semantic-release@17 @semantic-release/exec@5
36 | # run semantic-release with config:
37 | - cmd: node_modules\.bin\semantic-release
38 |
--------------------------------------------------------------------------------
/art/lessmsiicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/art/lessmsiicon.png
--------------------------------------------------------------------------------
/art/lessmsiicon.pxm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/art/lessmsiicon.pxm
--------------------------------------------------------------------------------
/art/lessmsiicon32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/art/lessmsiicon32x32.png
--------------------------------------------------------------------------------
/art/lessmsiicon32x32.pxm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/art/lessmsiicon32x32.pxm
--------------------------------------------------------------------------------
/contrib/python/LessMsi.py:
--------------------------------------------------------------------------------
1 | __all__=["doExtraction", "ExtrProgress"]
2 | import typing
3 | import clr
4 | clr.AddReference("LessMsi.core")
5 | clr.AddReference("LessIO")
6 | import LessIO
7 | from LessMsi.Msi import Wixtracts
8 | from pathlib import Path
9 |
10 | __license__="MIT"
11 | __copyright__=r"""
12 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 |
18 | Copyright (c) 2004 Scott Willeke (http://scott.willeke.com)
19 |
20 | Authors:
21 | Scott Willeke (scott@willeke.com)
22 | """
23 |
24 | class ExtrProgress:
25 | """Represents progress of unpacking"""
26 | __slots__=("current", "total", "fileName")
27 | def __init__(self, current:int, total:int, fileName:Path):
28 | self.current=current
29 | self.total=total
30 | self.fileName=fileName
31 | def __repr__(self):
32 | return self.__class__.__name__+"("+", ".join((str(self.current), str(self.total), str(self.fileName)))+")"
33 |
34 | from time import sleep
35 | def doExtraction(msiFileName:Path, outDirName:Path="", filesToExtract:typing.Iterable[Path]=None, progressCallback=None):
36 | """Extracts files from a .msi.
37 | See https://github.com/activescott/lessmsi/blob/master/src/LessMsi.Cli/Program.cs#L104 for more info
38 | """
39 |
40 | msiFileName=str(Path(msiFileName).absolute())
41 | outDirName=str(Path(outDirName).absolute())
42 | if filesToExtract:
43 | filesToExtract=(str(Path(outDirName).absolute()) for f in filesToExtract)
44 | msiFile = LessIO.Path(msiFileName)
45 | if progressCallback:
46 | def cb(progress:Wixtracts.ExtractionProgress):
47 | #progress.Activity
48 | return progressCallback(ExtrProgress(progress.FilesExtractedSoFar, progress.TotalFileCount, Path(progress.CurrentFileName)))
49 | cb=clr.System.AsyncCallback(cb)
50 | else:
51 | cb=None
52 | Wixtracts.ExtractFiles(msiFile, outDirName, filesToExtract, cb)
53 |
54 | try:
55 | from tqdm import tqdm
56 | def doExtractionWithTqdmProgressBar(msiFileName:Path, outDirName:Path="", filesToExtract:typing.Iterable[Path]=None, progressCallback=None):
57 | """Extracts files from a .msi showing a tqdm-based progressbar."""
58 | prev=0
59 | with tqdm(unit="file") as pb:
60 | def cb(progr:ExtrProgress):
61 | nonlocal pb, prev
62 | pb.desc=str(progr.fileName)
63 | delta=progr.current-prev
64 | prev=progr.current
65 | pb.total=progr.total
66 | pb.update(delta)
67 | if progressCallback:
68 | return progressCallback(progr)
69 | doExtraction(msiFileName, outDirName=outDirName, filesToExtract=filesToExtract, progressCallback=cb)
70 | __all__.append(doExtractionWithTqdmProgressBar.__name__)
71 | except ImportError:
72 | pass
73 |
--------------------------------------------------------------------------------
/contrib/python/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = LessMsi
3 | #version = 0.1
4 | author = KOLANICH
5 | url = https://github.com/KOLANICH/lessmsi/tree/python/contrib/python
6 | description = A python wrapper for extraction of MSIs with lessmsi
7 | keywords = msi, cab, extract, python
8 | license = MIT
9 | classifiers =
10 | Programming Language :: Python
11 | Programming Language :: Python :: 3
12 | Development Status :: 4 - Beta
13 | Environment :: Other Environment
14 | Intended Audience :: Developers
15 | License :: OSI Approved
16 | License :: OSI Approved :: MIT License
17 | Operating System :: OS Independent
18 | Topic :: Software Development :: Libraries :: Python Modules
19 | Topic :: Science
20 |
21 | [options]
22 | python_requires = >=3.4
23 | zip_safe = True
24 | py_modules = LessMsi
25 | setup_requires = setuptools_scm;
26 | install_requires =
27 | pythonnet
28 |
29 |
--------------------------------------------------------------------------------
/contrib/python/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from pathlib import Path
3 | from setuptools import setup
4 | from setuptools.config import read_configuration
5 |
6 | cfg = read_configuration(Path(__file__).parent / 'setup.cfg')
7 | #print(cfg)
8 | cfg["options"].update(cfg["metadata"])
9 | cfg=cfg["options"]
10 | setup(use_scm_version = True, **cfg)
11 |
--------------------------------------------------------------------------------
/lib/wix.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/lib/wix.dll
--------------------------------------------------------------------------------
/lib/wixcab.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/lib/wixcab.dll
--------------------------------------------------------------------------------
/misc/CompoundDocuments/CompoundDocumentViewer11.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/misc/CompoundDocuments/CompoundDocumentViewer11.zip
--------------------------------------------------------------------------------
/misc/CompoundDocuments/SSView.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/misc/CompoundDocuments/SSView.zip
--------------------------------------------------------------------------------
/misc/screenshot-explorerintegration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/misc/screenshot-explorerintegration.png
--------------------------------------------------------------------------------
/misc/screenshot-filestab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/misc/screenshot-filestab.png
--------------------------------------------------------------------------------
/misc/screenshot-preferences.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/misc/screenshot-preferences.png
--------------------------------------------------------------------------------
/misc/screenshot-summarytab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/misc/screenshot-summarytab.png
--------------------------------------------------------------------------------
/misc/screenshot-tabletab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/misc/screenshot-tabletab.png
--------------------------------------------------------------------------------
/release.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | branches: [
3 | "master"
4 | ],
5 | plugins: [
6 | "@semantic-release/commit-analyzer",
7 | ["@semantic-release/release-notes-generator", {
8 | writerOpts: {
9 | footerPartial: `
10 | {{#if noteGroups}}
11 | {{#each noteGroups}}
12 |
13 | ### {{title}}
14 |
15 | {{#each notes}}
16 | * {{text}}
17 | {{/each}}
18 | {{/each}}
19 | {{/if}}
20 |
21 | You can install or upgrade by extracting all files from the zip attached to this release into a single directory or via [Chocolatey](https://chocolatey.org/packages/lessmsi).
22 | `
23 | }
24 | }],
25 | // github config docs: https://github.com/semantic-release/github
26 | ["@semantic-release/github", {
27 | "assets": [
28 | {"path": "src/.deploy/chocolateypackage/*.nupkg", "label": "Chocolatey Package"},
29 | {"path": "src/.deploy/*.zip", "label": "Zip of lessmsi application binaries"}
30 | ]
31 | }],
32 | ["@semantic-release/exec", {
33 | "verifyConditionsCmd": "src\\.build\\semantic-release-verify.cmd",
34 | "prepareCmd": "src\\.build\\semantic-release-prepare.cmd ${nextRelease.version}",
35 | "publishCmd": "src\\.build\\semantic-release-publish.cmd ${nextRelease.version}",
36 | }],
37 | ]
38 | };
39 |
--------------------------------------------------------------------------------
/src/.build/MSBuild.Community.Tasks.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/src/.build/MSBuild.Community.Tasks.dll
--------------------------------------------------------------------------------
/src/.build/VERIFICATION.txt:
--------------------------------------------------------------------------------
1 | VERIFICATION
2 | This package is published by the author and this file is here only because chocolatey requires it as described at https://github.com/chocolatey/package-validator/wiki/VerificationFileMissing
--------------------------------------------------------------------------------
/src/.build/chocolateyInstall.ps1:
--------------------------------------------------------------------------------
1 |
2 | try {
3 |
4 | $theFile = Join-Path $(Split-Path -parent $MyInvocation.MyCommand.Definition) "AddWindowsExplorerShortcut.exe.ignore"
5 | #Write-Host "Creating " $theFile
6 | New-Item $theFile -type file
7 |
8 | $zipFile = Join-Path $(Split-Path -parent $MyInvocation.MyCommand.Definition) "__ZIP_FILE__"
9 |
10 | $toolsDir = (Split-Path -parent $MyInvocation.MyCommand.Definition)
11 | Get-ChocolateyUnzip -FileFullPath "$zipFile" -Destination $toolsDir
12 |
13 | } catch {
14 | throw $_.Exception
15 | }
16 |
--------------------------------------------------------------------------------
/src/.build/lessmsi.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | lessmsi
5 | LessMSI
6 | 1.0.*
7 | Scott Willeke
8 | Scott Willeke
9 | LessMSI - Easily extract the contents of an MSI
10 | LessMSI is a utility with a graphical user interface and a command line interface that can be used to view and extract the contents of an MSI file. For Windows. Usage on the command line: lessmsi x msiFileName [outDir]
11 |
12 | msi extract extraction install
13 | https://lessmsi.activescott.com
14 | https://www.opensource.org/licenses/mit-license.php
15 | false
16 | https://github.com/activescott/lessmsi
17 | https://github.com/activescott/lessmsi
18 | https://github.com/activescott/lessmsi/wiki
19 | https://github.com/activescott/lessmsi/releases
20 | https://github.com/activescott/lessmsi/issues
21 | https://lessmsi.activescott.com/images/lessmsiicon32x32.png
22 | https://github.com/activescott/lessmsi/issues
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/.build/nuget-restore.bat:
--------------------------------------------------------------------------------
1 | REM: Deos nothing: nuget.exe restore -Verbosity Detailed -NonInteractive -OutputDirectory c:\src\lessmsi\src\packages\ -Source "https://api.nuget.org/v3/index.json" c:\src\lessmsi\src\packages\repositories.config
2 | REM: Error, seems to ignore SolutionDirectory: nuget.exe restore -Verbosity Detailed -NonInteractive -OutputDirectory c:\src\lessmsi\src\packages\ -Source "https://api.nuget.org/v3/index.json" -SolutionDirectory c:\src\lessmsi\src
3 | nuget.exe restore -Verbosity Detailed -NonInteractive -OutputDirectory c:\src\lessmsi\src\packages\ -Source "https://api.nuget.org/v3/index.json" c:\src\lessmsi\src\LessMsi.sln
4 |
--------------------------------------------------------------------------------
/src/.build/semantic-release-prepare.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | REM This script called by semantic-release to "prepare" the build.
4 | REM Since semantic-release determines the version number based on commits, we rebuild it with the new version number:
5 |
6 | IF [%1]==[] (
7 | ECHO ERROR: Must supply build version as first parameter to this bat file! 1>&2
8 | EXIT 1
9 | )
10 | set _BUILD_VERSION=%1
11 |
12 | ECHO Running msbuild...
13 |
14 | msbuild .\src\.build\lessmsi.msbuild /p:TheVersion=%_BUILD_VERSION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
15 |
16 | REM NOTE: ECHO does not clear/set errorlevel https://ss64.com/nt/errorlevel.htmls
17 | ECHO Running msbuild complete.
18 |
19 | EXIT %ERRORLEVEL%
20 |
--------------------------------------------------------------------------------
/src/.build/semantic-release-publish.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | REM note: Nodejs configured and semantic-release insalled in appveyor.yml
4 | REM This script is called /by/ semantic-release to publish to chocolatey
5 |
6 | set THIS_DIR=%~dp0
7 |
8 | IF [%1]==[] (
9 | ECHO ERROR: Must supply build version as first parameter to this bat file! 1>&2
10 | EXIT 1
11 | )
12 | set _BUILD_VERSION=%1
13 |
14 | ECHO Running choco push...
15 |
16 | choco push --source https://push.chocolatey.org/ --api-key=%CHOCO_KEY% "%THIS_DIR%..\.deploy\chocolateypackage\lessmsi.%_BUILD_VERSION%.nupkg"
17 |
18 | REM NOTE: ECHO does not clear/set errorlevel https://ss64.com/nt/errorlevel.htmls
19 | ECHO Running choco push complete. Errorlevel was %ERRORLEVEL%
20 |
21 | EXIT %ERRORLEVEL%
22 |
--------------------------------------------------------------------------------
/src/.build/semantic-release-verify.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | IF DEFINED CHOCO_KEY (
4 | echo "CHOCO_KEY is defined"
5 | EXIT 0
6 | ) ELSE (
7 | echo "CHOCO_KEY is NOT defined" 1>&2
8 | EXIT 1
9 | )
10 |
--------------------------------------------------------------------------------
/src/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/activescott/lessmsi/ca8ca506d194ea710208c129701cba5bf850db5a/src/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/src/.nuget/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/CommonAssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // Permission is hereby granted, free of charge, to any person obtaining
2 | // a copy of this software and associated documentation files (the
3 | // "Software"), to deal in the Software without restriction, including
4 | // without limitation the rights to use, copy, modify, merge, publish,
5 | // distribute, sublicense, and/or sell copies of the Software, and to
6 | // permit persons to whom the Software is furnished to do so, subject to
7 | // the following conditions:
8 | //
9 | // The above copyright notice and this permission notice shall be
10 | // included in all copies or substantial portions of the Software.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 | //
20 | // Copyright (c) 2004-2013 Scott Willeke (http://scott.willeke.com)
21 | //
22 | // Authors:
23 | // Scott Willeke (scott@willeke.com)
24 | //
25 | using System.Reflection;
26 | using System.Runtime.InteropServices;
27 |
28 | [assembly: AssemblyVersion("1.0.10.*")]
29 | [assembly: AssemblyCopyright("Copyright Scott Willeke © 2004-2021")]
30 |
31 | // Setting ComVisible to false makes the types in this assembly not visible
32 | // to COM components. If you need to access a type in this assembly from
33 | // COM, set the ComVisible attribute to true on that type.
34 | [assembly: ComVisible(false)]
--------------------------------------------------------------------------------
/src/ExplorerShortcutHelper/ExplorerShortcutHelper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 9.0.30729
7 | 2.0
8 | {13F99803-59EF-4DEA-BE1C-68561A427B08}
9 | Exe
10 | Properties
11 | Willeke.Scott.ExplorerShortcutHelper
12 | AddWindowsExplorerShortcut
13 | v4.8
14 | 512
15 |
16 |
17 | app.manifest
18 |
19 |
20 | 3.5
21 |
22 | false
23 | publish\
24 | true
25 | Disk
26 | false
27 | Foreground
28 | 7
29 | Days
30 | false
31 | false
32 | true
33 | 0
34 | 1.0.0.%2a
35 | false
36 | true
37 |
38 |
39 |
40 | true
41 | bin\Debug\
42 | DEBUG;TRACE
43 | full
44 | x86
45 | prompt
46 | AllRules.ruleset
47 | false
48 |
49 |
50 | bin\Release\
51 | TRACE
52 | true
53 | pdbonly
54 | x86
55 | prompt
56 | AllRules.ruleset
57 | false
58 |
59 |
60 |
61 | Properties\CommonAssemblyInfo.cs
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | False
74 | .NET Framework 3.5 SP1 Client Profile
75 | false
76 |
77 |
78 | False
79 | .NET Framework 3.5 SP1
80 | true
81 |
82 |
83 | False
84 | Microsoft Visual Basic PowerPacks 10.0
85 | true
86 |
87 |
88 | False
89 | Windows Installer 3.1
90 | true
91 |
92 |
93 |
94 |
101 |
--------------------------------------------------------------------------------
/src/ExplorerShortcutHelper/Program.cs:
--------------------------------------------------------------------------------
1 | // Permission is hereby granted, free of charge, to any person obtaining
2 | // a copy of this software and associated documentation files (the
3 | // "Software"), to deal in the Software without restriction, including
4 | // without limitation the rights to use, copy, modify, merge, publish,
5 | // distribute, sublicense, and/or sell copies of the Software, and to
6 | // permit persons to whom the Software is furnished to do so, subject to
7 | // the following conditions:
8 | //
9 | // The above copyright notice and this permission notice shall be
10 | // included in all copies or substantial portions of the Software.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 | //
20 | // Copyright (c) 2004-2013 Scott Willeke (http://scott.willeke.com)
21 | //
22 | // Authors:
23 | // Scott Willeke (scott@willeke.com)
24 | //
25 | using System;
26 | using System.Globalization;
27 |
28 | namespace Willeke.Scott.ExplorerShortcutHelper
29 | {
30 | class Program
31 | {
32 | static int Main(string[] args)
33 | {
34 | bool? isAddingKey = null;
35 | if (args != null && args.Length > 0)
36 | {
37 | var addOrRemoveString = args[0]; // add or remove
38 | if (!string.IsNullOrEmpty(addOrRemoveString))
39 | {
40 | if ("add".Equals(addOrRemoveString, StringComparison.InvariantCulture))
41 | isAddingKey = true;
42 | else if ("remove".Equals(addOrRemoveString, StringComparison.InvariantCulture))
43 | isAddingKey = false;
44 | }
45 | }
46 |
47 | if (!isAddingKey.HasValue)
48 | return CommandLineError("Invalid argument. Expected 'add' or 'remove'.");
49 |
50 |
51 | if (args.Length < 2)
52 | return CommandLineError("Invalid argument. Expected a unique command name.");
53 |
54 | string commandName = args[1];
55 |
56 | if (args.Length < 3)
57 | return CommandLineError("Invalid argument. Expected a file class.");
58 |
59 | var fileClass = args[2]; //e.g. "Msi.Package";
60 |
61 | if (isAddingKey.Value)
62 | { // if we're adding a key we're expecting more arguments:
63 |
64 | if (args.Length < 3)
65 | return CommandLineError("Invalid argument. Expected caption.");
66 |
67 | var contextMenuCaption = args[3]; // e.g. "&Extract Files";
68 | var shellCommandToExecute = args[4]; //e.g. '\"' + GetExePath() + "\" /x \"%1\" \"%1_extracted\"";
69 |
70 | var message = string.Format(CultureInfo.InvariantCulture,
71 | "Adding a shortcut for '{0}' files that will execute the following command: '{1}'...",
72 | fileClass, shellCommandToExecute);
73 |
74 | Console.WriteLine(message);
75 | try
76 | {
77 | RegistryTool.RegisterFileVerb(commandName, fileClass, contextMenuCaption, shellCommandToExecute);
78 | }
79 | catch (Exception oops)
80 | {
81 | Console.WriteLine("Error adding shortcut menu: " + oops.ToString());
82 | return -1;
83 | }
84 | Console.WriteLine("Shortcut added.");
85 | }
86 | else
87 | {
88 | Console.WriteLine("Removing shortcut...");
89 | try
90 | {
91 | RegistryTool.UnRegisterFileVerb(commandName, fileClass);
92 | }
93 | catch (Exception oops)
94 | {
95 | Console.WriteLine("Error removing shortcut menu: " + oops.ToString());
96 | return -1;
97 | }
98 | Console.WriteLine("Shortcut removed.");
99 | }
100 | return 0;
101 | }
102 |
103 | private static int CommandLineError(string errorMessage)
104 | {
105 | Console.WriteLine("Adds or removes a shortcut menu item for a specific type of file in Windows Explorer.");
106 | Console.WriteLine();
107 | Console.WriteLine("Usage: AddWindowsExplorerShortcut add|remove commandName fileClass [caption shellCommand]");
108 | Console.WriteLine();
109 | Console.WriteLine(errorMessage);
110 | return 100;
111 | }
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/src/ExplorerShortcutHelper/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // Permission is hereby granted, free of charge, to any person obtaining
2 | // a copy of this software and associated documentation files (the
3 | // "Software"), to deal in the Software without restriction, including
4 | // without limitation the rights to use, copy, modify, merge, publish,
5 | // distribute, sublicense, and/or sell copies of the Software, and to
6 | // permit persons to whom the Software is furnished to do so, subject to
7 | // the following conditions:
8 | //
9 | // The above copyright notice and this permission notice shall be
10 | // included in all copies or substantial portions of the Software.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 | //
20 | // Copyright (c) 2004-2013 Scott Willeke (http://scott.willeke.com)
21 | //
22 | // Authors:
23 | // Scott Willeke (scott@willeke.com)
24 | //
25 |
26 | using System.Reflection;
27 |
28 | // General Information about an assembly is controlled through the following
29 | // set of attributes. Change these attribute values to modify the information
30 | // associated with an assembly.
31 | [assembly: AssemblyTitle("Explorer Shortcut Helper for Lessmsi")]
32 | [assembly: AssemblyProduct("Explorer Shortcut Helper for Lessmsi")]
33 |
34 |
--------------------------------------------------------------------------------
/src/ExplorerShortcutHelper/RegistryTools.cs:
--------------------------------------------------------------------------------
1 | // Permission is hereby granted, free of charge, to any person obtaining
2 | // a copy of this software and associated documentation files (the
3 | // "Software"), to deal in the Software without restriction, including
4 | // without limitation the rights to use, copy, modify, merge, publish,
5 | // distribute, sublicense, and/or sell copies of the Software, and to
6 | // permit persons to whom the Software is furnished to do so, subject to
7 | // the following conditions:
8 | //
9 | // The above copyright notice and this permission notice shall be
10 | // included in all copies or substantial portions of the Software.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 | //
20 | // Copyright (c) 2004 Scott Willeke (http://scott.willeke.com)
21 | //
22 | // Authors:
23 | // Scott Willeke (scott@willeke.com)
24 | //
25 | using System;
26 | using System.Security;
27 | using Microsoft.Win32;
28 |
29 | namespace Willeke.Scott.ExplorerShortcutHelper
30 | {
31 | internal class RegistryTool
32 | {
33 | ///
34 | /// Registers a verb for the specified windows explorer/shell file class.
35 | ///
36 | /// A unique name for your verb/command. It should be just different from any other applications command.
37 | /// The file class for the file to register the verb for (see http://msdn.microsoft.com/en-us/library/bb776870(VS.85).aspx ).
38 | /// The caption for the verb.
39 | /// The command to execute (as it would be executed in ShellExecute).
40 | public static void RegisterFileVerb(string commandName, string fileClassName, string contextMenuCaption, string shellCommandToExecute)
41 | {
42 | string regRoot = fileClassName + @"\shell\" + commandName;
43 | var extractKey = OpenRegistryKeyWithAddedErrorInfo(regRoot);
44 | extractKey.SetValue("", contextMenuCaption);
45 |
46 | var commandKeyValue = OpenRegistryKeyWithAddedErrorInfo(regRoot + "\\command");
47 | var existingValue = commandKeyValue.GetValue("") as string;
48 | if (existingValue != null && existingValue.StartsWith(shellCommandToExecute))
49 | return;
50 | commandKeyValue.SetValue("", shellCommandToExecute);
51 | }
52 |
53 | ///
54 | /// Deletes/unregisteres a verb previously registered with .
55 | ///
56 | /// See .
57 | /// See .
58 | public static void UnRegisterFileVerb(string commandName, string fileClassName)
59 | {
60 | var extractKey = OpenRegistryKeyWithAddedErrorInfo(fileClassName + @"\shell\");
61 | extractKey.DeleteSubKeyTree(commandName);
62 | }
63 |
64 | ///
65 | /// Opens a key with write permission and adds error information if it fails.
66 | ///
67 | private static RegistryKey OpenRegistryKeyWithAddedErrorInfo(string registryKeyName)
68 | {
69 | RegistryKey extractKey;
70 | try
71 | {
72 | extractKey = Registry.ClassesRoot.CreateSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree);
73 | }
74 | catch (SecurityException eSecurity)
75 | {
76 | throw new SecurityException("You do not have the permissions required to create or write the registry key '" + registryKeyName + "'.", eSecurity);
77 | }
78 | catch (Exception eCatchAll)
79 | {
80 | throw new Exception("Unable to open registry key '" + registryKeyName + "'.", eCatchAll);
81 | }
82 | return extractKey;
83 | }
84 |
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/ExplorerShortcutHelper/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/ExplorerShortcutHelper/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | From: http://www.opensource.org/licenses/mit-license.php
3 |
4 | LICENSE
5 |
6 | // Permission is hereby granted, free of charge, to any person obtaining
7 | // a copy of this software and associated documentation files (the
8 | // "Software"), to deal in the Software without restriction, including
9 | // without limitation the rights to use, copy, modify, merge, publish,
10 | // distribute, sublicense, and/or sell copies of the Software, and to
11 | // permit persons to whom the Software is furnished to do so, subject to
12 | // the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be
15 | // included in all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 | //
25 | // Copyright (c) 2004 Scott Willeke (http://scott.willeke.com)
26 | //
27 | // Authors:
28 | // Scott Willeke (scott@willeke.com)
29 | //
--------------------------------------------------------------------------------
/src/LessMsi.Cli/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/LessMsi.Cli/ExtractCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 | using LessMsi.Msi;
5 | using NDesk.Options;
6 |
7 | namespace LessMsi.Cli
8 | {
9 | internal class ExtractCommand : LessMsiCommand
10 | {
11 | public override void Run(List allArgs)
12 | {
13 | var args = allArgs.Skip(1).ToList();
14 | // "x msi_name [path_to_extract\] [file_names]+
15 | if (args.Count < 1)
16 | throw new OptionException("Invalid argument. Extract command must at least specify the name of an msi file.", "x");
17 |
18 | var i = 0;
19 | var msiFile = args[i++];
20 | if (!File.Exists(msiFile))
21 | throw new OptionException("Invalid argument. Specified msi file does not exist.", "x");
22 | var filesToExtract = new List();
23 | var extractDir = "";
24 | if (i < args.Count)
25 | {
26 | if (extractDir == "" && (args[i].EndsWith("\\") || args[i].EndsWith("\"")))
27 | extractDir = args[i];
28 | else
29 | filesToExtract.Add(args[i]);
30 | }
31 | while (++i < args.Count)
32 | filesToExtract.Add(args[i]);
33 |
34 | Program.DoExtraction(msiFile, extractDir.TrimEnd('\"'), filesToExtract, getExtractionMode(allArgs[0]));
35 | }
36 |
37 | private ExtractionMode getExtractionMode(string commandArgument)
38 | {
39 | commandArgument = commandArgument.ToLowerInvariant();
40 |
41 | if (commandArgument == "xfo")
42 | {
43 | return ExtractionMode.OverwriteFlatExtraction;
44 | }
45 |
46 | if (commandArgument == "xfr")
47 | {
48 | return ExtractionMode.RenameFlatExtraction;
49 | }
50 |
51 | if (commandArgument == "xo")
52 | {
53 | return ExtractionMode.OverwriteExtraction;
54 | }
55 |
56 | return ExtractionMode.PreserveDirectoriesExtraction;
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/src/LessMsi.Cli/LessMsi.Cli.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | x86
7 | {7ED32F80-2B7B-4F90-ADE9-22B8F3935C63}
8 | Exe
9 | Properties
10 | LessMsi.Cli
11 | lessmsi
12 | v4.8
13 | 512
14 |
15 |
16 | ..\
17 | true
18 |
19 |
20 | true
21 | bin\Debug\
22 | DEBUG;TRACE
23 | full
24 | x86
25 | prompt
26 | MinimumRecommendedRules.ruleset
27 | false
28 |
29 |
30 | bin\Release\
31 | TRACE
32 | true
33 | pdbonly
34 | x86
35 | prompt
36 | MinimumRecommendedRules.ruleset
37 | false
38 |
39 |
40 | LessMsi.Cli.Program
41 |
42 |
43 |
44 | ..\packages\LessIO.1.0.34\lib\net40\LessIO.dll
45 | True
46 |
47 |
48 |
49 |
50 | ..\..\lib\wix.dll
51 |
52 |
53 |
54 |
55 | Properties\CommonAssemblyInfo.cs
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | {1fd916f2-1df6-42f6-9ad4-83565fbf8e5c}
74 | LessMsi.Core
75 |
76 |
77 |
78 |
79 |
80 | xcopy /y "$(SolutionDir)packages\libmspack4n.0.9.10\build\x86\mspack.dll" "$(TargetDir)"
81 |
82 |
89 |
--------------------------------------------------------------------------------
/src/LessMsi.Cli/LessMsiCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace LessMsi.Cli
4 | {
5 | internal abstract class LessMsiCommand
6 | {
7 | public abstract void Run(List args);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/LessMsi.Cli/ListTableCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Globalization;
5 | using System.Linq;
6 | using System.Text;
7 | using LessMsi.Msi;
8 | using Microsoft.Tools.WindowsInstallerXml.Msi;
9 | using NDesk.Options;
10 |
11 | namespace LessMsi.Cli
12 | {
13 | internal class ListTableCommand : LessMsiCommand
14 | {
15 | public override void Run(List args)
16 | {
17 | /* examples:
18 | * lessmsi l -t Component c:\theinstall.msi
19 | * lessmsi l -t Property c:\theinstall.msi
20 | */
21 | args = args.Skip(1).ToList();
22 | var tableName = "";
23 | var options = new OptionSet {
24 | { "t=", "Specifies the table to list.", t => tableName = t }
25 | };
26 | var extra = options.Parse(args);
27 | if (extra.Count < 1)
28 | throw new OptionException("You must specify the msi file to list from.", "l");
29 | if (string.IsNullOrEmpty(tableName))
30 | throw new OptionException("You must specify the table name to list.", "t");
31 |
32 | var csv = new StringBuilder();
33 | Debug.Print("Opening msi file '{0}'.", extra[0]);
34 | using (var msidb = MsiDatabase.Create(new LessIO.Path(extra[0])))
35 | {
36 | Debug.Print("Opening table '{0}'.", tableName);
37 | var query = string.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName);
38 | using (var view = new ViewWrapper(msidb.OpenExecuteView(query)))
39 | {
40 | for (var index = 0; index < view.Columns.Length; index++)
41 | {
42 | var col = view.Columns[index];
43 | if (index > 0)
44 | csv.Append(',');
45 | csv.Append(col.Name);
46 | }
47 | csv.AppendLine();
48 | foreach (var row in view.Records)
49 | {
50 | for (var colIndex = 0; colIndex < row.Length; colIndex++)
51 | {
52 | if (colIndex > 0)
53 | csv.Append(',');
54 | var val = Convert.ToString(row[colIndex], CultureInfo.InvariantCulture);
55 | var newLine = Environment.NewLine;
56 | string[] requireEscapeChars = { ",", newLine };
57 | Array.ForEach(requireEscapeChars, s => {
58 | if (val.Contains(s))
59 | val = "\"" + val + "\"";
60 | });
61 | csv.Append(val);
62 | }
63 | csv.AppendLine();
64 | }
65 | }
66 | }
67 | Console.Write(csv.ToString());
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/src/LessMsi.Cli/OpenGuiCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using NDesk.Options;
6 |
7 | namespace LessMsi.Cli
8 | {
9 | internal class OpenGuiCommand : LessMsiCommand
10 | {
11 | public override void Run(List args)
12 | {
13 | if (args.Count < 2)
14 | throw new OptionException("You must specify the name of the msi file to open when using the o command.", "o");
15 |
16 | ShowGui(args);
17 | }
18 |
19 | public static void ShowGui(List args)
20 | {
21 | var guiExe = Path.Combine(AppPath, "lessmsi-gui.exe");
22 | if (File.Exists(guiExe))
23 | {
24 | var p = new Process();
25 | p.StartInfo.FileName = guiExe;
26 |
27 | //should we wait for exit?
28 | if (args.Count > 0)
29 | {
30 | // We add double quotes to support paths with spaces, for ex: "E:\Downloads and Sofware\potato.msi".
31 | p.StartInfo.Arguments = string.Format("\"{0}\"", args[1]);
32 | p.Start();
33 | }
34 | else
35 | p.Start();
36 | }
37 | }
38 |
39 | private static string AppPath
40 | {
41 | get
42 | {
43 | var codeBase = new Uri(typeof(OpenGuiCommand).Assembly.CodeBase);
44 | var local = Path.GetDirectoryName(codeBase.LocalPath);
45 | return local;
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/LessMsi.Cli/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | // Permission is hereby granted, free of charge, to any person obtaining
2 | // a copy of this software and associated documentation files (the
3 | // "Software"), to deal in the Software without restriction, including
4 | // without limitation the rights to use, copy, modify, merge, publish,
5 | // distribute, sublicense, and/or sell copies of the Software, and to
6 | // permit persons to whom the Software is furnished to do so, subject to
7 | // the following conditions:
8 | //
9 | // The above copyright notice and this permission notice shall be
10 | // included in all copies or substantial portions of the Software.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 | //
20 | // Copyright (c) 2004-2013 Scott Willeke (http://scott.willeke.com)
21 | //
22 | // Authors:
23 | // Scott Willeke (scott@willeke.com)
24 | //
25 |
26 | using System.Reflection;
27 |
28 | [assembly: AssemblyTitle("Less MSIérables (lessmsi) CLI interface")]
29 | [assembly: AssemblyProduct("Less MSIérables (lessmsi)")]
30 |
--------------------------------------------------------------------------------
/src/LessMsi.Cli/ShowHelpCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace LessMsi.Cli
5 | {
6 | internal class ShowHelpCommand : LessMsiCommand
7 | {
8 | public override void Run(List args)
9 | {
10 | ShowHelp("");
11 | }
12 |
13 | public static void ShowHelp(string errorMessage)
14 | {
15 | string helpString =
16 | @"Usage:
17 | lessmsi [options] [] [file_names]
18 |
19 | Commands:
20 | x Extracts all or specified files from the specified msi_name.
21 | xfo Extracts all or specified files from the specified msi_name to the same folder while overwriting files with the same name.
22 | xfr Extracts all or specified files from the specified msi_name to the same folder while renaming files with the same name with a count suffix.
23 | l Lists the contents of the specified msi table as CSV to stdout. Table is
24 | specified with -t switch. Example: lessmsi l -t Component c:\foo.msi
25 | v Lists the value of the ProductVersion Property in the msi
26 | (typically this is the version of the MSI).
27 | o Opens the specified msi_name in the GUI.
28 | h Shows this help page.
29 |
30 | For more information see http://lessmsi.activescott.com
31 | ";
32 | if (!string.IsNullOrEmpty(errorMessage))
33 | {
34 | helpString = "\r\nError: " + errorMessage + "\r\n\r\n" + helpString;
35 | }
36 | Console.WriteLine(helpString);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/LessMsi.Cli/ShowVersionCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using LessMsi.Msi;
5 | using Microsoft.Tools.WindowsInstallerXml.Msi;
6 | using NDesk.Options;
7 |
8 | namespace LessMsi.Cli
9 | {
10 | internal class ShowVersionCommand : LessMsiCommand
11 | {
12 | public override void Run(List args)
13 | {
14 | // args[0]=v, args[1]=filename.msi
15 | if (args.Count < 2)
16 | throw new OptionException("You must specify an msi filename.", "v");
17 | var msiFileName = args[1];
18 | using (var msidb = new Database(msiFileName, OpenDatabase.ReadOnly))
19 | {
20 | const string tableName = "Property";
21 | var query = string.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName);
22 | using (var view = new ViewWrapper(msidb.OpenExecuteView(query)))
23 | {
24 | foreach (var row in view.Records)
25 | {
26 | var property = (string)row[view.ColumnIndex("Property")];
27 | var value = row[view.ColumnIndex("Value")];
28 | if (string.Equals("ProductVersion", property, StringComparison.InvariantCultureIgnoreCase))
29 | {
30 | Console.WriteLine(value);
31 | return;
32 | }
33 | }
34 | Console.WriteLine("Version not found!");
35 | }
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/LessMsi.Cli/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/LessMsi.Core/LessMsi.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | x86
7 | {1FD916F2-1DF6-42F6-9AD4-83565FBF8E5C}
8 | Library
9 | Properties
10 | LessMsi
11 | lessmsi.core
12 | v4.8
13 | 512
14 |
15 |
16 | ..\
17 | true
18 |
19 |
20 |
21 |
22 | true
23 | bin\Debug\
24 | DEBUG;TRACE
25 | full
26 | x86
27 | prompt
28 | MinimumRecommendedRules.ruleset
29 | false
30 | false
31 |
32 |
33 | bin\Release\
34 | TRACE
35 | true
36 | pdbonly
37 | x86
38 | prompt
39 | MinimumRecommendedRules.ruleset
40 | false
41 | false
42 |
43 |
44 |
45 | ..\packages\LessIO.1.0.34\lib\net40\LessIO.dll
46 | True
47 |
48 |
49 | ..\packages\libmspack4n.0.9.10\lib\net40\libmspackn.dll
50 | True
51 |
52 |
53 |
54 |
55 | ..\..\lib\wix.dll
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | Properties\CommonAssemblyInfo.cs
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/src/LessMsi.Core/Msi/ColumnInfo.cs:
--------------------------------------------------------------------------------
1 | // Permission is hereby granted, free of charge, to any person obtaining
2 | // a copy of this software and associated documentation files (the
3 | // "Software"), to deal in the Software without restriction, including
4 | // without limitation the rights to use, copy, modify, merge, publish,
5 | // distribute, sublicense, and/or sell copies of the Software, and to
6 | // permit persons to whom the Software is furnished to do so, subject to
7 | // the following conditions:
8 | //
9 | // The above copyright notice and this permission notice shall be
10 | // included in all copies or substantial portions of the Software.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 | //
20 | // Copyright (c) 2004 Scott Willeke (http://scott.willeke.com)
21 | //
22 | // Authors:
23 | // Scott Willeke (scott@willeke.com)
24 | //
25 | using System;
26 |
27 | namespace LessMsi.Msi
28 | {
29 | ///
30 | /// FYI: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/column_definition_format.asp
31 | ///
32 | public class ColumnInfo
33 | {
34 | public ColumnInfo(string name, string typeID)
35 | {
36 | this.Name = name;
37 | this.TypeID = typeID;
38 | }
39 |
40 | public string Name;
41 |
42 | ///
43 | /// s? String, variable length (?=1-255)
44 | /// s0 String, variable length
45 | /// i2 Short integer
46 | /// i4 Long integer
47 | /// v0 Binary Stream
48 | /// g? Temporary string (?=0-255)
49 | /// j? Temporary integer (?=0,1,2,4)
50 | /// O0 Temporary object
51 | /// An uppercase letter indicates that null values are allowed in the column.
52 | ///
53 | public string TypeID;
54 |
55 |
56 | public bool IsString
57 | {
58 | get
59 | {
60 | return
61 | TypeID[0] == 's' || TypeID[0] == 'S'
62 | || TypeID[0] == 'g' || TypeID[0] == 'G'
63 | || TypeID[0] == 'l' || TypeID[0] == 'L';
64 | }
65 | }
66 |
67 | public bool IsInteger
68 | {
69 | get
70 | {
71 | return
72 | TypeID[0] == 'i' || TypeID[0] == 'I'
73 | || TypeID[0] == 'j' || TypeID[0] == 'J'
74 | ;
75 | }
76 | }
77 |
78 | public bool IsStream
79 | {
80 | get
81 | {
82 | return
83 | TypeID[0] == 'v' || TypeID[0] == 'V';
84 | }
85 | }
86 |
87 | public bool IsObject
88 | {
89 | get { return string.Equals("O0", TypeID, StringComparison.InvariantCultureIgnoreCase); }
90 | }
91 |
92 | public int Size
93 | {
94 | get
95 | {
96 | return int.Parse(TypeID.Substring(1));
97 | }
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/src/LessMsi.Core/Msi/ExternalCabNotFoundException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace LessMsi.Msi
7 | {
8 | ///
9 | /// Thrown when the msi file indicates there should be an external .cab file (i.e. not embedded inside the MSI, but saved along side it) but that cab file does not exist.
10 | ///
11 | public class ExternalCabNotFoundException : Exception
12 | {
13 | public static ExternalCabNotFoundException CreateFromCabPath(string cabFileName, string expectedLocation)
14 | {
15 | var msg = string.Format("This msi file references a CAB file that is not embedded inside of the msi file itself. The CAB file is named {0} and was expected to be in the following folder: {1}", cabFileName, expectedLocation);
16 | return new ExternalCabNotFoundException(msg);
17 | }
18 |
19 | public ExternalCabNotFoundException()
20 | {
21 | }
22 |
23 | public ExternalCabNotFoundException(string message)
24 | : base(message)
25 | {
26 | }
27 |
28 | public ExternalCabNotFoundException(string message, Exception inner)
29 | : base(message, inner)
30 | {
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/LessMsi.Core/Msi/ExtractionMode.cs:
--------------------------------------------------------------------------------
1 | namespace LessMsi.Msi
2 | {
3 | public enum ExtractionMode
4 | {
5 | ///
6 | /// Default value indicating that a regular extraction should be performed.
7 | ///
8 | Default,
9 | ///
10 | /// Value indicating that a file extraction preserving directories should be performed.
11 | ///
12 | PreserveDirectoriesExtraction,
13 | ///
14 | /// Value indicating that a file extraction renaming identical files should be performed.
15 | ///
16 | RenameFlatExtraction,
17 | ///
18 | /// Value indicating that a file extraction overwriting identical files should be performed.
19 | ///
20 | OverwriteFlatExtraction,
21 | ///
22 | /// Value indicating that a file extraction overwriting identical files should be performed.
23 | /// While preserving the directories structures
24 | ///
25 | OverwriteExtraction
26 | }
27 | }
--------------------------------------------------------------------------------
/src/LessMsi.Core/Msi/MsiDatabase.cs:
--------------------------------------------------------------------------------
1 | // Permission is hereby granted, free of charge, to any person obtaining
2 | // a copy of this software and associated documentation files (the
3 | // "Software"), to deal in the Software without restriction, including
4 | // without limitation the rights to use, copy, modify, merge, publish,
5 | // distribute, sublicense, and/or sell copies of the Software, and to
6 | // permit persons to whom the Software is furnished to do so, subject to
7 | // the following conditions:
8 | //
9 | // The above copyright notice and this permission notice shall be
10 | // included in all copies or substantial portions of the Software.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 | //
20 | // Copyright (c) 2021 Scott Willeke (http://scott.willeke.com)
21 | //
22 | // Authors:
23 | // Scott Willeke (scott@willeke.com)
24 | //
25 | using Microsoft.Tools.WindowsInstallerXml.Msi;
26 |
27 | namespace LessMsi.Msi
28 | {
29 | ///
30 | /// Helper class for opening an MSI Database or MSI Patch file
31 | ///
32 | public static class MsiDatabase
33 | {
34 | ///
35 | /// Documented flag, unlisted in Microsoft.Tools.WindowsInstallerXml.Msi.OpenDatabase
36 | ///
37 | const uint MSIDBOPEN_PATCHFILE = 32;
38 |
39 | ///
40 | /// Create a Database object from either an .msi or .mso file
41 | ///
42 | /// The path to the database or patch file
43 | ///
44 | public static Database Create(LessIO.Path msiDatabaseFilePath)
45 | {
46 | try
47 | {
48 | return new Database(msiDatabaseFilePath.PathString, OpenDatabase.ReadOnly);
49 | }
50 | catch (System.IO.IOException)
51 | {
52 | // retry as patchfile (.msp)
53 | return new Database(msiDatabaseFilePath.PathString, OpenDatabase.ReadOnly | (OpenDatabase)MSIDBOPEN_PATCHFILE);
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/LessMsi.Core/Msi/TableWrapper.cs:
--------------------------------------------------------------------------------
1 | // Permission is hereby granted, free of charge, to any person obtaining
2 | // a copy of this software and associated documentation files (the
3 | // "Software"), to deal in the Software without restriction, including
4 | // without limitation the rights to use, copy, modify, merge, publish,
5 | // distribute, sublicense, and/or sell copies of the Software, and to
6 | // permit persons to whom the Software is furnished to do so, subject to
7 | // the following conditions:
8 | //
9 | // The above copyright notice and this permission notice shall be
10 | // included in all copies or substantial portions of the Software.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 | //
20 | // Copyright (c) 2004 Scott Willeke (http://scott.willeke.com)
21 | //
22 | // Authors:
23 | // Scott Willeke (scott@willeke.com)
24 | //
25 | using System;
26 | using System.Collections;
27 | using System.Collections.Specialized;
28 | using System.Diagnostics;
29 | using Microsoft.Tools.WindowsInstallerXml.Msi;
30 |
31 | namespace LessMsi.Msi
32 | {
33 | ///
34 | /// Represents a generic row in a table.
35 | ///
36 | public class TableRow
37 | {
38 | private readonly IDictionary _columns;
39 |
40 | private TableRow(IDictionary columns)
41 | {
42 | if (columns == null)
43 | throw new ArgumentNullException("columns");
44 | _columns = columns;
45 | }
46 |
47 | public static TableRow[] GetRowsFromTable(Database msidb, string tableName)
48 | {
49 | if (!msidb.TableExists(tableName))
50 | {
51 | Trace.WriteLine(string.Format("Table name '{0}' does not exist.", tableName));
52 | return new TableRow[0];
53 | }
54 |
55 | string query = string.Concat("SELECT * FROM `", tableName, "`");
56 | using (ViewWrapper view = new ViewWrapper(msidb.OpenExecuteView(query)))
57 | {
58 | ArrayList /**/ rows = new ArrayList(view.Records.Count);
59 |
60 | ColumnInfo[] columns = view.Columns;
61 | foreach (object[] values in view.Records)
62 | {
63 | HybridDictionary valueCollection = new HybridDictionary(values.Length);
64 | for (int cIndex = 0; cIndex < columns.Length; cIndex++)
65 | {
66 | valueCollection[columns[cIndex].Name] = values[cIndex];
67 | }
68 | rows.Add(new TableRow(valueCollection));
69 | }
70 | return (TableRow[]) rows.ToArray(typeof(TableRow));
71 | }
72 | }
73 |
74 | public string GetString(string columnName)
75 | {
76 | return Convert.ToString(GetValue(columnName));
77 | }
78 | public object GetValue(string columnName)
79 | {
80 | return _columns[columnName];
81 | }
82 |
83 | public Int32 GetInt32(string columnName)
84 | {
85 | return Convert.ToInt32(GetValue(columnName));
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/src/LessMsi.Core/Msi/ViewWrapper.cs:
--------------------------------------------------------------------------------
1 | // Permission is hereby granted, free of charge, to any person obtaining
2 | // a copy of this software and associated documentation files (the
3 | // "Software"), to deal in the Software without restriction, including
4 | // without limitation the rights to use, copy, modify, merge, publish,
5 | // distribute, sublicense, and/or sell copies of the Software, and to
6 | // permit persons to whom the Software is furnished to do so, subject to
7 | // the following conditions:
8 | //
9 | // The above copyright notice and this permission notice shall be
10 | // included in all copies or substantial portions of the Software.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 | //
20 | // Copyright (c) 2004 Scott Willeke (http://scott.willeke.com)
21 | //
22 | // Authors:
23 | // Scott Willeke (scott@willeke.com)
24 | //
25 | using System;
26 | using System.Collections.Generic;
27 | using System.Diagnostics;
28 | using Microsoft.Tools.WindowsInstallerXml.Msi;
29 |
30 | namespace LessMsi.Msi
31 | {
32 | public class ViewWrapper : IDisposable
33 | {
34 | public ViewWrapper(View underlyingView)
35 | {
36 | _underlyingView = underlyingView;
37 | CreateColumnInfos();
38 | }
39 |
40 | private View _underlyingView;
41 | private ColumnInfo[] _columns;
42 |
43 | public ColumnInfo[] Columns
44 | {
45 | get { return _columns; }
46 | }
47 |
48 | ///
49 | /// Returns the index of the specified column.
50 | ///
51 | /// The name of the column to return an index for.
52 | public int ColumnIndex(string columnName)
53 | {
54 | for(var i=0; i < Columns.Length; i++)
55 | {
56 | if (string.Equals(columnName, Columns[i].Name, StringComparison.InvariantCultureIgnoreCase))
57 | return i;
58 | }
59 | Debug.Fail("Column {0} not found.", columnName);
60 | return -1;
61 | }
62 |
63 | private void CreateColumnInfos()
64 | {
65 | const int MSICOLINFONAMES = 0;
66 | const int MSICOLINFOTYPES = 1;
67 |
68 | var colList = new List();
69 |
70 | Record namesRecord;
71 | Record typesRecord;
72 |
73 | _underlyingView.GetColumnInfo(MSICOLINFONAMES, out namesRecord);
74 | _underlyingView.GetColumnInfo(MSICOLINFOTYPES, out typesRecord);
75 | using (namesRecord)
76 | using (typesRecord)
77 | {
78 | var fieldCount = namesRecord.GetFieldCount();
79 | Debug.Assert(typesRecord.GetFieldCount() == fieldCount);
80 |
81 | for (var colIndex = 1; colIndex <= fieldCount; colIndex++)
82 | {
83 | colList.Add(new ColumnInfo(namesRecord.GetString(colIndex), typesRecord.GetString(colIndex)));
84 | }
85 | }
86 | _columns = colList.ToArray();
87 | }
88 |
89 |
90 | private List