├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── SharpMonoInjector
├── Assembler.cs
├── ExportedFunction.cs
├── Injector.cs
├── InjectorException.cs
├── Memory.cs
├── MonoImageOpenStatus.cs
├── Native.cs
├── ProcessUtils.cs
├── Properties
│ ├── Settings.Designer.cs
│ └── Settings.settings
└── SharpMonoInjector.csproj
├── UnityInspector.Communicator
├── Communicator.cs
└── UnityInspector.Communicator.csproj
├── UnityInspector.GUI
├── App.config
├── App.xaml
├── App.xaml.cs
├── Converters
│ ├── BooleanConverter.cs
│ ├── BooleanToVisibilityConverter.cs
│ ├── ICollectionToBooleanConverter.cs
│ └── ObjectMemberTemplateSelector.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Models
│ ├── ArrayMember.cs
│ ├── CommunicatorClient.cs
│ ├── Component.cs
│ ├── EnumMember.cs
│ ├── GameObject.cs
│ ├── MonoProcess.cs
│ ├── ObjectMember.cs
│ ├── Rect.cs
│ ├── Vector2.cs
│ └── Vector3.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── UnityInspector.GUI.csproj
├── UnityStyle.xaml
├── Utils.cs
├── ViewModels
│ ├── ArrayMemberViewModel.cs
│ ├── ComponentViewModel.cs
│ ├── GameObjectViewModel.cs
│ ├── MainWindowViewModel.cs
│ ├── NumericTextBox.cs
│ ├── ObjectMemberViewModel.cs
│ ├── RelayCommand.cs
│ └── ViewModel.cs
├── icon.ico
└── packages.config
├── UnityInspector.Injector
├── CommunicatorServer.cs
├── Injector.cs
└── UnityInspector.Injector.csproj
└── UnityInspector.sln
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 | ExternalDlls/
13 |
14 | # Build results
15 | [Dd]ebug/
16 | [Dd]ebugPublic/
17 | [Rr]elease/
18 | [Rr]eleases/
19 | x64/
20 | x86/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 | [Ll]og/
25 |
26 | # Visual Studio 2015 cache/options directory
27 | .vs/
28 | # Uncomment if you have tasks that create the project's static files in wwwroot
29 | #wwwroot/
30 |
31 | # MSTest test Results
32 | [Tt]est[Rr]esult*/
33 | [Bb]uild[Ll]og.*
34 |
35 | # NUNIT
36 | *.VisualState.xml
37 | TestResult.xml
38 |
39 | # Build Results of an ATL Project
40 | [Dd]ebugPS/
41 | [Rr]eleasePS/
42 | dlldata.c
43 |
44 | # DNX
45 | project.lock.json
46 | project.fragment.lock.json
47 | artifacts/
48 |
49 | *_i.c
50 | *_p.c
51 | *_i.h
52 | *.ilk
53 | *.meta
54 | *.obj
55 | *.pch
56 | *.pdb
57 | *.pgc
58 | *.pgd
59 | *.rsp
60 | *.sbr
61 | *.tlb
62 | *.tli
63 | *.tlh
64 | *.tmp
65 | *.tmp_proj
66 | *.log
67 | *.vspscc
68 | *.vssscc
69 | .builds
70 | *.pidb
71 | *.svclog
72 | *.scc
73 |
74 | # Chutzpah Test files
75 | _Chutzpah*
76 |
77 | # Visual C++ cache files
78 | ipch/
79 | *.aps
80 | *.ncb
81 | *.opendb
82 | *.opensdf
83 | *.sdf
84 | *.cachefile
85 | *.VC.db
86 | *.VC.VC.opendb
87 |
88 | # Visual Studio profiler
89 | *.psess
90 | *.vsp
91 | *.vspx
92 | *.sap
93 |
94 | # TFS 2012 Local Workspace
95 | $tf/
96 |
97 | # Guidance Automation Toolkit
98 | *.gpState
99 |
100 | # ReSharper is a .NET coding add-in
101 | _ReSharper*/
102 | *.[Rr]e[Ss]harper
103 | *.DotSettings.user
104 |
105 | # JustCode is a .NET coding add-in
106 | .JustCode
107 |
108 | # TeamCity is a build add-in
109 | _TeamCity*
110 |
111 | # DotCover is a Code Coverage Tool
112 | *.dotCover
113 |
114 | # NCrunch
115 | _NCrunch_*
116 | .*crunch*.local.xml
117 | nCrunchTemp_*
118 |
119 | # MightyMoose
120 | *.mm.*
121 | AutoTest.Net/
122 |
123 | # Web workbench (sass)
124 | .sass-cache/
125 |
126 | # Installshield output folder
127 | [Ee]xpress/
128 |
129 | # DocProject is a documentation generator add-in
130 | DocProject/buildhelp/
131 | DocProject/Help/*.HxT
132 | DocProject/Help/*.HxC
133 | DocProject/Help/*.hhc
134 | DocProject/Help/*.hhk
135 | DocProject/Help/*.hhp
136 | DocProject/Help/Html2
137 | DocProject/Help/html
138 |
139 | # Click-Once directory
140 | publish/
141 |
142 | # Publish Web Output
143 | *.[Pp]ublish.xml
144 | *.azurePubxml
145 | # TODO: Comment the next line if you want to checkin your web deploy settings
146 | # but database connection strings (with potential passwords) will be unencrypted
147 | #*.pubxml
148 | *.publishproj
149 |
150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
151 | # checkin your Azure Web App publish settings, but sensitive information contained
152 | # in these scripts will be unencrypted
153 | PublishScripts/
154 |
155 | # NuGet Packages
156 | *.nupkg
157 | # The packages folder can be ignored because of Package Restore
158 | **/packages/*
159 | # except build/, which is used as an MSBuild target.
160 | !**/packages/build/
161 | # Uncomment if necessary however generally it will be regenerated when needed
162 | #!**/packages/repositories.config
163 | # NuGet v3's project.json files produces more ignoreable files
164 | *.nuget.props
165 | *.nuget.targets
166 |
167 | # Microsoft Azure Build Output
168 | csx/
169 | *.build.csdef
170 |
171 | # Microsoft Azure Emulator
172 | ecf/
173 | rcf/
174 |
175 | # Windows Store app package directories and files
176 | AppPackages/
177 | BundleArtifacts/
178 | Package.StoreAssociation.xml
179 | _pkginfo.txt
180 |
181 | # Visual Studio cache files
182 | # files ending in .cache can be ignored
183 | *.[Cc]ache
184 | # but keep track of directories ending in .cache
185 | !*.[Cc]ache/
186 |
187 | # Others
188 | ClientBin/
189 | ~$*
190 | *~
191 | *.dbmdl
192 | *.dbproj.schemaview
193 | *.jfm
194 | *.pfx
195 | *.publishsettings
196 | node_modules/
197 | orleans.codegen.cs
198 |
199 | # Since there are multiple workflows, uncomment next line to ignore bower_components
200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
201 | #bower_components/
202 |
203 | # RIA/Silverlight projects
204 | Generated_Code/
205 |
206 | # Backup & report files from converting an old project file
207 | # to a newer Visual Studio version. Backup files are not needed,
208 | # because we have git ;-)
209 | _UpgradeReport_Files/
210 | Backup*/
211 | UpgradeLog*.XML
212 | UpgradeLog*.htm
213 |
214 | # SQL Server files
215 | *.mdf
216 | *.ldf
217 |
218 | # Business Intelligence projects
219 | *.rdl.data
220 | *.bim.layout
221 | *.bim_*.settings
222 |
223 | # Microsoft Fakes
224 | FakesAssemblies/
225 |
226 | # GhostDoc plugin setting file
227 | *.GhostDoc.xml
228 |
229 | # Node.js Tools for Visual Studio
230 | .ntvs_analysis.dat
231 |
232 | # Visual Studio 6 build log
233 | *.plg
234 |
235 | # Visual Studio 6 workspace options file
236 | *.opt
237 |
238 | # Visual Studio LightSwitch build output
239 | **/*.HTMLClient/GeneratedArtifacts
240 | **/*.DesktopClient/GeneratedArtifacts
241 | **/*.DesktopClient/ModelManifest.xml
242 | **/*.Server/GeneratedArtifacts
243 | **/*.Server/ModelManifest.xml
244 | _Pvt_Extensions
245 |
246 | # Paket dependency manager
247 | .paket/paket.exe
248 | paket-files/
249 |
250 | # FAKE - F# Make
251 | .fake/
252 |
253 | # JetBrains Rider
254 | .idea/
255 | *.sln.iml
256 |
257 | # CodeRush
258 | .cr/
259 |
260 | # Python Tools for Visual Studio (PTVS)
261 | __pycache__/
262 | *.pyc
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Mohammed ALMadhoun
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #  UnityInspector
2 |
3 | A simple tool for inspecting and editing managed Unity3d games in real-time, using a simple layout you can see game objects in the active scene, and you can click at any game object to see its components, and click at any component to see its members and edit values.
4 |
5 |
6 |
7 |
8 |
9 | ## How to build
10 | 1. Clone the project.
11 | 2. Add UnityEngine.dll refrence to UnityInspector.Inejctor.
12 | 3. Tadaaa.
13 |
14 | ## How UnityInspector works
15 | First I tried to build this using a direct memory read and write method and it works kinda well (I'm lying), but using the previous method will leave a lot of work to do for each unity3d engine version (but it works on managed and il2cpp games).
16 | So now this project is using the awesome [warbler/SharpMonoInjector](https://github.com/warbler/SharpMonoInjector) to inject a managed dll inside mono which communicates through TCP with the main program, well and some cool C# code.
17 |
18 |
19 |
20 |
21 | ## Todo
22 | - [ ] Add data locks
23 | - [ ] Some error handling
24 | - [ ] Support nested objects
25 | - [ ] Change GameObjects and components references
26 | - [ ] Browse prefabs and other GameObjects
27 |
--------------------------------------------------------------------------------
/SharpMonoInjector/Assembler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace SharpMonoInjector
5 | {
6 | public class Assembler
7 | {
8 | private readonly List _asm = new List();
9 |
10 | public void MovRax(IntPtr arg)
11 | {
12 | _asm.AddRange(new byte[] {0x48, 0xB8});
13 | _asm.AddRange(BitConverter.GetBytes((long)arg));
14 | }
15 |
16 | public void MovRcx(IntPtr arg)
17 | {
18 | _asm.AddRange(new byte[] {0x48, 0xB9});
19 | _asm.AddRange(BitConverter.GetBytes((long)arg));
20 | }
21 |
22 | public void MovRdx(IntPtr arg)
23 | {
24 | _asm.AddRange(new byte[] {0x48, 0xBA});
25 | _asm.AddRange(BitConverter.GetBytes((long)arg));
26 | }
27 |
28 | public void MovR8(IntPtr arg)
29 | {
30 | _asm.AddRange(new byte[] {0x49, 0xB8});
31 | _asm.AddRange(BitConverter.GetBytes((long)arg));
32 | }
33 |
34 | public void MovR9(IntPtr arg)
35 | {
36 | _asm.AddRange(new byte[] {0x49, 0xB9});
37 | _asm.AddRange(BitConverter.GetBytes((long)arg));
38 | }
39 |
40 | public void SubRsp(byte arg)
41 | {
42 | _asm.AddRange(new byte[] {0x48, 0x83, 0xEC});
43 | _asm.Add(arg);
44 | }
45 |
46 | public void CallRax()
47 | {
48 | _asm.AddRange(new byte[] {0xFF, 0xD0});
49 | }
50 |
51 | public void AddRsp(byte arg)
52 | {
53 | _asm.AddRange(new byte[] {0x48, 0x83, 0xC4});
54 | _asm.Add(arg);
55 | }
56 |
57 | public void MovRaxTo(IntPtr dest)
58 | {
59 | _asm.AddRange(new byte[] { 0x48, 0xA3 });
60 | _asm.AddRange(BitConverter.GetBytes((long)dest));
61 | }
62 |
63 | public void Push(IntPtr arg)
64 | {
65 | _asm.Add((int)arg < 128 ? (byte)0x6A : (byte)0x68);
66 | _asm.AddRange((int)arg <= 255 ? new[] {(byte)arg} : BitConverter.GetBytes((int)arg));
67 | }
68 |
69 | public void MovEax(IntPtr arg)
70 | {
71 | _asm.Add(0xB8);
72 | _asm.AddRange(BitConverter.GetBytes((int)arg));
73 | }
74 |
75 | public void CallEax()
76 | {
77 | _asm.AddRange(new byte[] {0xFF, 0xD0});
78 | }
79 |
80 | public void AddEsp(byte arg)
81 | {
82 | _asm.AddRange(new byte[] {0x83, 0xC4});
83 | _asm.Add(arg);
84 | }
85 |
86 | public void MovEaxTo(IntPtr dest)
87 | {
88 | _asm.Add(0xA3);
89 | _asm.AddRange(BitConverter.GetBytes((int)dest));
90 | }
91 |
92 | public void Return()
93 | {
94 | _asm.Add(0xC3);
95 | }
96 |
97 | public byte[] ToByteArray() => _asm.ToArray();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/SharpMonoInjector/ExportedFunction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SharpMonoInjector
4 | {
5 | public struct ExportedFunction
6 | {
7 | public string Name;
8 |
9 | public IntPtr Address;
10 |
11 | public ExportedFunction(string name, IntPtr address)
12 | {
13 | Name = name;
14 | Address = address;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/SharpMonoInjector/Injector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using System.ComponentModel;
4 | using System.Collections.Generic;
5 | using System.Runtime.InteropServices;
6 | using System.Diagnostics;
7 | using System.Linq;
8 |
9 | namespace SharpMonoInjector
10 | {
11 | public class Injector : IDisposable
12 | {
13 | private const string mono_get_root_domain = "mono_get_root_domain";
14 |
15 | private const string mono_thread_attach = "mono_thread_attach";
16 |
17 | private const string mono_image_open_from_data = "mono_image_open_from_data";
18 |
19 | private const string mono_assembly_load_from_full = "mono_assembly_load_from_full";
20 |
21 | private const string mono_assembly_get_image = "mono_assembly_get_image";
22 |
23 | private const string mono_class_from_name = "mono_class_from_name";
24 |
25 | private const string mono_class_get_method_from_name = "mono_class_get_method_from_name";
26 |
27 | private const string mono_runtime_invoke = "mono_runtime_invoke";
28 |
29 | private const string mono_assembly_close = "mono_assembly_close";
30 |
31 | private const string mono_image_strerror = "mono_image_strerror";
32 |
33 | private const string mono_object_get_class = "mono_object_get_class";
34 |
35 | private const string mono_class_get_name = "mono_class_get_name";
36 |
37 | private readonly Dictionary Exports = new Dictionary
38 | {
39 | { mono_get_root_domain, IntPtr.Zero },
40 | { mono_thread_attach, IntPtr.Zero },
41 | { mono_image_open_from_data, IntPtr.Zero },
42 | { mono_assembly_load_from_full, IntPtr.Zero },
43 | { mono_assembly_get_image, IntPtr.Zero },
44 | { mono_class_from_name, IntPtr.Zero },
45 | { mono_class_get_method_from_name, IntPtr.Zero },
46 | { mono_runtime_invoke, IntPtr.Zero },
47 | { mono_assembly_close, IntPtr.Zero },
48 | { mono_image_strerror, IntPtr.Zero },
49 | { mono_object_get_class, IntPtr.Zero },
50 | { mono_class_get_name, IntPtr.Zero }
51 | };
52 |
53 | private Memory _memory;
54 |
55 | private IntPtr _rootDomain;
56 |
57 | private bool _attach;
58 |
59 | private readonly IntPtr _handle;
60 |
61 | private IntPtr _mono;
62 |
63 | public bool Is64Bit { get; private set; }
64 |
65 | public Injector(string processName)
66 | {
67 | Process process = Process.GetProcesses()
68 | .FirstOrDefault(p => p.ProcessName
69 | .Equals(processName, StringComparison.OrdinalIgnoreCase));
70 |
71 | if (process == null)
72 | throw new InjectorException($"Could not find a process with the name {processName}");
73 |
74 | if ((_handle = Native.OpenProcess(ProcessAccessRights.PROCESS_ALL_ACCESS, false, process.Id)) == IntPtr.Zero)
75 | throw new InjectorException("Failed to open process", new Win32Exception(Marshal.GetLastWin32Error()));
76 |
77 | Is64Bit = ProcessUtils.Is64BitProcess(_handle);
78 |
79 | if (!ProcessUtils.GetMonoModule(_handle, out _mono))
80 | throw new InjectorException("Failed to find mono.dll in the target process");
81 |
82 | _memory = new Memory(_handle);
83 | }
84 |
85 | public Injector(int processId)
86 | {
87 | Process process = Process.GetProcesses()
88 | .FirstOrDefault(p => p.Id == processId);
89 |
90 | if (process == null)
91 | throw new InjectorException($"Could not find a process with the id {processId}");
92 |
93 | if ((_handle = Native.OpenProcess(ProcessAccessRights.PROCESS_ALL_ACCESS, false, process.Id)) == IntPtr.Zero)
94 | throw new InjectorException("Failed to open process", new Win32Exception(Marshal.GetLastWin32Error()));
95 |
96 | Is64Bit = ProcessUtils.Is64BitProcess(_handle);
97 |
98 | if (!ProcessUtils.GetMonoModule(_handle, out _mono))
99 | throw new InjectorException("Failed to find mono.dll in the target process");
100 |
101 | _memory = new Memory(_handle);
102 | }
103 |
104 | public Injector(IntPtr processHandle, IntPtr monoModule)
105 | {
106 | if ((_handle = processHandle) == IntPtr.Zero)
107 | throw new ArgumentException("Argument cannot be zero", nameof(processHandle));
108 |
109 | if ((_mono = monoModule) == IntPtr.Zero)
110 | throw new ArgumentException("Argument cannot be zero", nameof(monoModule));
111 |
112 | Is64Bit = ProcessUtils.Is64BitProcess(_handle);
113 | _memory = new Memory(_handle);
114 | }
115 |
116 | public void Dispose()
117 | {
118 | _memory.Dispose();
119 | Native.CloseHandle(_handle);
120 | }
121 |
122 | private void ObtainMonoExports()
123 | {
124 | foreach (ExportedFunction ef in ProcessUtils.GetExportedFunctions(_handle, _mono))
125 | if (Exports.ContainsKey(ef.Name))
126 | Exports[ef.Name] = ef.Address;
127 |
128 | foreach (var kvp in Exports)
129 | if (kvp.Value == IntPtr.Zero)
130 | throw new InjectorException($"Failed to obtain the address of {kvp.Key}()");
131 | }
132 |
133 | public IntPtr Inject(byte[] rawAssembly, string @namespace, string className, string methodName)
134 | {
135 | if (rawAssembly == null)
136 | throw new ArgumentNullException(nameof(rawAssembly));
137 |
138 | if (rawAssembly.Length == 0)
139 | throw new ArgumentException($"{nameof(rawAssembly)} cannot be empty", nameof(rawAssembly));
140 |
141 | if (className == null)
142 | throw new ArgumentNullException(nameof(className));
143 |
144 | if (methodName == null)
145 | throw new ArgumentNullException(nameof(methodName));
146 |
147 | IntPtr rawImage, assembly, image, @class, method;
148 |
149 | ObtainMonoExports();
150 | _rootDomain = GetRootDomain();
151 | rawImage = OpenImageFromData(rawAssembly);
152 | _attach = true;
153 | assembly = OpenAssemblyFromImage(rawImage);
154 | image = GetImageFromAssembly(assembly);
155 | @class = GetClassFromName(image, @namespace, className);
156 | method = GetMethodFromName(@class, methodName);
157 | RuntimeInvoke(method);
158 | return assembly;
159 | }
160 |
161 | public void Eject(IntPtr assembly, string @namespace, string className, string methodName)
162 | {
163 | if (assembly == IntPtr.Zero)
164 | throw new ArgumentException($"{nameof(assembly)} cannot be zero", nameof(assembly));
165 |
166 | if (className == null)
167 | throw new ArgumentNullException(nameof(className));
168 |
169 | if (methodName == null)
170 | throw new ArgumentNullException(nameof(methodName));
171 |
172 | IntPtr image, @class, method;
173 |
174 | ObtainMonoExports();
175 | _rootDomain = GetRootDomain();
176 | _attach = true;
177 | image = GetImageFromAssembly(assembly);
178 | @class = GetClassFromName(image, @namespace, className);
179 | method = GetMethodFromName(@class, methodName);
180 | RuntimeInvoke(method);
181 | CloseAssembly(assembly);
182 | }
183 |
184 | private static void ThrowIfNull(IntPtr ptr, string methodName)
185 | {
186 | if (ptr == IntPtr.Zero)
187 | throw new InjectorException($"{methodName}() returned NULL");
188 | }
189 |
190 | private IntPtr GetRootDomain()
191 | {
192 | IntPtr rootDomain = Execute(Exports[mono_get_root_domain]);
193 | ThrowIfNull(rootDomain, mono_get_root_domain);
194 | return rootDomain;
195 | }
196 |
197 | private IntPtr OpenImageFromData(byte[] assembly)
198 | {
199 | IntPtr statusPtr = _memory.Allocate(4);
200 | IntPtr rawImage = Execute(Exports[mono_image_open_from_data],
201 | _memory.AllocateAndWrite(assembly), (IntPtr)assembly.Length, (IntPtr)1, statusPtr);
202 |
203 | MonoImageOpenStatus status = (MonoImageOpenStatus)_memory.ReadInt(statusPtr);
204 |
205 | if (status != MonoImageOpenStatus.MONO_IMAGE_OK) {
206 | IntPtr messagePtr = Execute(Exports[mono_image_strerror], (IntPtr)status);
207 | string message = _memory.ReadString(messagePtr, 256, Encoding.UTF8);
208 | throw new InjectorException($"{mono_image_open_from_data}() failed: {message}");
209 | }
210 |
211 | return rawImage;
212 | }
213 |
214 | private IntPtr OpenAssemblyFromImage(IntPtr image)
215 | {
216 | IntPtr statusPtr = _memory.Allocate(4);
217 | IntPtr assembly = Execute(Exports[mono_assembly_load_from_full],
218 | image, _memory.AllocateAndWrite(new byte[1]), statusPtr, IntPtr.Zero);
219 |
220 | MonoImageOpenStatus status = (MonoImageOpenStatus)_memory.ReadInt(statusPtr);
221 |
222 | if (status != MonoImageOpenStatus.MONO_IMAGE_OK) {
223 | IntPtr messagePtr = Execute(Exports[mono_image_strerror], (IntPtr)status);
224 | string message = _memory.ReadString(messagePtr, 256, Encoding.UTF8);
225 | throw new InjectorException($"{mono_assembly_load_from_full}() failed: {message}");
226 | }
227 |
228 | return assembly;
229 | }
230 |
231 | private IntPtr GetImageFromAssembly(IntPtr assembly)
232 | {
233 | IntPtr image = Execute(Exports[mono_assembly_get_image], assembly);
234 | ThrowIfNull(image, mono_assembly_get_image);
235 | return image;
236 | }
237 |
238 | private IntPtr GetClassFromName(IntPtr image, string @namespace, string className)
239 | {
240 | IntPtr @class = Execute(Exports[mono_class_from_name],
241 | image, _memory.AllocateAndWrite(@namespace), _memory.AllocateAndWrite(className));
242 | ThrowIfNull(@class, mono_class_from_name);
243 | return @class;
244 | }
245 |
246 | private IntPtr GetMethodFromName(IntPtr @class, string methodName)
247 | {
248 | IntPtr method = Execute(Exports[mono_class_get_method_from_name],
249 | @class, _memory.AllocateAndWrite(methodName), IntPtr.Zero);
250 | ThrowIfNull(method, mono_class_get_method_from_name);
251 | return method;
252 | }
253 |
254 | private string GetClassName(IntPtr monoObject)
255 | {
256 | IntPtr @class = Execute(Exports[mono_object_get_class], monoObject);
257 | ThrowIfNull(@class, mono_object_get_class);
258 | IntPtr className = Execute(Exports[mono_class_get_name], @class);
259 | ThrowIfNull(className, mono_class_get_name);
260 | return _memory.ReadString(className, 256, Encoding.UTF8);
261 | }
262 |
263 | private string ReadMonoString(IntPtr monoString)
264 | {
265 | int len = _memory.ReadInt(monoString + (Is64Bit ? 0x10 : 0x8));
266 | return _memory.ReadUnicodeString(monoString + (Is64Bit ? 0x14 : 0xC), len * 2);
267 | }
268 |
269 | private void RuntimeInvoke(IntPtr method)
270 | {
271 | IntPtr excPtr = Is64Bit ? _memory.AllocateAndWrite((long)0) : _memory.AllocateAndWrite(0);
272 |
273 | IntPtr result = Execute(Exports[mono_runtime_invoke],
274 | method, IntPtr.Zero, IntPtr.Zero, excPtr);
275 |
276 | IntPtr exc = (IntPtr)_memory.ReadInt(excPtr);
277 |
278 | if (exc != IntPtr.Zero) {
279 | string className = GetClassName(exc);
280 | string message = ReadMonoString((IntPtr)_memory.ReadInt(exc + (Is64Bit ? 0x20 : 0x10)));
281 | throw new InjectorException($"The managed method threw an exception: ({className}) {message}");
282 | }
283 | }
284 |
285 | private void CloseAssembly(IntPtr assembly)
286 | {
287 | IntPtr result = Execute(Exports[mono_assembly_close], assembly);
288 | ThrowIfNull(result, mono_assembly_close);
289 | }
290 |
291 | private IntPtr Execute(IntPtr address, params IntPtr[] args)
292 | {
293 | IntPtr retValPtr = Is64Bit
294 | ? _memory.AllocateAndWrite((long)0)
295 | : _memory.AllocateAndWrite(0);
296 |
297 | byte[] code = Assemble(address, retValPtr, args);
298 | IntPtr alloc = _memory.AllocateAndWrite(code);
299 |
300 | IntPtr thread = Native.CreateRemoteThread(
301 | _handle, IntPtr.Zero, 0, alloc, IntPtr.Zero, 0, out _);
302 |
303 | if (thread == IntPtr.Zero)
304 | throw new InjectorException("Failed to create a remote thread", new Win32Exception(Marshal.GetLastWin32Error()));
305 |
306 | WaitResult result = Native.WaitForSingleObject(thread, -1);
307 |
308 | if (result == WaitResult.WAIT_FAILED)
309 | throw new InjectorException("Failed to wait for a remote thread", new Win32Exception(Marshal.GetLastWin32Error()));
310 |
311 | IntPtr ret = Is64Bit
312 | ? (IntPtr)_memory.ReadLong(retValPtr)
313 | : (IntPtr)_memory.ReadInt(retValPtr);
314 |
315 | if ((long)ret == 0x00000000C0000005)
316 | throw new InjectorException($"An access violation occurred while executing {Exports.First(e => e.Value == address).Key}()");
317 |
318 | return ret;
319 | }
320 |
321 | private byte[] Assemble(IntPtr functionPtr, IntPtr retValPtr, IntPtr[] args)
322 | {
323 | return Is64Bit
324 | ? Assemble64(functionPtr, retValPtr, args)
325 | : Assemble86(functionPtr, retValPtr, args);
326 | }
327 |
328 | private byte[] Assemble86(IntPtr functionPtr, IntPtr retValPtr, IntPtr[] args)
329 | {
330 | Assembler asm = new Assembler();
331 |
332 | if (_attach) {
333 | asm.Push(_rootDomain);
334 | asm.MovEax(Exports[mono_thread_attach]);
335 | asm.CallEax();
336 | asm.AddEsp(4);
337 | }
338 |
339 | for (int i = args.Length - 1; i >= 0; i--)
340 | asm.Push(args[i]);
341 |
342 | asm.MovEax(functionPtr);
343 | asm.CallEax();
344 | asm.AddEsp((byte)(args.Length * 4));
345 | asm.MovEaxTo(retValPtr);
346 | asm.Return();
347 |
348 | return asm.ToByteArray();
349 | }
350 |
351 | private byte[] Assemble64(IntPtr functionPtr, IntPtr retValPtr, IntPtr[] args)
352 | {
353 | Assembler asm = new Assembler();
354 |
355 | asm.SubRsp(40);
356 |
357 | if (_attach) {
358 | asm.MovRax(Exports[mono_thread_attach]);
359 | asm.MovRcx(_rootDomain);
360 | asm.CallRax();
361 | }
362 |
363 | asm.MovRax(functionPtr);
364 |
365 | for (int i = 0; i < args.Length; i++) {
366 | switch (i) {
367 | case 0:
368 | asm.MovRcx(args[i]);
369 | break;
370 | case 1:
371 | asm.MovRdx(args[i]);
372 | break;
373 | case 2:
374 | asm.MovR8(args[i]);
375 | break;
376 | case 3:
377 | asm.MovR9(args[i]);
378 | break;
379 | }
380 | }
381 |
382 | asm.CallRax();
383 | asm.AddRsp(40);
384 | asm.MovRaxTo(retValPtr);
385 | asm.Return();
386 |
387 | return asm.ToByteArray();
388 | }
389 | }
390 | }
391 |
--------------------------------------------------------------------------------
/SharpMonoInjector/InjectorException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SharpMonoInjector
4 | {
5 | public class InjectorException : Exception
6 | {
7 | public InjectorException(string message) : base(message)
8 | {
9 | }
10 |
11 | public InjectorException(string message, Exception innerException) : base(message, innerException)
12 | {
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/SharpMonoInjector/Memory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Runtime.InteropServices;
5 | using System.Text;
6 |
7 | namespace SharpMonoInjector
8 | {
9 | public class Memory : IDisposable
10 | {
11 | private readonly IntPtr _handle;
12 |
13 | private readonly Dictionary _allocations = new Dictionary();
14 |
15 | public Memory(IntPtr processHandle)
16 | {
17 | _handle = processHandle;
18 | }
19 |
20 | public string ReadString(IntPtr address, int length, Encoding encoding)
21 | {
22 | List bytes = new List();
23 |
24 | for (int i = 0; i < length; i++) {
25 | byte read = ReadBytes(address + bytes.Count, 1)[0];
26 |
27 | if (read == 0x00)
28 | break;
29 |
30 | bytes.Add(read);
31 | }
32 |
33 | return encoding.GetString(bytes.ToArray());
34 | }
35 |
36 | public string ReadUnicodeString(IntPtr address, int length)
37 | {
38 | return Encoding.Unicode.GetString(ReadBytes(address, length));
39 | }
40 |
41 | public short ReadShort(IntPtr address)
42 | {
43 | return BitConverter.ToInt16(ReadBytes(address, 2), 0);
44 | }
45 |
46 | public int ReadInt(IntPtr address)
47 | {
48 | return BitConverter.ToInt32(ReadBytes(address, 4), 0);
49 | }
50 |
51 | public long ReadLong(IntPtr address)
52 | {
53 | return BitConverter.ToInt64(ReadBytes(address, 8), 0);
54 | }
55 |
56 | public byte[] ReadBytes(IntPtr address, int size)
57 | {
58 | byte[] bytes = new byte[size];
59 |
60 | if (!Native.ReadProcessMemory(_handle, address, bytes, size))
61 | throw new InjectorException("Failed to read process memory", new Win32Exception(Marshal.GetLastWin32Error()));
62 |
63 | return bytes;
64 | }
65 |
66 | public IntPtr AllocateAndWrite(byte[] data)
67 | {
68 | IntPtr addr = Allocate(data.Length);
69 | Write(addr, data);
70 | return addr;
71 | }
72 |
73 | public IntPtr AllocateAndWrite(string data) => AllocateAndWrite(Encoding.UTF8.GetBytes(data));
74 |
75 | public IntPtr AllocateAndWrite(int data) => AllocateAndWrite(BitConverter.GetBytes(data));
76 |
77 | public IntPtr AllocateAndWrite(long data) => AllocateAndWrite(BitConverter.GetBytes(data));
78 |
79 | public IntPtr Allocate(int size)
80 | {
81 | IntPtr addr =
82 | Native.VirtualAllocEx(_handle, IntPtr.Zero, size,
83 | AllocationType.MEM_COMMIT, MemoryProtection.PAGE_EXECUTE_READWRITE);
84 |
85 | if (addr == IntPtr.Zero)
86 | throw new InjectorException("Failed to allocate process memory", new Win32Exception(Marshal.GetLastWin32Error()));
87 |
88 | _allocations.Add(addr, size);
89 | return addr;
90 | }
91 |
92 | public void Write(IntPtr addr, byte[] data)
93 | {
94 | if (!Native.WriteProcessMemory(_handle, addr, data, data.Length))
95 | throw new InjectorException("Failed to write process memory", new Win32Exception(Marshal.GetLastWin32Error()));
96 | }
97 |
98 | public void Dispose()
99 | {
100 | foreach (var kvp in _allocations)
101 | Native.VirtualFreeEx(_handle, kvp.Key, kvp.Value, MemoryFreeType.MEM_DECOMMIT);
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/SharpMonoInjector/MonoImageOpenStatus.cs:
--------------------------------------------------------------------------------
1 | namespace SharpMonoInjector
2 | {
3 | public enum MonoImageOpenStatus
4 | {
5 | MONO_IMAGE_OK,
6 | MONO_IMAGE_ERROR_ERRNO,
7 | MONO_IMAGE_MISSING_ASSEMBLYREF,
8 | MONO_IMAGE_IMAGE_INVALID
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/SharpMonoInjector/Native.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Text;
4 |
5 | namespace SharpMonoInjector
6 | {
7 | [StructLayout(LayoutKind.Sequential)]
8 | public struct MODULEINFO
9 | {
10 | public IntPtr lpBaseOfDll;
11 |
12 | public int SizeOfImage;
13 |
14 | public IntPtr EntryPoint;
15 | }
16 |
17 | public enum ModuleFilter : uint
18 | {
19 | LIST_MODULES_DEFAULT = 0x0,
20 | LIST_MODULES_32BIT = 0x01,
21 | LIST_MODULES_64BIT = 0x02,
22 | LIST_MODULES_ALL = 0x03
23 | }
24 |
25 | [Flags]
26 | public enum AllocationType
27 | {
28 | MEM_COMMIT = 0x00001000,
29 | MEM_RESERVE = 0x00002000,
30 | MEM_RESET = 0x00080000,
31 | MEM_RESET_UNDO = 0x1000000,
32 | MEM_LARGE_PAGES = 0x20000000,
33 | MEM_PHYSICAL = 0x00400000,
34 | MEM_TOP_DOWN = 0x00100000
35 | }
36 |
37 | [Flags]
38 | public enum MemoryProtection
39 | {
40 | PAGE_EXECUTE = 0x10,
41 | PAGE_EXECUTE_READ = 0x20,
42 | PAGE_EXECUTE_READWRITE = 0x40,
43 | PAGE_EXECUTE_WRITECOPY = 0x80,
44 | PAGE_NOACCESS = 0x01,
45 | PAGE_READONLY = 0x02,
46 | PAGE_READWRITE = 0x4,
47 | PAGE_WRITECOPY = 0x8,
48 | PAGE_TARGETS_INVALID = 0x40000000,
49 | PAGE_TARGETS_NO_UPDATE = 0x40000000,
50 | PAGE_GUARD = 0x100,
51 | PAGE_NOCACHE = 0x200,
52 | PAGE_WRITECOMBINE = 0x400
53 | }
54 |
55 | [Flags]
56 | public enum MemoryFreeType
57 | {
58 | MEM_DECOMMIT = 0x4000,
59 | MEM_RELEASE = 0x8000
60 | }
61 |
62 | [Flags]
63 | public enum ThreadCreationFlags
64 | {
65 | None = 0,
66 | CREATE_SUSPENDED = 0x00000004,
67 | STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000
68 | }
69 |
70 | public enum WaitResult : uint
71 | {
72 | WAIT_ABANDONED = 0x00000080,
73 | WAIT_OBJECT_0 = 0x00000000,
74 | WAIT_TIMEOUT = 0x00000102,
75 | WAIT_FAILED = 0xFFFFFFFF
76 | }
77 |
78 | [Flags]
79 | public enum ProcessAccessRights : uint
80 | {
81 | PROCESS_ALL_ACCESS = 0x1FFFFF,
82 | PROCESS_CREATE_PROCESS = 0x0080,
83 | PROCESS_CREATE_THREAD = 0x0002,
84 | PROCESS_DUP_HANDLE = 0x0040,
85 | PROCESS_QUERY_INFORMATION = 0x0400,
86 | PROCESS_QUERY_LIMITED_INFORMATION = 0x1000,
87 | PROCESS_SET_INFORMATION = 0x0200,
88 | PROCESS_SET_QUOTA = 0x0100,
89 | PROCESS_SUSPEND_RESUME = 0x0800,
90 | PROCESS_TERMINATE = 0x0001,
91 | PROCESS_VM_OPERATION = 0x0008,
92 | PROCESS_VM_READ = 0x0010,
93 | PROCESS_VM_WRITE = 0x0020,
94 | SYNCHRONIZE = 0x00100000
95 | }
96 |
97 | public static class Native
98 | {
99 | [DllImport("kernel32.dll", SetLastError = true)]
100 | public static extern IntPtr OpenProcess(ProcessAccessRights dwDesiredAccess, bool bInheritHandle, int processId);
101 |
102 | [DllImport("kernel32.dll", SetLastError = true)]
103 | public static extern bool CloseHandle(IntPtr handle);
104 |
105 | [DllImport("kernel32.dll")]
106 | [return: MarshalAs(UnmanagedType.Bool)]
107 | public static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process);
108 |
109 | [DllImport("psapi.dll", SetLastError = true)]
110 | public static extern bool EnumProcessModulesEx(IntPtr hProcess, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)] [In][Out] IntPtr[] lphModule, int cb, [MarshalAs(UnmanagedType.U4)] out int lpcbNeeded, ModuleFilter dwFilterFlag);
111 |
112 | [DllImport("psapi.dll")]
113 | public static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] uint nSize);
114 |
115 | [DllImport("psapi.dll", SetLastError = true)]
116 | public static extern bool GetModuleInformation(IntPtr hProcess, IntPtr hModule, out MODULEINFO lpmodinfo, uint cb);
117 |
118 | [DllImport("kernel32.dll", SetLastError = true)]
119 | [return: MarshalAs(UnmanagedType.Bool)]
120 | public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, int lpNumberOfBytesWritten = 0);
121 |
122 | [DllImport("kernel32.dll", SetLastError = true)]
123 | [return: MarshalAs(UnmanagedType.Bool)]
124 | public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, int lpNumberOfBytesRead = 0);
125 |
126 | [DllImport("kernel32.dll", SetLastError = true)]
127 | public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
128 |
129 | [DllImport("kernel32.dll", SetLastError = true)]
130 | [return: MarshalAs(UnmanagedType.Bool)]
131 | public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, MemoryFreeType dwFreeType);
132 |
133 | [DllImport("kernel32.dll", SetLastError = true)]
134 | public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, int dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, ThreadCreationFlags dwCreationFlags, out int lpThreadId);
135 |
136 | [DllImport("kernel32.dll", SetLastError = true)]
137 | public static extern WaitResult WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/SharpMonoInjector/ProcessUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Runtime.InteropServices;
6 | using System.Text;
7 |
8 | namespace SharpMonoInjector
9 | {
10 | public static class ProcessUtils
11 | {
12 | public static IEnumerable GetExportedFunctions(IntPtr handle, IntPtr mod)
13 | {
14 | using (Memory memory = new Memory(handle)) {
15 | int e_lfanew = memory.ReadInt(mod + 0x3C);
16 | IntPtr ntHeaders = mod + e_lfanew;
17 | IntPtr optionalHeader = ntHeaders + 0x18;
18 | IntPtr dataDirectory = optionalHeader + (Is64BitProcess(handle) ? 0x70 : 0x60);
19 | IntPtr exportDirectory = mod + memory.ReadInt(dataDirectory);
20 | IntPtr names = mod + memory.ReadInt(exportDirectory + 0x20);
21 | IntPtr ordinals = mod + memory.ReadInt(exportDirectory + 0x24);
22 | IntPtr functions = mod + memory.ReadInt(exportDirectory + 0x1C);
23 | int count = memory.ReadInt(exportDirectory + 0x18);
24 |
25 | for (int i = 0; i < count; i++) {
26 | int offset = memory.ReadInt(names + i * 4);
27 | string name = memory.ReadString(mod + offset, 32, Encoding.ASCII);
28 | short ordinal = memory.ReadShort(ordinals + i * 2);
29 | IntPtr address = mod + memory.ReadInt(functions + ordinal * 4);
30 |
31 | if (address != IntPtr.Zero)
32 | yield return new ExportedFunction(name, address);
33 | }
34 | }
35 | }
36 |
37 | public static bool GetMonoModule(IntPtr handle, out IntPtr monoModule)
38 | {
39 | int size = Is64BitProcess(handle) ? 8 : 4;
40 |
41 | IntPtr[] ptrs = new IntPtr[0];
42 |
43 | if (!Native.EnumProcessModulesEx(
44 | handle, ptrs, 0, out int bytesNeeded, ModuleFilter.LIST_MODULES_ALL)) {
45 | throw new InjectorException("Failed to enumerate process modules", new Win32Exception(Marshal.GetLastWin32Error()));
46 | }
47 |
48 | int count = bytesNeeded / size;
49 | ptrs = new IntPtr[count];
50 |
51 | if (!Native.EnumProcessModulesEx(
52 | handle, ptrs, bytesNeeded, out bytesNeeded, ModuleFilter.LIST_MODULES_ALL)) {
53 | throw new InjectorException("Failed to enumerate process modules", new Win32Exception(Marshal.GetLastWin32Error()));
54 | }
55 |
56 | for (int i = 0; i < count; i++) {
57 | StringBuilder path = new StringBuilder(260);
58 | Native.GetModuleFileNameEx(handle, ptrs[i], path, 260);
59 |
60 | if (path.ToString().IndexOf("mono", StringComparison.OrdinalIgnoreCase) > -1) {
61 | if (!Native.GetModuleInformation(handle, ptrs[i], out MODULEINFO info, (uint)(size * ptrs.Length)))
62 | throw new InjectorException("Failed to get module information", new Win32Exception(Marshal.GetLastWin32Error()));
63 |
64 | var funcs = GetExportedFunctions(handle, info.lpBaseOfDll);
65 |
66 | if (funcs.Any(f => f.Name == "mono_get_root_domain")) {
67 | monoModule = info.lpBaseOfDll;
68 | return true;
69 | }
70 | }
71 | }
72 |
73 | monoModule = IntPtr.Zero;
74 | return false;
75 | }
76 |
77 | public static bool Is64BitProcess(IntPtr handle)
78 | {
79 | if (!Environment.Is64BitOperatingSystem)
80 | return false;
81 |
82 | if (!Native.IsWow64Process(handle, out bool isWow64))
83 | return IntPtr.Size == 8; // assume it's the same as the current process
84 |
85 | return !isWow64;
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/SharpMonoInjector/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SharpMonoInjector.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SharpMonoInjector/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/SharpMonoInjector/SharpMonoInjector.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | AnyCPU
6 |
7 |
8 |
9 | ..\..\build\debug
10 |
11 |
12 |
13 | ..\..\build\release
14 |
15 |
16 |
17 |
18 | True
19 | True
20 | Settings.settings
21 |
22 |
23 |
24 |
25 |
26 | SettingsSingleFileGenerator
27 | Settings.Designer.cs
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/UnityInspector.Communicator/Communicator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Net;
4 | using System.Net.Sockets;
5 | using System.Text;
6 |
7 | namespace UnityInspector.Communicator
8 | {
9 | public abstract class Communicator
10 | {
11 | public static Communicator Instance { get; set; }
12 | private const int Port = 23123;
13 | public enum Commands : byte
14 | {
15 | Destroy = 0,
16 | GetHierarchy = 1,
17 | GetChildren = 2,
18 | InvokeMethod = 3,
19 | GetComponents = 4,
20 | GetComponentType = 5,
21 | GetComponentMembers = 6,
22 | GetValueOfMember = 7,
23 | SetValueOfMember = 8
24 | }
25 | public enum Types : byte
26 | {
27 | Undefined = 0,
28 | Unknown = 1,
29 | Bool = 2,
30 | Byte = 3,
31 | Short = 4,
32 | Int = 5,
33 | Long = 6,
34 | Float = 7,
35 | Double = 8,
36 | String = 9,
37 | Array = 10,
38 | ArrayAsMember = 11,
39 | GameObject = 12,
40 | Vector3 = 14,
41 | Enum = 15,
42 | Color = 16,
43 | Vector2 = 17,
44 | Rect = 18,
45 | }
46 | public enum CommunicatorType
47 | {
48 | Server,
49 | Clinet
50 | }
51 | protected TcpListener server;
52 | protected TcpClient client;
53 | private NetworkStream pipe;
54 | private ASCIIEncoding stringEncoding;
55 |
56 | public Communicator (CommunicatorType communicatorType)
57 | {
58 | Instance = this;
59 | stringEncoding = new ASCIIEncoding ();
60 | try
61 | {
62 | if (communicatorType == CommunicatorType.Server)
63 | {
64 | server = new TcpListener (IPAddress.Any, Port);
65 | server.Start ();
66 | }
67 | else
68 | {
69 | client = new TcpClient ("localhost", Port);
70 | pipe = client.GetStream ();
71 | }
72 | }
73 | catch (Exception ex)
74 | {
75 | Console.WriteLine (ex);
76 | }
77 | }
78 | public abstract void Close();
79 | public bool Connected { get { return client.Connected; } }
80 | public bool DataAvailable ()
81 | {
82 | if (client != null && client.Connected)
83 | return pipe.DataAvailable;
84 |
85 | if (server.Pending ())
86 | {
87 | client = server.AcceptTcpClient();
88 | pipe = client.GetStream();
89 | }
90 | return false;
91 | }
92 |
93 | public void WriteBool (bool value)
94 | {
95 | pipe.Write (BitConverter.GetBytes (value), 0, 1);
96 | }
97 | public bool ReadBool ()
98 | {
99 | byte[] inBuffer = new byte[1];
100 | pipe.Read (inBuffer, 0, 1);
101 | return BitConverter.ToBoolean (inBuffer, 0);
102 | }
103 |
104 | public void WriteByte (byte value)
105 | {
106 | pipe.WriteByte (value);
107 | }
108 | public byte ReadByte ()
109 | {
110 | return (byte) pipe.ReadByte ();
111 | }
112 |
113 | public void WriteShort (short value)
114 | {
115 | pipe.Write (BitConverter.GetBytes (value), 0, 2);
116 | }
117 | public short ReadShort ()
118 | {
119 | byte[] inBuffer = new byte[2];
120 | pipe.Read (inBuffer, 0, 2);
121 | return BitConverter.ToInt16 (inBuffer, 0);
122 | }
123 |
124 | public void WriteInt (int value)
125 | {
126 | pipe.Write (BitConverter.GetBytes (value), 0, 4);
127 | }
128 | public int ReadInt ()
129 | {
130 | byte[] inBuffer = new byte[4];
131 | pipe.Read (inBuffer, 0, 4);
132 | return BitConverter.ToInt32 (inBuffer, 0);
133 | }
134 |
135 | public void WriteLong (long value)
136 | {
137 | pipe.Write (BitConverter.GetBytes (value), 0, 8);
138 | pipe.Flush ();
139 | }
140 | public long ReadLong ()
141 | {
142 | byte[] inBuffer = new byte[8];
143 | pipe.Read (inBuffer, 0, 8);
144 | return BitConverter.ToInt64 (inBuffer, 0);
145 | }
146 |
147 | public void WriteFloat (float value)
148 | {
149 | pipe.Write (BitConverter.GetBytes (value), 0, 4);
150 | pipe.Flush ();
151 | }
152 | public float ReadFloat ()
153 | {
154 | byte[] inBuffer = new byte[4];
155 | pipe.Read (inBuffer, 0, 4);
156 | return BitConverter.ToSingle (inBuffer, 0);
157 | }
158 |
159 | public void WriteDouble (double value)
160 | {
161 | pipe.Write (BitConverter.GetBytes (value), 0, 8);
162 | pipe.Flush ();
163 | }
164 | public double ReadDouble ()
165 | {
166 | byte[] inBuffer = new byte[8];
167 | pipe.Read (inBuffer, 0, 8);
168 | return BitConverter.ToDouble (inBuffer, 0);
169 | }
170 |
171 | public void WriteString (string value)
172 | {
173 | if (value.Length > 255)
174 | Console.Error.WriteLine ("The length of the string must be lower than 256");
175 | byte len = (byte) value.Length;
176 | pipe.WriteByte (len);
177 | if (len != 0)
178 | pipe.Write (stringEncoding.GetBytes (value), 0, len);
179 | }
180 | public string ReadString ()
181 | {
182 | int len = pipe.ReadByte ();
183 | if (len == 0)
184 | return "";
185 | byte[] inBuffer = new byte[len];
186 | pipe.Read (inBuffer, 0, len);
187 | return stringEncoding.GetString (inBuffer);
188 | }
189 |
190 | public void WriteArrayAsMember (Array value)
191 | {
192 | if (value != null && value.Length > 0)
193 | {
194 | WriteInt (value.Length);
195 | WriteByte ((byte) GetObjectType (value.GetValue (0)));
196 | }
197 | else
198 | {
199 | WriteInt (0);
200 | WriteByte ((byte) Types.Undefined);
201 | }
202 | }
203 | public abstract object ReadArrayAsMember ();
204 |
205 | public void WriteArray (Array value)
206 | {
207 | if (value != null && value.Length > 0)
208 | {
209 | Types firstElementType = GetObjectType (value.GetValue (0));
210 | WriteInt (value.Length);
211 | WriteByte ((byte) firstElementType);
212 | for (int i = 0; i < value.Length; i++)
213 | {
214 | WriteObject (value.GetValue (i), true, firstElementType);
215 | }
216 | }
217 | else
218 | {
219 | WriteInt (0);
220 | }
221 | }
222 | public Array ReadArray ()
223 | {
224 | int len = ReadInt ();
225 | Array values = new object[len];
226 | Types knownType = Types.Undefined;
227 | if (len > 0)
228 | knownType = (Types) ReadByte ();
229 |
230 | for (int i = 0; i < len; i++)
231 | {
232 | values.SetValue(ReadObject (knownType), i);
233 | }
234 | return values;
235 | }
236 |
237 | public void WriteObjects (object[] objects)
238 | {
239 | WriteInt (objects.Length);
240 | for (int i = 0; i < objects.Length; i++)
241 | {
242 | object obj = objects[i];
243 | WriteObject (obj);
244 | }
245 | }
246 | public object[] ReadObjects ()
247 | {
248 | int len = ReadInt ();
249 | object[] objects = new object[len];
250 | for (int i = 0; i < len; i++)
251 | {
252 | objects[i] = ReadObject ();
253 | }
254 | return objects;
255 | }
256 |
257 | public static Types GetObjectType (object obj, bool IsMember = false)
258 | {
259 | if (obj is bool)
260 | return Types.Bool;
261 | if (obj is byte)
262 | return Types.Byte;
263 | if (obj is short)
264 | return Types.Short;
265 | if (obj is int)
266 | return Types.Int;
267 | if (obj is long)
268 | return Types.Long;
269 | if (obj is float)
270 | return Types.Float;
271 | if (obj is double)
272 | return Types.Double;
273 | if (obj is string)
274 | return Types.String;
275 | if (obj is Array && IsMember)
276 | return Types.ArrayAsMember;
277 | if (obj is Array)
278 | return Types.Array;
279 | if (obj is T)
280 | return Types.GameObject;
281 | if (obj != null && obj.GetType ().Name == "Vector3")
282 | return Types.Vector3;
283 | if (obj is Enum || (obj != null && obj.GetType ().Name == "EnumMember"))
284 | return Types.Enum;
285 | if (obj != null && obj.GetType ().Name.StartsWith ("Color"))
286 | return Types.Color;
287 | if (obj != null && obj.GetType ().Name == "Vector2")
288 | return Types.Vector2;
289 | if (obj != null && obj.GetType ().Name == "Rect")
290 | return Types.Rect;
291 | return Types.Unknown;
292 | }
293 | public void WriteObject (object obj, bool IsMember = false, Types knownType = Types.Undefined)
294 | {
295 | Types objType = knownType;
296 | if (knownType == Types.Undefined)
297 | {
298 | objType = GetObjectType (obj, IsMember);
299 | WriteByte ((byte) objType);
300 | }
301 | switch (objType)
302 | {
303 | case Types.Bool:
304 | WriteBool ((bool) obj);
305 | break;
306 | case Types.Byte:
307 | WriteByte ((byte) obj);
308 | break;
309 | case Types.Short:
310 | WriteShort ((short) obj);
311 | break;
312 | case Types.Int:
313 | WriteInt ((int) obj);
314 | break;
315 | case Types.Long:
316 | WriteLong ((long) obj);
317 | break;
318 | case Types.Float:
319 | WriteFloat ((float) obj);
320 | break;
321 | case Types.Double:
322 | WriteDouble ((double) obj);
323 | break;
324 | case Types.String:
325 | WriteString ((string) obj);
326 | break;
327 | case Types.Array:
328 | WriteArray ((Array) obj);
329 | break;
330 | case Types.ArrayAsMember:
331 | WriteArrayAsMember ((Array) obj);
332 | break;
333 | case Types.GameObject:
334 | WriteGameObject ((T) obj);
335 | break;
336 | case Types.Vector3:
337 | WriteVector3 (obj);
338 | break;
339 | case Types.Enum:
340 | WriteEnum (obj);
341 | break;
342 | case Types.Color:
343 | WriteColor (obj);
344 | break;
345 | case Types.Vector2:
346 | WriteVector2 (obj);
347 | break;
348 | case Types.Rect:
349 | WriteRect (obj);
350 | break;
351 | case Types.Unknown:
352 | WriteString ((obj==null)?"null":obj.GetType().Name);
353 | break;
354 |
355 | }
356 | }
357 | public object ReadObject (Types knownType = Types.Undefined)
358 | {
359 | Types objType = knownType;
360 | if (knownType == Types.Undefined)
361 | objType = (Types) ReadByte ();
362 |
363 | switch (objType)
364 | {
365 | case Types.Bool:
366 | return ReadBool ();
367 | case Types.Byte:
368 | return ReadByte ();
369 | case Types.Short:
370 | return ReadShort ();
371 | case Types.Int:
372 | return ReadInt ();
373 | case Types.Long:
374 | return ReadLong ();
375 | case Types.Float:
376 | return ReadFloat ();
377 | case Types.Double:
378 | return ReadDouble ();
379 | case Types.String:
380 | return ReadString ();
381 | case Types.Array:
382 | return ReadArray ();
383 | case Types.ArrayAsMember:
384 | return ReadArrayAsMember ();
385 | case Types.GameObject:
386 | return ReadGameObjectByRef ();
387 | case Types.Vector3:
388 | return ReadVector3 ();
389 | case Types.Enum:
390 | return ReadEnum ();
391 | case Types.Color:
392 | return ReadColor ();
393 | case Types.Vector2:
394 | return ReadVector2 ();
395 | case Types.Rect:
396 | return ReadRect ();
397 | case Types.Unknown:
398 | return ReadString ();
399 | }
400 | return null;
401 | }
402 |
403 | public void Flush ()
404 | {
405 | pipe.Flush ();
406 | }
407 |
408 |
409 | public abstract void WriteGameObject (T obj);
410 | public abstract T ReadGameObject ();
411 | public abstract void WriteGameObjectRef (T obj);
412 | public abstract T ReadGameObjectByRef ();
413 |
414 | public abstract void WriteVector3 (object value);
415 | public abstract object ReadVector3 ();
416 |
417 | public abstract void WriteVector2 (object value);
418 | public abstract object ReadVector2 ();
419 |
420 | public abstract void WriteRect (object value);
421 | public abstract object ReadRect ();
422 |
423 | public abstract void WriteEnum (object value);
424 | public abstract object ReadEnum ();
425 |
426 | public abstract void WriteColor (object value);
427 | public abstract object ReadColor ();
428 |
429 |
430 | }
431 | }
432 |
--------------------------------------------------------------------------------
/UnityInspector.Communicator/UnityInspector.Communicator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {AF3B27C3-615E-46DD-80B0-76C6E106C64D}
8 | Library
9 | Properties
10 | UnityInspector.Communicator
11 | UnityInspector.Communicator
12 | v4.6.1
13 | 512
14 | true
15 |
16 |
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 | false
27 |
28 |
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 | false
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace UnityInspector.GUI
10 | {
11 | ///
12 | /// Interaction logic for App.xaml
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Converters/BooleanConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows.Data;
8 |
9 | namespace UnityInspector.GUI.Converters
10 | {
11 | public class BooleanConverter : IValueConverter
12 | {
13 | public BooleanConverter(T trueValue, T falseValue)
14 | {
15 | True = trueValue;
16 | False = falseValue;
17 | }
18 |
19 | public T True { get; set; }
20 | public T False { get; set; }
21 |
22 | public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
23 | {
24 | return value is bool && ((bool)value) ? True : False;
25 | }
26 |
27 | public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
28 | {
29 | return value is T && EqualityComparer.Default.Equals((T)value, True);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Converters/BooleanToVisibilityConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 |
8 | namespace UnityInspector.GUI.Converters
9 | {
10 | public sealed class BooleanToVisibilityConverter : BooleanConverter
11 | {
12 | public BooleanToVisibilityConverter() :
13 | base(Visibility.Visible, Visibility.Collapsed)
14 | { }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Converters/ICollectionToBooleanConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Globalization;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows.Data;
9 |
10 | namespace UnityInspector.GUI.Converters
11 | {
12 | class ICollectionToBooleanConverter : IValueConverter
13 | {
14 | public object Convert (object value, Type targetType, object parameter, CultureInfo culture)
15 | {
16 | if (value == null) return false;
17 | return ((ICollection) value).Count > 0;
18 | }
19 |
20 | public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture)
21 | {
22 | throw new NotImplementedException ();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Converters/ObjectMemberTemplateSelector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using UnityInspector.GUI.Models;
9 | using UnityInspector.GUI.ViewModels;
10 |
11 | namespace UnityInspector.GUI.Converters
12 | {
13 | public class ObjectMemberTemplateSelector : DataTemplateSelector
14 | {
15 | public override DataTemplate SelectTemplate (object item, DependencyObject container)
16 | {
17 | FrameworkElement element = container as FrameworkElement;
18 | ObjectMemberViewModel member = item as ObjectMemberViewModel;
19 | if (member.Type == CommunicatorClient.Types.Bool)
20 | {
21 | return element.FindResource ("BoolMemberDataTemplate") as DataTemplate;
22 | }
23 | else if (member.Type > CommunicatorClient.Types.Bool && member.Type < CommunicatorClient.Types.String)
24 | {
25 | return element.FindResource ("NumericMemberDataTemplate") as DataTemplate;
26 | }
27 | else if (member.Type == CommunicatorClient.Types.Array)
28 | {
29 | return element.FindResource ("ArrayMemberDataTemplate") as DataTemplate;
30 | }
31 | else if (member.Type == CommunicatorClient.Types.Vector3)
32 | {
33 | return element.FindResource ("Vector3MemberDataTemplate") as DataTemplate;
34 | }
35 | else if (member.Type == CommunicatorClient.Types.Enum)
36 | {
37 | return element.FindResource ("EnumMemberDataTemplate") as DataTemplate;
38 | }
39 | else if (member.Type == CommunicatorClient.Types.Color)
40 | {
41 | return element.FindResource ("ColorMemberDataTemplate") as DataTemplate;
42 | }
43 | else if (member.Type == CommunicatorClient.Types.Vector2)
44 | {
45 | return element.FindResource ("Vector2MemberDataTemplate") as DataTemplate;
46 | }
47 | else if (member.Type == CommunicatorClient.Types.Rect)
48 | {
49 | return element.FindResource ("RectMemberDataTemplate") as DataTemplate;
50 | }
51 | else if(member.Type == CommunicatorClient.Types.GameObject)
52 | {
53 | return element.FindResource("GameObjectMemberDataTemplate") as DataTemplate;
54 | }
55 | else if(member.Type != CommunicatorClient.Types.Unknown)
56 | {
57 | return element.FindResource ("TextBoxMemberDataTemplate") as DataTemplate;
58 | }
59 | else
60 | {
61 | return element.FindResource ("UnknownMemberDataTemplate") as DataTemplate;
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
138 |
139 |
140 |
141 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
160 |
161 |
162 |
163 |
164 |
174 |
175 |
176 |
177 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using System.Windows.Controls.Primitives;
10 | using System.Windows.Data;
11 | using System.Windows.Documents;
12 | using System.Windows.Input;
13 | using System.Windows.Media;
14 | using System.Windows.Media.Imaging;
15 | using System.Windows.Navigation;
16 | using System.Windows.Shapes;
17 | using UnityInspector.GUI.Models;
18 |
19 | namespace UnityInspector.GUI
20 | {
21 |
22 | ///
23 | /// Interaction logic for MainWindow.xaml
24 | ///
25 | public partial class MainWindow : Window
26 | {
27 |
28 | public MainWindow ()
29 | {
30 | InitializeComponent ();
31 |
32 |
33 | }
34 |
35 | private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
36 | {
37 | if (CommunicatorClient.Instance != null)
38 | CommunicatorClient.Instance.Close();
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Models/ArrayMember.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using UnityInspector.Communicator;
7 |
8 | namespace UnityInspector.GUI.Models
9 | {
10 | public class ArrayMember
11 | {
12 | public ObjectMember objectMember;
13 | public int Length { get; }
14 | public CommunicatorClient.Types BaseType { get; }
15 | public ObjectMember[] Values
16 | {
17 | get
18 | {
19 | object[] values = (object[]) (Array) objectMember.GetValue ();
20 | ObjectMember[] valuesAsMember = new ObjectMember[values.Length];
21 | for (int i = 0; i < values.Length; i++)
22 | {
23 | valuesAsMember[i] = new ObjectMember (objectMember, BaseType, "[" + i + "]", values[i]);
24 | }
25 | return valuesAsMember;
26 | }
27 | }
28 |
29 | public ArrayMember (int length, Communicator.Types baseType)
30 | {
31 | Length = length;
32 | BaseType = baseType;
33 | }
34 |
35 | public override bool Equals(object obj)
36 | {
37 | if (obj != null && obj is ArrayMember)
38 | return objectMember.Equals(((ArrayMember)obj).objectMember);
39 | return false;
40 | }
41 | public override int GetHashCode()
42 | {
43 | return objectMember.GetHashCode();
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Models/CommunicatorClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows.Media;
7 | using UnityInspector.Communicator;
8 |
9 | namespace UnityInspector.GUI.Models
10 | {
11 | class CommunicatorClient : Communicator
12 | {
13 | public new static CommunicatorClient Instance { get; set; }
14 | public CommunicatorClient() : base (CommunicatorType.Clinet)
15 | {
16 | Instance = this;
17 | }
18 | public override void Close()
19 | {
20 | WriteByte((byte)Commands.Destroy);
21 | Flush ();
22 | client.Close();
23 | Instance = null;
24 | }
25 |
26 | public GameObject[] ReadHierarchy ()
27 | {
28 | WriteByte ((byte) Commands.GetHierarchy);
29 | int len = ReadInt ();
30 | GameObject[] gameObjects = new GameObject[len];
31 | for (int i = 0; i < len; i++)
32 | {
33 | gameObjects[i] = ReadGameObject ();
34 | }
35 | return gameObjects;
36 | }
37 | public GameObject[] GetChildren (GameObject gameObject)
38 | {
39 | WriteByte ((byte) Commands.GetChildren);
40 | WriteGameObjectRef (gameObject);
41 | int len = ReadInt ();
42 | GameObject[] gameObjects = new GameObject[len];
43 | for (int i = 0; i < len; i++)
44 | {
45 | gameObjects[i] = ReadGameObject ();
46 | }
47 | return gameObjects;
48 | }
49 | public void InvokeMethod (GameObject gameObject, string method, object[] args)
50 | {
51 | WriteByte ((byte) Commands.InvokeMethod);
52 | WriteGameObjectRef (gameObject);
53 | WriteString (method);
54 | WriteObjects (args);
55 | }
56 | public Component[] GetComponents (GameObject gameObject)
57 | {
58 | WriteByte ((byte) Commands.GetComponents);
59 | WriteGameObjectRef (gameObject);
60 | int len = ReadInt ();
61 | Component[] components = new Component[len];
62 | for (int i = 0; i < len; i++)
63 | {
64 | components[i] = new Component (gameObject, i, ReadString (), ReadByte ());
65 | }
66 | return components;
67 | }
68 | public ObjectMember[] GetComponentMembers (Component component)
69 | {
70 | WriteByte ((byte) Commands.GetComponentMembers);
71 | WriteGameObjectRef (component.Owner);
72 | WriteInt (component.Index);
73 | int len = ReadInt ();
74 | ObjectMember[] members = new ObjectMember[len];
75 | for (int i = 0; i < len; i++)
76 | {
77 | Types type = (Types)ReadByte();
78 | string name = ReadString();
79 | members[i] = new ObjectMember (component,
80 | type,
81 | name,
82 | ReadObject ());
83 | }
84 | return members;
85 | }
86 | public object GetValueOfMember (GameObject gameObject, string member, object value)
87 | {
88 | WriteByte ((byte) Commands.GetValueOfMember);
89 | WriteGameObjectRef (gameObject);
90 | WriteString (member);
91 | return ReadObject ();
92 | }
93 | public void SetValueOfMember (GameObject gameObject, string member, object value)
94 | {
95 | WriteByte ((byte) Commands.SetValueOfMember);
96 | WriteGameObjectRef (gameObject);
97 | WriteString (member);
98 | WriteObject (value);
99 | }
100 |
101 | public override object ReadArrayAsMember ()
102 | {
103 | ArrayMember ret = new ArrayMember (ReadInt (), (Types) ReadByte ());
104 | return ret;
105 | }
106 |
107 | public override void WriteGameObject (GameObject obj)
108 | {
109 | throw new NotImplementedException ();
110 | }
111 | public override GameObject ReadGameObject ()
112 | {
113 | return new GameObject (ReadInt (), ReadString (), ReadBool ());
114 | }
115 | public override void WriteGameObjectRef (GameObject obj)
116 | {
117 | WriteInt (obj.Address);
118 | }
119 | public override GameObject ReadGameObjectByRef ()
120 | {
121 | return ReadGameObject ();
122 | }
123 |
124 | public override void WriteVector3 (object value)
125 | {
126 | Vector3 vector3 = (Vector3) value;
127 | WriteFloat (vector3.X);
128 | WriteFloat (vector3.Y);
129 | WriteFloat (vector3.Z);
130 | }
131 | public override object ReadVector3 ()
132 | {
133 | return new Vector3 (ReadFloat (), ReadFloat (), ReadFloat ());
134 | }
135 |
136 | public override void WriteVector2 (object value)
137 | {
138 | Vector2 vector2 = (Vector2) value;
139 | WriteFloat (vector2.X);
140 | WriteFloat (vector2.Y);
141 | }
142 | public override object ReadVector2 ()
143 | {
144 | return new Vector2 (ReadFloat (), ReadFloat ());
145 | }
146 |
147 | public override void WriteRect(object value)
148 | {
149 | Rect rect = (Rect) value;
150 | WriteFloat (rect.X);
151 | WriteFloat (rect.Y);
152 | WriteFloat (rect.W);
153 | WriteFloat (rect.H);
154 | }
155 | public override object ReadRect ()
156 | {
157 | return new Rect (ReadFloat (), ReadFloat (), ReadFloat (), ReadFloat ());
158 | }
159 |
160 | public override void WriteEnum (object value)
161 | {
162 | WriteString (((EnumMember) value).Value);
163 | }
164 | public override object ReadEnum ()
165 | {
166 | byte len = ReadByte ();
167 | string[] values = new string[len];
168 | for (int i = 0; i < len; i++)
169 | {
170 | values[i] = ReadString ();
171 | }
172 | return new EnumMember (values, ReadString ());
173 | }
174 |
175 | public override void WriteColor (object value)
176 | {
177 | Color color = ((Color) value);
178 | WriteByte (color.R);
179 | WriteByte (color.G);
180 | WriteByte (color.B);
181 | WriteByte (color.A);
182 | }
183 | public override object ReadColor ()
184 | {
185 | Color color = new Color ();
186 | color.R = ReadByte ();
187 | color.G = ReadByte ();
188 | color.B = ReadByte ();
189 | color.A = ReadByte ();
190 | return color;
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Models/Component.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace UnityInspector.GUI.Models
9 | {
10 | public class Component
11 | {
12 | public GameObject Owner { get; }
13 | public string Name { get; }
14 | public bool IsBehaviour { get; }
15 | public int Index { get; }
16 | private bool _enabled;
17 | public bool Enabled
18 | {
19 | get
20 | {
21 | return _enabled;
22 | }
23 | set
24 | {
25 | if (!IsBehaviour) return;
26 | _enabled = value;
27 | CommunicatorClient.Instance.SetValueOfMember (Owner, Index + ":enabled", _enabled);
28 | }
29 | }
30 |
31 | public Component (GameObject gameObject, int index, string name, byte state)
32 | {
33 | Index = index;
34 | Owner = gameObject;
35 | Name = name;
36 | if (state == 0)
37 | {
38 | IsBehaviour = false;
39 | _enabled = true;
40 | } else
41 | {
42 | IsBehaviour = true;
43 | _enabled = (state == 0x01);
44 | }
45 | }
46 |
47 | public ObjectMember[] Members
48 | {
49 | get
50 | {
51 | return CommunicatorClient.Instance.GetComponentMembers (this);
52 | }
53 | }
54 | public override string ToString ()
55 | {
56 | return Name;
57 | }
58 |
59 | public override bool Equals (object obj)
60 | {
61 |
62 | if (obj != null && obj is Component) {
63 | return (Owner.Equals(((Component) obj).Owner) && Index == ((Component) obj).Index && Name == ((Component) obj).Name);
64 | }
65 | return false;
66 | }
67 | public override int GetHashCode ()
68 | {
69 | return Owner.GetHashCode () + Index + Name.GetHashCode ();
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Models/EnumMember.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace UnityInspector.GUI.Models
8 | {
9 | class EnumMember
10 | {
11 | private string _value;
12 |
13 | public ObjectMember objectMember;
14 | public string[] Values { get; }
15 | public string Value {
16 | get
17 | {
18 | return _value;
19 | }
20 | set
21 | {
22 | _value = value;
23 | if (objectMember != null)
24 | objectMember.UpdateValue ();
25 | }
26 | }
27 |
28 | public EnumMember (string[] values, string value)
29 | {
30 | Values = values;
31 | _value = value;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Models/GameObject.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using UnityInspector.Communicator;
8 |
9 | namespace UnityInspector.GUI.Models
10 | {
11 | public class GameObject
12 | {
13 | private bool _active;
14 | public int Address { get; }
15 | public string Name { get; }
16 | public bool Active
17 | {
18 | get { return _active; }
19 | set
20 | {
21 | _active = value;
22 | CommunicatorClient.Instance.InvokeMethod (this, "SetActive", new object[] { _active });
23 | }
24 | }
25 | public GameObject[] Children
26 | {
27 | get
28 | {
29 | if (CommunicatorClient.Instance == null || !CommunicatorClient.Instance.Connected)
30 | return new GameObject[0];
31 | if (Address == 0)
32 | return CommunicatorClient.Instance.ReadHierarchy ();
33 | return CommunicatorClient.Instance.GetChildren (this);
34 | }
35 | }
36 |
37 | public Component[] Components
38 | {
39 | get
40 | {
41 | if (Address == 0 || CommunicatorClient.Instance == null || !CommunicatorClient.Instance.Connected)
42 | return new Component[0];
43 | return CommunicatorClient.Instance.GetComponents (this);
44 | }
45 | }
46 |
47 | public GameObject (int address, string name, bool active)
48 | {
49 | Address = address;
50 | Name = name;
51 | _active = active;
52 | }
53 |
54 | public override string ToString ()
55 | {
56 | return Name;
57 | }
58 | public override bool Equals (object obj)
59 | {
60 | if (obj is GameObject && obj != null)
61 | return Address == ((GameObject) obj).Address;
62 | return false;
63 | }
64 | public override int GetHashCode ()
65 | {
66 | return Address;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Models/MonoProcess.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace UnityInspector.GUI.ViewModels
4 | {
5 | public class MonoProcess
6 | {
7 | public IntPtr MonoModule { get; set; }
8 |
9 | public string Name { get; set; }
10 |
11 | public int Id { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/UnityInspector.GUI/Models/ObjectMember.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using UnityInspector.Communicator;
7 |
8 | namespace UnityInspector.GUI.Models
9 | {
10 | public class ObjectMember
11 | {
12 | private object _value;
13 |
14 | public Component Owner { get; }
15 | public ObjectMember Parent { get; }
16 | public CommunicatorClient.Types Type { get; }
17 | public string Name { get; }
18 | public object Value {
19 | get {
20 | return _value;
21 | }
22 | set
23 | {
24 | object tempValue;
25 | if (value is string)
26 | tempValue = FromString ((string) value);
27 | else
28 | tempValue = value;
29 | if (!_value.Equals(tempValue) && tempValue != null)
30 | {
31 | _value = tempValue;
32 | UpdateValue ();
33 | }
34 | }
35 | }
36 |
37 | public ObjectMember (Component owner, Communicator.Types type, string name, object value)
38 | {
39 | Owner = owner;
40 | Type = type;
41 | Name = name;
42 | _value = value;
43 | if (_value is Vector3)
44 | ((Vector3) _value).objectMember = this;
45 | else if (_value is EnumMember)
46 | ((EnumMember) _value).objectMember = this;
47 | else if (_value is Vector2)
48 | ((Vector2) _value).objectMember = this;
49 | else if (_value is Rect)
50 | ((Rect) _value).objectMember = this;
51 | else if (_value is ArrayMember)
52 | ((ArrayMember) _value).objectMember = this;
53 | }
54 | public ObjectMember (ObjectMember parent, Communicator.Types type, string name, object value) : this (parent.Owner, type, name, value)
55 | {
56 | Parent = parent;
57 | }
58 |
59 | public object FromString (string value)
60 | {
61 | double valueDouble = 0;
62 | if (Type > CommunicatorClient.Types.Bool && Type < CommunicatorClient.Types.String)
63 | {
64 | bool isDouble = double.TryParse (value, out valueDouble);
65 | if (!isDouble)
66 | return null;
67 | }
68 |
69 | switch (Type)
70 | {
71 | case CommunicatorClient.Types.Bool:
72 | return value == "True";
73 | case CommunicatorClient.Types.Byte:
74 | return (byte) valueDouble;
75 | case CommunicatorClient.Types.Short:
76 | return (short) valueDouble;
77 | case CommunicatorClient.Types.Int:
78 | return (int) valueDouble;
79 | case CommunicatorClient.Types.Long:
80 | return (long) valueDouble;
81 | case CommunicatorClient.Types.Float:
82 | return (float) valueDouble;
83 | case CommunicatorClient.Types.Double:
84 | return (double) valueDouble;
85 | case CommunicatorClient.Types.String:
86 | return value;
87 | case CommunicatorClient.Types.GameObject:
88 | return null;
89 | }
90 |
91 | return null;
92 | }
93 | public void UpdateValue ()
94 | {
95 | CommunicatorClient.Instance.SetValueOfMember (Owner.Owner, GetPath (), _value);
96 | }
97 | public object GetValue ()
98 | {
99 | return CommunicatorClient.Instance.GetValueOfMember (Owner.Owner, GetPath (), _value);
100 | }
101 | public string GetPath()
102 | {
103 | return (Parent == null) ? Owner.Index + ":" + Name : Parent.GetPath() + "." + Name;
104 | }
105 |
106 | public override bool Equals (object obj)
107 | {
108 | if (obj != null && obj is ObjectMember)
109 | return (Owner.Equals (((ObjectMember) obj).Owner) && Name == ((ObjectMember) obj).Name);
110 | return false;
111 | }
112 | public override int GetHashCode ()
113 | {
114 | return Owner.GetHashCode () + Name.GetHashCode();
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Models/Rect.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace UnityInspector.GUI.Models
8 | {
9 | class Rect
10 | {
11 | public ObjectMember objectMember;
12 | private float _x, _y, _w, _h;
13 | public float X
14 | {
15 | get
16 | {
17 | return _x;
18 | }
19 | set
20 | {
21 | _x = value;
22 | if (objectMember != null) objectMember.UpdateValue ();
23 | }
24 | }
25 | public float Y
26 | {
27 | get
28 | {
29 | return _y;
30 | }
31 | set
32 | {
33 | _y = value;
34 | if (objectMember != null) objectMember.UpdateValue ();
35 | }
36 | }
37 | public float W
38 | {
39 | get
40 | {
41 | return _w;
42 | }
43 | set
44 | {
45 | _w = value;
46 | if (objectMember != null) objectMember.UpdateValue ();
47 | }
48 | }
49 | public float H
50 | {
51 | get
52 | {
53 | return _h;
54 | }
55 | set
56 | {
57 | _h = value;
58 | if (objectMember != null) objectMember.UpdateValue ();
59 | }
60 | }
61 |
62 |
63 | public Rect (float x, float y, float w, float h)
64 | {
65 | X = x;
66 | Y = y;
67 | W = w;
68 | H = h;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Models/Vector2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace UnityInspector.GUI.Models
8 | {
9 | class Vector2
10 | {
11 | public ObjectMember objectMember;
12 | private float _x, _y;
13 | public float X
14 | {
15 | get
16 | {
17 | return _x;
18 | }
19 | set
20 | {
21 | _x = value;
22 | if (objectMember != null) objectMember.UpdateValue ();
23 | }
24 | }
25 | public float Y
26 | {
27 | get
28 | {
29 | return _y;
30 | }
31 | set
32 | {
33 | _y = value;
34 | if (objectMember != null) objectMember.UpdateValue ();
35 | }
36 | }
37 |
38 |
39 | public Vector2 (float x, float y)
40 | {
41 | X = x;
42 | Y = y;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Models/Vector3.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace UnityInspector.GUI.Models
8 | {
9 | class Vector3
10 | {
11 | public ObjectMember objectMember;
12 | private float _x, _y, _z;
13 | public float X
14 | {
15 | get
16 | {
17 | return _x;
18 | }
19 | set
20 | {
21 | _x = value;
22 | if (objectMember != null) objectMember.UpdateValue ();
23 | }
24 | }
25 | public float Y
26 | {
27 | get
28 | {
29 | return _y;
30 | }
31 | set
32 | {
33 | _y = value;
34 | if (objectMember != null) objectMember.UpdateValue ();
35 | }
36 | }
37 | public float Z
38 | {
39 | get
40 | {
41 | return _z;
42 | }
43 | set
44 | {
45 | _z = value;
46 | if (objectMember != null) objectMember.UpdateValue ();
47 | }
48 | }
49 |
50 |
51 | public Vector3 (float x, float y, float z)
52 | {
53 | X = x;
54 | Y = y;
55 | Z = z;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle ("UnityInspector.GUI")]
11 | [assembly: AssemblyDescription ("A tool for inspecting and editing managed Unity3d game in real-time")]
12 | [assembly: AssemblyConfiguration ("")]
13 | [assembly: AssemblyCompany ("Mohelm97")]
14 | [assembly: AssemblyProduct ("UnityInspector")]
15 | [assembly: AssemblyCopyright ("Copyright (c) 2019 Mohammed ALMadhoun")]
16 | [assembly: AssemblyTrademark ("")]
17 | [assembly: AssemblyCulture ("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible (false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo (
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion ("1.0.0.0")]
55 | [assembly: AssemblyFileVersion ("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace UnityInspector.GUI.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UnityInspector.GUI.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace UnityInspector.GUI.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/UnityInspector.GUI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {71B1ADBF-8B1E-4E92-B81E-0F3E512BE93D}
8 | WinExe
9 | UnityInspector.GUI
10 | UnityInspector
11 | v4.6.1
12 | 512
13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 4
15 | true
16 | true
17 | false
18 | publish\
19 | true
20 | Disk
21 | false
22 | Foreground
23 | 7
24 | Days
25 | false
26 | false
27 | true
28 | 0
29 | 1.0.0.%2a
30 | false
31 | true
32 |
33 |
34 | AnyCPU
35 | true
36 | full
37 | false
38 | bin\Debug\
39 | DEBUG;TRACE
40 | prompt
41 | 4
42 | false
43 |
44 |
45 | AnyCPU
46 | pdbonly
47 | true
48 | bin\Release\
49 | TRACE
50 | prompt
51 | 4
52 | false
53 | true
54 |
55 |
56 | icon.ico
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 4.0
69 |
70 |
71 |
72 |
73 |
74 | ..\packages\Extended.Wpf.Toolkit.3.5.0\lib\net40\Xceed.Wpf.Toolkit.dll
75 |
76 |
77 |
78 |
79 | MSBuild:Compile
80 | Designer
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | MSBuild:Compile
105 | Designer
106 |
107 |
108 | App.xaml
109 | Code
110 |
111 |
112 | MainWindow.xaml
113 | Code
114 |
115 |
116 | Designer
117 | MSBuild:Compile
118 |
119 |
120 |
121 |
122 |
123 |
124 | Code
125 |
126 |
127 | True
128 | True
129 | Resources.resx
130 |
131 |
132 | True
133 | Settings.settings
134 | True
135 |
136 |
137 | ResXFileCodeGenerator
138 | Resources.Designer.cs
139 |
140 |
141 |
142 | SettingsSingleFileGenerator
143 | Settings.Designer.cs
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | {500155a7-3c29-4944-9031-6994b56e1797}
152 | SharpMonoInjector
153 |
154 |
155 | {af3b27c3-615e-46dd-80b0-76c6e106c64d}
156 | UnityInspector.Communicator
157 |
158 |
159 |
160 |
161 | False
162 | Microsoft .NET Framework 4.6.1 %28x86 and x64%29
163 | true
164 |
165 |
166 | False
167 | .NET Framework 3.5 SP1
168 | false
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/UnityStyle.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
121 |
122 |
166 |
167 |
168 |
206 |
207 |
213 |
214 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
260 |
261 |
313 |
314 |
315 |
339 |
340 |
341 |
344 |
345 |
346 |
361 |
362 |
363 |
382 |
383 |
384 |
433 |
434 |
435 |
467 |
468 |
469 |
470 |
499 |
500 |
510 |
511 |
549 |
550 |
551 |
552 |
567 |
568 |
590 |
591 |
594 |
666 |
667 |
668 |
669 |
694 |
695 |
712 |
713 |
714 |
800 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/Utils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace UnityInspector.GUI
9 | {
10 | class Utils
11 | {
12 | public static void SyncObservableCollection (ObservableCollection collection, T[] array)
13 | {
14 | for (int i = 0; i < array.Length; i++)
15 | {
16 | int oldIndex = collection.IndexOf (array[i]);
17 | if (oldIndex == -1)
18 | {
19 | collection.Insert (i, array[i]);
20 | }
21 | else
22 | {
23 | T oldChild = collection[oldIndex];
24 | if (oldIndex != i)
25 | collection.Move (oldIndex, i);
26 | }
27 | }
28 | // If we move all object to the correct order,
29 | // and add the new one in the correct position
30 | // then the deleted one will be after children.length
31 | int collectionCount = collection.Count;
32 | for (int i = array.Length; i < collectionCount; i++)
33 | {
34 | collection.RemoveAt (array.Length);
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/ViewModels/ArrayMemberViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using UnityInspector.GUI.Models;
9 |
10 | namespace UnityInspector.GUI.ViewModels
11 | {
12 | public class ArrayMemberViewModel : ViewModel
13 | {
14 | public ArrayMember arrayMember;
15 | public int Length { get { return arrayMember.Length; } }
16 | public CommunicatorClient.Types BaseType { get { return arrayMember.BaseType; } }
17 | public bool IsExpanded { get; set; }
18 | public string StringType { get { return arrayMember.BaseType + "[" + arrayMember.Length + "]"; } }
19 | private ObservableCollection _values;
20 |
21 | public object Values
22 | {
23 | get
24 | {
25 | UpdateValues ();
26 | return _values;
27 | }
28 | }
29 |
30 | public ArrayMemberViewModel (ArrayMember arrayMember)
31 | {
32 | this.arrayMember = arrayMember;
33 | }
34 | public void Update (ArrayMember arrayMember)
35 | {
36 | this.arrayMember = arrayMember;
37 | RaisePropertyChanged("StringType");
38 | if (IsExpanded)
39 | RaisePropertyChanged("Values");
40 | }
41 | private void UpdateValues ()
42 | {
43 | ObjectMemberViewModel[] values = Array.ConvertAll (arrayMember.Values, e => new ObjectMemberViewModel (e));
44 | if (_values == null)
45 | {
46 | _values = new ObservableCollection (values);
47 | }
48 | else
49 | {
50 | Utils.SyncObservableCollection (_values, values);
51 | for (int i = 0; i < _values.Count; i++)
52 | _values[i].Update (values[i].objectMember);
53 | }
54 | }
55 |
56 | public override bool Equals(object obj)
57 | {
58 | if (obj != null && obj is ArrayMemberViewModel)
59 | return (arrayMember.Equals(((ArrayMemberViewModel)obj).arrayMember));
60 |
61 | return false;
62 | }
63 | public override int GetHashCode()
64 | {
65 | return arrayMember.GetHashCode();
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/ViewModels/ComponentViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows;
9 | using UnityInspector.GUI.Models;
10 | using Component = UnityInspector.GUI.Models.Component;
11 |
12 | namespace UnityInspector.GUI.ViewModels
13 | {
14 | public class ComponentViewModel : ViewModel
15 | {
16 | public Component component;
17 | public string Name { get { return component.Name; } }
18 | public bool IsBehaviour { get { return component.IsBehaviour; } }
19 | public Visibility Visibility { get { return IsBehaviour ? Visibility.Visible : Visibility.Hidden; } }
20 | public bool Enabled { get { return component.Enabled; } set { component.Enabled = value; } }
21 | public bool IsSelected { get; set; }
22 |
23 | private ObservableCollection _members;
24 | public ObservableCollection Members {
25 | get
26 | {
27 | UpdateMembers ();
28 | return _members;
29 | }
30 | }
31 |
32 | public ComponentViewModel (Component component)
33 | {
34 | this.component = component;
35 | }
36 |
37 | public void Update (Component newComponent)
38 | {
39 | component = newComponent;
40 | RaisePropertyChanged ("Enabled");
41 | RaisePropertyChanged ("Members");
42 | }
43 |
44 | private void UpdateMembers ()
45 | {
46 | ObjectMemberViewModel[] members = Array.ConvertAll (component.Members, e => new ObjectMemberViewModel (e));
47 | if (_members == null)
48 | {
49 | _members = new ObservableCollection (members);
50 | }
51 | else
52 | {
53 | Utils.SyncObservableCollection (_members, members);
54 | for (int i = 0; i < _members.Count; i++)
55 | _members[i].Update (members[i].objectMember);
56 | }
57 | }
58 |
59 | public override bool Equals (object obj)
60 | {
61 | if (obj != null && obj is ComponentViewModel)
62 | return (component.Equals (((ComponentViewModel) obj).component));
63 |
64 | return false;
65 | }
66 | public override int GetHashCode ()
67 | {
68 | return component.GetHashCode ();
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/ViewModels/GameObjectViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows;
9 | using UnityInspector.GUI.Models;
10 | using Component = UnityInspector.GUI.Models.Component;
11 |
12 | namespace UnityInspector.GUI.ViewModels
13 | {
14 | public class GameObjectViewModel : ViewModel
15 | {
16 | public GameObject gameObject;
17 | public string Name { get { return gameObject.Name; } }
18 | public bool Active { get { return gameObject.Active; }
19 | set
20 | {
21 | gameObject.Active = value;
22 | RaisePropertyChanged ("Active");
23 | }
24 | }
25 | public bool IsExpanded { get; set; }
26 | public bool IsSelected { get; set; }
27 |
28 | private ObservableCollection _children;
29 | public ObservableCollection Children
30 | {
31 | get
32 | {
33 | UpdateChildren ();
34 | return _children;
35 | }
36 | }
37 |
38 | private ObservableCollection _components;
39 | public ObservableCollection Components
40 | {
41 | get
42 | {
43 | UpdateComponents ();
44 | return _components;
45 | }
46 | }
47 |
48 | public GameObjectViewModel (GameObject gameObject)
49 | {
50 | this.gameObject = gameObject;
51 | }
52 |
53 | void UpdateChildren ()
54 | {
55 | GameObjectViewModel[] children = Array.ConvertAll (gameObject.Children, e => new GameObjectViewModel (e));
56 | if (_children == null)
57 | {
58 | _children = new ObservableCollection (children);
59 | }
60 | else
61 | {
62 | Utils.SyncObservableCollection (_children, children);
63 | for (int i = 0; i < _children.Count; i++)
64 | _children[i].Update (children[i].gameObject, IsExpanded);
65 | }
66 | }
67 | void UpdateComponents ()
68 | {
69 | ComponentViewModel[] components = Array.ConvertAll (gameObject.Components, e => new ComponentViewModel (e));
70 | if (_components == null)
71 | {
72 | _components = new ObservableCollection (components);
73 | }
74 | else
75 | {
76 | Utils.SyncObservableCollection (_components, components);
77 | for (int i = 0; i < _components.Count; i++)
78 | _components[i].Update (components[i].component);
79 | }
80 | }
81 |
82 | public void Update (GameObject newGameObject, bool parentIsExpanded)
83 | {
84 | if (newGameObject != null)
85 | gameObject = newGameObject;
86 | RaisePropertyChanged ("Name");
87 | RaisePropertyChanged ("Active");
88 | if (parentIsExpanded)
89 | RaisePropertyChanged ("Children");
90 | RaisePropertyChanged ("Components");
91 | }
92 | public void Update ()
93 | {
94 | Update (null, true);
95 | }
96 |
97 | public override bool Equals (object obj)
98 | {
99 | if (obj != null && obj is GameObjectViewModel)
100 | return (gameObject.Equals(((GameObjectViewModel) obj).gameObject));
101 |
102 | return false;
103 | }
104 | public override int GetHashCode ()
105 | {
106 | return gameObject.GetHashCode ();
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/ViewModels/MainWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | using SharpMonoInjector;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Collections.ObjectModel;
5 | using System.ComponentModel;
6 | using System.Diagnostics;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Runtime.CompilerServices;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 | using System.Windows;
13 | using System.Windows.Input;
14 | using System.Windows.Threading;
15 | using UnityInspector.GUI.Models;
16 |
17 | namespace UnityInspector.GUI.ViewModels
18 | {
19 | class MainWindowViewModel : ViewModel
20 | {
21 | private MonoProcess _selectedProcess;
22 | CommunicatorClient communicator;
23 |
24 | public ICollection Processes { get; set; }
25 | public ICommand RefreshCommand { get; set; }
26 | public ICommand InjectCommand { get; set; }
27 | public ICommand DisconnectCommand { get; set; }
28 | public ICommand RefreshTreeCommand { get; set; }
29 | private int _refreshRate;
30 | public int RefreshRate {
31 | get {
32 | return _refreshRate;
33 | }
34 | set {
35 | _refreshRate = value;
36 | if (refreshTimer == null && _refreshRate != 0)
37 | {
38 | refreshTimer = new DispatcherTimer (new TimeSpan (10000000 / _refreshRate), DispatcherPriority.Background, delegate
39 | {
40 | RefreshTree (null);
41 | }, Application.Current.MainWindow.Dispatcher);
42 | }
43 | else if (_refreshRate != 0)
44 | {
45 | refreshTimer.Interval = new TimeSpan (10000000 / _refreshRate);
46 | if (!refreshTimer.IsEnabled) refreshTimer.Start ();
47 | }
48 | else if (refreshTimer != null && _refreshRate == 0)
49 | {
50 | refreshTimer.Stop ();
51 | }
52 | }
53 | }
54 | private DispatcherTimer refreshTimer;
55 | public GameObjectViewModel MainSceneGameObject { get; set; }
56 |
57 | private string _status;
58 | public string Status { get { return _status; } set
59 | {
60 | _status = value;
61 | RaisePropertyChanged("Status");
62 | }
63 | }
64 | public bool IsConnected { get { return CommunicatorClient.Instance != null && CommunicatorClient.Instance.Connected; } }
65 | public MonoProcess SelectedProcess
66 | {
67 | get { return _selectedProcess; }
68 | set
69 | {
70 | _selectedProcess = value;
71 | RaisePropertyChanged ("SelectedProcess");
72 | }
73 | }
74 |
75 | public MainWindowViewModel () {
76 | RefreshCommand = new RelayCommand (GetProcesses);
77 | InjectCommand = new RelayCommand (Inject);
78 | DisconnectCommand = new RelayCommand (Disconnect);
79 | RefreshTreeCommand = new RelayCommand (RefreshTree);
80 | MainSceneGameObject = new GameObjectViewModel (new GameObject (0, "MainScene", true));
81 | MainSceneGameObject.IsExpanded = true;
82 | Status = "Idle";
83 | }
84 | private void RefreshTree (object parameter)
85 | {
86 | MainSceneGameObject.Update (null, true);
87 | }
88 | private async void Inject (object parameter)
89 | {
90 | Status = "Injecting";
91 | await Task.Run (() =>
92 | {
93 | Status = "Getting access";
94 | IntPtr handle = Native.OpenProcess (ProcessAccessRights.PROCESS_ALL_ACCESS, false, SelectedProcess.Id);
95 | string AssemblyPath = @"UnityInspector.Injector.dll";
96 | if (handle == IntPtr.Zero)
97 | {
98 | Status = "Access denied";
99 | return;
100 | }
101 |
102 | byte[] file;
103 |
104 | try
105 | {
106 | Status = "Reading UnityInspector.Injector.dll";
107 | file = File.ReadAllBytes (AssemblyPath);
108 | }
109 | catch (IOException)
110 | {
111 | Status = "Can't read UnityInspector.Injector.dll";
112 | return;
113 | }
114 |
115 | using (Injector injector = new Injector (handle, SelectedProcess.MonoModule))
116 | {
117 | try
118 | {
119 | Status = "Injecting";
120 | IntPtr asm = injector.Inject (file, "UnityInspectorInjector", "Injector", "OnLoad");
121 | }
122 | catch (InjectorException ie)
123 | {
124 | Status = ie.Message;
125 | return;
126 | }
127 | catch (Exception e)
128 | {
129 | Status = e.Message;
130 | return;
131 | }
132 | }
133 | StartCommunicator ();
134 | });
135 | }
136 |
137 | private void StartCommunicator ()
138 | {
139 | Status = "Starting Communicator";
140 | communicator = new CommunicatorClient();
141 | Status = "Connected";
142 | RaisePropertyChanged ("IsConnected");
143 | MainSceneGameObject.Update ();
144 | }
145 | private void Disconnect (object parameter)
146 | {
147 | RefreshRate = 0;
148 | if (communicator != null)
149 | communicator.Close ();
150 | RaisePropertyChanged("IsConnected");
151 | RefreshTree (null);
152 | Status = "Idel";
153 | }
154 | private async void GetProcesses (object parameter)
155 | {
156 | Collection processes = new Collection ();
157 | string prevStatus = Status;
158 | await Task.Run (() =>
159 | {
160 | Status = "Getting Processes";
161 | int cp = Process.GetCurrentProcess ().Id;
162 | foreach (Process p in Process.GetProcesses ())
163 | {
164 | if (p.Id == cp || p.MainWindowHandle == IntPtr.Zero)
165 | continue;
166 | const ProcessAccessRights flags = ProcessAccessRights.PROCESS_QUERY_INFORMATION | ProcessAccessRights.PROCESS_VM_READ;
167 | IntPtr handle;
168 |
169 | if ((handle = Native.OpenProcess (flags, false, p.Id)) != IntPtr.Zero)
170 | {
171 | if (ProcessUtils.GetMonoModule (handle, out IntPtr mono))
172 | {
173 | processes.Add (new MonoProcess
174 | {
175 | MonoModule = mono,
176 | Id = p.Id,
177 | Name = p.ProcessName
178 | });
179 | }
180 |
181 | Native.CloseHandle (handle);
182 | }
183 | }
184 | });
185 | Processes = processes;
186 | RaisePropertyChanged ("Processes");
187 | Status = prevStatus;
188 | if (Processes.Count > 0)
189 | {
190 | SelectedProcess = Processes.First();
191 | }
192 |
193 | }
194 |
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/ViewModels/NumericTextBox.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.RegularExpressions;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using System.Windows.Data;
10 | using System.Windows.Input;
11 |
12 | namespace UnityInspector.GUI.ViewModels
13 | {
14 | public class NumericTextBox : TextBox
15 | {
16 | bool isControlling;
17 | Point lastMousePosition;
18 | public double doubleValue;
19 | BindingExpression be;
20 | public NumericTextBox () : base ()
21 | {
22 | Cursor = Cursors.SizeWE;
23 |
24 | }
25 |
26 | protected override void OnInitialized(EventArgs e)
27 | {
28 | base.OnInitialized(e);
29 | be = GetBindingExpression(TextProperty);
30 | }
31 |
32 | protected override void OnMouseMove (MouseEventArgs e)
33 | {
34 | if (isControlling)
35 | {
36 | Point currentMousePosition = e.GetPosition (this);
37 | float deltaMove = (float) (currentMousePosition.X - lastMousePosition.X) / 10;
38 | doubleValue += deltaMove;
39 | Text = doubleValue.ToString ();
40 | be.UpdateSource();
41 | lastMousePosition = currentMousePosition;
42 | }
43 | }
44 | protected override void OnMouseUp (MouseButtonEventArgs e)
45 | {
46 | base.OnMouseUp (e);
47 | isControlling = false;
48 | }
49 | protected override void OnTextInput(TextCompositionEventArgs e)
50 | {
51 | base.OnTextInput(e);
52 | if (Text[Text.Length - 1] != '.')
53 | be.UpdateSource();
54 | }
55 |
56 | protected override void OnMouseDown (MouseButtonEventArgs e)
57 | {
58 | base.OnMouseDown (e);
59 | doubleValue = 0;
60 | double.TryParse (Text, out doubleValue);
61 | isControlling = true;
62 | lastMousePosition = e.GetPosition (this);
63 | }
64 |
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/ViewModels/ObjectMemberViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using UnityInspector.GUI.Models;
8 |
9 | namespace UnityInspector.GUI.ViewModels
10 | {
11 | public class ObjectMemberViewModel : ViewModel
12 | {
13 | public ObjectMember objectMember;
14 | public CommunicatorClient.Types Type { get { return objectMember.Type; } }
15 | public string Name { get { return objectMember.Name; } }
16 | private object _value;
17 | public object Value { get { return _value; } set { objectMember.Value = value; _value = objectMember.Value; } }
18 |
19 | public ObjectMemberViewModel (ObjectMember objectMember)
20 | {
21 | this.objectMember = objectMember;
22 | _value = objectMember.Value;
23 | if (_value is ArrayMember arrayMember)
24 | {
25 | _value = new ArrayMemberViewModel (arrayMember);
26 | }
27 | }
28 |
29 | public void Update (ObjectMember newObjectMember)
30 | {
31 | objectMember = newObjectMember;
32 | if (_value is ArrayMemberViewModel valueViewModel)
33 | {
34 | valueViewModel.Update ((ArrayMember) objectMember.Value);
35 | }
36 | else
37 | {
38 | _value = objectMember.Value;
39 | RaisePropertyChanged("Value");
40 | }
41 | }
42 |
43 | public override bool Equals (object obj)
44 | {
45 | if (obj != null && obj is ObjectMemberViewModel)
46 | return (objectMember.Equals (((ObjectMemberViewModel) obj).objectMember));
47 |
48 | return false;
49 | }
50 | public override int GetHashCode ()
51 | {
52 | return objectMember.GetHashCode ();
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/ViewModels/RelayCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows.Input;
7 |
8 | namespace UnityInspector.GUI.ViewModels
9 | {
10 | public class RelayCommand : ICommand
11 | {
12 | public event EventHandler CanExecuteChanged;
13 |
14 | private readonly Action _execute;
15 |
16 | private readonly Func _canExecute;
17 |
18 | public RelayCommand (Action execute, Func canExecute = null)
19 | {
20 | _execute = execute;
21 | _canExecute = canExecute;
22 | }
23 |
24 | public bool CanExecute (object parameter) => _canExecute == null || _canExecute (parameter);
25 |
26 | public void Execute (object parameter) => _execute (parameter);
27 |
28 | public void RaiseCanExecuteChanged () => CanExecuteChanged?.Invoke (this, EventArgs.Empty);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/ViewModels/ViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace UnityInspector.GUI.ViewModels
9 | {
10 | public abstract class ViewModel : INotifyPropertyChanged
11 | {
12 | public event PropertyChangedEventHandler PropertyChanged;
13 | protected void RaisePropertyChanged(string name)
14 | {
15 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/UnityInspector.GUI/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mohelm97/UnityInspector/5c1ebebd568e9c573f95423f85047c34421ee9a0/UnityInspector.GUI/icon.ico
--------------------------------------------------------------------------------
/UnityInspector.GUI/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/UnityInspector.Injector/CommunicatorServer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Text;
6 | using UnityEngine;
7 | using UnityInspector.Communicator;
8 |
9 | namespace UnityInspectorInjector
10 | {
11 | class CommunicatorServer : Communicator
12 | {
13 | public new static CommunicatorServer Instance { get; set; }
14 | private Dictionary refDict = new Dictionary ();
15 |
16 | public CommunicatorServer() : base (CommunicatorType.Server)
17 | {
18 | Instance = this;
19 | }
20 | public override void Close()
21 | {
22 | Instance = null;
23 | server.Stop();
24 | client.Close();
25 | }
26 |
27 | public override GameObject ReadGameObject ()
28 | {
29 | throw new NotImplementedException ();
30 | }
31 | public override GameObject ReadGameObjectByRef ()
32 | {
33 | int gameObjectRef = ReadInt ();
34 | return refDict[gameObjectRef];
35 | }
36 |
37 | public override void WriteGameObject (GameObject obj)
38 | {
39 | // The instance id of an object is always guaranteed to be unique.
40 | int gameObjectRef = obj.GetInstanceID ();
41 | if (!refDict.ContainsKey(gameObjectRef))
42 | refDict.Add (gameObjectRef, obj);
43 |
44 | WriteInt (gameObjectRef);
45 | WriteString (obj.name);
46 | WriteBool (obj.activeSelf);
47 | }
48 | public void WriteGameObject (Transform obj)
49 | {
50 | WriteGameObject (obj.gameObject);
51 | }
52 | public override void WriteGameObjectRef (GameObject obj)
53 | {
54 | WriteInt (obj.GetInstanceID ());
55 | }
56 |
57 | public override void WriteVector3 (object value)
58 | {
59 | Vector3 vector3 = (Vector3) value;
60 | WriteFloat (vector3.x);
61 | WriteFloat (vector3.y);
62 | WriteFloat (vector3.z);
63 | }
64 | public override object ReadVector3 ()
65 | {
66 | return new Vector3 (ReadFloat (), ReadFloat (), ReadFloat ());
67 | }
68 |
69 | public override void WriteVector2 (object value)
70 | {
71 | Vector2 vector2 = (Vector2) value;
72 | WriteFloat (vector2.x);
73 | WriteFloat (vector2.y);
74 | }
75 | public override object ReadVector2 ()
76 | {
77 | return new Vector2 (ReadFloat (), ReadFloat ());
78 | }
79 |
80 | public override void WriteRect (object value)
81 | {
82 | Rect rect = (Rect) value;
83 | WriteFloat (rect.x);
84 | WriteFloat (rect.y);
85 | WriteFloat (rect.width);
86 | WriteFloat (rect.height);
87 | }
88 | public override object ReadRect ()
89 | {
90 | return new Rect (ReadFloat (), ReadFloat (), ReadFloat (), ReadFloat ());
91 | }
92 |
93 | public override void WriteEnum (object value)
94 | {
95 | Array enumType = Enum.GetValues (value.GetType ());
96 | WriteByte ((byte) enumType.Length);
97 | for (int i = 0; i < enumType.Length; i++)
98 | WriteString (enumType.GetValue (i).ToString ());
99 | WriteString (value.ToString ());
100 | }
101 | public override object ReadEnum ()
102 | {
103 | return new EnumWrapper(ReadString ());
104 | }
105 |
106 | public override void WriteColor (object value)
107 | {
108 | Color color = (Color) value;
109 | WriteByte ((byte) (color.r * 255));
110 | WriteByte ((byte) (color.g * 255));
111 | WriteByte ((byte) (color.b * 255));
112 | WriteByte ((byte) (color.a * 255));
113 | }
114 | public override object ReadColor ()
115 | {
116 | return new Color (ReadByte() / 255f, ReadByte () / 255f, ReadByte () / 255f, ReadByte () / 255f);
117 | }
118 |
119 | public override object ReadArrayAsMember ()
120 | {
121 | throw new NotImplementedException ();
122 | }
123 | }
124 | public class EnumWrapper
125 | {
126 | readonly string stringValue;
127 | public EnumWrapper (string value)
128 | {
129 | this.stringValue = value;
130 | }
131 | public object GetEnumValue (MemberInfo member)
132 | {
133 | Type enumType;
134 | if (member.MemberType == MemberTypes.Field)
135 | enumType = ((FieldInfo) member).FieldType;
136 | else
137 | enumType = ((PropertyInfo) member).PropertyType;
138 | return Enum.Parse (enumType, stringValue);
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/UnityInspector.Injector/Injector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 | using UnityEngine.SceneManagement;
4 | using System.Reflection;
5 | using System.Collections;
6 | using Commands = UnityInspectorInjector.CommunicatorServer.Commands;
7 | namespace UnityInspectorInjector
8 | {
9 | public class Injector
10 | {
11 | public static GameObject test;
12 | public static void OnLoad ()
13 | {
14 | if (test != null)
15 | MonoBehaviour.Destroy(test);
16 | if (CommunicatorServer.Instance != null)
17 | CommunicatorServer.Instance.Close();
18 | test = new GameObject ();
19 | MonoBehaviour.DontDestroyOnLoad (test);
20 |
21 | test.AddComponent ();
22 | Application.runInBackground = true;
23 | }
24 | }
25 | public class InjectorComp : MonoBehaviour
26 | {
27 | CommunicatorServer communicator;
28 | void Start ()
29 | {
30 | communicator = new CommunicatorServer();
31 | StartCoroutine (Poll ());
32 | }
33 | IEnumerator Poll ()
34 | {
35 | while (true)
36 | {
37 | yield return null;
38 | int commandsExeced = 0;
39 | while (communicator.DataAvailable () && commandsExeced < 10)
40 | {
41 | commandsExeced++;
42 | Commands command = (Commands) communicator.ReadByte ();
43 | ExecuteCommand (command);
44 | }
45 |
46 | }
47 | }
48 |
49 | void ExecuteCommand (Commands command)
50 | {
51 | if (command == Commands.Destroy)
52 | {
53 | communicator.Close();
54 | Destroy(gameObject);
55 | }
56 | else if (command == Commands.GetHierarchy)
57 | {
58 | // I don't use WriteArray in this method for speed prpouses
59 | GameObject[] gameObjects = SceneManager.GetActiveScene ().GetRootGameObjects ();
60 | communicator.WriteInt (gameObjects.Length);
61 | for (int i = 0; i < gameObjects.Length; i++)
62 | communicator.WriteGameObject (gameObjects[i]);
63 | }
64 | else if (command == Commands.GetChildren)
65 | {
66 | GameObject obj = communicator.ReadGameObjectByRef ();
67 | communicator.WriteInt (obj.transform.childCount);
68 | for (int i = 0; i < obj.transform.childCount; i++)
69 | communicator.WriteGameObject (obj.transform.GetChild (i));
70 | }
71 | else if (command == Commands.InvokeMethod)
72 | {
73 | object obj = ReadNestedRef (out string methodName);
74 |
75 | MethodInfo methodInfo = obj.GetType ().GetMethod (methodName);
76 | methodInfo.Invoke (obj, communicator.ReadObjects ());
77 | }
78 | else if (command == Commands.GetComponents)
79 | {
80 | GameObject obj = communicator.ReadGameObjectByRef ();
81 | Component[] comps = obj.GetComponents ();
82 | communicator.WriteInt (comps.Length);
83 | for (int i = 0; i < comps.Length; i++)
84 | {
85 | Type compType = comps[i].GetType ();
86 | communicator.WriteString (compType.Name);
87 | PropertyInfo enabledField = compType.GetProperty ("enabled");
88 | if (enabledField != null)
89 | communicator.WriteByte ((Byte) ((bool) enabledField.GetValue (comps[i], null) ? 1 : 2));
90 | else
91 | communicator.WriteByte (0);
92 | }
93 | }
94 | else if (command == Commands.GetComponentMembers)
95 | {
96 | GameObject obj = communicator.ReadGameObjectByRef ();
97 | int compIndex = communicator.ReadInt ();
98 | Component comp = obj.GetComponents ()[compIndex];
99 | Type compType = comp.GetType ();
100 | MemberInfo[] members = GetFilteredMembers (compType);
101 | communicator.WriteInt (members.Length);
102 | for (int i = 0; i < members.Length; i++)
103 | {
104 | MemberInfo member = members[i];
105 |
106 | object value;
107 | if (member.MemberType == MemberTypes.Field)
108 | value = ((FieldInfo) member).GetValue (comp);
109 | else
110 | value = ((PropertyInfo) member).GetValue (comp, null);
111 |
112 | if (value is Component)
113 | value = ((Component) value).gameObject;
114 |
115 | communicator.WriteByte ((byte) CommunicatorServer.GetObjectType (value));
116 | communicator.WriteString (member.Name);
117 | communicator.WriteObject (value, true);
118 |
119 | }
120 | }
121 | else if (command == Commands.GetValueOfMember)
122 | {
123 | object obj = ReadNestedRef (out string memberName);
124 |
125 | MemberInfo member = obj.GetType ().GetMember (memberName)[0];
126 | object value;
127 | if (memberName[0] != '[')
128 | {
129 | if (member.MemberType == MemberTypes.Field)
130 | value = ((FieldInfo)member).GetValue(obj);
131 | else
132 | value = ((PropertyInfo)member).GetValue(obj, null);
133 | }
134 | else
135 | {
136 | int index = int.Parse(memberName.Substring(1, memberName.Length - 2));
137 | value = ((Array)obj).GetValue(index);
138 | }
139 | if (value is Component)
140 | value = ((Component)value).gameObject;
141 | CommunicatorServer.Instance.WriteObject (value);
142 | }
143 | else if (command == Commands.SetValueOfMember)
144 | {
145 | object obj = ReadNestedRef (out string memberName);
146 | object value = communicator.ReadObject();
147 | //TODO: If value is gameobject but membertype is component we need to get the component of that gameobject
148 |
149 | if (memberName[0] != '[')
150 | {
151 | MemberInfo member = obj.GetType().GetMember(memberName)[0];
152 |
153 | if (value is EnumWrapper)
154 | value = ((EnumWrapper)value).GetEnumValue(member);
155 |
156 | if (value != null)
157 | {
158 | if (member.MemberType == MemberTypes.Field)
159 | ((FieldInfo)member).SetValue(obj, value);
160 | else
161 | ((PropertyInfo)member).SetValue(obj, value, null);
162 | }
163 | }
164 | else
165 | {
166 | int index = int.Parse(memberName.Substring(1, memberName.Length - 2));
167 | ((Array)obj).SetValue (value, index);
168 | }
169 |
170 | }
171 | }
172 |
173 | object ReadNestedRef (out string memberName)
174 | {
175 | object obj = communicator.ReadGameObjectByRef ();
176 | memberName = communicator.ReadString ();
177 | int colonLocation = memberName.IndexOf (':');
178 | if (colonLocation != -1)
179 | {
180 | int compIndex = int.Parse (memberName.Substring (0, colonLocation));
181 | obj = ((GameObject) obj).GetComponents ()[compIndex];
182 | memberName = memberName.Substring (colonLocation + 1);
183 | }
184 | string[] path = memberName.Split('.');
185 | for (int i=0; i < path.Length - 1; i++)
186 | {
187 | string point = path[i];
188 | if (point[0] != '[')
189 | {
190 | MemberInfo member = obj.GetType().GetMember(path[i])[0];
191 | if (member.MemberType == MemberTypes.Field)
192 | obj = ((FieldInfo)member).GetValue (obj);
193 | else
194 | obj = ((PropertyInfo)member).GetValue(obj, null);
195 | } else
196 | {
197 | //TODO
198 | }
199 | }
200 | memberName = path[path.Length - 1];
201 | return obj;
202 | }
203 | public MemberInfo[] GetFilteredMembers (Type type)
204 | {
205 | return Array.FindAll (type.GetMembers (), m =>
206 | {
207 | if (m.DeclaringType != type)
208 | return false;
209 | if (m.MemberType == MemberTypes.Property && !((PropertyInfo) m).CanWrite)
210 | return false;
211 | if (m.MemberType != MemberTypes.Field && m.MemberType != MemberTypes.Property)
212 | return false;
213 | return true;
214 | });
215 | }
216 |
217 | private void OnDestroy()
218 | {
219 | communicator.Close();
220 | }
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/UnityInspector.Injector/UnityInspector.Injector.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {4477ADED-191B-4D36-B4A4-C44F1E74292B}
8 | Library
9 | Properties
10 | UnityInspectorInjector
11 | UnityInspector.Injector
12 | v3.5
13 | 512
14 | true
15 | Unity Subset v3.5
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 | true
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | False
43 | ..\ExternalDlls\UnityEngine.dll
44 |
45 |
46 |
47 |
48 | Communicator.cs
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | xcopy "$(TargetDir)$(TargetFileName)" "$(SolutionDir)\UnityInspector.GUI\$(OutDir)" /Y
67 |
68 |
--------------------------------------------------------------------------------
/UnityInspector.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28307.421
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityInspector.GUI", "UnityInspector.GUI\UnityInspector.GUI.csproj", "{71B1ADBF-8B1E-4E92-B81E-0F3E512BE93D}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityInspector.Injector", "UnityInspector.Injector\UnityInspector.Injector.csproj", "{4477ADED-191B-4D36-B4A4-C44F1E74292B}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityInspector.Communicator", "UnityInspector.Communicator\UnityInspector.Communicator.csproj", "{AF3B27C3-615E-46DD-80B0-76C6E106C64D}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpMonoInjector", "SharpMonoInjector\SharpMonoInjector.csproj", "{500155A7-3C29-4944-9031-6994B56E1797}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {71B1ADBF-8B1E-4E92-B81E-0F3E512BE93D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {71B1ADBF-8B1E-4E92-B81E-0F3E512BE93D}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {71B1ADBF-8B1E-4E92-B81E-0F3E512BE93D}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {71B1ADBF-8B1E-4E92-B81E-0F3E512BE93D}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {4477ADED-191B-4D36-B4A4-C44F1E74292B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {4477ADED-191B-4D36-B4A4-C44F1E74292B}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {4477ADED-191B-4D36-B4A4-C44F1E74292B}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {4477ADED-191B-4D36-B4A4-C44F1E74292B}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {AF3B27C3-615E-46DD-80B0-76C6E106C64D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {AF3B27C3-615E-46DD-80B0-76C6E106C64D}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {AF3B27C3-615E-46DD-80B0-76C6E106C64D}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {AF3B27C3-615E-46DD-80B0-76C6E106C64D}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {500155A7-3C29-4944-9031-6994B56E1797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {500155A7-3C29-4944-9031-6994B56E1797}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {500155A7-3C29-4944-9031-6994B56E1797}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {500155A7-3C29-4944-9031-6994B56E1797}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(ExtensibilityGlobals) = postSolution
41 | SolutionGuid = {8C06758D-F486-449B-A0B3-711AD966344F}
42 | EndGlobalSection
43 | EndGlobal
44 |
--------------------------------------------------------------------------------