完全移植,但还有多处二义性错误
This commit is contained in:
BIN
Windows/CS/Framework4.0/Toprie/Toprie.ico
Normal file
BIN
Windows/CS/Framework4.0/Toprie/Toprie.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Threading;
|
||||
|
||||
namespace JoyD.Windows.CS.Toprie
|
||||
{
|
||||
@@ -59,7 +60,12 @@ namespace JoyD.Windows.CS.Toprie
|
||||
/// </summary>
|
||||
private void InitializeDeviceManager()
|
||||
{
|
||||
_deviceManager = new DeviceManager();
|
||||
_deviceManager = new DeviceManager
|
||||
{
|
||||
AutoReconnectEnabled = true,
|
||||
ReconnectInterval = 2000, // 2秒
|
||||
MaxReconnectAttempts = 5
|
||||
};
|
||||
|
||||
// 注册图像接收事件
|
||||
_deviceManager.ImageReceived += DeviceManager_ImageReceived;
|
||||
@@ -69,6 +75,8 @@ namespace JoyD.Windows.CS.Toprie
|
||||
|
||||
// 注册连接异常事件
|
||||
_deviceManager.ConnectionException += DeviceManager_ConnectionException;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -81,34 +89,98 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动并连接设备
|
||||
/// 启动相机
|
||||
/// </summary>
|
||||
public void StartCamera()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 设置为热图模式
|
||||
_deviceManager.CurrentImageMode = ImageMode.Thermal;
|
||||
|
||||
// 启用自动重连
|
||||
_deviceManager.AutoReconnectEnabled = true;
|
||||
|
||||
// 连接设备
|
||||
bool connected = _deviceManager.ConnectDevice();
|
||||
|
||||
if (connected)
|
||||
// 只有在没有接收图像时才启动
|
||||
if (!_isReceivingImage)
|
||||
{
|
||||
// 开始接收图像
|
||||
StartReceiveImage();
|
||||
// 清理错误显示
|
||||
ShowError(string.Empty);
|
||||
|
||||
// 使用异步方式连接设备和设置模式,避免UI线程阻塞
|
||||
ThreadPool.QueueUserWorkItem(delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
// 设置为热图模式
|
||||
bool modeSet = false;
|
||||
try
|
||||
{
|
||||
_deviceManager.SetImageMode(ImageMode.Thermal);
|
||||
modeSet = true;
|
||||
Console.WriteLine("已设置热图模式");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"设置热图模式失败: {ex.Message}");
|
||||
}
|
||||
|
||||
// 启用自动重连
|
||||
_deviceManager.AutoReconnectEnabled = true;
|
||||
|
||||
// 尝试连接设备
|
||||
if (_deviceManager.ConnectDevice())
|
||||
{
|
||||
Console.WriteLine("设备连接成功");
|
||||
|
||||
// 如果热图模式未成功设置,在连接成功后再次尝试
|
||||
if (!modeSet)
|
||||
{
|
||||
try
|
||||
{
|
||||
_deviceManager.SetImageMode(ImageMode.Thermal);
|
||||
Console.WriteLine("连接后已设置热图模式");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"连接后设置热图模式失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 在连接成功后开始接收图像
|
||||
this.Invoke(new Action(() =>
|
||||
{
|
||||
// 再次检查是否已在UI线程设置为接收状态
|
||||
if (!_isReceivingImage)
|
||||
{
|
||||
StartReceiveImage();
|
||||
}
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 连接失败时显示错误
|
||||
string errorMsg = "设备连接失败,请检查设备状态或网络连接";
|
||||
this.Invoke(new Action(() => ShowError(errorMsg)));
|
||||
Console.WriteLine(errorMsg);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 捕获所有异常,防止后台线程崩溃
|
||||
string errorMsg = $"启动相机异常: {ex.Message}";
|
||||
this.Invoke(new Action(() => ShowError(errorMsg)));
|
||||
Console.WriteLine(errorMsg);
|
||||
Console.WriteLine(ex.StackTrace);
|
||||
}
|
||||
});
|
||||
|
||||
Console.WriteLine("相机启动流程已开始");
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowError("无法连接到设备,请检查设备是否在线");
|
||||
Console.WriteLine("相机已在运行状态");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowError($"启动相机失败: {ex.Message}");
|
||||
Console.WriteLine($"启动相机主流程错误: {ex.Message}");
|
||||
Console.WriteLine(ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,36 +227,176 @@ namespace JoyD.Windows.CS.Toprie
|
||||
/// </summary>
|
||||
private void DeviceManager_ImageReceived(object sender, ImageReceivedEventArgs e)
|
||||
{
|
||||
if (this.InvokeRequired)
|
||||
// 不在这里使用using,因为我们需要将图像传递给UI线程
|
||||
Image image = e.ImageData;
|
||||
|
||||
if (image == null)
|
||||
{
|
||||
// 在UI线程上更新图像
|
||||
this.Invoke(new Action(() => UpdateImage(e.ImageData)));
|
||||
Console.WriteLine("接收到空图像数据");
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
try
|
||||
{
|
||||
UpdateImage(e.ImageData);
|
||||
// 尝试创建图像的克隆以传递给UI线程
|
||||
Image clonedImage = null;
|
||||
try
|
||||
{
|
||||
clonedImage = (Image)image.Clone();
|
||||
|
||||
// 验证克隆的图像是否有效
|
||||
if (clonedImage.Width <= 0 || clonedImage.Height <= 0)
|
||||
{
|
||||
Console.WriteLine("克隆的图像尺寸无效");
|
||||
clonedImage.Dispose();
|
||||
image.Dispose(); // 确保原始图像也被释放
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"克隆图像失败: {ex.Message}");
|
||||
image.Dispose(); // 确保原始图像被释放
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保在UI线程上更新,并且检查控件是否已被释放
|
||||
if (!this.IsDisposed && !this.Disposing)
|
||||
{
|
||||
if (this.InvokeRequired)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用BeginInvoke在UI线程上更新图像,避免可能的死锁问题
|
||||
this.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateImageOnUI(clonedImage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"更新UI图像失败: {ex.Message}");
|
||||
// 如果UI更新失败,确保克隆的图像被释放
|
||||
if (clonedImage != null)
|
||||
{
|
||||
clonedImage.Dispose();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 异常情况下确保释放图像资源
|
||||
clonedImage?.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 在UI线程上直接更新图像
|
||||
UpdateImageOnUI(clonedImage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果控件已释放,确保释放图像资源
|
||||
clonedImage.Dispose();
|
||||
}
|
||||
|
||||
// 释放原始图像资源
|
||||
image.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"处理图像接收事件失败: {ex.Message}");
|
||||
// 确保在任何异常情况下都释放原始图像
|
||||
if (image != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
image.Dispose();
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新图像显示
|
||||
/// 在UI线程上更新图像
|
||||
/// </summary>
|
||||
private void UpdateImage(Image image)
|
||||
private void UpdateImageOnUI(Image image)
|
||||
{
|
||||
// 空值检查
|
||||
if (image == null)
|
||||
{
|
||||
Console.WriteLine("传入UpdateImageOnUI的图像为空");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 释放之前的图像资源
|
||||
if (imageBox.Image != null)
|
||||
// 更新图像前先检查控件是否存在/已释放
|
||||
if (this.IsDisposed || imageBox == null || imageBox.IsDisposed)
|
||||
{
|
||||
imageBox.Image.Dispose();
|
||||
// 如果控件已释放,确保图像也被释放
|
||||
image.Dispose();
|
||||
Console.WriteLine("控件已释放,无法更新图像");
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置新图像
|
||||
imageBox.Image = (Image)image.Clone();
|
||||
// 保存旧图像引用,以便在设置新图像后释放
|
||||
Image oldImage = imageBox.Image;
|
||||
|
||||
try
|
||||
{
|
||||
// 尝试设置新图像
|
||||
imageBox.Image = image;
|
||||
|
||||
// 验证图像是否成功设置
|
||||
if (imageBox.Image != image)
|
||||
{
|
||||
Console.WriteLine("图像设置失败,可能参数无效");
|
||||
image.Dispose(); // 释放未成功设置的图像
|
||||
return;
|
||||
}
|
||||
|
||||
// 仅在新图像成功设置后释放旧图像
|
||||
if (oldImage != null && oldImage != image) // 防止自引用释放
|
||||
{
|
||||
oldImage.Dispose();
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex) when (ex.Message.Contains("参数无效"))
|
||||
{
|
||||
// 特别处理"参数无效"异常
|
||||
Console.WriteLine($"图像参数无效: {ex.Message}");
|
||||
image.Dispose(); // 释放无效图像
|
||||
|
||||
// 尝试设置旧图像回来,如果可能的话
|
||||
if (oldImage != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
imageBox.Image = oldImage;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 如果设置旧图像也失败,释放它
|
||||
oldImage.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"更新图像失败: {ex.Message}");
|
||||
Console.WriteLine($"更新图像UI失败: {ex.Message}");
|
||||
// 确保在任何异常情况下都释放传入的图像资源
|
||||
try
|
||||
{
|
||||
image.Dispose();
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,13 +405,26 @@ namespace JoyD.Windows.CS.Toprie
|
||||
/// </summary>
|
||||
private void DeviceManager_ConnectionStatusChanged(object sender, ConnectionStatusChangedEventArgs e)
|
||||
{
|
||||
if (this.InvokeRequired)
|
||||
// 确保在UI线程上更新,并且检查控件是否已被释放
|
||||
if (!this.IsDisposed && !this.Disposing)
|
||||
{
|
||||
this.Invoke(new Action(() => HandleConnectionStatusChanged(e)));
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleConnectionStatusChanged(e);
|
||||
if (this.InvokeRequired)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用BeginInvoke代替Invoke,避免可能的死锁问题
|
||||
this.BeginInvoke(new Action(() => HandleConnectionStatusChanged(e)));
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// 捕获控件已释放异常,避免程序崩溃
|
||||
Console.WriteLine("控件已释放,跳过UI更新");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleConnectionStatusChanged(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,10 +433,28 @@ namespace JoyD.Windows.CS.Toprie
|
||||
/// </summary>
|
||||
private void HandleConnectionStatusChanged(ConnectionStatusChangedEventArgs e)
|
||||
{
|
||||
// 更新UI状态
|
||||
UpdateUIState(e.NewStatus == ConnectionStatus.Connected);
|
||||
|
||||
switch (e.NewStatus)
|
||||
{
|
||||
case ConnectionStatus.Connected:
|
||||
Console.WriteLine("设备已连接");
|
||||
|
||||
// 清除错误信息
|
||||
ShowError(string.Empty);
|
||||
|
||||
// 确保设置为热图模式
|
||||
try
|
||||
{
|
||||
_deviceManager.SetImageMode(ImageMode.Thermal);
|
||||
Console.WriteLine("连接成功后确认热图模式");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"连接成功后设置热图模式失败: {ex.Message}");
|
||||
}
|
||||
|
||||
if (!_isReceivingImage)
|
||||
{
|
||||
StartReceiveImage();
|
||||
@@ -219,31 +462,109 @@ namespace JoyD.Windows.CS.Toprie
|
||||
break;
|
||||
case ConnectionStatus.Disconnected:
|
||||
Console.WriteLine("设备已断开连接");
|
||||
_isReceivingImage = false;
|
||||
|
||||
// 停止接收图像
|
||||
if (_isReceivingImage)
|
||||
{
|
||||
_deviceManager.StopReceiveImage();
|
||||
_isReceivingImage = false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(e.Message))
|
||||
{
|
||||
ShowError(e.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowError("设备连接已断开");
|
||||
}
|
||||
break;
|
||||
case ConnectionStatus.Connecting:
|
||||
case ConnectionStatus.Reconnecting:
|
||||
Console.WriteLine("正在连接设备...");
|
||||
Console.WriteLine($"正在连接设备...{(!string.IsNullOrEmpty(e.Message) ? " " + e.Message : "")}");
|
||||
ShowError(string.Empty); // 清除之前的错误信息
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新UI状态
|
||||
/// </summary>
|
||||
/// <param name="isConnected">是否已连接</param>
|
||||
private void UpdateUIState(bool isConnected)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 根据连接状态更新图像框状态
|
||||
if (imageBox != null && !imageBox.IsDisposed)
|
||||
{
|
||||
if (isConnected)
|
||||
{
|
||||
imageBox.BorderStyle = BorderStyle.FixedSingle;
|
||||
}
|
||||
else
|
||||
{
|
||||
imageBox.BorderStyle = BorderStyle.Fixed3D;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新图像框的边框颜色,提供视觉反馈
|
||||
if (imageBox != null)
|
||||
{
|
||||
imageBox.BorderStyle = isConnected ? BorderStyle.FixedSingle : BorderStyle.Fixed3D;
|
||||
// 可选:设置不同的边框颜色以提供更好的视觉反馈
|
||||
if (isConnected)
|
||||
{
|
||||
imageBox.BackColor = Color.LightGreen;
|
||||
}
|
||||
else
|
||||
{
|
||||
imageBox.BackColor = Color.LightGray;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"UI状态已更新为: {(isConnected ? "已连接" : "未连接")}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"更新UI状态时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设备管理器连接异常事件处理
|
||||
/// </summary>
|
||||
private void DeviceManager_ConnectionException(object sender, ConnectionExceptionEventArgs e)
|
||||
{
|
||||
if (this.InvokeRequired)
|
||||
// 确保在UI线程上更新,并且检查控件是否已被释放
|
||||
if (!this.IsDisposed && !this.Disposing)
|
||||
{
|
||||
this.Invoke(new Action(() => ShowError($"连接异常: {e.ContextMessage}")));
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowError($"连接异常: {e.ContextMessage}");
|
||||
if (this.InvokeRequired)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用BeginInvoke在UI线程上显示错误
|
||||
this.BeginInvoke(new Action(() =>
|
||||
{
|
||||
// 记录异常日志
|
||||
Console.WriteLine($"连接异常发生于 {e.Timestamp}: {e.ContextMessage}\n{e.Exception.Message}\n{e.Exception.StackTrace}");
|
||||
|
||||
ShowError($"连接异常: {e.ContextMessage}");
|
||||
}));
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// 捕获控件已释放异常,避免程序崩溃
|
||||
Console.WriteLine("控件已释放,跳过异常处理");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 记录异常日志
|
||||
Console.WriteLine($"连接异常发生于 {e.Timestamp}: {e.ContextMessage}\n{e.Exception.Message}\n{e.Exception.StackTrace}");
|
||||
|
||||
ShowError($"连接异常: {e.ContextMessage}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,8 +578,19 @@ namespace JoyD.Windows.CS.Toprie
|
||||
// 可以根据需要添加UI上的错误显示
|
||||
// 这里简化处理,只在控制台输出
|
||||
|
||||
// 检查imageBox是否存在且尺寸有效
|
||||
if (imageBox == null || imageBox.Width <= 0 || imageBox.Height <= 0)
|
||||
{
|
||||
Console.WriteLine("imageBox尺寸无效,跳过错误显示图像创建");
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保使用有效的尺寸创建Bitmap
|
||||
int width = Math.Max(100, imageBox.Width);
|
||||
int height = Math.Max(100, imageBox.Height);
|
||||
|
||||
// 如果需要在图像区域显示错误文字,可以使用以下代码
|
||||
using (Bitmap errorBitmap = new Bitmap(imageBox.Width, imageBox.Height))
|
||||
using (Bitmap errorBitmap = new Bitmap(width, height))
|
||||
using (Graphics g = Graphics.FromImage(errorBitmap))
|
||||
{
|
||||
g.Clear(Color.Black);
|
||||
@@ -272,7 +604,7 @@ namespace JoyD.Windows.CS.Toprie
|
||||
g.DrawString(message, font, brush, textLocation);
|
||||
}
|
||||
|
||||
UpdateImage(errorBitmap);
|
||||
UpdateImageOnUI(errorBitmap);
|
||||
}
|
||||
|
||||
// 启动定时器,3秒后清除错误显示
|
||||
@@ -287,7 +619,19 @@ namespace JoyD.Windows.CS.Toprie
|
||||
{
|
||||
_errorDisplayTimer.Stop();
|
||||
// 清除错误显示,恢复到等待图像状态
|
||||
using (Bitmap waitingBitmap = new Bitmap(imageBox.Width, imageBox.Height))
|
||||
|
||||
// 检查imageBox是否存在且尺寸有效
|
||||
if (imageBox == null || imageBox.Width <= 0 || imageBox.Height <= 0)
|
||||
{
|
||||
Console.WriteLine("imageBox尺寸无效,跳过等待图像创建");
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保使用有效的尺寸创建Bitmap
|
||||
int width = Math.Max(100, imageBox.Width);
|
||||
int height = Math.Max(100, imageBox.Height);
|
||||
|
||||
using (Bitmap waitingBitmap = new Bitmap(width, height))
|
||||
using (Graphics g = Graphics.FromImage(waitingBitmap))
|
||||
{
|
||||
g.Clear(Color.Black);
|
||||
@@ -302,7 +646,7 @@ namespace JoyD.Windows.CS.Toprie
|
||||
g.DrawString(waitingText, font, brush, textLocation);
|
||||
}
|
||||
|
||||
UpdateImage(waitingBitmap);
|
||||
UpdateImageOnUI(waitingBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,9 +674,12 @@ namespace JoyD.Windows.CS.Toprie
|
||||
// 取消注册事件并释放设备管理器
|
||||
if (_deviceManager != null)
|
||||
{
|
||||
|
||||
// 移除所有事件监听
|
||||
_deviceManager.ImageReceived -= DeviceManager_ImageReceived;
|
||||
_deviceManager.ConnectionStatusChanged -= DeviceManager_ConnectionStatusChanged;
|
||||
_deviceManager.ConnectionException -= DeviceManager_ConnectionException;
|
||||
// 释放设备管理器资源
|
||||
_deviceManager.Dispose();
|
||||
_deviceManager = null;
|
||||
}
|
||||
@@ -346,7 +693,7 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
|
||||
// 无论是否在设计模式下,都需要释放图像资源
|
||||
if (imageBox.Image != null)
|
||||
if (imageBox != null && !imageBox.IsDisposed && imageBox.Image != null)
|
||||
{
|
||||
imageBox.Image.Dispose();
|
||||
imageBox.Image = null;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,9 @@
|
||||
<IncludeBuildOutput>true</IncludeBuildOutput>
|
||||
<IncludeContentInPack>true</IncludeContentInPack>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>Toprie.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<!-- 添加NuGet打包支持 -->
|
||||
<Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />
|
||||
<!-- 确保项目支持pack目标 -->
|
||||
@@ -74,5 +77,8 @@
|
||||
<DependentUpon>Camera.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Toprie.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
BIN
Windows/CS/Framework4.0/Toprie/Toprie/Toprie.ico
Normal file
BIN
Windows/CS/Framework4.0/Toprie/Toprie/Toprie.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
Reference in New Issue
Block a user