ok

alphons <alphons@heijden.com> 10 Jan 2025, 21:46
0f7ad038ae14f9ba0d64a2cc3e6910aa2459ce1c
4 files changed
  • RedisGui/App.config
  • RedisGui/FormMain.Designer.cs
  • RedisGui/FormMain.cs
  • RedisGui/Helper.cs
diff --git a/RedisGui/App.config b/RedisGui/App.config
new file mode 100644
index 0000000..49cc43e
--- /dev/null
+++ b/RedisGui/App.config
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+</configuration>
\ No newline at end of file
diff --git a/RedisGui/FormMain.Designer.cs b/RedisGui/FormMain.Designer.cs
index 9f0d9b2..2b5c261 100644
--- a/RedisGui/FormMain.Designer.cs
+++ b/RedisGui/FormMain.Designer.cs
@@ -87,8 +87,9 @@
// exitToolStripMenuItem
//
exitToolStripMenuItem.Name = "exitToolStripMenuItem";
- exitToolStripMenuItem.Size = new Size(93, 22);
+ exitToolStripMenuItem.Size = new Size(180, 22);
exitToolStripMenuItem.Text = "Exit";
+ exitToolStripMenuItem.Click += ExitToolStripMenuItem_Click;
//
// statusStrip1
//
@@ -210,7 +211,7 @@
listView1.GridLines = true;
listView1.Location = new Point(12, 79);
listView1.Name = "listView1";
- listView1.Size = new Size(615, 233);
+ listView1.Size = new Size(609, 233);
listView1.TabIndex = 0;
listView1.UseCompatibleStateImageBehavior = false;
listView1.View = View.Details;
@@ -237,7 +238,7 @@
txtData.Name = "txtData";
txtData.ReadOnly = true;
txtData.ScrollBars = ScrollBars.Both;
- txtData.Size = new Size(1019, 77);
+ txtData.Size = new Size(1019, 71);
txtData.TabIndex = 5;
//
// contextMenuStrip1
diff --git a/RedisGui/FormMain.cs b/RedisGui/FormMain.cs
index 281cf7d..4e80532 100644
--- a/RedisGui/FormMain.cs
+++ b/RedisGui/FormMain.cs
@@ -1,403 +1,332 @@
using RedisGui.Properties;
using StackExchange.Redis;
-using System.Buffers.Binary;
using System.Diagnostics;
using System.Net;
-using System.Reflection;
-using System.Text;
-using System.Text.Json;
-using System.Text.RegularExpressions;
-namespace RedisGui
-{
- public partial class FormMain : Form
- {
- private ConnectionMultiplexer? connection;
- private IServer? server;
- private IDatabase? db;
+namespace RedisGui;
- public FormMain()
- {
- InitializeComponent();
+public partial class FormMain : Form
+{
+ private const string CONNECTION = "127.0.0.1:6379";
+ private ConnectionMultiplexer? connection;
+ private IServer? server;
+ private IDatabase? db;
- imageList1.Images.Add(SystemIcons.GetStockIcon(StockIconId.World, 24));
- imageList1.Images.Add(SystemIcons.GetStockIcon(StockIconId.NetworkConnect, 24));
- imageList1.Images.Add(SystemIcons.GetStockIcon(StockIconId.Key, 24));
+ public FormMain()
+ {
+ InitializeComponent();
- }
+ imageList1.Images.Add(SystemIcons.GetStockIcon(StockIconId.World, 24));
+ imageList1.Images.Add(SystemIcons.GetStockIcon(StockIconId.NetworkConnect, 24));
+ imageList1.Images.Add(SystemIcons.GetStockIcon(StockIconId.Key, 24));
+ }
- private void Form_Load(object sender, EventArgs e)
- {
- MakeConnection("127.0.0.1:6379");
- }
+ private async void Form_Load(object sender, EventArgs e)
+ {
+ await MakeConnectionAsync(CONNECTION);
+ }
- private void AddConnectionToolStripMenuItem_Click(object sender, EventArgs e)
- {
- MakeConnection("127.0.0.1:6379");
- }
+ private async void AddConnectionToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ await MakeConnectionAsync(CONNECTION);
+ }
- private void MakeConnection(string ConnectionString)
- {
- this.connection = ConnectionMultiplexer.Connect(ConnectionString, x => x.AllowAdmin = true);
+ private async Task MakeConnectionAsync(string ConnectionString)
+ {
+ this.connection = ConnectionMultiplexer.Connect(ConnectionString, x => x.AllowAdmin = true);
- EndPoint endPoint = connection.GetEndPoints().First();
+ EndPoint endPoint = connection.GetEndPoints().First();
- this.server = connection.GetServer(endPoint);
+ this.server = connection.GetServer(endPoint);
- this.db = connection.GetDatabase();
+ this.db = connection.GetDatabase();
- var node = this.treeView1.Nodes.Add("", endPoint.ToString(), 1, 1);
+ var node = this.treeView1.Nodes.Add("", endPoint.ToString(), 1, 1);
- LoadKeys(node);
+ await LoadKeysAsync(node);
- this.timer1.Start();
- }
+ this.timer1.Start();
+ }
- private void LoadKeys(TreeNode node)
+ private async Task<List<RedisKey>> SearchRedisKeysAsync(string key)
+ {
+ if (this.server == null)
+ return [];
+
+ IAsyncEnumerator<RedisKey> keysAsync = this.server
+ .KeysAsync(pattern: $"{key}*", pageSize: 100)
+ .GetAsyncEnumerator();
+ List<RedisKey> keys = [];
+ while (await keysAsync.MoveNextAsync())
{
- if (this.server == null)
- return;
-
- try
- {
- RedisKey[] keys = this.server.Keys(pattern: "*").ToArray();
-
- node.Nodes.Clear();
-
- foreach (var key in keys)
- {
- node.Nodes.Add(new TreeNode(key, 2, 2));
- }
- }
- catch (Exception ex)
- {
- MessageBox.Show($"Error loading keys: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
+ keys.Add(keysAsync.Current);
}
+ return keys;
+ }
- private void TreeView_AfterSelect(object sender, TreeViewEventArgs e)
- {
- if (e.Node == null)
- return;
- var key = new RedisKey(e.Node.Text);
+ private async Task LoadKeysAsync(TreeNode node)
+ {
+ if (this.server == null)
+ return;
- if (this.db == null)
- return;
+ try
+ {
+ var keys = await SearchRedisKeysAsync(string.Empty);
- if (e.Node.Parent == null)
- return;
+ node.Nodes.Clear();
- if (!db.KeyExists(key))
+ foreach (var key in keys)
{
- e.Node.Remove();
- return;
+ node.Nodes.Add(new TreeNode(key, 2, 2));
}
-
- ShowValue(key);
-
}
-
- private static string PrettyPrintJson(string json)
+ catch (Exception ex)
{
- try
- {
- var jsonElement = JsonSerializer.Deserialize<JsonElement>(json);
- var options = new JsonSerializerOptions { WriteIndented = true };
- return JsonSerializer.Serialize(jsonElement, options);
- }
- catch
- {
- return json;
- }
+ MessageBox.Show($"Error loading keys: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
+ }
- private static string ConvertByteArrayToHexText(byte[] byteArray)
- {
- StringBuilder hexBuilder = new();
-
- StringBuilder chars = new();
-
- for (int i = 0; i < byteArray.Length; i++)
- {
- if (i % 32 == 0)
- {
- hexBuilder.Append(i.ToString("X6"));
- hexBuilder.Append(" - ");
- }
- hexBuilder.Append(byteArray[i].ToString("X2"));
- hexBuilder.Append(' ');
-
- var c = (char)byteArray[i];
- if (c < ' ' || c > 0x7f)
- c = ' ';
- chars.Append(c);
+ private void TreeView_AfterSelect(object sender, TreeViewEventArgs e)
+ {
+ if (e.Node == null)
+ return;
- if ((i + 1) % 16 == 0)
- {
- hexBuilder.Append("- ");
- }
+ var key = new RedisKey(e.Node.Text);
- if ((i + 1) % 32 == 0)
- {
- hexBuilder.AppendLine(chars.ToString());
- chars = new();
- }
- }
+ if (this.db == null)
+ return;
- var last = byteArray.Length % 32;
- var todo = (32 * 3) - 3 * last + 2;
- if (last < 16)
- todo += 2;
- if (todo > 0)
- {
- for (int i = 0; i < todo; i++)
- hexBuilder.Append(' ');
- hexBuilder.AppendLine(chars.ToString());
- }
+ if (e.Node.Parent == null)
+ return;
- return hexBuilder.ToString();
+ if (!db.KeyExists(key))
+ {
+ e.Node.Remove();
+ return;
}
+ ShowValue(key);
- public static Int16 ReadInt16(byte[] b, int index) => BinaryPrimitives.ReadInt16BigEndian(b.AsSpan(index, 2));
- public static Int32 ReadInt32(byte[] b, int index) => BinaryPrimitives.ReadInt32BigEndian(b.AsSpan(index, 4));
+ }
- private static void SessionDecoder(ListView lv, byte[] b)
- {
- lv.Items.Add(new ListViewItem(["Version", $"{b[0]}.{b[1]}"]));
- var count = ReadInt16(b, 2);
- lv.Items.Add(new ListViewItem(["Keys", $"{count}"]));
- var guid = new Guid(b.AsSpan(4, 16));
- lv.Items.Add(new ListViewItem(["Guid", $"{guid}"]));
- var index = 20;
- for (int i = 0; i < count; i++)
- {
- var lenName = ReadInt16(b, index);
- index += 2;
- var name = Encoding.UTF8.GetString(b.AsSpan(index, lenName));
- index += lenName;
- var lenVal = ReadInt32(b, index);
- index += 4;
- var val = Encoding.UTF8.GetString(b.AsSpan(index, lenVal));
- index += lenVal;
- lv.Items.Add(new ListViewItem([name, val]));
- }
- }
- private void ShowValue(RedisKey key)
- {
- this.lblType.Text = "";
- this.lblKey.Text = "";
+ private void ShowValue(RedisKey key)
+ {
+ this.lblType.Text = "";
+ this.lblKey.Text = "";
- this.listView1.Items.Clear();
- this.txtData.Clear();
+ this.listView1.Items.Clear();
+ this.txtData.Clear();
- if (this.db == null)
- return;
+ if (this.db == null)
+ return;
- if (!db.KeyExists(key))
- return;
+ if (!db.KeyExists(key))
+ return;
- RedisType type = this.db.KeyType(key);
+ RedisType type = this.db.KeyType(key);
- this.lblType.Text = type.ToString();
+ this.lblType.Text = type.ToString();
- this.lblKey.Text = key.ToString();
+ this.lblKey.Text = key.ToString();
- switch (type)
- {
- case RedisType.String:
- Debug.WriteLine($"String: {db.StringGet(key)}");
- break;
- case RedisType.Hash:
- foreach (HashEntry entry in db.HashGetAll(key))
- {
+ switch (type)
+ {
+ case RedisType.String:
+ Debug.WriteLine($"String: {db.StringGet(key)}");
+ break;
+ case RedisType.Hash:
+ foreach (HashEntry entry in db.HashGetAll(key))
+ {
- var val = entry.Value.ToString();
- var name = entry.Name.ToString();
+ var val = entry.Value.ToString();
+ var name = entry.Name.ToString();
- if (val != "-1")
+ if (val != "-1")
+ {
+ switch (name)
{
- switch (name)
- {
- case "absexp":
- val = new DateTime(long.Parse(val)).ToString();
- break;
- case "sldexp":
- val = TimeSpan.FromMilliseconds(long.Parse(val) / 10000).ToString();
- break;
- case "data":
- if (val.StartsWith('{') && val.EndsWith('}'))
- this.txtData.Text = Regex.Unescape(PrettyPrintJson(val));
- if (val[0] == 0x02)
+ case "absexp":
+ val = new DateTime(long.Parse(val)).ToString();
+ break;
+ case "sldexp":
+ val = TimeSpan.FromMilliseconds(long.Parse(val) / 10000).ToString();
+ break;
+ case "data":
+ if (val.StartsWith('{') && val.EndsWith('}'))
+ this.txtData.Text = Helper.PrettyPrintJson(val);
+ if (val[0] == 0x02)
+ {
+ if (entry.Value.Box() is byte[] buffer)
{
- if (entry.Value.Box() is byte[] buffer)
- {
- //this.txtData.Text = ConvertByteArrayToHexText(buffer);
- SessionDecoder(this.listView1, buffer);
- val = $"*binary* (len {buffer.Length})";
- }
+ //this.txtData.Text = Helper.ConvertByteArrayToHexText(buffer);
+ Helper.SessionDecoder(this.listView1, buffer);
+ val = $"*binary* (len {buffer.Length})";
}
- break;
- }
+ }
+ break;
}
-
- this.listView1.Items.Add(new ListViewItem([entry.Name.ToString(), val]));
- }
- break;
- case RedisType.List:
- foreach (var item in db.ListRange(key))
- {
- Debug.WriteLine(item);
- }
- break;
- case RedisType.Set:
- foreach (var item in db.SetMembers(key))
- {
- Debug.WriteLine(item);
}
- break;
- case RedisType.SortedSet:
- foreach (var item in db.SortedSetRangeByRankWithScores(key))
- {
- Debug.WriteLine($"{item.Element}: {item.Score}");
- }
- break;
- case RedisType.Stream:
- break;
- default:
- Debug.WriteLine($"Unknown type for key: {key}");
- break;
- }
+ this.listView1.Items.Add(new ListViewItem([entry.Name.ToString(), val]));
+ }
+ break;
+ case RedisType.List:
+ foreach (var item in db.ListRange(key))
+ {
+ Debug.WriteLine(item);
+ }
+ break;
+ case RedisType.Set:
+ foreach (var item in db.SetMembers(key))
+ {
+ Debug.WriteLine(item);
+ }
+ break;
+ case RedisType.SortedSet:
+ foreach (var item in db.SortedSetRangeByRankWithScores(key))
+ {
+ Debug.WriteLine($"{item.Element}: {item.Score}");
+ }
+ break;
+ case RedisType.Stream:
+ break;
+ default:
+ Debug.WriteLine($"Unknown type for key: {key}");
+ break;
}
- private async Task ShowServerPropertiesAsync()
- {
- this.lblType.Text = "";
- this.lblKey.Text = "";
+ }
- this.listView1.Items.Clear();
+ private async Task ShowServerPropertiesAsync()
+ {
+ this.lblType.Text = "";
+ this.lblKey.Text = "";
- if (this.server == null)
- return;
+ this.listView1.Items.Clear();
- var kvs = await this.server.ConfigGetAsync("*");
+ if (this.server == null)
+ return;
- foreach (var item in kvs)
- {
- this.listView1.Items.Add(new ListViewItem([item.Key, item.Value]));
- }
- }
+ var kvs = await this.server.ConfigGetAsync("*");
- private async Task ShowServerInfoAsync()
+ foreach (var item in kvs)
{
- this.lblType.Text = "";
- this.lblKey.Text = "";
+ this.listView1.Items.Add(new ListViewItem([item.Key, item.Value]));
+ }
+ }
+
+ private async Task ShowServerInfoAsync()
+ {
+ this.lblType.Text = "";
+ this.lblKey.Text = "";
- this.listView1.Items.Clear();
+ this.listView1.Items.Clear();
- if (this.server == null)
- return;
+ if (this.server == null)
+ return;
- var groups = await this.server.InfoAsync();
+ var groups = await this.server.InfoAsync();
- foreach (var group in groups)
+ foreach (var group in groups)
+ {
+ this.listView1.Items.Add(new ListViewItem([group.Key, "======================"]));
+ foreach (var item in group)
{
- this.listView1.Items.Add(new ListViewItem([group.Key, "======================"]));
- foreach (var item in group)
- {
- this.listView1.Items.Add(new ListViewItem([item.Key, item.Value]));
- }
+ this.listView1.Items.Add(new ListViewItem([item.Key, item.Value]));
}
}
+ }
- async private void ShowConfigurationToolStripMenuItem_Click(object sender, EventArgs e)
- {
- await ShowServerPropertiesAsync();
- }
+ async private void ShowConfigurationToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ await ShowServerPropertiesAsync();
+ }
- async private void ShowInformationToolStripMenuItem_Click(object sender, EventArgs e)
- {
- await ShowServerInfoAsync();
- }
+ async private void ShowInformationToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ await ShowServerInfoAsync();
+ }
- private void TreeView_MouseUp(object sender, MouseEventArgs e)
- {
- if (e.Button == MouseButtons.Left)
- return;
+ private void TreeView_MouseUp(object sender, MouseEventArgs e)
+ {
+ if (e.Button == MouseButtons.Left)
+ return;
- TreeNode node = this.treeView1.GetNodeAt(e.Location);
- if (node != null)
- {
- this.treeView1.SelectedNode = node;
- if (node.Parent == null)
- this.contextMenuStrip2.Show(this.treeView1, e.Location);
- return;
- }
+ TreeNode node = this.treeView1.GetNodeAt(e.Location);
+ if (node != null)
+ {
+ this.treeView1.SelectedNode = node;
+ if (node.Parent == null)
+ this.contextMenuStrip2.Show(this.treeView1, e.Location);
+ return;
+ }
- this.contextMenuStrip1.Show(this.treeView1, e.Location);
+ this.contextMenuStrip1.Show(this.treeView1, e.Location);
- }
+ }
- async private void closeConnectionToolStripMenuItem_Click(object sender, EventArgs e)
- {
- this.treeView1.SelectedNode.Remove();
- this.db = null;
- this.server = null;
- if (this.connection != null)
- await this.connection.CloseAsync();
- this.connection = null;
- }
+ async private void closeConnectionToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ this.treeView1.SelectedNode.Remove();
+ this.db = null;
+ this.server = null;
+ if (this.connection != null)
+ await this.connection.CloseAsync();
+ this.connection = null;
+ }
- private void ListView_SelectedIndexChanged(object sender, EventArgs e)
- {
- if (this.listView1.SelectedIndices.Count != 1)
- return;
+ private void ListView_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (this.listView1.SelectedIndices.Count != 1)
+ return;
- var lvi = this.listView1.Items[this.listView1.SelectedIndices[0]];
+ var lvi = this.listView1.Items[this.listView1.SelectedIndices[0]];
- if (lvi == null)
- return;
+ if (lvi == null)
+ return;
- var val = lvi.SubItems[1].Text;
- if ((val.StartsWith('{') && val.EndsWith('}')) || (val.StartsWith('[') && val.EndsWith(']')))
- this.txtData.Text = Regex.Unescape(PrettyPrintJson(val));
- else
- this.txtData.Text = val;
- }
+ var val = lvi.SubItems[1].Text;
+ if ((val.StartsWith('{') && val.EndsWith('}')) || (val.StartsWith('[') && val.EndsWith(']')))
+ this.txtData.Text = Helper.PrettyPrintJson(val);
+ else
+ this.txtData.Text = val;
+ }
- private void Timer_Tick(object sender, EventArgs e)
- {
- if (this.server == null)
- return;
+ private async void Timer_Tick(object sender, EventArgs e)
+ {
+ if (this.server == null)
+ return;
- this.toolStripStatusLabel1.Image = Resources.green;
+ this.toolStripStatusLabel1.Image = Resources.green;
- var keys = this.server.Keys(pattern: "*").Select(x => x.ToString()).ToList();
+ var keys = await SearchRedisKeysAsync(string.Empty);
- foreach (TreeNode connectionNode in this.treeView1.Nodes)
+ foreach (TreeNode connectionNode in this.treeView1.Nodes)
+ {
+ for (int j = connectionNode.Nodes.Count - 1; j >= 0; j--)
{
- for(int j= connectionNode.Nodes.Count -1; j>=0; j--)
- {
- TreeNode keyNode = connectionNode.Nodes[j];
+ TreeNode keyNode = connectionNode.Nodes[j];
- if (keys.Contains(keyNode.Text))
- keys.Remove(keyNode.Text);
- else
- keyNode.Remove();
- }
- foreach(var newKey in keys)
- {
- connectionNode.Nodes.Add("", newKey, 2, 2);
- }
+ if (keys.Contains(keyNode.Text))
+ keys.Remove(keyNode.Text);
+ else
+ keyNode.Remove();
+ }
+ foreach (var newKey in keys)
+ {
+ connectionNode.Nodes.Add("", newKey, 2, 2);
}
+ }
- this.toolStripStatusLabel1.Image = Resources.blue;
+ this.toolStripStatusLabel1.Image = Resources.blue;
+ }
- }
+ private void ExitToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ this.Close();
}
}
diff --git a/RedisGui/Helper.cs b/RedisGui/Helper.cs
new file mode 100644
index 0000000..6a1b9c8
--- /dev/null
+++ b/RedisGui/Helper.cs
@@ -0,0 +1,100 @@
+using System.Buffers.Binary;
+using System.Text;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+
+namespace RedisGui;
+
+public class Helper
+{
+ private static Int16 ReadInt16(byte[] b, ref int index)
+ {
+ var value = BinaryPrimitives.ReadInt16BigEndian(b.AsSpan(index, 2));
+ index += 2;
+ return value;
+ }
+
+ private static Int32 ReadInt32(byte[] b, ref int index)
+ {
+ var value = BinaryPrimitives.ReadInt32BigEndian(b.AsSpan(index, 4));
+ index += 4;
+ return value;
+ }
+
+ private static Guid ReadGuid(byte[] b, ref int index)
+ {
+ var guid = new Guid(b.AsSpan(index, 16));
+ index += 16;
+ return guid;
+ }
+
+ private static string ReadName(byte[] b, ref int index)
+ {
+ var lenName = ReadInt16(b, ref index);
+ var name = Encoding.UTF8.GetString(b.AsSpan(index, lenName));
+ index += lenName;
+ return name;
+ }
+
+ private static string ReadVal(byte[] b, ref int index)
+ {
+ var lenVal = ReadInt32(b, ref index);
+ var val = Encoding.UTF8.GetString(b.AsSpan(index, lenVal));
+ index += lenVal;
+ return val;
+ }
+
+ private static string ReadVersion(byte[] b, ref int index)
+ {
+ var major = b[index++];
+ var minor = b[index++];
+ return $"{major}.{minor}";
+ }
+
+ public static void SessionDecoder(ListView lv, byte[] b)
+ {
+ var index = 0;
+ lv.Items.Add(new ListViewItem([ "Version", ReadVersion(b, ref index) ]));
+ var count = ReadInt16(b, ref index);
+ lv.Items.Add(new ListViewItem([ "Keys", count.ToString() ]));
+ var guid = ReadGuid(b, ref index);
+ lv.Items.Add(new ListViewItem([ "Guid", guid.ToString() ]));
+
+ for (int i = 0; i < count; i++)
+ {
+ var name = ReadName(b, ref index);
+ var val = ReadVal(b, ref index);
+ lv.Items.Add(new ListViewItem([ name, val ]));
+ }
+ }
+
+ public static string PrettyPrintJson(string json)
+ {
+ try
+ {
+ var jsonElement = JsonSerializer.Deserialize<JsonElement>(json);
+ var options = new JsonSerializerOptions { WriteIndented = true };
+ return Regex.Unescape(JsonSerializer.Serialize(jsonElement, options));
+ }
+ catch
+ {
+ return Regex.Unescape(json);
+ }
+ }
+
+ public static string ConvertByteArrayToHexText(byte[] byteArray)
+ {
+ StringBuilder hexBuilder = new();
+
+ for (int i = 0; i < byteArray.Length; i += 32)
+ {
+ var chunk = byteArray.Skip(i).Take(32).ToArray();
+ var hexPart = string.Join(" ", chunk.Select((b, index) => (index > 0 && index % 16 == 0) ? $"- {b:X2}" : b.ToString("X2")));
+ var asciiPart = new string(chunk.Select(b => (char)(b < 32 || b > 127 ? ' ' : (char)b)).ToArray());
+ hexBuilder.AppendLine($"{i:X6} - {hexPart.PadRight(32 * 3 + 1)} - {asciiPart}");
+ }
+
+ return hexBuilder.ToString();
+ }
+
+}