Skip to content

Commit

Permalink
fix(wip): Replaced Bing Maps with MapsUI.WPF library (issue #165) and…
Browse files Browse the repository at this point in the history
… improved the logic

chore: Bumping .NET version to 8.0.
refactor(wip): Minor refactoring (enabled implicit usings - impacts all files), modified some list initializers to use the simplified syntax).
chore: Updated CsWin32 library and some P/Invoke calls in Common project.
feat(wip): Improved the connections list (layout and logic).
fix: Connections list now uses a custom GroupedObservableCollectionView to allow displaying complex objects as group header.
  • Loading branch information
wokhan committed Jul 13, 2024
1 parent e59348a commit d82368f
Show file tree
Hide file tree
Showing 44 changed files with 766 additions and 450 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 7.0.100
dotnet-version: 8.0.300
- name: Build and publish
run: dotnet publish ${{ matrix.release }} -c Release ${{ matrix.self-contained }}
- name: Zip published files
run: compress-archive -path ./bin/net7.0-windows/${{ matrix.pub-folder }}publish/* -destinationpath ./bin/net7.0-windows/wfn-${{ matrix.release-name }}.zip
run: compress-archive -path ./bin/net8.0-windows/${{ matrix.pub-folder }}publish/* -destinationpath ./bin/net8.0-windows/wfn-${{ matrix.release-name }}.zip
- name: Deploy nightly
uses: WebFreak001/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
with:
upload_url: https://uploads.github.com/repos/wokhan/WFN/releases/24573510/assets{?name,label} # find out this value by opening https://api.github.com/repos/<owner>/<repo>/releases in your browser and copy the full "upload_url" value including the {?name,label} part
release_id: 24573510 # same as above (id can just be taken out the upload_url, it's used to find old releases)
asset_path: ./bin/net7.0-windows/wfn-${{ matrix.release-name }}.zip # path to archive to upload
asset_path: ./bin/net8.0-windows/wfn-${{ matrix.release-name }}.zip # path to archive to upload
asset_name: wfn-$$-${{ matrix.release-name }}.zip # name to upload the release as, use $$ to insert date (YYYYMMDD) and 6 letter commit hash
asset_content_type: application/zip # required by GitHub API
max_releases: 5 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 7.0.100
dotnet-version: 8.0.300
- name: Build and publish
run: dotnet publish ${{ matrix.release }} -c Release ${{ matrix.self-contained }}
- name: Zip published files
run: compress-archive -path ./bin/net7.0-windows/${{ matrix.pub-folder }}publish/* -destinationpath ./bin/net7.0-windows/wfn-${{ matrix.release-name }}.zip
run: compress-archive -path ./bin/net8.0-windows/${{ matrix.pub-folder }}publish/* -destinationpath ./bin/net8.0-windows/wfn-${{ matrix.release-name }}.zip
- name: Get release
id: get_release
uses: bruceadams/[email protected]
Expand All @@ -41,7 +41,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.get_release.outputs.upload_url }}
asset_path: ./bin/net7.0-windows/wfn-${{ matrix.release-name }}.zip
asset_path: ./bin/net8.0-windows/wfn-${{ matrix.release-name }}.zip
asset_name: wfn-${{ steps.get_release.outputs.tag_name }}-${{ matrix.release-name }}.zip
asset_content_type: application/zip

14 changes: 9 additions & 5 deletions Common.Tests/Common.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<AssemblyName>Wokhan.WindowsFirewallNotifier.Console.Tests</AssemblyName>
<RootNamespace>Wokhan.WindowsFirewallNotifier.Console.Tests</RootNamespace>
<Description>Windows Firewall Notifier - Common Tests</Description>
Expand All @@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MaxMind.GeoIP2">
<Version>5.1.0</Version>
<Version>5.2.0</Version>
</PackageReference>
<!--<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers">
<Version>2.9.8</Version>
Expand All @@ -27,11 +27,15 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>-->
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit.Console" Version="3.16.0" />
<PackageReference Include="NUnit.Console" Version="3.17.0" />
<PackageReference Include="NUnit.Extension.VSProjectLoader" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1">
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
2 changes: 1 addition & 1 deletion Common.Tests/Helpers/DnsResolverTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void TestDnsResolverResolveIpAddresses()
Task.WaitAll();

LogDictEntries();
Assert.AreEqual("dns.google", ResolvedIPInformation.CachedIPHostEntryDict["8.8.8.8"].resolvedHost);
Assert.That(ResolvedIPInformation.CachedIPHostEntryDict["8.8.8.8"].resolvedHost, Is.EqualTo("dns.google"));
Assert.True(ResolvedIPInformation.CachedIPHostEntryDict.Values.Count == 4);

ipList = new List<string>
Expand Down
10 changes: 5 additions & 5 deletions Common.Tests/Helpers/NetshHelperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public void TestFindMatchingFilterInfo()
Assert.NotNull(result);
Assert.False(result.HasErrors);
WriteDebugOutput($"name ={result.Name}, description={result.Description}");
Assert.AreEqual("Default Outbound", result.Name);
Assert.That(result.Name, Is.EqualTo("Default Outbound"));
Assert.True(result.Description is not null);
Assert.AreEqual(FiltersContextEnum.FILTERS, result.FoundIn);
Assert.That(result.FoundIn, Is.EqualTo(FiltersContextEnum.FILTERS));
}

//TODO: Commented out by @wokhan since test result depends on OS language and will fail on non-english ones
Expand Down Expand Up @@ -78,9 +78,9 @@ public void TestFindMatchingFilterInfo2Boot3()
Assert.NotNull(result);
Assert.False(result.HasErrors);
WriteDebugOutput($"name={result.Name}, description={result.Description}");
Assert.AreEqual("Boot Time Filter", result.Name);
Assert.That(result.Name, Is.EqualTo("Boot Time Filter"));
Assert.True(result.Description is not null);
Assert.AreEqual(result.FoundIn, FiltersContextEnum.WFPSTATE);
Assert.That(FiltersContextEnum.WFPSTATE, Is.EqualTo(result.FoundIn));
}

//TODO: Commented out by @wokhan since test result depends on OS language and will fail on non-english ones
Expand Down Expand Up @@ -108,7 +108,7 @@ public void TestFindMatchingFilterInfo4()
Assert.NotNull(result);
Assert.False(result.HasErrors);
WriteDebugOutput($"name={result.Name}, description={result.Description}");
Assert.AreEqual("Port Scanning Prevention Filter", result.Name);
Assert.That(result.Name, Is.EqualTo("Port Scanning Prevention Filter"));
Assert.True(result.Description is not null);
}
[Test, ManualTestCategory]
Expand Down
20 changes: 10 additions & 10 deletions Common/Common.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<AssemblyName>Wokhan.WindowsFirewallNotifier.Common</AssemblyName>
<RootNamespace>Wokhan.WindowsFirewallNotifier.Common</RootNamespace>
Expand All @@ -26,21 +26,21 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
<PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="MaxMind.GeoIP2" Version="5.1.0" />
<PackageReference Include="log4net" Version="2.0.17" />
<PackageReference Include="MaxMind.GeoIP2" Version="5.2.0" />
<!--<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>-->
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.1" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.206-beta">
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
<PackageReference Include="System.Management" Version="7.0.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="7.0.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.6" />
<PackageReference Include="System.Management" Version="8.0.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
<!-- Forcing versions for standalone win-xXX versions ?! -->
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
Expand Down
18 changes: 15 additions & 3 deletions Common/Net/IP/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public record Connection
public bool IsLoopback { get; private set; }

private MIB_TCP6ROW? tcp6MIBRow;

public bool IsMonitored { get; private set; }


Expand All @@ -47,7 +48,7 @@ internal Connection(MIB_TCPROW_OWNER_MODULE tcpRow)
OwningPid = tcpRow.dwOwningPid;
LocalAddress = new IPAddress(tcpRow.dwLocalAddr);
LocalPort = IPHelper.GetRealPort(tcpRow.dwLocalPort);
if (tcpRow.dwState != (uint)MIB_TCP_STATE.MIB_TCP_STATE_LISTEN)
if (!tcpRow.dwState.Equals(MIB_TCP_STATE.MIB_TCP_STATE_LISTEN))
{
RemoteAddress = new IPAddress(tcpRow.dwRemoteAddr);
RemotePort = IPHelper.GetRealPort(tcpRow.dwRemotePort);
Expand All @@ -67,7 +68,7 @@ internal Connection(MIB_TCP6ROW_OWNER_MODULE tcp6Row)
OwningPid = tcp6Row.dwOwningPid;
LocalAddress = new IPAddress(tcp6Row.ucLocalAddr.AsSpan().ToArray());
LocalPort = IPHelper.GetRealPort(tcp6Row.dwLocalPort);
if (tcp6Row.dwState != (uint)MIB_TCP_STATE.MIB_TCP_STATE_LISTEN)
if (!tcp6Row.dwState.Equals(MIB_TCP_STATE.MIB_TCP_STATE_LISTEN))
{
RemoteAddress = new IPAddress(tcp6Row.ucRemoteAddr.AsSpan().ToArray());
RemotePort = IPHelper.GetRealPort(tcp6Row.dwRemotePort);
Expand Down Expand Up @@ -110,6 +111,7 @@ public bool TryEnableStats()
}

var setting = new TCP_ESTATS_BANDWIDTH_RW_v0() { EnableCollectionInbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled, EnableCollectionOutbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled };
// Note: passing tcp6MIBROW as a parameter even for TCP V4 connections, but it will not be used as tcpMIBRow_LH (for V4) is taken directly from sourcerow
var r = sourceRow.SetPerTcpConnectionEStats(ref setting, tcp6MIBRow);

IsMonitored = (r == NO_ERROR && setting.EnableCollectionInbound == TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled && setting.EnableCollectionOutbound == TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled);
Expand All @@ -130,6 +132,7 @@ public bool TryEnableStats()

try
{
// Note: passing tcp6MIBROW as a parameter even for TCP V4 connections, but it will not be used as tcpMIBRow_LH (for V4) is taken directly from sourcerow
var rodObjectNullable = sourceRow.GetPerTcpConnectionEState(tcp6MIBRow);

if (rodObjectNullable is null)
Expand Down Expand Up @@ -159,6 +162,15 @@ public bool TryEnableStats()

return (inbound, outbound, true);
}
catch (InvalidOperationException)
{
IsMonitored = false;

_lastInboundReadValue = 0;
_lastOutboundReadValue = 0;

return (0, 0, false);
}
catch (Win32Exception we) when (we.NativeErrorCode == IPHelper.ERROR_NOT_FOUND)
{
IsMonitored = false;
Expand Down Expand Up @@ -236,7 +248,7 @@ public void UpdateWith(Connection rawConnection)
this.RemoteAddress = rawConnection.RemoteAddress;
this.RemotePort = rawConnection.RemotePort;
this.IsLoopback = rawConnection.IsLoopback;

this.tcp6MIBRow = rawConnection.tcp6MIBRow;
}
}
14 changes: 13 additions & 1 deletion Common/Net/IP/NativeOverrides/MIB_TCP6ROW_OWNER_MODULE.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;

using Wokhan.WindowsFirewallNotifier.Common.Net.IP;
Expand All @@ -19,12 +21,17 @@ unsafe uint IConnectionOwnerInfo.GetOwnerModule(IntPtr buffer, ref uint buffSize

unsafe TCP_ESTATS_BANDWIDTH_ROD_v0? IConnectionOwnerInfo.GetPerTcpConnectionEState(MIB_TCP6ROW? tcp6Row)
{
if (tcp6Row is null)
{
return null;
}

var rw = new TCP_ESTATS_BANDWIDTH_RW_v0() { EnableCollectionInbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled, EnableCollectionOutbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled };
var rod = new TCP_ESTATS_BANDWIDTH_ROD_v0();

var row = tcp6Row.Value;

var ret = NativeMethods.GetPerTcp6ConnectionEStats(in row, TCP_ESTATS_TYPE.TcpConnectionEstatsBandwidth, (byte*)&rw, 0, MarshalHelper.rwS, null, 0, 0, (byte*)&rod, 0, MarshalHelper.rodS);
var ret = NativeMethods.GetPerTcp6ConnectionEStats(&row, TCP_ESTATS_TYPE.TcpConnectionEstatsBandwidth, (byte*)&rw, 0, MarshalHelper.rwS, null, 0, 0, (byte*)&rod, 0, MarshalHelper.rodS);

if (ret == 0 && rw.EnableCollectionInbound == TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled && rw.EnableCollectionOutbound == TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled)
{
Expand All @@ -36,6 +43,11 @@ unsafe uint IConnectionOwnerInfo.GetOwnerModule(IntPtr buffer, ref uint buffSize

unsafe uint IConnectionOwnerInfo.SetPerTcpConnectionEStats(ref TCP_ESTATS_BANDWIDTH_RW_v0 rw, MIB_TCP6ROW? tcp6Row)
{
if (tcp6Row is null)
{
return 87; // ERROR_INVALID_PARAMETER
}

var row = tcp6Row.Value;
fixed (void* rwPtr = &rw)
{
Expand Down
4 changes: 2 additions & 2 deletions Common/Net/IP/NativeOverrides/MIB_TCPROW_OWNER_MODULE.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ unsafe uint IConnectionOwnerInfo.GetOwnerModule(IntPtr buffer, ref uint buffSize
return NativeMethods.GetOwnerModuleFromTcpEntry(this, TCPIP_OWNER_MODULE_INFO_CLASS.TCPIP_OWNER_MODULE_INFO_BASIC, buffer.ToPointer(), ref buffSize);
}

unsafe TCP_ESTATS_BANDWIDTH_ROD_v0? IConnectionOwnerInfo.GetPerTcpConnectionEState(MIB_TCP6ROW? tcp6Row)
unsafe TCP_ESTATS_BANDWIDTH_ROD_v0? IConnectionOwnerInfo.GetPerTcpConnectionEState(MIB_TCP6ROW? _)
{
var rw = new TCP_ESTATS_BANDWIDTH_RW_v0() { EnableCollectionInbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled, EnableCollectionOutbound = TCP_BOOLEAN_OPTIONAL.TcpBoolOptEnabled };
var rod = new TCP_ESTATS_BANDWIDTH_ROD_v0();
Expand All @@ -36,7 +36,7 @@ unsafe uint IConnectionOwnerInfo.GetOwnerModule(IntPtr buffer, ref uint buffSize
return null;
}

unsafe uint IConnectionOwnerInfo.SetPerTcpConnectionEStats(ref TCP_ESTATS_BANDWIDTH_RW_v0 rw, MIB_TCP6ROW? tcp6Row)
unsafe uint IConnectionOwnerInfo.SetPerTcpConnectionEStats(ref TCP_ESTATS_BANDWIDTH_RW_v0 rw, MIB_TCP6ROW? _)
{
uint ret;

Expand Down
4 changes: 3 additions & 1 deletion Common/Processes/ProcessHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Windows;

using Windows.Win32;
Expand Down Expand Up @@ -218,7 +219,8 @@ public unsafe static string GetLocalUserOwner(uint pid)
return String.Empty;
}

if (!NativeMethods.ConvertSidToStringSid(hTokenInformation.User.Sid, out PWSTR SID))
PWSTR SID = new PWSTR();
if (!NativeMethods.ConvertSidToStringSid(hTokenInformation.User.Sid, &SID))
{
LogHelper.Warning("Unable to retrieve process local user owner: SID cannot be converted!");
return String.Empty;
Expand Down
3 changes: 2 additions & 1 deletion Common/UAP/StorePackageHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ static StorePackageHelper()
}
finally
{
NativeMethods.FreeSid(pSID);
//Already freed since it's a safe handle?
//NativeMethods.FreeSid(pSID);
}
}
finally
Expand Down
2 changes: 2 additions & 0 deletions Console/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<BindingConverters:ValueToVisibilityConverter x:Key="valueToVisibility" />
<BindingConverters:ValueToVisibilityNegateConverter x:Key="valueToVisibilityNegate" />
<BindingConverters:ValueChecker x:Key="valueChecker" />
<!--<BindingConverters:UnitFormatConverter x:Key="unitFormatter" />-->

<LocalBindingConverters:UnitFormatConverter x:Key="unitFormatter" />

Expand Down Expand Up @@ -238,6 +239,7 @@
<!--<Setter Property="ext:CustomAdorner.CornerRadius" Value="2" />-->
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Margin" Value="3" />
</Style>
<Style TargetType="Label">
<Setter Property="VerticalAlignment" Value="Center" />
Expand Down
4 changes: 1 addition & 3 deletions Console/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.ComponentModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;

Expand Down
13 changes: 7 additions & 6 deletions Console/Console.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>true</ImplicitUsings>
<!-- auto-increment version - see: https://stackoverflow.com/a/60571414 -->
<!-- TODO: add this to other projects as well? -->
<FileVersion>2.6.$([System.DateTime]::UtcNow.Date.Subtract($([System.DateTime]::Parse("2020-01-01"))).TotalDays).$([System.Math]::Floor($([MSBuild]::Divide($([System.DateTime]::UtcNow.TimeOfDay.TotalSeconds), 1.32))))</FileVersion>
Expand Down Expand Up @@ -48,16 +49,16 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="CountryFlag.Wpf" Version="1.0.0" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-beta.701" />
<PackageReference Include="MaxMind.Db" Version="4.0.0" />
<PackageReference Include="MaxMind.GeoIP2" Version="5.1.0" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc2" />
<PackageReference Include="Mapsui.Wpf" Version="5.0.0-beta.1" />
<PackageReference Include="MaxMind.Db" Version="4.1.0" />
<PackageReference Include="MaxMind.GeoIP2" Version="5.2.0" />
<!--<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>-->
<PackageReference Include="Microsoft.Maps.MapControl.WPF" Version="1.0.0.3" />
<PackageReference Include="Wokhan.Core" Version="0.9.8-beta" />
<PackageReference Include="Wokhan.UI" Version="0.9.8-beta" />
</ItemGroup>
Expand Down
Loading

0 comments on commit d82368f

Please sign in to comment.