/
MainWindow.Utilities.cs
880 lines (773 loc) · 35 KB
/
MainWindow.Utilities.cs
1
2
3
4
5
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.Foundation.Metadata;
using Windows.Storage;
using WinUIEx;
namespace RegistryPreview
{
public sealed partial class MainWindow : WindowEx
{
/// <summary>
/// Method that opens and processes the passed in file name; expected to be an absolute path and a first time open
/// </summary>
private bool OpenRegistryFile(string filename)
{
// clamp to prevent attempts to open a file larger than 10MB
try
{
long fileLength = new System.IO.FileInfo(filename).Length;
if (fileLength > 1048576)
{
ShowMessageBox(resourceLoader.GetString("LargeRegistryFileTitle"), App.AppFilename + resourceLoader.GetString("LargeRegistryFile"), resourceLoader.GetString("OkButtonText"));
ChangeCursor(gridPreview, false);
return false;
}
}
catch
{
// Do nothing here - a missing or invalid file will be caught below
}
// Disable parts of the UI that can cause trouble when loading
ChangeCursor(gridPreview, true);
textBox.Text = string.Empty;
// clear the treeView and dataGrid no matter what
treeView.RootNodes.Clear();
ClearTable();
// update the current window's title with the current filename
UpdateWindowTitle(filename);
// Load in the whole file in one call and plop it all into textBox
FileStream fileStream = null;
try
{
FileStreamOptions fileStreamOptions = new FileStreamOptions();
fileStreamOptions.Access = FileAccess.Read;
fileStreamOptions.Share = FileShare.ReadWrite;
fileStreamOptions.Mode = FileMode.Open;
fileStream = new FileStream(filename, fileStreamOptions);
StreamReader streamReader = new StreamReader(fileStream);
string filenameText = streamReader.ReadToEnd();
textBox.Text = filenameText;
streamReader.Close();
}
catch
{
// restore TextChanged handler to make for clean UI
textBox.TextChanged += TextBox_TextChanged;
// Reset the cursor but leave textBox disabled as no content got loaded
ChangeCursor(gridPreview, false);
return false;
}
finally
{
// clean up no matter what
if (fileStream != null)
{
fileStream.Dispose();
}
}
// now that the file is loaded and in textBox, parse the data
ParseRegistryFile(textBox.Text);
// Getting here means that the entire REG file was parsed without incident
// so select the root of the tree and celebrate
if (treeView.RootNodes.Count > 0)
{
treeView.SelectedNode = treeView.RootNodes[0];
treeView.Focus(FocusState.Programmatic);
}
// reset the cursor
ChangeCursor(gridPreview, false);
return true;
}
/// <summary>
/// Method that re-opens and processes the filename the app already knows about; expected to not be a first time open
/// </summary>
private void RefreshRegistryFile()
{
// Disable parts of the UI that can cause trouble when loading
ChangeCursor(gridPreview, true);
// Get the current selected node so we can return focus to an existing node
TreeViewNode currentNode = treeView.SelectedNode;
// clear the treeView and dataGrid no matter what
treeView.RootNodes.Clear();
ClearTable();
// the existing text is still in textBox so parse the data again
ParseRegistryFile(textBox.Text);
// check to see if there was a key in treeView before the refresh happened
if (currentNode != null)
{
// since there is a valid node, get the FullPath of the key that was selected
string selectedFullPath = ((RegistryKey)currentNode.Content).FullPath;
// check to see if we still have the key in the new Dictionary of keys
if (mapRegistryKeys.ContainsKey(selectedFullPath))
{
// we found it! select it in the tree and pretend it was selected
TreeViewNode treeViewNode;
mapRegistryKeys.TryGetValue(selectedFullPath, out treeViewNode);
treeView.SelectedNode = treeViewNode;
TreeView_ItemInvoked(treeView, null);
}
else
{
// we failed to find an existing node; it could have been deleted in the edit
if (treeView.RootNodes.Count > 0)
{
treeView.SelectedNode = treeView.RootNodes[0];
}
}
}
else
{
// no node was previously selected so check for a RootNode and select it
if (treeView.RootNodes.Count > 0)
{
treeView.SelectedNode = treeView.RootNodes[0];
}
}
// enable the UI
ChangeCursor(gridPreview, false);
}
/// <summary>
/// Parses the text that is passed in, which should be the same text that's in textBox
/// </summary>
private bool ParseRegistryFile(string filenameText)
{
// if this is a not-first open, clear out the Dictionary of nodes
if (mapRegistryKeys != null)
{
mapRegistryKeys.Clear();
mapRegistryKeys = null;
}
// set up a new dictionary
mapRegistryKeys = new Dictionary<string, TreeViewNode>(StringComparer.InvariantCultureIgnoreCase);
// As we'll be processing the text one line at a time, this string will be the current line
string registryLine;
// Brute force editing: for textBox to show Cr-Lf corrected, we need to strip out the \n's
filenameText = filenameText.Replace("\r\n", "\r");
// split apart all of the text in textBox, where one element in the array represents one line
string[] registryLines = filenameText.Split("\r");
if (registryLines.Length <= 1)
{
// after the split, we have no lines so get out
ChangeCursor(gridPreview, false);
return false;
}
// REG files have to start with one of two headers and it's case insensitive
registryLine = registryLines[0];
registryLine = registryLine.ToLowerInvariant();
// make sure that this is a valid REG file, based on the first line of the file
switch (registryLine)
{
case REGISTRYHEADER4:
case REGISTRYHEADER5:
break;
default:
ShowMessageBox(APPNAME, App.AppFilename + resourceLoader.GetString("InvalidRegistryFile"), resourceLoader.GetString("OkButtonText"));
ChangeCursor(gridPreview, false);
return false;
}
// these are used for populating the tree as we read in one line at a time
TreeViewNode treeViewNode = null;
RegistryValue registryValue = null;
// start with the first element of the array
int index = 1;
registryLine = registryLines[index];
while (index < registryLines.Length)
{
// special case for when the registryLine begins with a @ - make some tweaks and
// let the regular processing handle the rest.
if (registryLine.StartsWith("@=-", StringComparison.InvariantCulture))
{
// REG file has a callout to delete the @ Value which won't work *but* the Registry Editor will
// clear the value of the @ Value instead, so it's still a valid line.
registryLine = registryLine.Replace("@=-", "\"(Default)\"=\"\"");
}
else if (registryLine.StartsWith("@=", StringComparison.InvariantCulture))
{
// This is the a Value called "(Default)" so we tweak the line for the UX
registryLine = registryLine.Replace("@=", "\"(Default)\"=");
}
// continue until we have nothing left to read
// switch logic, based off what the current line we're reading is
if (registryLine.StartsWith("[-", StringComparison.InvariantCulture))
{
// remove the - as we won't need it but it will get special treatment in the UI
registryLine = registryLine.Remove(1, 1);
// this is a key, so remove the first [ and last ]
registryLine = StripFirstAndLast(registryLine);
// do not track the result of this node, since it should have no children
AddTextToTree(registryLine, DELETEDKEYIMAGE);
}
else if (registryLine.StartsWith("[", StringComparison.InvariantCulture))
{
// this is a key, so remove the first [ and last ]
registryLine = StripFirstAndLast(registryLine);
treeViewNode = AddTextToTree(registryLine, KEYIMAGE);
}
else if (registryLine.StartsWith("\"", StringComparison.InvariantCulture) && registryLine.EndsWith("=-", StringComparison.InvariantCulture))
{
// this line deletes this value so it gets special treatment for the UI
registryLine = registryLine.Replace("=-", string.Empty);
// remove the "'s without removing all of them
registryLine = StripFirstAndLast(registryLine);
// Create a new listview item that will be used to display the delete value and store it
registryValue = new RegistryValue(registryLine, string.Empty, string.Empty);
SetValueToolTip(registryValue);
// store the ListViewItem, if we have a valid Key to attach to
if (treeViewNode != null)
{
StoreTheListValue((RegistryKey)treeViewNode.Content, registryValue);
}
}
else if (registryLine.StartsWith("\"", StringComparison.InvariantCulture))
{
// this is a named value
// split up the name from the value by looking for the first found =
int equal = registryLine.IndexOf('=');
if ((equal < 0) || (equal > registryLine.Length - 1))
{
// something is very wrong
Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "SOMETHING WENT WRONG: {0}", registryLine));
break;
}
// set the name and the value
string name = registryLine.Substring(0, equal);
name = StripFirstAndLast(name);
// Clean out any escaped characters in the value, only for the preview
name = StripEscapedCharacters(name);
string value = registryLine.Substring(equal + 1);
// Create a new listview item that will be used to display the value
registryValue = new RegistryValue(name, "REG_SZ", string.Empty);
// if the first and last character is a " then this is a string value; get rid of the first and last "
if (value.StartsWith("\"", StringComparison.InvariantCulture) && value.EndsWith("\"", StringComparison.InvariantCulture))
{
value = StripFirstAndLast(value);
}
else
{
// this is an invalid value as there are no "s in the right side of the =
registryValue.Type = "ERROR";
}
if (value.StartsWith("dword:", StringComparison.InvariantCultureIgnoreCase))
{
registryValue.Type = "REG_DWORD";
value = value.Replace("dword:", string.Empty);
}
else if (value.StartsWith("hex(b):", StringComparison.InvariantCultureIgnoreCase))
{
registryValue.Type = "REG_QWORD";
value = value.Replace("hex(b):", string.Empty);
}
else if (value.StartsWith("hex:", StringComparison.InvariantCultureIgnoreCase))
{
registryValue.Type = "REG_BINARY";
value = value.Replace("hex:", string.Empty);
}
else if (value.StartsWith("hex(2):", StringComparison.InvariantCultureIgnoreCase))
{
registryValue.Type = "REG_EXAND_SZ";
value = value.Replace("hex(2):", string.Empty);
}
else if (value.StartsWith("hex(7):", StringComparison.InvariantCultureIgnoreCase))
{
registryValue.Type = "REG_MULTI_SZ";
value = value.Replace("hex(7):", string.Empty);
}
// If the end of a decimal line ends in a \ then you have to keep
// reading the block as a single value!
while (value.EndsWith(@",\", StringComparison.InvariantCulture))
{
value = value.TrimEnd('\\');
index++;
if (index >= registryLines.Length)
{
ChangeCursor(gridPreview, false);
return false;
}
registryLine = registryLines[index];
registryLine = registryLine.TrimStart();
value += registryLine;
}
// Clean out any escaped characters in the value, only for the preview
value = StripEscapedCharacters(value);
// update the ListViewItem with this information
if (registryValue.Type != "ERROR")
{
registryValue.Value = value;
}
// update the ToolTip
SetValueToolTip(registryValue);
// store the ListViewItem, if we have a valid Key to attach to
if (treeViewNode != null)
{
StoreTheListValue((RegistryKey)treeViewNode.Content, registryValue);
}
}
// if we get here, it's not a Key (starts with [) or Value (starts with ") so it's likely waste (comments that start with ; fall out here)
// read the next line from the REG file
index++;
// if we've gone too far, escape the proc!
if (index >= registryLines.Length)
{
// check to see if anything got parsed!
if (treeView.RootNodes.Count <= 0)
{
ShowMessageBox(APPNAME, App.AppFilename + resourceLoader.GetString("InvalidRegistryFile"), resourceLoader.GetString("OkButtonText"));
}
ChangeCursor(gridPreview, false);
return false;
}
// carry on with the next line
registryLine = registryLines[index];
}
// last check, to see if anything got parsed!
if (treeView.RootNodes.Count <= 0)
{
ShowMessageBox(APPNAME, App.AppFilename + resourceLoader.GetString("InvalidRegistryFile"), resourceLoader.GetString("OkButtonText"));
ChangeCursor(gridPreview, false);
return false;
}
return true;
}
/// <summary>
/// We're going to store this ListViewItem in an ArrayList which will then
/// be attached to the most recently returned TreeNode that came back from
/// AddTextToTree. If there's already a list there, we will use that list and
/// add our new node to it.
/// </summary>
private void StoreTheListValue(RegistryKey registryKey, RegistryValue registryValue)
{
ArrayList arrayList = null;
if (registryKey.Tag == null)
{
arrayList = new ArrayList();
}
else
{
arrayList = (ArrayList)registryKey.Tag;
}
arrayList.Add(registryValue);
// shove the updated array into the Tag property
registryKey.Tag = arrayList;
}
/// <summary>
/// Adds the REG file that's being currently being viewed to the app's title bar
/// </summary>
private void UpdateWindowTitle(string filename)
{
string[] file = filename.Split('\\');
if (file.Length > 0)
{
appWindow.Title = file[file.Length - 1] + " - " + APPNAME;
}
else
{
appWindow.Title = filename + " - " + APPNAME;
}
}
/// <summary>
/// No REG file was opened, so leave the app's title bar alone
/// </summary>
private void UpdateWindowTitle()
{
appWindow.Title = APPNAME;
}
/// <summary>
/// Helper method that assumes everything is enabled/disabled together
/// </summary>
private void UpdateToolBarAndUI(bool enable)
{
UpdateToolBarAndUI(enable, enable, enable);
}
/// <summary>
/// Enable command bar buttons and textBox.
/// Note that writeButton and textBox all update with the same value on purpose
/// </summary>
private void UpdateToolBarAndUI(bool enableWrite, bool enableRefresh, bool enableEdit)
{
refreshButton.IsEnabled = enableRefresh;
editButton.IsEnabled = enableEdit;
writeButton.IsEnabled = enableWrite;
}
/// <summary>
/// Helper method that creates a new TreeView node, attaches it to a parent if any, and then passes the new node back to the caller
/// mapRegistryKeys is a collection of all of the [] lines in the file
/// keys comes from the REG file and represents a bunch of nodes
/// </summary>
private TreeViewNode AddTextToTree(string keys, string image)
{
string[] individualKeys = keys.Split('\\');
string fullPath = keys;
TreeViewNode returnNewNode = null, newNode = null, previousNode = null;
// Walk the list of keys backwards
for (int i = individualKeys.Length - 1; i >= 0; i--)
{
// when a Key is marked for deletion, make sure it only sets the icon for the bottom most leaf
if (image == DELETEDKEYIMAGE)
{
if (i < individualKeys.Length - 1)
{
image = KEYIMAGE;
}
else
{
// special casing for Registry roots
switch (individualKeys[i])
{
case "HKEY_CLASSES_ROOT":
case "HKEY_CURRENT_USER":
case "HKEY_LOCAL_MACHINE":
case "HKEY_USERS":
case "HKEY_CURRENT_CONFIG":
image = KEYIMAGE;
break;
}
}
}
// First check the dictionary, and return the current node if it already exists
if (mapRegistryKeys.ContainsKey(fullPath))
{
// was a new node created?
if (returnNewNode == null)
{
// if no new nodes have been created, send out the node we should have already
mapRegistryKeys.TryGetValue(fullPath, out returnNewNode);
}
else
{
// as a new node was created, hook it up to this found parent
mapRegistryKeys.TryGetValue(fullPath, out newNode);
newNode.Children.Add(previousNode);
}
// return the new node no matter what
return returnNewNode;
}
// Since the path is not in the tree, create a new node and add it to the dictionary
RegistryKey registryKey = new RegistryKey(individualKeys[i], fullPath, image, GetFolderToolTip(image));
newNode = new TreeViewNode() { Content = registryKey, IsExpanded = true };
mapRegistryKeys.Add(fullPath, newNode);
// if this is the first new node we're creating, we need to return it to the caller
if (previousNode == null)
{
// capture the first node so it can be returned
returnNewNode = newNode;
}
else
{
// The newly created node is a parent to the previously created node, as add it here.
newNode.Children.Add(previousNode);
}
// before moving onto the next node, tag the previous node and update the path
previousNode = newNode;
fullPath = fullPath.Replace(string.Format(CultureInfo.InvariantCulture, @"\{0}", individualKeys[i]), string.Empty);
// One last check: if we get here, the parent of this node is not yet in the tree, so we need to add it as a RootNode
if (i == 0)
{
treeView.RootNodes.Add(newNode);
treeView.UpdateLayout();
}
}
return returnNewNode;
}
/// <summary>
/// Wrapper method that shows a simple one-button message box, parented by the main application window
/// </summary>
private async void ShowMessageBox(string title, string content, string closeButtonText)
{
ContentDialog contentDialog = new ContentDialog()
{
Title = title,
Content = content,
CloseButtonText = closeButtonText,
};
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
contentDialog.XamlRoot = this.Content.XamlRoot;
}
await contentDialog.ShowAsync();
}
/// <summary>
/// Wrapper method that shows a Save/Don't Save/Cancel message box, parented by the main application window and shown when closing the app
/// </summary>
private async void HandleDirtyClosing(string title, string content, string primaryButtonText, string secondaryButtonText, string closeButtonText)
{
ContentDialog contentDialog = new ContentDialog()
{
Title = title,
Content = content,
PrimaryButtonText = primaryButtonText,
SecondaryButtonText = secondaryButtonText,
CloseButtonText = closeButtonText,
DefaultButton = ContentDialogButton.Primary,
};
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
contentDialog.XamlRoot = this.Content.XamlRoot;
}
ContentDialogResult contentDialogResult = await contentDialog.ShowAsync();
switch (contentDialogResult)
{
case ContentDialogResult.Primary:
// Save, then close
SaveFile();
break;
case ContentDialogResult.Secondary:
// Don't save, and then close!
saveButton.IsEnabled = false;
break;
default:
// Cancel closing!
return;
}
// if we got here, we should try to close again
App.Current.Exit();
}
/// <summary>
/// Method will open the Registry Editor or merge the current REG file into the Registry via the Editor
/// Process will prompt for elevation if it needs it.
/// </summary>
private void OpenRegistryEditor(string fileMerge)
{
Process process = new Process();
process.StartInfo.FileName = "regedit.exe";
process.StartInfo.UseShellExecute = true;
if (File.Exists(fileMerge))
{
// If Merge was called, pass in the filename as a param to the Editor
process.StartInfo.Arguments = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", fileMerge);
}
try
{
process.Start();
}
catch
{
ShowMessageBox(
resourceLoader.GetString("UACDialogTitle"),
resourceLoader.GetString("UACDialogError"),
resourceLoader.GetString("OkButtonText"));
}
}
/// <summary>
/// Utility method that clears out the GridView as there's no other way to do it.
/// </summary>
private void ClearTable()
{
if (listRegistryValues != null)
{
listRegistryValues.Clear();
}
dataGrid.ItemsSource = null;
}
/// <summary>
/// Change the current app cursor at the grid level to be a wait cursor. Sort of works, sort of doesn't, but it's a nice attempt.
/// </summary>
public void ChangeCursor(UIElement uiElement, bool wait)
{
// You can only change the Cursor if the visual tree is loaded
if (!visualTreeReady)
{
return;
}
InputCursor cursor = InputSystemCursor.Create(wait ? InputSystemCursorShape.Wait : InputSystemCursorShape.Arrow);
System.Type type = typeof(UIElement);
type.InvokeMember("ProtectedCursor", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, uiElement, new object[] { cursor }, CultureInfo.InvariantCulture);
}
/// <summary>
/// Wrapper method that saves the current file in place, using the current text in textBox.
/// </summary>
private void SaveFile()
{
ChangeCursor(gridPreview, true);
// set up the FileStream for all writing
FileStream fileStream = null;
try
{
// attempt to open the existing file for writing
FileStreamOptions fileStreamOptions = new FileStreamOptions();
fileStreamOptions.Access = FileAccess.Write;
fileStreamOptions.Share = FileShare.Write;
fileStreamOptions.Mode = FileMode.OpenOrCreate;
fileStream = new FileStream(App.AppFilename, fileStreamOptions);
StreamWriter streamWriter = new StreamWriter(fileStream, System.Text.Encoding.Unicode);
// if we get here, the file is open and writable so dump the whole contents of textBox
string filenameText = textBox.Text;
streamWriter.Write(filenameText);
streamWriter.Flush();
streamWriter.Close();
// only change when the save is successful
saveButton.IsEnabled = false;
}
catch (UnauthorizedAccessException ex)
{
// this exception is thrown if the file is there but marked as read only
ShowMessageBox(
resourceLoader.GetString("ErrorDialogTitle"),
ex.Message,
resourceLoader.GetString("OkButtonText"));
}
catch
{
// this catch handles all other exceptions thrown when trying to write the file out
ShowMessageBox(
resourceLoader.GetString("ErrorDialogTitle"),
resourceLoader.GetString("FileSaveError"),
resourceLoader.GetString("OkButtonText"));
}
finally
{
// clean up no matter what
if (fileStream != null)
{
fileStream.Dispose();
}
}
// restore the cursor
ChangeCursor(gridPreview, false);
}
private void OpenSettingsFile(string path, string filename)
{
string fileContents = string.Empty;
string storageFile = Path.Combine(path, filename);
if (File.Exists(storageFile))
{
try
{
TextReader reader = new StreamReader(storageFile);
fileContents = reader.ReadToEnd();
reader.Close();
}
catch
{
// set up default JSON blob
fileContents = "{ }";
}
}
try
{
jsonSettings = Windows.Data.Json.JsonObject.Parse(fileContents);
}
catch
{
// set up default JSON blob
fileContents = "{ }";
jsonSettings = Windows.Data.Json.JsonObject.Parse(fileContents);
}
}
/// <summary>
/// Save the settings JSON blob out to a local file
/// </summary>
private async void SaveSettingsFile(string path, string filename)
{
StorageFolder storageFolder = null;
StorageFile storageFile = null;
string fileContents = string.Empty;
try
{
storageFolder = await StorageFolder.GetFolderFromPathAsync(path);
}
catch (FileNotFoundException ex)
{
Debug.WriteLine(ex.Message);
Directory.CreateDirectory(path);
storageFolder = await StorageFolder.GetFolderFromPathAsync(path);
}
try
{
storageFile = await storageFolder.CreateFileAsync(filename, CreationCollisionOption.OpenIfExists);
}
catch (FileNotFoundException ex)
{
Debug.WriteLine(ex.Message);
storageFile = await storageFolder.CreateFileAsync(filename);
}
try
{
fileContents = jsonSettings.Stringify();
await Windows.Storage.FileIO.WriteTextAsync(storageFile, fileContents);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
/// <summary>
/// Rip the first and last character off a string,
/// checking that the string is at least 2 characters long to avoid errors
/// </summary>
private string StripFirstAndLast(string line)
{
if (line.Length > 1)
{
line = line.Remove(line.Length - 1, 1);
line = line.Remove(0, 1);
}
return line;
}
/// <summary>
/// Replace any escaped characters in the REG file with their counterparts, for the UX
/// </summary>
private string StripEscapedCharacters(string value)
{
value = value.Replace("\\\\", "\\"); // Replace \\ with \ in the UI
value = value.Replace("\\\"", "\""); // Replace \" with " in the UI
return value;
}
/// <summary>
/// Loads and returns a string for a given Key's image in the tree, based off the current set image
/// </summary>
private string GetFolderToolTip(string key)
{
string value = string.Empty;
switch (key)
{
case DELETEDKEYIMAGE:
value = resourceLoader.GetString("ToolTipDeletedKey");
break;
case KEYIMAGE:
value = resourceLoader.GetString("ToolTipAddedKey");
break;
}
return value;
}
/// <summary>
/// Loads a string for a given Value's image in the grid, based off the current type and updates the RegistryValue that's passed in
/// </summary>
private void SetValueToolTip(RegistryValue registryValue)
{
string value = string.Empty;
switch (registryValue.Type)
{
case "REG_SZ":
case "REG_EXAND_SZ":
case "REG_MULTI_SZ":
value = resourceLoader.GetString("ToolTipStringValue");
break;
case "ERROR":
value = resourceLoader.GetString("ToolTipErrorValue");
break;
case "":
value = resourceLoader.GetString("ToolTipDeletedValue");
break;
default:
value = resourceLoader.GetString("ToolTipBinaryValue");
break;
}
registryValue.ToolTipText = value;
}
}
}