ソースを参照

01 feat: 上传AppTime源文件

Chunxian Zhang 2年前
コミット
5371ecd1e5
145個のファイルの変更253989行の追加0行の削除
  1. +1
    -0
      .gitignore
  2. +25
    -0
      appTime/AppTime-0.12/AppTime.sln
  3. +2
    -0
      appTime/AppTime-0.12/AppTime/.gitattributes
  4. +2837
    -0
      appTime/AppTime-0.12/AppTime/AppTime.csproj
  5. +623
    -0
      appTime/AppTime-0.12/AppTime/Controller.cs
  6. +136
    -0
      appTime/AppTime-0.12/AppTime/DB.cs
  7. +114
    -0
      appTime/AppTime-0.12/AppTime/Ffmpeg.cs
  8. +267
    -0
      appTime/AppTime-0.12/AppTime/FrmMain.Designer.cs
  9. +165
    -0
      appTime/AppTime-0.12/AppTime/FrmMain.cs
  10. +2391
    -0
      appTime/AppTime-0.12/AppTime/FrmMain.resx
  11. +63
    -0
      appTime/AppTime-0.12/AppTime/InitDB.cs
  12. +82
    -0
      appTime/AppTime-0.12/AppTime/Program.cs
  13. +36
    -0
      appTime/AppTime-0.12/AppTime/Properties/AssemblyInfo.cs
  14. +63
    -0
      appTime/AppTime-0.12/AppTime/Properties/Resources.Designer.cs
  15. +117
    -0
      appTime/AppTime-0.12/AppTime/Properties/Resources.resx
  16. +62
    -0
      appTime/AppTime-0.12/AppTime/Properties/Settings.Designer.cs
  17. +15
    -0
      appTime/AppTime-0.12/AppTime/Properties/Settings.settings
  18. +69
    -0
      appTime/AppTime-0.12/AppTime/Properties/app.manifest
  19. +418
    -0
      appTime/AppTime-0.12/AppTime/Recorder.cs
  20. +64
    -0
      appTime/AppTime-0.12/AppTime/Utils.cs
  21. +178
    -0
      appTime/AppTime-0.12/AppTime/WebServer.cs
  22. +1799
    -0
      appTime/AppTime-0.12/AppTime/WinApi.cs
  23. +54
    -0
      appTime/AppTime-0.12/AppTime/app.config
  24. +11
    -0
      appTime/AppTime-0.12/AppTime/app.manifest
  25. バイナリ
      appTime/AppTime-0.12/AppTime/apptime.ico
  26. バイナリ
      appTime/AppTime-0.12/AppTime/data.db
  27. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/avcodec-57.dll
  28. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/avcodec-58.dll
  29. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/avdevice-57.dll
  30. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/avdevice-58.dll
  31. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/avfilter-6.dll
  32. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/avfilter-7.dll
  33. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/avformat-57.dll
  34. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/avformat-58.dll
  35. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/avutil-55.dll
  36. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/avutil-56.dll
  37. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/ffmpeg.exe
  38. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/postproc-54.dll
  39. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/postproc-55.dll
  40. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/swresample-2.dll
  41. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/swresample-3.dll
  42. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/swscale-4.dll
  43. バイナリ
      appTime/AppTime-0.12/AppTime/ffmpeg/swscale-5.dll
  44. +4
    -0
      appTime/AppTime-0.12/AppTime/obj/Debug/.NETFramework,Version=v4.8.AssemblyAttributes.cs
  45. バイナリ
      appTime/AppTime-0.12/AppTime/obj/Debug/AppTime.csproj.AssemblyReference.cache
  46. +7
    -0
      appTime/AppTime-0.12/AppTime/packages.config
  47. バイナリ
      appTime/AppTime-0.12/files/icon.jpg
  48. バイナリ
      appTime/AppTime-0.12/files/list.jpg
  49. バイナリ
      appTime/AppTime-0.12/files/playback.gif
  50. バイナリ
      appTime/AppTime-0.12/files/playback.mp4
  51. バイナリ
      appTime/AppTime-0.12/files/tag.jpg
  52. バイナリ
      appTime/AppTime-0.12/files/tagview.jpg
  53. バイナリ
      appTime/AppTime-0.12/files/time.jpg
  54. バイナリ
      appTime/AppTime-0.12/files/timebar.mp4
  55. バイナリ
      appTime/AppTime-0.12/files/tv.jpg
  56. バイナリ
      appTime/AppTime-0.12/files/view.jpg
  57. バイナリ
      appTime/AppTime-0.12/files/vs.jpg
  58. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/.signature.p7s
  59. +20
    -0
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/LICENSE.md
  60. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/Newtonsoft.Json.12.0.3.nupkg
  61. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net20/Newtonsoft.Json.dll
  62. +10298
    -0
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net20/Newtonsoft.Json.xml
  63. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net35/Newtonsoft.Json.dll
  64. +9446
    -0
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net35/Newtonsoft.Json.xml
  65. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net40/Newtonsoft.Json.dll
  66. +9646
    -0
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net40/Newtonsoft.Json.xml
  67. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net45/Newtonsoft.Json.dll
  68. +11262
    -0
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net45/Newtonsoft.Json.xml
  69. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.0/Newtonsoft.Json.dll
  70. +10950
    -0
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.0/Newtonsoft.Json.xml
  71. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.3/Newtonsoft.Json.dll
  72. +11072
    -0
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.3/Newtonsoft.Json.xml
  73. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.dll
  74. +11237
    -0
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.xml
  75. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.dll
  76. +9010
    -0
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.xml
  77. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.dll
  78. +10950
    -0
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.xml
  79. バイナリ
      appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/packageIcon.png
  80. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/.signature.p7s
  81. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/SQLitePCLRaw.core.1.1.14.nupkg
  82. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/MonoAndroid/SQLitePCLRaw.core.dll
  83. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/Xamarin.Mac20/SQLitePCLRaw.core.dll
  84. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/Xamarin.iOS10/SQLitePCLRaw.core.dll
  85. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/net35/SQLitePCLRaw.core.dll
  86. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/net40/SQLitePCLRaw.core.dll
  87. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/net45/SQLitePCLRaw.core.dll
  88. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/netstandard1.0/SQLitePCLRaw.core.dll
  89. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/netstandard1.1/SQLitePCLRaw.core.dll
  90. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/portable-net40+sl5+netcore45+wp8+MonoAndroid10+MonoTouch10+Xamarin.iOS10/SQLitePCLRaw.core.dll
  91. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/portable-net45+netcore45+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10/SQLitePCLRaw.core.dll
  92. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/portable-net45+netcore45+wpa81+wp8+MonoAndroid10+MonoTouch10+Xamarin.iOS10/SQLitePCLRaw.core.dll
  93. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/uap10.0/SQLitePCLRaw.core.dll
  94. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/win8/SQLitePCLRaw.core.dll
  95. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/win81/SQLitePCLRaw.core.dll
  96. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/wpa81/SQLitePCLRaw.core.dll
  97. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.provider.e_sqlite3.net45.1.1.14/.signature.p7s
  98. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.provider.e_sqlite3.net45.1.1.14/SQLitePCLRaw.provider.e_sqlite3.net45.1.1.14.nupkg
  99. バイナリ
      appTime/AppTime-0.12/packages/SQLitePCLRaw.provider.e_sqlite3.net45.1.1.14/lib/net45/SQLitePCLRaw.provider.e_sqlite3.dll
  100. バイナリ
      appTime/AppTime-0.12/packages/System.Data.SQLite.Core.1.0.113.1/.signature.p7s

+ 1
- 0
.gitignore ファイルの表示

@ -0,0 +1 @@
webui

+ 25
- 0
appTime/AppTime-0.12/AppTime.sln ファイルの表示

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30907.101
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppTime", "AppTime\AppTime.csproj", "{4892E56B-5942-444B-B41C-1E4F22A3752A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4892E56B-5942-444B-B41C-1E4F22A3752A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4892E56B-5942-444B-B41C-1E4F22A3752A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4892E56B-5942-444B-B41C-1E4F22A3752A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4892E56B-5942-444B-B41C-1E4F22A3752A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2525871C-F224-48A9-9189-E784A51F63A8}
EndGlobalSection
EndGlobal

+ 2
- 0
appTime/AppTime-0.12/AppTime/.gitattributes ファイルの表示

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

+ 2837
- 0
appTime/AppTime-0.12/AppTime/AppTime.csproj
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 623
- 0
appTime/AppTime-0.12/AppTime/Controller.cs ファイルの表示

@ -0,0 +1,623 @@

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static AppTime.Recorder;
namespace AppTime
{
class Controller
{
public WebServer Server =>Program.server;
public Recorder Recorder=>Program.recorder;
#region basic
int getColor(string str)
{
return str.GetHashCode();
}
static Dictionary<long, int> appColors = new Dictionary<long, int>();
int getAppColor(long appId)
{
if(!appColors.TryGetValue(appId, out var color))
{
var text = db.ExecuteValue<string>($"select text from app where id={appId}");
lock (appColors)
{
color = appColors[appId] = getColor(text);
}
}
return color;
}
static Dictionary<long, int> tagColors = new Dictionary<long, int>();
int getTagColor(long tagId)
{
if (!tagColors.TryGetValue(tagId, out var color))
{
if (tagId == -1)
{
color = tagColors[tagId] = BitConverter.ToInt32(new byte[] { 0xFF, 0x99, 0x99, 0x99 }, 0);
}
else
{
var text = db.ExecuteValue<string>($"select text from tag where id={tagId}");
color = tagColors[tagId] = getColor(text);
}
}
return color;
}
public unsafe byte[] getPeriodBar(DateTime timefrom, DateTime timeto, string view, int width)
{
if (width <= 0 || width > 8000)
{
return null;
}
var totalsecs = (timeto - timefrom).TotalSeconds;
IEnumerable<dynamic> data;
if (view == "app")
{
data = db.ExecuteDynamic(@"
select
app.id appId,
p.timeStart,
p.timeEnd
from app
join win on win.appid = app.id
join period p on p.winid = win.id
where
timeStart between @v0 and @v1
or timeEnd between @v0 and @v1
or @v0 between timeStart and timeEnd
order by p.timeStart
",
timefrom, timeto
);
}
else
{
data = db.ExecuteDynamic(@"
select
ifnull(tag.id,-1) tagId,
p.timeStart,
p.timeEnd
from app
join win on win.appid = app.id
join period p on p.winid = win.id
left join tag on tag.id = win.tagId or (win.tagId = 0 and tag.id = app.tagId)
where
timeStart between @v0 and @v1
or timeEnd between @v0 and @v1
or @v0 between timeStart and timeEnd
order by p.timeStart
",
timefrom, timeto
);
}
//绘制PeriodBar,直接写内存比gdi+快
var imgdata = new int[width];
foreach (var period in data)
{
var from = Math.Max(0, (int)Math.Round((period.timeStart - timefrom).TotalSeconds / totalsecs * width));
var to = Math.Min(width - 1, (int)Math.Round((period.timeEnd - timefrom).TotalSeconds / totalsecs * width));
for (var x = from; x <= Math.Min(width - 1, to); x++)
{
imgdata[x] = view == "app" ? (int)getAppColor(period.appId) : (int)getTagColor(period.tagId);
}
}
fixed (int* p = &imgdata[0])
{
var ptr = new IntPtr(p);
using var bmp = new Bitmap(width, 1, width * 4, PixelFormat.Format32bppArgb, ptr);
using var mem = new MemoryStream();
bmp.Save(mem, ImageFormat.Png);
return mem.ToArray();
}
}
public object getTree(DateTime timefrom, DateTime timeto, string view, long parentKey)
{
var result = new List<object>();
var totalSeconds = (timeto - timefrom).TotalSeconds;
IEnumerable<dynamic> data;
if (view == "app")
{
if (parentKey == 0)
{
data = db.ExecuteDynamic(@"
select
app.id appId,
app.text appText,
tag.text tagText,
sum(julianday(case when timeEnd > @v1 then @v1 else timeEnd end) -
julianday(case when timeStart < @v0 then @v0 else timeStart end)) days
from app
join win on win.appid = app.id
join period p on p.winid = win.id
left join tag on tag.id = app.tagId
where
timeStart between @v0 and @v1
or timeEnd between @v0 and @v1
or @v0 between timeStart and timeEnd
group by app.id
order by days desc
",
timefrom, timeto
);
foreach (var i in data)
{
var time = new TimeSpan((long)(i.days * TimeSpan.TicksPerDay));
result.Add(
new
{
i.appId,
i.tagText,
text = i.appText,
time = time.ToString(@"hh\:mm\:ss"),
percent = Math.Round(time.TotalSeconds * 100 / totalSeconds, 2) + "%",
children = new object[0]
}
);
}
return result;
}
data = db.ExecuteDynamic(@"
select
win.id winId,
win.text winText,
tag.text tagText,
sum(julianday(case when timeEnd > @v1 then @v1 else timeEnd end) -
julianday(case when timeStart < @v0 then @v0 else timeStart end)) days
from win
join period p on p.winid = win.id
left join tag on tag.id = win.tagId
where
win.appId = @v2
and (
timeStart between @v0 and @v1
or timeEnd between @v0 and @v1
or @v0 between timeStart and timeEnd
)
group by win.id
order by days desc
",
timefrom, timeto, parentKey
);
foreach (var i in data)
{
var time = new TimeSpan((long)(i.days * TimeSpan.TicksPerDay));
result.Add(
new
{
i.winId,
i.tagText,
text = string.IsNullOrWhiteSpace(i.winText) ? "(无标题)" : i.winText,
time = time.ToString(@"hh\:mm\:ss"),
percent = Math.Round(time.TotalSeconds * 100 / totalSeconds, 2) + "%"
}
);
}
return result;
}
if (parentKey == 0)
{
data = db.ExecuteDynamic(@"
select
ifnull(tag.id,-1) tagId,
ifnull(tag.text, '()') tagText,
sum(julianday(case when timeEnd > @v1 then @v1 else timeEnd end) -
julianday(case when timeStart < @v0 then @v0 else timeStart end)) days
from win
join app on app.id = win.appid
join period p on p.winid = win.id
left join tag on tag.id = win.tagId or (win.tagId = 0 and tag.id = app.tagId)
where
timeStart between @v0 and @v1
or timeEnd between @v0 and @v1
or @v0 between timeStart and timeEnd
group by tag.id
order by days desc
",
timefrom, timeto
);
foreach (var i in data)
{
var time = new TimeSpan((long)(i.days * TimeSpan.TicksPerDay));
result.Add(
new
{
i.tagId,
i.tagText,
time = time.ToString(@"hh\:mm\:ss"),
percent = Math.Round(time.TotalSeconds * 100 / totalSeconds, 2) + "%"
}
);
}
return result;
}
data = db.ExecuteDynamic(@"
select
win.id winId,
win.text winText,
sum(julianday(case when timeEnd > @v1 then @v1 else timeEnd end) -
julianday(case when timeStart < @v0 then @v0 else timeStart end)) days
from win
join period p on p.winid = win.id
join app on app.id = win.appid
left join tag on tag.id = win.tagId or (win.tagId = 0 and tag.id = app.tagId)
where
ifnull(tag.id, -1) = @v2
and (
timeStart between @v0 and @v1
or timeEnd between @v0 and @v1
or @v0 between timeStart and timeEnd
)
group by win.id
order by days desc
",
timefrom, timeto, parentKey
);
foreach (var i in data)
{
var time = new TimeSpan((long)(i.days * TimeSpan.TicksPerDay));
result.Add(
new
{
i.winId,
text = string.IsNullOrWhiteSpace(i.winText) ? "(无标题)" : i.winText,
time = time.ToString(@"hh\:mm\:ss"),
percent = Math.Round(time.TotalSeconds * 100 / totalSeconds, 2) + "%"
}
);
}
return result;
}
/// <summary>
/// 时间
/// </summary>
public class TimeInfo
{
/// <summary>
/// 原始时间
/// </summary>
public DateTime timeSrc;
/// <summary>
/// 切换到应用的时间
/// </summary>
public DateTime timeStart;
/// <summary>
/// 应用名
/// </summary>
public string app;
/// <summary>
/// 应用id
/// </summary>
public long appId;
/// <summary>
/// 窗口标题
/// </summary>
public string title;
}
/// <summary>
/// 获取指定时间的记录信息
/// </summary>
/// <param name="time"></param>
/// <returns></returns>
public TimeInfo getTimeInfo(DateTime time)
{
var data = db.ExecuteDynamic(@"
SELECT timeStart, app.text appText, win.text winText, app.id appId
from period
join win on win.id = period.winid
join app on app.id = win.appid
where @v0 between timeStart and timeEnd
limit 1",
time
).FirstOrDefault();
if (data == null)
{
return null;
}
return new TimeInfo
{
timeSrc = time,
timeStart = data.timeStart,
app = data.appText,
title = data.winText,
appId = data.appId
};
}
static byte[] defaultIcon;
public byte[] getIcon(int appId, bool large)
{
var path = Recorder.GetIconPath(appId, large);
if (File.Exists(path))
{
return File.ReadAllBytes(path);
}
if (defaultIcon == null)
{
defaultIcon = File.ReadAllBytes("./webui/img/icon.png");
}
return defaultIcon;
}
static byte[] imageNone = null;
TimeSpan getTime(string file)
{
return TimeSpan.ParseExact(Path.GetFileNameWithoutExtension(file), "hhmmss", CultureInfo.InvariantCulture);
}
/// <summary>
/// 查找不满足条件的最后一个元素
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="largerThenTarget"></param>
/// <returns></returns>
T find<T>(IList<T> items, Func<T, bool> largerThenTarget)
{
if (items.Count == 0)
{
return default;
}
var match = items[0];
if (largerThenTarget(match))
{
return default;
}
for (var i = 1; i < items.Count; i++)
{
var item = items[i];
if (largerThenTarget(item))
{
break;
}
match = item;
}
return match;
}
static Thread lastThread;
static readonly object threadLock = new object();
/// <summary>
/// 获取指定时间的截图
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
public byte[] getImage(TimeInfo info)
{
if (imageNone == null)
{
imageNone = File.ReadAllBytes(Path.Combine(Server.WebRootPath, "img", "none.png"));
}
//只响应最后一个请求,避免运行多个ffmpeg占用资源。
lock (threadLock)
{
Ffmpeg.KillLastFfmpeg();
if (lastThread != null && lastThread.IsAlive)
{
lastThread.Abort();
}
lastThread = Thread.CurrentThread;
}
try
{
//先从buffer中找
{
var buffers = new List<MemoryBuffer>(Recorder.flushing);
if (Recorder.buffer != null)
{
buffers.Add(Recorder.buffer);
}
var match = find(buffers, i => i.StartTime > info.timeSrc);
if (match != null)
{
var time = info.timeSrc - match.StartTime;
var frame = find(match.Frames, f => (match.StartTime + f.Time) > info.timeSrc);
if (frame != null)
{
return frame.Data;
}
}
}
//从文件系统找
{
var path = Recorder.getFileName(info.timeSrc);
var needtime = info.timeSrc.TimeOfDay;
var needtimetext = needtime.ToString("hhmmss");
var match = (from f in Directory.GetFiles(Path.GetDirectoryName(path), "????????." + Recorder.ExName)
where Path.GetFileNameWithoutExtension(f).CompareTo(needtimetext) < 0
orderby f
select f).LastOrDefault();
if (match != null)
{
var time = needtime - getTime(match);
var data = Ffmpeg.Snapshot(match, time);
if (data != null && data.Length > 0)
{
return data;
}
}
return imageNone;
}
}
catch (ThreadAbortException)
{
return imageNone;
}
}
#endregion
#region tag
private long nextTagId = 0;
private long NextTagId()
{
if (nextTagId == 0)
{
nextTagId = db.ExecuteValue<long>("select ifnull(max(id), 0) + 1 from tag");
}
return nextTagId++;
}
public bool existsTag(string text)
{
return db.ExecuteValue< bool>(
"select exists(select * from tag where text = @text)",
new SQLiteParameter("text", text)
);
}
public bool addTag(string text)
{
if(existsTag(text))
{
return false;
}
db.Execute(
"insert into tag (id, text) values(@id, @text)",
new SQLiteParameter("id", NextTagId()),
new SQLiteParameter("text", text)
);
return true;
}
public void removeTag(int tagId)
{
db.Execute(
"delete from tag where id = @id",
new SQLiteParameter("id", tagId)
);
db.Execute($"update app set tagId=0 where tagId={tagId}");
db.Execute($"update win set tagId=0 where tagId={tagId}");
}
public void clearAppTag(long appId)
{
db.Execute("update app set tagid = 0 where id = @v0", appId);
}
public void clearWinTag(long winId)
{
db.Execute("update win set tagid = 0 where id = @v0", winId);
}
public DataTable getTags()
{
return db.ExecuteTable("select id, text from tag order by id");
}
public bool isTagUsed(int tagId)
{
return db.ExecuteValue<bool>(
@"select exists(
select * from app where tagId = @tagId
union all
select * from win where tagId = @tagId
)",
new SQLiteParameter("tagId", tagId)
);
}
DB db = DB.Instance;
public bool renameTag(long tagId, string newName)
{
if(existsTag(newName))
{
return false;
}
db.Execute("update tag set text=@newName where id=@tagId",
new SQLiteParameter("newName", newName),
new SQLiteParameter("tagId", tagId)
);
return true;
}
public void tagApp(long appId, long tagId)
{
db.Execute(
"update app set tagid = @tagId where id=@appId",
new SQLiteParameter("appId", appId),
new SQLiteParameter("tagId", tagId)
);
}
public void tagWin(long winId, long tagId)
{
db.Execute(
"update win set tagid = @tagId where id=@winId",
new SQLiteParameter("winId", winId),
new SQLiteParameter("tagId", tagId)
);
}
#endregion
}
}

+ 136
- 0
appTime/AppTime-0.12/AppTime/DB.cs ファイルの表示

@ -0,0 +1,136 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.OleDb;
using System.Data.SQLite;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace AppTime
{
class DB
{
public readonly static DB Instance = new DB();
DbConnection conn;
DbProviderFactory factory = SQLiteFactory.Instance;
public DB()
{
var connectionString = new SQLiteConnectionStringBuilder()
{
DataSource= Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "data.db")
}.ToString();
conn = new SQLiteConnection
{
ConnectionString = connectionString
};
}
T execute<T>(Func<DbCommand, T> handler, string sql, params object[] args)
{
lock (this)
{
conn.Open();
using var cmd = conn.CreateCommand();
cmd.CommandText = sql;
for (var i = 0; i < args.Length; i++)
{
var arg = args[i];
if (!(arg is DbParameter param))
{
param = factory.CreateParameter();
param.ParameterName = $"@v{i}";
param.Value = arg;
}
cmd.Parameters.Add(param);
}
var result = handler(cmd);
conn.Close();
return result;
}
}
public int Execute(string sql, params object[] args)
{
return execute(cmd => cmd.ExecuteNonQuery(), sql, args) ;
}
public List<object[]> ExecuteData(string sql, params object[] args)
{
return execute(cmd =>
{
var result = new List<object[]>();
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
var row = new object[reader.FieldCount];
reader.GetValues(row);
result.Add(row);
}
return result;
}, sql, args);
}
public T ExecuteValue<T>(string sql, params object[] args)
{
return (T)Convert.ChangeType(ExecuteData(sql, args)[0][0], typeof(T));
}
public T[] ExecuteColumn<T>(string sql, params object[] args)
{
var data = ExecuteData(sql, args);
var result = new T[data.Count];
for(var i = 0;i<data.Count;i++)
{
result[i] = (T)Convert.ChangeType(data[i][0], typeof(T));
}
return result;
}
public DataTable ExecuteTable(string sql, params object[] args)
{
return execute(cmd =>
{
using var adapter = factory.CreateDataAdapter();
adapter.SelectCommand = cmd;
var result = new DataTable();
adapter.Fill(result);
return result;
}, sql, args);
}
public IEnumerable<dynamic> ExecuteDynamic(string sql, params object[] args)
{
return from r in ExecuteTable(sql, args).AsEnumerable() select new DynamicDataRow(r);
}
}
class DynamicDataRow : DynamicObject
{
DataRow row;
public DynamicDataRow(DataRow row)
{
this.row = row;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = row[binder.Name];
return true;
}
}
}

+ 114
- 0
appTime/AppTime-0.12/AppTime/Ffmpeg.cs ファイルの表示

@ -0,0 +1,114 @@
using AppTime.Properties;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static AppTime.Recorder;
namespace AppTime
{
class Ffmpeg
{
static Process lastFfmpeg;
public static void KillLastFfmpeg()
{
if (lastFfmpeg != null && !lastFfmpeg.HasExited)
{
Utils.Try(() => lastFfmpeg.Kill());
lastFfmpeg = null;
}
}
public static byte[] Snapshot(string file, TimeSpan time)
{
var args = $@"-loglevel quiet -ss {time} -i ""{file}"" -y -frames 1 -q:v 2 -f image2 -";
var info = new ProcessStartInfo(@"ffmpeg\ffmpeg.exe", args)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = true,
WorkingDirectory = Path.GetDirectoryName(Application.ExecutablePath)
};
var p = lastFfmpeg = Process.Start(info);
var output = p.StandardOutput.BaseStream;
var data = new List<byte>();
var b = output.ReadByte();
while (b != -1)
{
data.Add((byte)b);
b = output.ReadByte();
}
return data.ToArray();
}
public static void Save(string file, params Frame[] images)
{
if (images.Length == 0)
{
return;
}
var rate = images.Length / ((images.Last().Time - images.First().Time).TotalSeconds + 1);
var crf = Settings.Default.ImageQuality;//0-质量最高 63-质量最低 实测40质量也不错且体积较小
var tempfile = Path.Combine(Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file) + ".tmp");
var args = $@"-loglevel quiet -f image2pipe -r {rate} -i - -vcodec libx264 -crf {crf} -f matroska -y ""{tempfile}""";
var info = new ProcessStartInfo(@"ffmpeg\ffmpeg.exe", args)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = true,
WorkingDirectory = Path.GetDirectoryName(Application.ExecutablePath)
};
var p = Process.Start(info);
p.PriorityClass = ProcessPriorityClass.BelowNormal;
foreach (var i in images)
{
p.StandardInput.BaseStream.Write(i.Data, 0, i.Data.Length);
}
p.StandardInput.Close();
p.WaitForExit();
if (File.Exists(file))
{
File.Delete(file);
}
File.Move(tempfile, file);
}
}
public class Frame
{
public TimeSpan Time;
public byte[] Data;
public Frame(TimeSpan time, byte[] data)
{
this.Time = time;
this.Data = data;
}
}
}

+ 267
- 0
appTime/AppTime-0.12/AppTime/FrmMain.Designer.cs ファイルの表示

@ -0,0 +1,267 @@
namespace AppTime
{
partial class FrmMain
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmMain));
this.txtDataPath = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.btnDataPath = new System.Windows.Forms.Button();
this.cboRecordScreen = new System.Windows.Forms.ComboBox();
this.btnOK = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
this.btnOpen = new System.Windows.Forms.ToolStripMenuItem();
this.btnSetting = new System.Windows.Forms.ToolStripMenuItem();
this.btnAbout = new System.Windows.Forms.ToolStripMenuItem();
this.btnExit = new System.Windows.Forms.ToolStripMenuItem();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.label4 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.chkAutoRun = new System.Windows.Forms.CheckBox();
this.cboImageQuality = new System.Windows.Forms.ComboBox();
this.contextMenuStrip.SuspendLayout();
this.groupBox1.SuspendLayout();
this.SuspendLayout();
//
// txtDataPath
//
this.txtDataPath.Location = new System.Drawing.Point(132, 34);
this.txtDataPath.Name = "txtDataPath";
this.txtDataPath.Size = new System.Drawing.Size(494, 21);
this.txtDataPath.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(40, 37);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(77, 12);
this.label1.TabIndex = 1;
this.label1.Text = "数据存储位置";
//
// btnDataPath
//
this.btnDataPath.Location = new System.Drawing.Point(641, 32);
this.btnDataPath.Name = "btnDataPath";
this.btnDataPath.Size = new System.Drawing.Size(75, 23);
this.btnDataPath.TabIndex = 2;
this.btnDataPath.Text = "浏览(&B)...";
this.btnDataPath.UseVisualStyleBackColor = true;
this.btnDataPath.Click += new System.EventHandler(this.btnDataPath_Click);
//
// cboRecordScreen
//
this.cboRecordScreen.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cboRecordScreen.FormattingEnabled = true;
this.cboRecordScreen.Items.AddRange(new object[] {
"记录最近30天",
"记录最近14天",
"无限制记录",
"不记录"});
this.cboRecordScreen.Location = new System.Drawing.Point(98, 33);
this.cboRecordScreen.Name = "cboRecordScreen";
this.cboRecordScreen.Size = new System.Drawing.Size(121, 20);
this.cboRecordScreen.TabIndex = 4;
//
// btnOK
//
this.btnOK.Location = new System.Drawing.Point(366, 245);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(158, 45);
this.btnOK.TabIndex = 6;
this.btnOK.Text = "确定(&O)";
this.btnOK.UseVisualStyleBackColor = true;
this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
//
// btnCancel
//
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(556, 245);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(158, 45);
this.btnCancel.TabIndex = 7;
this.btnCancel.Text = "取消(&C)";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// notifyIcon
//
this.notifyIcon.ContextMenuStrip = this.contextMenuStrip;
this.notifyIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon.Icon")));
this.notifyIcon.Visible = true;
this.notifyIcon.DoubleClick += new System.EventHandler(this.notifyIcon_DoubleClick);
//
// contextMenuStrip
//
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.btnOpen,
this.btnSetting,
this.btnAbout,
this.btnExit});
this.contextMenuStrip.Name = "contextMenuStrip";
this.contextMenuStrip.Size = new System.Drawing.Size(121, 92);
//
// btnOpen
//
this.btnOpen.Font = new System.Drawing.Font("Microsoft YaHei UI", 9F, System.Drawing.FontStyle.Bold);
this.btnOpen.Name = "btnOpen";
this.btnOpen.Size = new System.Drawing.Size(120, 22);
this.btnOpen.Text = "打开(&O)";
this.btnOpen.Click += new System.EventHandler(this.btnOpen_Click);
//
// btnSetting
//
this.btnSetting.Name = "btnSetting";
this.btnSetting.Size = new System.Drawing.Size(120, 22);
this.btnSetting.Text = "设置(&S)";
this.btnSetting.Click += new System.EventHandler(this.btnSetting_Click);
//
// btnAbout
//
this.btnAbout.Name = "btnAbout";
this.btnAbout.Size = new System.Drawing.Size(120, 22);
this.btnAbout.Text = "关于(&A)";
this.btnAbout.Click += new System.EventHandler(this.btnAbout_Click);
//
// btnExit
//
this.btnExit.Name = "btnExit";
this.btnExit.Size = new System.Drawing.Size(120, 22);
this.btnExit.Text = "退出(&E)";
this.btnExit.Click += new System.EventHandler(this.btnExit_Click);
//
// groupBox1
//
this.groupBox1.Controls.Add(this.cboImageQuality);
this.groupBox1.Controls.Add(this.label4);
this.groupBox1.Controls.Add(this.label2);
this.groupBox1.Controls.Add(this.cboRecordScreen);
this.groupBox1.Location = new System.Drawing.Point(34, 85);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(682, 132);
this.groupBox1.TabIndex = 10;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "记录屏幕";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(14, 79);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(53, 12);
this.label4.TabIndex = 17;
this.label4.Text = "图片质量";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(15, 36);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(53, 12);
this.label2.TabIndex = 11;
this.label2.Text = "留存时间";
//
// chkAutoRun
//
this.chkAutoRun.AutoSize = true;
this.chkAutoRun.Location = new System.Drawing.Point(40, 255);
this.chkAutoRun.Name = "chkAutoRun";
this.chkAutoRun.Size = new System.Drawing.Size(114, 16);
this.chkAutoRun.TabIndex = 11;
this.chkAutoRun.Text = "开机自动启动(&A)";
this.chkAutoRun.UseVisualStyleBackColor = true;
//
// cboImageQuality
//
this.cboImageQuality.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cboImageQuality.FormattingEnabled = true;
this.cboImageQuality.Items.AddRange(new object[] {
"记录最近30天",
"记录最近14天",
"无限制记录",
"不记录"});
this.cboImageQuality.Location = new System.Drawing.Point(98, 76);
this.cboImageQuality.Name = "cboImageQuality";
this.cboImageQuality.Size = new System.Drawing.Size(121, 20);
this.cboImageQuality.TabIndex = 20;
//
// FrmMain
//
this.AcceptButton = this.btnOK;
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(751, 320);
this.Controls.Add(this.chkAutoRun);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnOK);
this.Controls.Add(this.btnDataPath);
this.Controls.Add(this.label1);
this.Controls.Add(this.txtDataPath);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "FrmMain";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "设置";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmMain_FormClosing);
this.Load += new System.EventHandler(this.FrmMain_Load);
this.Shown += new System.EventHandler(this.FrmMain_Shown);
this.contextMenuStrip.ResumeLayout(false);
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox txtDataPath;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button btnDataPath;
private System.Windows.Forms.ComboBox cboRecordScreen;
private System.Windows.Forms.Button btnOK;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.NotifyIcon notifyIcon;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip;
private System.Windows.Forms.ToolStripMenuItem btnOpen;
private System.Windows.Forms.ToolStripMenuItem btnSetting;
private System.Windows.Forms.ToolStripMenuItem btnAbout;
private System.Windows.Forms.ToolStripMenuItem btnExit;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.CheckBox chkAutoRun;
private System.Windows.Forms.ComboBox cboImageQuality;
}
}

+ 165
- 0
appTime/AppTime-0.12/AppTime/FrmMain.cs ファイルの表示

@ -0,0 +1,165 @@

using AppTime.Properties;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Text;
using System.Web.ModelBinding;
using System.Web.UI.WebControls;
using System.Windows.Forms;
namespace AppTime
{
public partial class FrmMain : Form
{
const string appname = "AppTime";
const string regkey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
public FrmMain()
{
InitializeComponent();
}
private void FrmMain_Load(object sender, EventArgs e)
{
cboRecordScreen.DataSource = new[] {
new {Text="最近30天", Value=30},
new {Text="最近15天", Value=15},
new {Text="无限制", Value=int.MaxValue},
new {Text="不留存", Value=0},
};
cboRecordScreen.DisplayMember = "Text";
cboRecordScreen.ValueMember = "Value";
cboImageQuality.DataSource = new[] {
new {Text="最省磁盘", Value=63},
new {Text="均衡", Value=50},
new {Text="高质量", Value=40},
};
cboImageQuality.DisplayMember = "Text";
cboImageQuality.ValueMember = "Value";
}
private void btnOpen_Click(object sender, EventArgs e)
{
Process.Start($@"http://localhost:{Program.Port}/");
}
private void notifyIcon_DoubleClick(object sender, EventArgs e)
{
btnOpen_Click(null, null);
}
private void btnCancel_Click(object sender, EventArgs e)
{
this.Hide();
}
bool cancelClose = true;
private void btnExit_Click(object sender, EventArgs e)
{
this.Hide();
cancelClose = false;
Program.recorder.FlushScreenBuffer();
this.Close();
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
this.Hide();
e.Cancel = cancelClose;
}
private void btnOK_Click(object sender, EventArgs e)
{
try
{
Directory.CreateDirectory(txtDataPath.Text);
Settings.Default.DataPath = txtDataPath.Text == Application.StartupPath ? "" : txtDataPath.Text;
Program.recorder.BuildDataPath();
}
catch
{
MessageBox.Show("数据存储位置无效,请重新选择。");
}
Settings.Default.ImageQuality = (int) cboImageQuality.SelectedValue;
Settings.Default.RecordScreenDays = (int)cboRecordScreen.SelectedValue;
Settings.Default.Save();
using var reg = Registry.CurrentUser.CreateSubKey(regkey);
try
{
if (chkAutoRun.Checked)
{
reg.SetValue(appname, Application.ExecutablePath);
}
else
{
reg.DeleteValue(appname);
}
}
catch (UnauthorizedAccessException)
{
MessageBox.Show("设置启动失败,请检查:\r\n\r\n1、关闭杀毒软件(如360等);\r\n2、以管理员身份运行本程序。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
this.Hide();
}
private void FrmMain_Shown(object sender, EventArgs e)
{
this.Hide();
}
private void btnSetting_Click(object sender, EventArgs e)
{
if (!this.Visible)
{
if (string.IsNullOrEmpty(Settings.Default.DataPath))
{
txtDataPath.Text = Application.StartupPath;
}
else
{
txtDataPath.Text = Settings.Default.DataPath;
}
cboRecordScreen.SelectedValue = Settings.Default.RecordScreenDays;
cboImageQuality.SelectedValue = Settings.Default.ImageQuality;
using var reg = Registry.CurrentUser.CreateSubKey(regkey);
chkAutoRun.Checked = (reg.GetValue(appname) as string) == Application.ExecutablePath;
this.Show();
}
}
private void btnAbout_Click(object sender, EventArgs e)
{
MessageBox.Show($"AppTime桌面时间管理\r\nV{Application.ProductVersion}\r\n\r\n联系作者:newdraw@hotmail.com", "关于", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void btnDataPath_Click(object sender, EventArgs e)
{
using var dlg = new FolderBrowserDialog();
if(dlg.ShowDialog()== DialogResult.OK)
{
txtDataPath.Text = dlg.SelectedPath;
}
}
}
}

+ 2391
- 0
appTime/AppTime-0.12/AppTime/FrmMain.resx
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 63
- 0
appTime/AppTime-0.12/AppTime/InitDB.cs ファイルの表示

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AppTime
{
/**
*
*/
class InitDB
{
private DB db;
public InitDB()
{
db = DB.Instance;
}
public void Start()
{
// 创建数据表
db.Execute("CREATE TABLE IF NOT EXISTS \"app\" (\"id\" INTEGER NOT NULL, \"process\" text NOT NULL," +
"\"text\" TEXT NOT NULL DEFAULT process, " +
"\"tagId\" INTEGER NOT NULL DEFAULT(0), " +
"PRIMARY KEY(\"id\") ) WITHOUT ROWID");
db.Execute("CREATE TABLE IF NOT EXISTS \"period\" ( \"timeStart\" DATETIME NOT NULL, \"timeEnd\" DATETIME NOT NULL, " +
"\"winId\" INTEGER NOT NULL," +
"PRIMARY KEY(\"timeStart\")," +
"UNIQUE(\"timeStart\" ASC) )WITHOUT ROWID");
db.Execute("CREATE TABLE IF NOT EXISTS \"tag\" ( \"id\" INTEGER NOT NULL, \"text\" TEXT NOT NULL," +
"PRIMARY KEY(\"id\"), UNIQUE(\"id\" ASC), UNIQUE(\"text\" ASC) ) WITHOUT ROWID");
db.Execute("CREATE TABLE IF NOT EXISTS \"win\" (\"id\" INTEGER NOT NULL," +
"\"appId\" INTEGER NOT NULL, \"text\" TEXT NOT NULL, " +
"\"tagId\" INTEGER NOT NULL DEFAULT(0), " +
"PRIMARY KEY(\"id\") ) WITHOUT ROWID");
// 创建索引
long existIndex = (long)db.ExecuteData("SELECT count(*) FROM sqlite_master WHERE type=\"table\" AND name =\'is_index\'")[0][0];
// 判断是否已经存在索引
if (existIndex == 0)
{
db.Execute("CREATE UNIQUE INDEX \"ix_app\" ON \"app\"( \"process\" ASC )");
db.Execute("CREATE INDEX \"ix_app_tagId\" ON \"app\" ( \"tagId\" ASC )");
db.Execute("CREATE UNIQUE INDEX \"ix_period\" ON \"period\" ( \"timeStart\" ASC, \"timeEnd\" ASC )");
db.Execute("CREATE UNIQUE INDEX \"ix_win\" ON \"win\" ( \"appId\" ASC, \"text\" ASC )");
db.Execute("CREATE INDEX \"ix_win_tagId\" ON \"win\" (\"tagId\" ASC )");
// 此表仅用于标识索引已经创建完成
db.Execute("CREATE TABLE IF NOT EXISTS \"is_index\"( \"id\" INTEGER NOT NULL, PRIMARY KEY(\"id\"))");
}
}
}
}

+ 82
- 0
appTime/AppTime-0.12/AppTime/Program.cs ファイルの表示

@ -0,0 +1,82 @@

using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace AppTime
{
static class Program
{
public const int Port = 15720;
public static InitDB init;
public static Recorder recorder;
public static WebServer server;
public static Controller controller;
public static FrmMain frmMain;
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
#if DEBUG
CopyWebUI();
#endif
init = new InitDB();
init.Start();
recorder = new Recorder();
recorder.Start();
controller = new Controller();
server = new WebServer();
server.Start(Port, controller, "./webui");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(frmMain = new FrmMain());
}
static void CopyWebUI()
{
CopyDirectory("../../webui", "./webui");
}
static void CopyDirectory(string src, string dest)
{
if (!Directory.Exists(dest))
{
Directory.CreateDirectory(dest);
}
foreach (var srcfile in Directory.GetFiles(src))
{
var destfile = Path.Combine(dest, Path.GetFileName(srcfile));
//只复制更新的文件
if (File.Exists(destfile) && File.GetLastWriteTime(srcfile) == File.GetLastWriteTime(destfile))
{
continue;
}
File.Copy(srcfile, destfile, true);
}
foreach (var srcdir in Directory.GetDirectories(src))
{
var destdir = Path.Combine(dest, Path.GetFileName(srcdir));
CopyDirectory(srcdir, destdir);
}
}
}
}

+ 36
- 0
appTime/AppTime-0.12/AppTime/Properties/AssemblyInfo.cs ファイルの表示

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("AppTime")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AppTime")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("4892e56b-5942-444b-b41c-1e4f22a3752a")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.0.0.0")]
[assembly: AssemblyFileVersion("0.12.0.0")]

+ 63
- 0
appTime/AppTime-0.12/AppTime/Properties/Resources.Designer.cs ファイルの表示

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace AppTime.Properties {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AppTime.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性
/// 重写当前线程的 CurrentUICulture 属性。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

+ 117
- 0
appTime/AppTime-0.12/AppTime/Properties/Resources.resx ファイルの表示

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

+ 62
- 0
appTime/AppTime-0.12/AppTime/Properties/Settings.Designer.cs ファイルの表示

@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace AppTime.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string DataPath {
get {
return ((string)(this["DataPath"]));
}
set {
this["DataPath"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("30")]
public int RecordScreenDays {
get {
return ((int)(this["RecordScreenDays"]));
}
set {
this["RecordScreenDays"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("50")]
public int ImageQuality {
get {
return ((int)(this["ImageQuality"]));
}
set {
this["ImageQuality"] = value;
}
}
}
}

+ 15
- 0
appTime/AppTime-0.12/AppTime/Properties/Settings.settings ファイルの表示

@ -0,0 +1,15 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="AppTime.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="DataPath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="RecordScreenDays" Type="System.Int32" Scope="User">
<Value Profile="(Default)">30</Value>
</Setting>
<Setting Name="ImageQuality" Type="System.Int32" Scope="User">
<Value Profile="(Default)">50</Value>
</Setting>
</Settings>
</SettingsFile>

+ 69
- 0
appTime/AppTime-0.12/AppTime/Properties/app.manifest ファイルの表示

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC 清单选项
如果想要更改 Windows 用户帐户控制级别,请使用
以下节点之一替换 requestedExecutionLevel 节点。n
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
如果你的应用程序需要此虚拟化来实现向后兼容性,则删除此
元素。
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" ID="Custom" SameSite="site" Unrestricted="true" />
</applicationRequestMinimum>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
Windows 版本的列表。取消评论适当的元素,
Windows 将自动选择最兼容的环境。 -->
<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
<!-- Windows 10 -->
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
</application>
</compatibility>
<!-- 指示该应用程序可以感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI,无需
选择加入。选择加入此设置的 Windows 窗体应用程序(目标设定为 .NET Framework 4.6 )还应
在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。-->
<!--
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
-->
<!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

+ 418
- 0
appTime/AppTime-0.12/AppTime/Recorder.cs ファイルの表示

@ -0,0 +1,418 @@

using AppTime.Properties;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.SQLite;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AppTime
{
class Recorder
{
public const string ExName = "mkv";
public Recorder()
{
BuildDataPath();
}
/// <summary>
/// 统计周期。
/// </summary>
public int IntervalMs = 1000;
public void Start()
{
new Thread(RecorderThreadProc) { IsBackground = true }.Start();
}
class App
{
public string WinText;
public string AppProcess;
public DateTime TimeStart;
public long WinId;
}
public void BuildDataPath()
{
Directory.CreateDirectory(IconPath);
Directory.CreateDirectory(ScreenPath);
}
Dictionary<int, Process> processes;
public Process GetProcess(int processID)
{
if (processes != null && processes.TryGetValue(processID, out var p))
{
return p;
}
processes = Process.GetProcesses().ToDictionary(p => p.Id);
return processes[processID];
}
class app
{
public long id;
public string process;
public Dictionary<string, win> wins = new Dictionary<string, win>();
}
class win
{
public long id;
public string text;
}
long nextAppId = 0;
Icon GetIcon(string fileName, bool largeIcon)
{
var shfi = new SHFILEINFO();
WinApi.SHGetFileInfo(fileName, 0, ref shfi,
(uint)Marshal.SizeOf(shfi),
(uint)FileInfoFlags.SHGFI_ICON | (uint)FileInfoFlags.SHGFI_USEFILEATTRIBUTES
| (uint)(largeIcon ? FileInfoFlags.SHGFI_LARGEICON : FileInfoFlags.SHGFI_SMALLICON)
);
return Icon.FromHandle(shfi.hIcon);
}
void SaveIcon(Icon icon, string filename)
{
using var img = icon.ToBitmap();
img.Save(filename);
}
public string GetIconPath(long appId, bool large)
{
return Path.Combine(IconPath, $"{appId}{(large ? "l" : "s")}.png");
}
Dictionary<string, app> apps = new Dictionary<string, app>();
app GetApp(Process process)
{
var name = process.ProcessName;
if (apps.TryGetValue(name, out var app))
{
return app;
}
var data = db.ExecuteDynamic(
@"select id from app where process = @process",
new SQLiteParameter("process", name)
).FirstOrDefault();
if (data == null)
{
if (nextAppId == 0)
{
nextAppId = (int)(long)db.ExecuteData("select ifnull(max(id),0) + 1 from app")[0][0];
}
var text = "";
try
{
text = process.MainModule.FileVersionInfo.FileDescription;
using var iconl = GetIcon(process.MainModule.FileName, true);
SaveIcon(iconl, GetIconPath(nextAppId, true));
using var icons = GetIcon(process.MainModule.FileName, false);
SaveIcon(icons, GetIconPath(nextAppId, false));
}
catch (Win32Exception)
{
//ignore
}
catch (FileNotFoundException)
{
//ignore
}
if (string.IsNullOrWhiteSpace(text))
{
text = process.ProcessName;
}
db.Execute(
"insert into app (id, process, text, tagId) values(@id, @process, @text, 0)",
new SQLiteParameter("id", nextAppId),
new SQLiteParameter("process", name),
new SQLiteParameter("text", text)
);
app = new app { id = nextAppId, process = name };
nextAppId++;
}
else
{
app = new app
{
id = data.id,
process = name
};
}
apps.Add(name, app);
//fix icons
var largeIconPath = GetIconPath(app.id, true);
if (!File.Exists(largeIconPath))
{
try
{
using var iconl = GetIcon(process.MainModule.FileName, true);
SaveIcon(iconl, largeIconPath);
using var icons = GetIcon(process.MainModule.FileName, false);
SaveIcon(icons, GetIconPath(app.id, false));
}
catch(Win32Exception)
{
}
}
return app;
}
long nextWinId = 0;
win GetWin(Process process, string winText)
{
var app = GetApp(process);
if (app.wins.TryGetValue(winText, out var win))
{
return win;
}
var data = db.ExecuteDynamic(
"select id from win where appid=@appid and text=@winText",
new SQLiteParameter("appid", app.id),
new SQLiteParameter("winText", winText)
).FirstOrDefault();
if (data == null)
{
if (nextWinId == 0)
{
nextWinId = (int)(long)db.ExecuteData("select ifnull(max(id),0) + 1 from win")[0][0];
}
db.Execute(
"insert into win (id, appId, text) values(@id, @appId, @text)",
new SQLiteParameter("id", nextWinId),
new SQLiteParameter("appId", app.id),
new SQLiteParameter("text", winText)
);
win = new win { id = nextWinId, text = winText };
nextWinId++;
}
else
{
win = new win
{
id = data.id,
text = winText
};
}
app.wins.Add(winText, win);
return win;
}
DB db = DB.Instance;
public void RecorderThreadProc()
{
App lastApp = null;
while (true)
{
var now = DateTime.Now;
var hwnd = WinApi.GetForegroundWindow();
var text = new StringBuilder(255);
WinApi.GetWindowText(hwnd, text, 255);
var winText = text.ToString();
WinApi.GetWindowThreadProcessId(hwnd, out var processid);
var process = GetProcess(processid);
var appname = process.ProcessName;
if (lastApp != null)
{
db.Execute(
"update period set timeend = @v1 where timestart = @v0",
lastApp.TimeStart,
now.AddMilliseconds(-1)//必须减小,否则可能与下个周期开始时间重叠
);
}
if (lastApp == null || lastApp.AppProcess != appname || lastApp.WinText != winText)
{
var win = GetWin(process, winText);
lastApp = new App { WinId = win.id, AppProcess = appname, TimeStart = now, WinText = winText };
db.Execute(
"insert into [period](winid, timeStart, timeEnd) values(@v0, @v1, @v1)",
win.id, now
);
}
Screenshot(now);
//等到下一个周期
var nextTime = now.AddMilliseconds(IntervalMs);
now = DateTime.Now;
if(nextTime > now)
{
Thread.Sleep(nextTime - now);
}
}
}
string DataPath => string.IsNullOrWhiteSpace(Settings.Default.DataPath) ? Application.StartupPath : Settings.Default.DataPath;
public string ScreenPath => Path.Combine(DataPath, "images");
public string IconPath => Path.Combine(DataPath, "icons");
ImageCodecInfo jpgcodec = ImageCodecInfo.GetImageDecoders().First(codec => codec.MimeType == "image/jpeg");
///// <summary>
///// 获取图片文件路径
///// </summary>
///// <param name="timeStart"></param>
///// <param name="timeImage"></param>
///// <returns></returns>
//public string getImageFile(DateTime timeStart, DateTime timeImage)
//{
// var folder = Path.Combine(ScreenPath, timeImage.ToString("yyyyMMdd"));
// var filename = $"{timeStart:HHmmss}+{Math.Round((timeImage - timeStart).TotalSeconds)}";
// return Path.Combine(folder, $"{filename}.jpg");
//}
public string getFileName(DateTime time)
{
return Path.Combine(ScreenPath, $"{time:yyyyMMdd}", $"{time:HHmmss}." + Recorder.ExName);
}
DateTime lastCheck = DateTime.MinValue.Date;
/// <summary>
/// 截图
/// </summary>
/// <param name="now"></param>
/// <param name="lastApp"></param>
void Screenshot(DateTime now)
{
//检查记录天数限制
if (lastCheck != now.Date)
{
var firstDate = now.Date.AddDays(-Settings.Default.RecordScreenDays);
var dirs = Directory.EnumerateDirectories(ScreenPath, "????????");
foreach (var i in dirs)
{
if (DateTime.TryParseExact(Path.GetFileName(i), "yyyyMMdd", CultureInfo.CurrentCulture, DateTimeStyles.None, out var date))
{
if (date < firstDate)
{
Directory.Delete(i);
}
}
}
lastCheck = now.Date;
}
if (Settings.Default.RecordScreenDays == 0)
{
return;
}
if (buffer == null)
{
buffer = new MemoryBuffer(now);
}
using var img = GetScreen();
using var mem = new MemoryStream();
img.Save(mem, ImageFormat.Jpeg);
buffer.Frames.Add(new Frame(now - buffer.StartTime, mem.ToArray()));
if ((now - buffer.StartTime).TotalSeconds >= 10 * 60 || now.Date != buffer.StartTime.Date) //固定为10mins,防止保存时间长,减少出问题时影响的时长。
{
FlushScreenBuffer();
}
}
public void FlushScreenBuffer()
{
//切换到新buffer
var newBuffer = new MemoryBuffer(DateTime.Now);
var b = buffer;
buffer = newBuffer;
//加入flushing
lock (flushing)
{
flushing.Add(b);
}
var path = getFileName(b.StartTime);
var folder = Path.GetDirectoryName(path);
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
new Thread(() =>
{
Ffmpeg.Save(getFileName(b.StartTime), b.Frames.ToArray());
lock (flushing)
{
flushing.Remove(b);
}
})
{
Priority = ThreadPriority.BelowNormal,
IsBackground = false
}.Start();
}
public class MemoryBuffer
{
public readonly DateTime StartTime;
public readonly List<Frame> Frames = new List<Frame>();
public MemoryBuffer(DateTime startTime)
{
StartTime = startTime;
}
}
public MemoryBuffer buffer;
public List<MemoryBuffer> flushing = new List<MemoryBuffer>();
Bitmap GetScreen()
{
var result = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
using var g = Graphics.FromImage(result);
retry:
try
{
g.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size);
}
catch
{
goto retry;
}
return result;
}
}
}

+ 64
- 0
appTime/AppTime-0.12/AppTime/Utils.cs ファイルの表示

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AppTime
{
static class Utils
{
public static T CheckTimeout<T>(Func<T> act, Func<Thread, T> whenTimeout, int timeoutMs, bool abortThread = true)
{
var isTimeout = true;
Exception ex = null;
var t = new Thread(() =>
{
try
{
act();
isTimeout = false;
}
catch (Exception e)
{
ex = e;
}
});
t.Start();
t.Join(timeoutMs);
if (isTimeout)
{
if (abortThread)
{
t.Abort();
}
return whenTimeout(t);
}
if (ex != null)
{
throw ex;
}
return default(T);
}
public static Exception Try(Action act)
{
try
{
act();
return null;
}
catch (Exception ex)
{
return ex;
}
}
}
}

+ 178
- 0
appTime/AppTime-0.12/AppTime/WebServer.cs ファイルの表示

@ -0,0 +1,178 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Web;
using System.Windows.Forms;
namespace AppTime
{
class WebServer
{
HttpListener listener;
Thread thread;
//public HttpListenerRequest Request;
//public HttpListenerResponse Response;
public string WebRootPath;
HashSet<string> defaultPage = new HashSet<string>(StringComparer.OrdinalIgnoreCase){
"index.html",
"index.htm",
"default.html",
"default.htm"
};
public void Start(int port, object controller, string webfolder = "web")
{
thread = new Thread(() =>
{
WebRootPath = webfolder = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), webfolder);
var folders = Directory.GetDirectories(webfolder, "*", SearchOption.AllDirectories);
var webroot = $@"http://localhost:{port}/";
listener = new HttpListener();
foreach (var f in folders)
{
listener.Prefixes.Add(webroot + f.Substring(webfolder.Length + 1).Replace("\\", "/") + "/");
}
listener.Prefixes.Add(webroot);
listener.Start();
while (true)
{
var ctx = listener.GetContext();
//Debug.WriteLine($"Web Request:{ctx.Request.Url}");
//ThreadPool.QueueUserWorkItem(_ => processRequest(ctx, webfolder, controller));
new Thread(() => processRequest(
ctx,
webfolder,
controller)
)
{ IsBackground = true, Name = "WebServer Process Request" }.Start();
}
})
{
Name = "WebServer",
IsBackground = true
};
thread.Start();
}
void processRequest(HttpListenerContext context, string webfolder, object controller)
{
var request = context.Request;
var file = webfolder + request.RawUrl.Replace("/", "\\");
string query = "";
var p = file.IndexOf("?");
if (p > 0)
{
query = file.Substring(p + 1);
file = file.Substring(0, p);
}
var response = context.Response;
byte[] data = null;
if (Path.GetFileName(Path.GetDirectoryName(file)).Equals("data", StringComparison.OrdinalIgnoreCase))
{
try
{
if (string.IsNullOrWhiteSpace(query))
{
using var reader = new StreamReader(request.InputStream);
query = reader.ReadToEnd();
}
var info = JObject.Parse(HttpUtility.UrlDecode(query));
var method = controller.GetType().GetMethod(Path.GetFileName(file));
var args = info.Value<JArray>("args").Cast<object>().ToArray();
var @params = method.GetParameters();
for (var i = 0; i < args.Length; i++)
{
if (args[i] is JObject jo)
{
args[i] = jo.ToObject(@params[i].ParameterType);
}
else
{
args[i] = Convert.ChangeType(args[i], @params[i].ParameterType);
}
}
var result = method.Invoke(controller, args);
if (result is byte[] bytes)
{
data = bytes;
}
else
{
data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(result));
}
}
catch (Exception ex)
{
data = Encoding.UTF8.GetBytes($"bad request: method:{Path.GetFileName(file)} query:{query}");
}
}
else
{
if (Directory.Exists(file))
{
var def = Directory.GetFiles(file).FirstOrDefault(f => defaultPage.Contains(Path.GetFileName(f), StringComparer.OrdinalIgnoreCase));
if (def != null)
{
file = def;
}
}
if (File.Exists(file))
{
var mime = MimeMapping.GetMimeMapping(file);
response.Headers.Add("Content-type", mime);
if (mime.StartsWith("text/"))
{
response.ContentEncoding = Encoding.GetEncoding("utf-8");
data = Encoding.UTF8.GetBytes(File.ReadAllText(file, Encoding.Default));
}
else
{
data = File.ReadAllBytes(file);
}
}
}
if (data == null)
{
data = Encoding.UTF8.GetBytes($"file not found: {file}");
}
response.ContentLength64 = data.Length;
var output = response.OutputStream;
try
{
output.Write(data, 0, data.Length);
}
catch (HttpListenerException ex)
{
}
output.Close();
}
public void Stop()
{
thread.Abort();
listener.Stop();
listener.Close();
}
}
}

+ 1799
- 0
appTime/AppTime-0.12/AppTime/WinApi.cs
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 54
- 0
appTime/AppTime-0.12/AppTime/app.config ファイルの表示

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<section name="AppTime.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /></startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SQLitePCLRaw.core" publicKeyToken="1488e028ca7ab535" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.3.851" newVersion="2.0.3.851" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Accord" publicKeyToken="fa1a88e29555ccf7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.8.2.0" newVersion="3.8.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Accord.Video" publicKeyToken="fa1a88e29555ccf7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.8.2.0" newVersion="3.8.2.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<userSettings>
<AppTime.Properties.Settings>
<setting name="DataPath" serializeAs="String">
<value />
</setting>
<setting name="RecordScreenDays" serializeAs="String">
<value>30</value>
</setting>
<setting name="ImageQuality" serializeAs="String">
<value>50</value>
</setting>
</AppTime.Properties.Settings>
</userSettings>
</configuration>

+ 11
- 0
appTime/AppTime-0.12/AppTime/app.manifest ファイルの表示

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

バイナリ
appTime/AppTime-0.12/AppTime/apptime.ico ファイルの表示

変更前 変更後

バイナリ
appTime/AppTime-0.12/AppTime/data.db ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/avcodec-57.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/avcodec-58.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/avdevice-57.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/avdevice-58.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/avfilter-6.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/avfilter-7.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/avformat-57.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/avformat-58.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/avutil-55.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/avutil-56.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/ffmpeg.exe ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/postproc-54.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/postproc-55.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/swresample-2.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/swresample-3.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/swscale-4.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/AppTime/ffmpeg/swscale-5.dll ファイルの表示


+ 4
- 0
appTime/AppTime-0.12/AppTime/obj/Debug/.NETFramework,Version=v4.8.AssemblyAttributes.cs ファイルの表示

@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]

バイナリ
appTime/AppTime-0.12/AppTime/obj/Debug/AppTime.csproj.AssemblyReference.cache ファイルの表示


+ 7
- 0
appTime/AppTime-0.12/AppTime/packages.config ファイルの表示

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net48" />
<package id="SQLitePCLRaw.core" version="1.1.14" targetFramework="net48" />
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.14" targetFramework="net48" />
<package id="System.Data.SQLite.Core" version="1.0.113.1" targetFramework="net48" />
</packages>

バイナリ
appTime/AppTime-0.12/files/icon.jpg ファイルの表示

変更前 変更後
幅: 140  |  高さ: 135  |  サイズ: 1.5 KiB

バイナリ
appTime/AppTime-0.12/files/list.jpg ファイルの表示

変更前 変更後
幅: 609  |  高さ: 816  |  サイズ: 36 KiB

バイナリ
appTime/AppTime-0.12/files/playback.gif ファイルの表示

変更前 変更後
幅: 800  |  高さ: 450  |  サイズ: 192 KiB

バイナリ
appTime/AppTime-0.12/files/playback.mp4 ファイルの表示


バイナリ
appTime/AppTime-0.12/files/tag.jpg ファイルの表示

変更前 変更後
幅: 618  |  高さ: 220  |  サイズ: 17 KiB

バイナリ
appTime/AppTime-0.12/files/tagview.jpg ファイルの表示

変更前 変更後
幅: 362  |  高さ: 169  |  サイズ: 4.1 KiB

バイナリ
appTime/AppTime-0.12/files/time.jpg ファイルの表示

変更前 変更後
幅: 588  |  高さ: 51  |  サイズ: 4.3 KiB

バイナリ
appTime/AppTime-0.12/files/timebar.mp4 ファイルの表示


バイナリ
appTime/AppTime-0.12/files/tv.jpg ファイルの表示

変更前 変更後
幅: 2560  |  高さ: 1400  |  サイズ: 113 KiB

バイナリ
appTime/AppTime-0.12/files/view.jpg ファイルの表示

変更前 変更後
幅: 185  |  高さ: 55  |  サイズ: 1.6 KiB

バイナリ
appTime/AppTime-0.12/files/vs.jpg ファイルの表示

変更前 変更後
幅: 2560  |  高さ: 1400  |  サイズ: 112 KiB

バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/.signature.p7s ファイルの表示


+ 20
- 0
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/LICENSE.md ファイルの表示

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2007 James Newton-King
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/Newtonsoft.Json.12.0.3.nupkg ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net20/Newtonsoft.Json.dll ファイルの表示


+ 10298
- 0
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net20/Newtonsoft.Json.xml
ファイル差分が大きすぎるため省略します
ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net35/Newtonsoft.Json.dll ファイルの表示


+ 9446
- 0
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net35/Newtonsoft.Json.xml
ファイル差分が大きすぎるため省略します
ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net40/Newtonsoft.Json.dll ファイルの表示


+ 9646
- 0
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net40/Newtonsoft.Json.xml
ファイル差分が大きすぎるため省略します
ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net45/Newtonsoft.Json.dll ファイルの表示


+ 11262
- 0
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/net45/Newtonsoft.Json.xml
ファイル差分が大きすぎるため省略します
ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.0/Newtonsoft.Json.dll ファイルの表示


+ 10950
- 0
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.0/Newtonsoft.Json.xml
ファイル差分が大きすぎるため省略します
ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.3/Newtonsoft.Json.dll ファイルの表示


+ 11072
- 0
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard1.3/Newtonsoft.Json.xml
ファイル差分が大きすぎるため省略します
ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.dll ファイルの表示


+ 11237
- 0
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.xml
ファイル差分が大きすぎるため省略します
ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.dll ファイルの表示


+ 9010
- 0
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.xml
ファイル差分が大きすぎるため省略します
ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.dll ファイルの表示


+ 10950
- 0
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.xml
ファイル差分が大きすぎるため省略します
ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/Newtonsoft.Json.12.0.3/packageIcon.png ファイルの表示

変更前 変更後
幅: 512  |  高さ: 512  |  サイズ: 8.7 KiB

バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/.signature.p7s ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/SQLitePCLRaw.core.1.1.14.nupkg ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/MonoAndroid/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/Xamarin.Mac20/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/Xamarin.iOS10/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/net35/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/net40/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/net45/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/netstandard1.0/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/netstandard1.1/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/portable-net40+sl5+netcore45+wp8+MonoAndroid10+MonoTouch10+Xamarin.iOS10/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/portable-net45+netcore45+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/portable-net45+netcore45+wpa81+wp8+MonoAndroid10+MonoTouch10+Xamarin.iOS10/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/uap10.0/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/win8/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/win81/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.core.1.1.14/lib/wpa81/SQLitePCLRaw.core.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.provider.e_sqlite3.net45.1.1.14/.signature.p7s ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.provider.e_sqlite3.net45.1.1.14/SQLitePCLRaw.provider.e_sqlite3.net45.1.1.14.nupkg ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/SQLitePCLRaw.provider.e_sqlite3.net45.1.1.14/lib/net45/SQLitePCLRaw.provider.e_sqlite3.dll ファイルの表示


バイナリ
appTime/AppTime-0.12/packages/System.Data.SQLite.Core.1.0.113.1/.signature.p7s ファイルの表示


変更されたファイルが多すぎるため、一部のファイルは表示されません

読み込み中…
キャンセル
保存