v0.9.4.0 Release!

1.导出HTML新增了文件与表情的支持,需要导出表情,请先使用表情预下载功能,由于略微做了一些频率限制,预下载可能会较久,请耐心等待。
2.调整了软件本身的显示逻辑
3.旧版消息工具已经移动至管理界面,如需使用,工作区,右键,管理即可
This commit is contained in:
Suxue
2023-12-13 18:35:32 +08:00
parent 2399fa5f4a
commit 5ae3e6ef5d
11 changed files with 261 additions and 137 deletions

View File

@@ -10,6 +10,7 @@ using WechatBakTool.Model;
using System.Xml;
using Newtonsoft.Json;
using WechatBakTool.ViewModel;
using System.Security.Policy;
namespace WechatBakTool.Export
{
@@ -92,53 +93,82 @@ namespace WechatBakTool.Export
}
HtmlBody += string.Format("<p class=\"content\"><video controls style=\"max-height:300px;max-width:300px;\"><source src=\"{0}\" type=\"video/mp4\" /></video></p></div>", path);
}
else if(msg.Type == 47)
{
string? path = reader.GetAttachment(WXMsgType.Emoji, msg);
if (path == null)
{
#if DEBUG
File.AppendAllText("debug.log", string.Format("[D]{0} {1}:{2}\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), "Emoji Error Path=>", path));
File.AppendAllText("debug.log", string.Format("[D]{0} {1}:{2}\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), "Emoji Error Msg=>", JsonConvert.SerializeObject(msg)));
#endif
HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", "表情未预下载或加密表情");
continue;
}
HtmlBody += string.Format("<p class=\"content\"><img src=\"{0}\" style=\"max-height:300px;max-width:300px;\"/></p></div>", path);
}
else if (msg.Type == 49)
{
using (var decoder = LZ4Decoder.Create(true, 64))
if(msg.SubType == 6||msg.SubType == 19||msg.SubType == 40)
{
byte[] target = new byte[10240];
int res = 0;
if (msg.CompressContent != null)
res = LZ4Codec.Decode(msg.CompressContent, 0, msg.CompressContent.Length, target, 0, target.Length);
byte[] data = target.Skip(0).Take(res).ToArray();
string xml = Encoding.UTF8.GetString(data);
if (!string.IsNullOrEmpty(xml))
string? path = reader.GetAttachment(WXMsgType.File, msg);
if(path == null)
{
xml = xml.Replace("\n", "");
XmlDocument xmlObj = new XmlDocument();
xmlObj.LoadXml(xml);
if (xmlObj.DocumentElement != null)
{
string title = "";
string appName = "";
string url = "";
XmlNodeList? findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/title");
if (findNode != null)
{
if (findNode.Count > 0)
{
title = findNode[0]!.InnerText;
}
}
findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/sourcedisplayname");
if (findNode != null)
{
if (findNode.Count > 0)
{
appName = findNode[0]!.InnerText;
}
}
findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/url");
if (findNode != null)
{
if (findNode.Count > 0)
{
url = findNode[0]!.InnerText;
}
}
HtmlBody += string.Format("<p class=\"content\">{0}|{1}</p><p><a href=\"{2}\">点击访问</a></p></div>", appName, title, url);
HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", "文件不存在");
continue;
}
else
{
HtmlBody += string.Format("<p class=\"content\">{0}</p><p><a href=\"{1}\">点击访问</a></p></div>", "文件:" + path, path);
}
}
else
{
using (var decoder = LZ4Decoder.Create(true, 64))
{
byte[] target = new byte[10240];
int res = 0;
if (msg.CompressContent != null)
res = LZ4Codec.Decode(msg.CompressContent, 0, msg.CompressContent.Length, target, 0, target.Length);
byte[] data = target.Skip(0).Take(res).ToArray();
string xml = Encoding.UTF8.GetString(data);
if (!string.IsNullOrEmpty(xml))
{
xml = xml.Replace("\n", "");
XmlDocument xmlObj = new XmlDocument();
xmlObj.LoadXml(xml);
if (xmlObj.DocumentElement != null)
{
string title = "";
string appName = "";
string url = "";
XmlNodeList? findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/title");
if (findNode != null)
{
if (findNode.Count > 0)
{
title = findNode[0]!.InnerText;
}
}
findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/sourcedisplayname");
if (findNode != null)
{
if (findNode.Count > 0)
{
appName = findNode[0]!.InnerText;
}
}
findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/url");
if (findNode != null)
{
if (findNode.Count > 0)
{
url = findNode[0]!.InnerText;
}
}
HtmlBody += string.Format("<p class=\"content\">{0}|{1}</p><p><a href=\"{2}\">点击访问</a></p></div>", appName, title, url);
}
}
}
}

View File

@@ -70,67 +70,74 @@ namespace WechatBakTool.Export
txtMsg = "[视频]";
break;
case 49:
try
if (msg.SubType == 6 || msg.SubType == 19 || msg.SubType == 40)
{
using (var decoder = LZ4Decoder.Create(true, 64))
txtMsg = "[文件]";
}
else
{
try
{
byte[] target = new byte[10240];
int res = 0;
if (msg.CompressContent != null)
res = LZ4Codec.Decode(msg.CompressContent, 0, msg.CompressContent.Length, target, 0, target.Length);
byte[] data = target.Skip(0).Take(res).ToArray();
string xml = Encoding.UTF8.GetString(data);
if (!string.IsNullOrEmpty(xml))
using (var decoder = LZ4Decoder.Create(true, 64))
{
xml = xml.Replace("\n", "");
XmlDocument xmlObj = new XmlDocument();
xmlObj.LoadXml(xml);
if (xmlObj.DocumentElement != null)
byte[] target = new byte[10240];
int res = 0;
if (msg.CompressContent != null)
res = LZ4Codec.Decode(msg.CompressContent, 0, msg.CompressContent.Length, target, 0, target.Length);
byte[] data = target.Skip(0).Take(res).ToArray();
string xml = Encoding.UTF8.GetString(data);
if (!string.IsNullOrEmpty(xml))
{
string title = "";
string appName = "";
string url = "";
XmlNodeList? findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/title");
if (findNode != null)
xml = xml.Replace("\n", "");
XmlDocument xmlObj = new XmlDocument();
xmlObj.LoadXml(xml);
if (xmlObj.DocumentElement != null)
{
if (findNode.Count > 0)
string title = "";
string appName = "";
string url = "";
XmlNodeList? findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/title");
if (findNode != null)
{
title = findNode[0]!.InnerText;
if (findNode.Count > 0)
{
title = findNode[0]!.InnerText;
}
}
findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/sourcedisplayname");
if (findNode != null)
{
if (findNode.Count > 0)
{
appName = findNode[0]!.InnerText;
}
}
findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/url");
if (findNode != null)
{
if (findNode.Count > 0)
{
url = findNode[0]!.InnerText;
}
}
txtMsg = string.Format("{0},标题:{1},链接:{2}", appName, title, url);
}
findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/sourcedisplayname");
if (findNode != null)
else
{
if (findNode.Count > 0)
{
appName = findNode[0]!.InnerText;
}
txtMsg = "[分享链接出错了]";
}
findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/url");
if (findNode != null)
{
if (findNode.Count > 0)
{
url = findNode[0]!.InnerText;
}
}
txtMsg = string.Format("{0},标题:{1},链接:{2}", appName, title, url);
}
else
{
txtMsg = "[分享链接出错了]";
}
}
else
{
txtMsg = "[分享链接出错了]";
}
}
}
catch
{
txtMsg = "[分享链接出错了]";
catch
{
txtMsg = "[分享链接出错了]";
}
}
break;
}

View File

@@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WechatBakTool"
mc:Ignorable="d" WindowStartupLocation="CenterScreen" WindowStyle="None" WindowState="Normal" Background="Transparent" AllowsTransparency="True" ResizeMode="NoResize"
Title="Main2" Height="550" Width="950" >
Title="WechatBakTool" Height="550" Width="950" >
<Window.Resources>
<Style TargetType="local:Main2">
<!-- 设置窗体的WindowChrome -->

View File

@@ -128,6 +128,7 @@ namespace WechatBakTool.Model
public string StrTalker { get; set; } = "";
[Column("StrContent")]
public string StrContent { get; set; } = "";
public string DisplayContent { get; set; } = "";
[Column("CompressContent")]
public byte[]? CompressContent { get; set; }
[Column("BytesExtra")]

View File

@@ -15,8 +15,11 @@
<Button Name="btn_export_all" Margin="35,110" Height="26" Width="60" Content="导出" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#2775b6" Foreground="White" BorderThickness="0" Click="btn_export_all_Click"></Button>
<Label Content="{Binding LabelStatus}" HorizontalAlignment="Left" Margin="110,110,0,0" VerticalAlignment="Top"/>
<Label Margin="30,155" Content="表情包预下载" HorizontalAlignment="Left" VerticalAlignment="Top" FontWeight="Bold" />
<Button Name="btn_emoji_download" Margin="35,185" Height="26" Width="60" Content="导出" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#2775b6" Foreground="White" BorderThickness="0" Click="btn_emoji_download_Click"></Button>
<Label Margin="30,155,0,0" Content="表情包预下载" HorizontalAlignment="Left" VerticalAlignment="Top" FontWeight="Bold" />
<Button Name="btn_emoji_download" Margin="35,185,0,0" Height="26" Width="60" Content="导出" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#2775b6" Foreground="White" BorderThickness="0" Click="btn_emoji_download_Click"></Button>
<Label Margin="30,225,0,0" Content="旧版分析工具" HorizontalAlignment="Left" VerticalAlignment="Top" FontWeight="Bold" />
<Button Name="btn_analyse" Margin="35,255,0,0" Height="26" Width="60" Content="打开" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#2775b6" Foreground="White" BorderThickness="0" Click="btn_analyse_Click" ></Button>
</Grid>
</Page>

View File

@@ -95,8 +95,23 @@ namespace WechatBakTool.Pages
{
if (UserReader != null)
{
UserReader.PreDownloadEmoji();
Task.Run(() =>
{
UserReader.PreDownloadEmoji();
MessageBox.Show("所有表情预下载完毕");
});
}
}
private void btn_analyse_Click(object sender, RoutedEventArgs e)
{
if (UserReader == null || Main2.CurrentUserBakConfig == null)
{
MessageBox.Show("请先读取数据");
return;
}
Analyse analyse = new Analyse(Main2.CurrentUserBakConfig, UserReader);
analyse.Show();
}
}
}

View File

@@ -126,7 +126,7 @@
</Setter.Value>
</Setter>
</Style>
<!-- 这里是listview滚动条的滑动块部分样式-->
<Style x:Key="ScrollBarThumbVertical" TargetType="{x:Type Thumb}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
@@ -158,7 +158,7 @@
<Grid Margin="0">
<Label Margin="60,8,0,0" FontWeight="Bold" VerticalAlignment="Top" Content="{Binding NickName}"/>
<Label Margin="60,25,0,8" VerticalAlignment="Top" HorizontalAlignment="Left" Width="380">
<TextBlock Text="{Binding StrContent}" TextWrapping="Wrap" />
<TextBlock Text="{Binding DisplayContent}" TextWrapping="Wrap" />
</Label>
</Grid>
</DataTemplate>
@@ -215,7 +215,7 @@
</ListView>
<Label Content="{Binding WXContact.NickName}" HorizontalAlignment="Left" Margin="258,21,0,0" VerticalAlignment="Top"/>
<ListView x:Name="list_msg" Margin="230,60,0,60" Background="Transparent" BorderThickness="0,1,0,1" BorderBrush="#BB2775b6" ItemTemplate="{DynamicResource MsgText}">
</ListView>
<ComboBox Name="cb_export" Width="120" Height="30" Style="{StaticResource ComboBoxStyle}" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="30,15" ItemsSource="{Binding ExportItems}" SelectedItem="{Binding SelectExportItem}" DisplayMemberPath="Name" SelectedValuePath="Value" IsEnabled="{Binding SelectContact}" Background="#2775b6" />
<Button x:Name="btn_open_workspace" Width="80" Height="30" Style="{StaticResource ButtonStyle}" Content="打开文件夹" BorderBrush="Transparent" BorderThickness="0" Background="#2775b6" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,390,15" Click="btn_open_workspace_Click">
@@ -225,7 +225,7 @@
</Style>
</Button.Resources>
</Button>
<Button x:Name="btn_analyse" Width="80" Height="30" Style="{StaticResource ButtonStyle}" Content="旧版消息工具" BorderBrush="Transparent" BorderThickness="0" Background="#2775b6" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,292,15" Click="btn_analyse_Click" >
<Button x:Name="btn_pre_emoji" Width="80" Height="30" Style="{StaticResource ButtonStyle}" Content="表情预下载" BorderBrush="Transparent" BorderThickness="0" Background="#2775b6" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,292,15" IsEnabled="{Binding SelectContact}" Click="btn_pre_emoji_Click" >
<Button.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="3"/>

View File

@@ -114,23 +114,10 @@ namespace WechatBakTool.Pages
Process.Start("explorer.exe ", Main2.CurrentUserBakConfig!.UserWorkspacePath);
}
private void btn_analyse_Click(object sender, RoutedEventArgs e)
{
if (UserReader == null || Main2.CurrentUserBakConfig == null)
{
MessageBox.Show("请先读取数据");
return;
}
Analyse analyse = new Analyse(Main2.CurrentUserBakConfig, UserReader);
analyse.Show();
}
private void Export_Click(object sender, RoutedEventArgs e)
{
Task.Run(() =>
{
if (ViewModel.WXContact == null || UserReader == null)
{
MessageBox.Show("请选择联系人", "错误");
@@ -170,5 +157,16 @@ namespace WechatBakTool.Pages
});
}
private void btn_pre_emoji_Click(object sender, RoutedEventArgs e)
{
if(UserReader != null && ViewModel.WXContact != null)
{
Task.Run(() => {
UserReader.PreDownloadEmoji(ViewModel.WXContact.UserName);
MessageBox.Show("用户所有表情预下载完毕");
});
}
}
}
}

View File

@@ -24,12 +24,16 @@
<br/>
### 免责声明
**本项目仅供学习使用,严禁商业使用**<br/>
**本项目完全免费,问你要钱的都是骗子**<br/>
**使用本项目初衷是作者研究微信数据库的运行使用,您使用本软件导致的后果,包含但不限于数据损坏,记录丢失等问题,作者不承担相关责任。**<br/>
**因软件特殊性质,请在使用时获得微信账号所有人授权。**
<br/>
### 隐私声明
**本项目不会上传任何你的数据至任何第三方系统**<br/>
**如果发生任何回传行为,请检查是否为第三方修改版本**<br/>
### 近期开发规划
本项目技术栈为:
C# + .NET6.0 + WPF MVVM目前MVVM不是特别完全莫喷 <br/>
@@ -40,6 +44,14 @@ C# + .NET6.0 + WPF MVVM目前MVVM不是特别完全莫喷 <br/>
- [ ] 手动模式(合适离线分析)
<br/>
### 部分问题
Q支持手机端吗<br/>
A<b>在手机端</b>使用迁移功能即可,路径:我->设置->聊天->聊天记录迁移与备份->迁移<br/>
<br/>
Q怎么导出全部的记录<br/>
A工作区->右键->管理,就见了。<br/>
<br/>
### 使用说明
**本说明为新版本说明,即将发版**<br/>
0.安装.NET Desktop Runtime(如已经安装忽略)<br/>

View File

@@ -21,6 +21,7 @@ using System.Windows;
using System.Net.Http;
using System.Reflection.Metadata;
using System.Threading;
using Newtonsoft.Json;
namespace WechatBakTool
{
@@ -37,6 +38,7 @@ namespace WechatBakTool
UserBakConfig = userBakConfig;
LoadDB(path);
InitCache();
EmojiCacheInit();
}
public void LoadDB(string path)
@@ -107,7 +109,7 @@ namespace WechatBakTool
}
}
public void PreDownloadEmoji()
public void PreDownloadEmoji(string username = "")
{
if (UserBakConfig == null)
return;
@@ -115,11 +117,10 @@ namespace WechatBakTool
HttpClientHandler handler = new HttpClientHandler() { AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate };
HttpClient httpClient = new HttpClient(handler);
List<WXMsg> msgs = GetTypeMsg("47");
List<WXMsg> msgs = GetTypeMsg("47", username);
int i = 0;
EmojiCacheInit();
// 下载前的Emoji Cache不用做了在Init的时候已经做了
foreach (var msg in msgs)
{
i++;
@@ -150,14 +151,18 @@ namespace WechatBakTool
url = item != null ? item.InnerText : "";
if (EmojiCache.ContainsKey(md5))
{
i--;
continue;
}
if (url == "")
{
i--;
continue;
}
else
{
string path = Path.Combine(UserBakConfig.UserWorkspacePath, "Emoji", md5 + ".jpg");
string path = Path.Combine(UserBakConfig.UserWorkspacePath, "Emoji", md5 + ".gif");
try
{
HttpResponseMessage res = httpClient.GetAsync(url).Result;
@@ -182,6 +187,9 @@ namespace WechatBakTool
}
}
// 下载完成后可能变化,检查一下
EmojiCacheInit();
}
public byte[]? GetHeadImgCahce(string username)
@@ -298,7 +306,7 @@ namespace WechatBakTool
return con.Query<WXChatRoom>(query);
}
public List<WXMsg> GetTypeMsg(string type)
public List<WXMsg> GetTypeMsg(string type,string username)
{
List<WXMsg> tmp = new List<WXMsg>();
for (int i = 0; i <= 99; i++)
@@ -307,8 +315,17 @@ namespace WechatBakTool
if (con == null)
return tmp;
string query = "select * from MSG where Type=?";
List<WXMsg> wXMsgs = con.Query<WXMsg>(query, type);
List<WXMsg> wXMsgs;
if (username == "")
{
string query = "select * from MSG where Type=?";
wXMsgs = con.Query<WXMsg>(query, type);
}
else
{
string query = "select * from MSG where Type=? and StrTalker = ?";
wXMsgs = con.Query<WXMsg>(query, type, username);
}
tmp.AddRange(wXMsgs);
}
return tmp;
@@ -351,9 +368,9 @@ namespace WechatBakTool
else
w.NickName = contact.NickName;
}
}
// 群聊处理
if (uid.Contains("@chatroom"))
{
string userId = "";
@@ -393,20 +410,37 @@ namespace WechatBakTool
}
}
}
// 发送人名字处理
if (w.IsSender)
w.NickName = "我";
w.DisplayContent = w.StrContent;
// 额外格式处理
if (w.Type != 1)
{
if (w.Type == 10000)
{
w.Type = 1;
w.NickName = "系统消息";
w.StrContent = w.StrContent.Replace("<revokemsg>", "").Replace("</revokemsg>", "");
w.DisplayContent = w.StrContent.Replace("<revokemsg>", "").Replace("</revokemsg>", "");
}
else if (w.Type == 49 && (w.SubType == 6 || w.SubType == 19 || w.SubType == 40))
{
WXSessionAttachInfo? attachInfos = GetWXMsgAtc(w);
if (attachInfos == null)
{
w.DisplayContent = "附件不存在";
}
else
{
w.DisplayContent = Path.Combine(UserBakConfig!.UserResPath, attachInfos.attachPath);
}
}
else
{
w.StrContent = "[界面未支持格式]Type=" + w.Type;
w.DisplayContent = "[界面未支持格式]Type=" + w.Type;
}
}
tmp.Add(w);
@@ -481,9 +515,10 @@ namespace WechatBakTool
if (!Directory.Exists(tmpPath))
Directory.CreateDirectory(tmpPath);
// 这部分是查找
// 如果是图片和视频,从附件库中搜索
string? path = null;
if (type == WXMsgType.Image || type == WXMsgType.Video)
if (type == WXMsgType.Image || type == WXMsgType.Video || type == WXMsgType.File)
{
WXSessionAttachInfo? atcInfo = GetWXMsgAtc(msg);
if (atcInfo == null)
@@ -507,39 +542,61 @@ namespace WechatBakTool
}
path = tmp_file_path;
}
else if (type == WXMsgType.Emoji)
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(msg.StrContent);
XmlNode? node = xmlDocument.SelectSingleNode("/msg/emoji");
if (node != null)
{
if (node.Attributes != null)
{
XmlNode? item = node.Attributes.GetNamedItem("md5");
string md5 = item != null ? item.InnerText : "";
if (EmojiCache.ContainsKey(md5))
{
path = string.Format("Emoji\\{0}.gif", md5);
}
}
}
}
if (path == null)
return null;
// 这部分是解密
// 获取到原路径后,开始进行解密转移,只有图片和语音需要解密,解密后是直接归档目录
if(type == WXMsgType.Image || type== WXMsgType.Audio)
if (type == WXMsgType.Image || type == WXMsgType.Audio)
{
path = DecryptAttachment(type, path);
}
else if (type == WXMsgType.Video)
else if (type == WXMsgType.Video || type == WXMsgType.File)
{
string video_dir = Path.Combine(UserBakConfig.UserWorkspacePath, "Video");
if(!Directory.Exists(video_dir))
Directory.CreateDirectory(video_dir);
string to_dir;
if (type == WXMsgType.Video)
to_dir = Path.Combine(UserBakConfig.UserWorkspacePath, "Video");
else
to_dir = Path.Combine(UserBakConfig.UserWorkspacePath, "File");
if (!Directory.Exists(to_dir))
Directory.CreateDirectory(to_dir);
FileInfo fileInfo = new FileInfo(path);
// 目标视频路径
string video_file_path = Path.Combine(video_dir, fileInfo.Name);
string to_file_path = Path.Combine(to_dir, fileInfo.Name);
// 视频的路径是相对路径,需要加上资源目录
path = Path.Combine(UserBakConfig.UserResPath, path);
// 原文件存在,目标不存在
if (!File.Exists(video_file_path) && File.Exists(path))
if (!File.Exists(to_file_path) && File.Exists(path))
{
// 复制
File.Copy(path, video_file_path);
path = video_file_path;
File.Copy(path, to_file_path);
path = to_file_path;
}
else if (File.Exists(video_file_path))
else if (File.Exists(to_file_path))
{
path = video_file_path;
path = to_file_path;
}
else
return null;
}
if (path == null)
@@ -611,5 +668,6 @@ namespace WechatBakTool
Video = 1,
Audio = 2,
File = 3,
Emoji = 4,
}
}

View File

@@ -6,9 +6,9 @@
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<AssemblyVersion>0.9.3.0</AssemblyVersion>
<FileVersion>0.9.3.0</FileVersion>
<Version>0.9.3.0</Version>
<AssemblyVersion>0.9.4.0</AssemblyVersion>
<FileVersion>0.9.4.0</FileVersion>
<Version>0.9.4.0</Version>
</PropertyGroup>
<ItemGroup>