我是Xamarin(和C#)的新手,我的任务是编写基本上是网络扫描程序的东西。我正在使用Xamarin Forms,因为该应用程序应该适用于iOS和Android。
首先,我创建了一个SQLite.SQLiteException (CannotOpen)
来存储通过ping网络上的设备生成的IP地址列表,但我现在正尝试将这些IP地址存储到SQLite数据库中。
我跟着Xamarin tutorial on the subject我已经能够合理地走得很远。我有它,所以它保存到Android上的数据库,然后显示在列表视图中(虽然我不是特别高兴我如何刷新列表视图的内容,但这是另一个问题)。
不幸的是,在iOS上,我无法获取数据库来存储值。每次尝试从扫描中添加第一个IP地址时,我都会得using System;
using SQLite;
using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
namespace ExampleUI
{
[Table("Device")]
public class Device : IComparable, INotifyPropertyChanged
{
private int _id;
[PrimaryKey, AutoIncrement]
public int ID {
get {
return _id;
}
set
{
Debug.WriteLine("ID SET");
this._id = value;
OnPropertyChanged(nameof(ID));
}
}
private string _ip;
public string ip
{
get
{
return _ip;
}
set
{
Debug.WriteLine("IP SET");
this._ip = value;
OnPropertyChanged(nameof(ip));
}
}
private string _mac;
public string mac
{
get
{
return _mac;
}
set{
Debug.WriteLine("MAC SET");
this._mac = value;
OnPropertyChanged(nameof(mac));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public Device()
{
}
public Device(string ip, string mac)
{
this.ip = ip;
this.mac = mac;
}
public int CompareTo(object obj)
{
Device device = obj as Device;
if (device == null)
{
throw new ArgumentException("Object is not a Device");
}
int lastOctet = 0;
var octet = ip.Split('.').Last();
Int32.TryParse(octet, out lastOctet);
int lastDeviceOctet = 0;
var deviceOctet = device.ip.Split('.').Last();
if (Int32.TryParse(deviceOctet, out lastDeviceOctet) && Int32.TryParse(octet, out lastOctet))
{
return lastOctet.CompareTo(lastDeviceOctet);
}
else {
throw new ArgumentException("Last octet is not an integer");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
。我正在使用GBPing库(使用Xamarin绑定)来执行ping操作。
在我开始运行扫描之前,我添加了对insert方法的调用,并且添加了很多。我不知道为什么它不能在后续实例中成功运行。
我的数据模型类是:
using System;
using SQLite;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Xamarin.Forms;
using System.Diagnostics;
using System.ComponentModel;
namespace ExampleUI
{
public class DeviceDatabase: INotifyPropertyChanged
{
static object locker = new object();
SQLiteConnection database;
private ObservableCollection<Device> _devices;
public ObservableCollection<Device> devices {
get {
this._devices.Sort();
return this._devices;
}
set
{
Debug.WriteLine("SETTING OBSERVABLE COLLECTION");
this._devices = value;
OnPropertyChanged("devices");
}
}
public DeviceDatabase()
{
database = DependencyService.Get<ISQLite>().GetConnection();
Debug.WriteLine(database);
database.CreateTable<Device>();
this._devices = new ObservableCollection<Device>(database.Table<Device>());
Debug.WriteLine("NO OF DEVICES IN COLLECTION:");
Debug.WriteLine(_devices.Count);
this._devices.CollectionChanged += _devices_CollectionChanged;
}
public ObservableCollection<Device> GetDevices()
{
lock (locker)
{
Debug.WriteLine("RETURNING LIST OF DEVICES");
List<Device> collection = database.Table<Device>().ToList();
ObservableCollection<Device> devicecollection = new ObservableCollection<Device>(collection);
devicecollection.Sort();
return devicecollection;
}
}
public Device GetDevice(int id)
{
lock (locker)
{
return database.Table<Device>().FirstOrDefault(x => x.ID == id);
}
}
public int SaveDevice(Device item)
{
Debug.WriteLine("SAVE DEVICE TO DB");
lock (locker)
{
if (item.ID != 0)
{
database.Update(item);
return item.ID;
}
else {
Debug.WriteLine(database.DatabasePath);
var query = database.Table<Device>().Where(v => v.ip.Equals(item.ip));
if (query.Count() == 0)
{
return database.Insert(item);
}
return 1;
}
}
}
public int DeleteDevice(int id)
{
lock (locker)
{
return database.Delete<Device>(id);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
void _devices_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Debug.WriteLine("_devices_CollectionChanged");
}
}
}
数据库类是:
App.xaml.cs
数据库在主using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace ExampleUI
{
public partial class App : Application
{
static DeviceDatabase database;
public App()
{
MainPage = new NavigationPage(new DevicePage());
}
public static DeviceDatabase Database
{
get
{
if (database == null)
{
database = new DeviceDatabase();
}
return database;
}
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
文件中设置:
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Net;
using System.Linq;
using System.Text;
using Foundation;
using System.Collections.ObjectModel;
using GBPinger;
using ExampleUI;
namespace GBPingHelper
{
static class Extensions
{
public static void Sort<T>(this ObservableCollection<T> collection) where T : IComparable
{
List<T> sorted = collection.OrderBy(x => x).ToList();
for (int i = 0; i < sorted.Count(); i++)
collection.Move(collection.IndexOf(sorted[i]), i);
}
}
public class PingHelper : GBPingDelegate
{
ObservableCollection<ExampleUI.Device> _devices { get; set; }
int currentPingers;
public delegate void DeviceScanCompleteHandler(object sender, PingArgs e);
public delegate void DeviceAddedHandler(object sender, PingArgs e);
public static event DeviceScanCompleteHandler DeviceScanComplete;
public static event DeviceAddedHandler DeviceAdded;
[DllImport(ObjCRuntime.Constants.SystemLibrary)]
static internal extern int sysctl([MarshalAs(UnmanagedType.LPArray)] int[] mib, int mibsize, IntPtr output, IntPtr oldLen, IntPtr newp, uint newlen);
public static Dictionary<String, String> AllMac = null;
public PingHelper()
{
_devices = new ObservableCollection<Device>();
currentPingers = 0;
}
public ObservableCollection<Device> Devices()
{
Console.WriteLine("HERE???? {0}", _devices.Count);
return _devices;
}
public void pingSubnet(string subnet)
{
currentPingers = 0;
for (int i = 1; i < 255; i++)
{
string ip = subnet + "." + i.ToString();
Console.WriteLine("IP IS: {0}", ip);
GBPing pinger = new GBPing();
pinger.Host = ip;
pinger.Delegate = this;
pinger.Timeout = 1.0;
pinger.PingPeriod = 1.0;
pinger.SetupWithBlock((success, error) =>
{
if (success)
{
pinger.StartPinging();
currentPingers += 1;
Console.WriteLine("Started...");
NSTimer.CreateScheduledTimer(TimeSpan.FromSeconds(5.0), delegate
{
pinger.Stop();
currentPingers -= 1;
pinger = null;
if (currentPingers == 0)
{
PingArgs args = new PingArgs();
args.Devices = Devices();
DeviceScanComplete(this, args);
}
});
}
else {
Console.WriteLine("Failed to start");
}
});
}
}
//delegate methods
public override void DidFailWithError(GBPing pinger, NSError error)
{
Console.WriteLine("DidFailWithError {0}", error.ToString());
}
public override void DidSendPingWithSummary(GBPing pinger, GBPingSummary summary)
{
Console.WriteLine("DidSendPingWithSummary {0}", summary.Host);
}
public override void DidFailToSendPingWithSummary(GBPing pinger, GBPingSummary summary, NSError error)
{
Console.WriteLine("DidFailToSendPingWithSummary {0}", summary.Host);
}
public override void DidTimeoutWithSummary(GBPing pinger, GBPingSummary summary)
{
Console.WriteLine("DidTimeoutWithSummary {0}", summary.Host);
}
public override void DidReceiveReplyWithSummary(GBPing pinger, GBPingSummary summary)
{
Console.WriteLine("DidReceiveReplyWithSummary {0}", summary.Host);
Dictionary<string, string> searchPattern = new Dictionary<string, string>();
searchPattern.Add("ip", summary.Host);
var device = Devices().FirstOrDefault(o => o.ip == summary.Host);
Console.WriteLine("THE DEVICE IS: {0}", device);
if (device == null)
{
Device deviceDetails = new Device(summary.Host, GetMACAddressByIp(summary.Host));
//_devices.Add(deviceDetails);
//_devices.Sort();
Console.WriteLine("ADDED DEVICE: {0}", _devices.Count);
//notify that a new device has been found
PingArgs args = new PingArgs();
args.Devices = new ObservableCollection<Device>();
args.Devices.Add(deviceDetails);
DeviceAdded(this, args);
}
}
public override void DidReceiveUnexpectedReplyWithSummary(GBPing pinger, GBPingSummary summary)
{
Console.WriteLine("DidReceiveUnexpectedReplyWithSummary {0}", summary.Host);
}
//mac address methods
public static String GetMACAddressByIp(String ip)
{
return GetMACAddressByIp(ip, false);
}
public static String GetMACAddressByIp(String ip, bool reset)
{
try
{
if (AllMac == null || reset)
{
AllMac = new Dictionary<string, string>();
int CTL_NET = 4;
int PF_ROUTE = 17;
int AF_INET = 2;
int NET_RT_FLAGS = 2;
int RTF_LLINFO = 0x400;
int[] mib = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO };
var pLen = Marshal.AllocHGlobal(sizeof(int));
sysctl(mib, 6, IntPtr.Zero, pLen, IntPtr.Zero, 0);
var length = Marshal.ReadInt32(pLen);
if (length == 0)
{
Marshal.FreeHGlobal(pLen);
return string.Empty;
}
var pBuf = Marshal.AllocHGlobal(length);
sysctl(mib, 6, pBuf, pLen, IntPtr.Zero, 0);
int off = 0;
while (true)
{
short len = Marshal.ReadInt16(pBuf + off);
IntPtr paddr = pBuf + off + 36 + 56;
byte sinlen = Marshal.ReadByte(paddr);
uint addr = (uint)Marshal.ReadInt32(paddr + 4);
string ipAddress = new IPAddress(BitConverter.GetBytes(addr)).ToString();
paddr += sinlen;
StringBuilder sb = new StringBuilder();
bool allZero = true;
for (int m = 0; m < 6; m++)
{
byte b = Marshal.ReadByte(paddr + 8 + m);
if (b != 0)
{
allZero = false;
}
if (sb.Length > 0)
{
sb.Append(":");
}
sb.Append(b.ToString("X2"));
}
String mac = sb.ToString();
if (!allZero)
{
AllMac[ipAddress] = mac;
}
off += len;
if (length <= off)
{
break;
}
}
Marshal.FreeHGlobal(pLen);
Marshal.FreeHGlobal(pBuf);
}
if (AllMac.ContainsKey(ip))
{
return AllMac[ip];
}
}
catch (Exception ex)
{
Console.WriteLine("Exception in GetMACAddressByIp:" + ex.ToString());
}
return "";
}
}
}
我在PingHelper类中使用了GBPing:
App.Database.SaveDevice
这用于IPAddressManager接口的iOS实现(这是调用DeviceAdded
的地方(参见方法using ExampleUI.iOS;
using Xamarin.Forms;
using System.Net.NetworkInformation;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using GBPingHelper;
using System.Linq;
[assembly: Dependency(typeof(IPAddressManager))]
namespace ExampleUI.iOS
{
public class IPAddressManager : IIPAddressManager
{
PingHelper helper;
public event EventHandler<PingArgs> DeviceCollectionUpdated;
public event EventHandler DeviceCollectionScanStarted;
public event EventHandler DeviceCollectionScanComplete;
public IPAddressManager()
{
helper = new PingHelper();
PingHelper.DeviceScanComplete += DeviceScanCompleted;
PingHelper.DeviceAdded += DeviceAdded;
}
public String GetIPAddress()
{
String ipAddress = "";
foreach (var netInterface in NetworkInterface.GetAllNetworkInterfaces())
{
if (netInterface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 ||
netInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
{
foreach (var addrInfo in netInterface.GetIPProperties().UnicastAddresses)
{
if (addrInfo.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
ipAddress = addrInfo.Address.ToString();
}
}
}
}
return ipAddress;
}
public ObservableCollection<ExampleUI.Device> Devices()
{
return helper.Devices();
}
public void PingSubnet()
{
DeviceCollectionScanStarted(this, EventArgs.Empty);
string ipaddress = GetIPAddress();
string[] octets = ipaddress.Split('.');
var subnetOctets = octets.Take(octets.Length - 1);
String subnetString = String.Join(".", subnetOctets);
helper.pingSubnet(subnetString);
}
public void DeviceScanCompleted(Object sender, PingArgs args)
{
Console.WriteLine("HERE AT LAST {0}", args.Devices);
DeviceCollectionScanComplete(this, EventArgs.Empty);
}
public void DeviceAdded(Object sender, PingArgs args)
{
Console.WriteLine("ADDRESS ADDED {0}", args.Devices[0].ip);
App.Database.SaveDevice(args.Devices[0]);
DeviceCollectionUpdated(this, args);
}
}
}
):
ListView
绑定到App.Database.SaveDevice
值的视图模型是(注意我在构造函数中调用了using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ExampleUI
{
public class MainPageViewModel: INotifyPropertyChanged
{
bool isLoading;
bool isRefreshing;
bool devicePresent;
private Command refreshDeviceCommand;
public Command RefreshDeviceCommand
{
get
{
return refreshDeviceCommand ?? (refreshDeviceCommand = new Command(ExecuteRefreshDeviceCommand, () =>
{
return !IsRefreshing;
}));
}
}
public bool IsLoading
{
get
{
return isLoading;
}
set
{
isLoading = value;
OnPropertyChanged("IsLoading");
}
}
public bool IsRefreshing
{
get
{
return isRefreshing;
}
set
{
isRefreshing = value;
Debug.WriteLine("SETTING REFRESHING TO {0}", value);
OnPropertyChanged("IsRefreshing");
}
}
public bool DevicePresent
{
get
{
return devicePresent;
}
set
{
devicePresent = value;
OnPropertyChanged("DevicePresent");
}
}
private ObservableCollection<Device> _devices;
public ObservableCollection<Device> Devices { get
{
return this._devices;
}
set
{
this._devices = value;
OnPropertyChanged("Devices");
}
}
//public ObservableCollection<Device> Devices
//{
// get
// {
// //return DependencyService.Get<IIPAddressManager>().Devices();
// return App.Database.devices;
// }
//}
public MainPageViewModel()
{
IsLoading = true;
IsRefreshing = false;
DevicePresent = false;
DependencyService.Get<IIPAddressManager>().DeviceCollectionUpdated += DeviceCollectionUpdated;
DependencyService.Get<IIPAddressManager>().DeviceCollectionScanComplete += DeviceCollectionScanComplete;
//Devices = App.Database.devices;
Devices = App.Database.GetDevices();
App.Database.SaveDevice(new Device("192.168.0.1", "00:00"));
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private void ExecuteRefreshDeviceCommand()
{
Debug.WriteLine("ExecuteRefreshDeviceCommand");
Debug.WriteLine(isRefreshing);
if (IsRefreshing)
{
IsRefreshing = true;
Debug.WriteLine("WILL REFRESH");
DependencyService.Get<IIPAddressManager>().PingSubnet();
}
}
void DeviceCollectionUpdated(object sender, PingArgs args)
{
Debug.WriteLine("DEVICE COLLECTION UPDATED");
IsLoading = false;
DevicePresent = true;
}
void DeviceCollectionScanComplete(object sender, EventArgs args)
{
Debug.WriteLine("DEVICE COLLECTION SCAN COMPLETE");
IsLoading = false;
DevicePresent = true;
IsRefreshing = false;
Devices = App.Database.GetDevices();
}
}
,工作正常:
using System;
using System.IO;
using Xamarin.Forms;
using ExampleUI.iOS;
using SQLite;
using Foundation;
[assembly: Dependency(typeof(SQLite_iOS))]
namespace ExampleUI.iOS
{
public class SQLite_iOS: ISQLite
{
public SQLiteConnection GetConnection()
{
var sqliteFilename = "Device.db3";
var docs = NSFileManager.DefaultManager.GetUrls(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomain.User)[0];
string documentsPath = docs.Path;
string libraryPath = Path.Combine(documentsPath, "..", "Library"); // Library folder
var path = Path.Combine(libraryPath, sqliteFilename);
Console.WriteLine(path);
var conn = new SQLiteConnection(path);
return conn;
}
}
}
异常捕获窗口如下所示:
ISQLite的iOS实现:
namespace MyGameBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Island
*
*
* @ORM\Table(name="islands")
* @ORM\Entity(repositoryClass="MyGameBundle\Repository\IslandRepository")
*/
class Island
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var int
*
* @ORM\Column(name="x", type="integer")
*/
private $x;
/**
* @var int
*
* @ORM\Column(name="y", type="integer")
*/
private $y;
/**
* @var Player
*
* @ORM\ManyToOne(targetEntity="MyGameBundle\Entity\Player", inversedBy="islands")
* @ORM\JoinColumn(name="player_id", nullable=false)
*/
private $player;
/**
* @var IslandResource[]
*
* @ORM\OneToMany(targetEntity="MyGameBundle\Entity\IslandResource", mappedBy="island")
*/
private $resources;
/**
* @var IslandBuilding[]
*
* @ORM\OneToMany(targetEntity="MyGameBundle\Entity\IslandBuilding", mappedBy="island")
*/
private $buildings;
/**
* @var IslandTroop[]
*
* @ORM\OneToMany(targetEntity="MyGameBundle\Entity\IslandTroop", mappedBy="island")
*/
private $troops;
/**
* @var TroopProcess[]
*
* @ORM\OneToMany(targetEntity="MyGameBundle\Entity\TroopProcess", mappedBy="island")
*/
private $process;
public function __construct()
{
$this->resources = new ArrayCollection();
$this->buildings = new ArrayCollection();
$this->troops = new ArrayCollection();
$this->process = new ArrayCollection();
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set x
*
* @param integer $x
*
* @return Island
*/
public function setX($x)
{
$this->x = $x;
return $this;
}
/**
* Get x
*
* @return int
*/
public function getX()
{
return $this->x;
}
/**
* Set y
*
* @param integer $y
*
* @return Island
*/
public function setY($y)
{
$this->y = $y;
return $this;
}
/**
* Get y
*
* @return int
*/
public function getY()
{
return $this->y;
}
/**
* @return Player
*/
public function getPlayer()
{
return $this->player;
}
/**
* @param Player $player
*/
public function setPlayer(Player $player)
{
$this->player = $player;
}
/**
* @return IslandResource[]
*/
public function getResources()
{
return $this->resources;
}
/**
* @param IslandResource[] $resources
*/
public function setResources(array $resources)
{
$this->resources = $resources;
}
/**
* @return IslandBuilding[]
*/
public function getBuildings()
{
return $this->buildings;
}
/**
* @param IslandBuilding[] $buildings
*/
public function setBuildings(array $buildings)
{
$this->buildings = $buildings;
}
/**
* @return IslandTroop[]
*/
public function getTroops()
{
return $this->troops;
}
/**
* @param IslandTroop[] $troops
*/
public function setTroops(array $troops)
{
$this->troops = $troops;
}
/**
* @return TroopProcess[]
*/
public function getProcess()
{
return $this->process;
}
/**
* @param TroopProcess[] $process
*/
public function setProcess(array $process)
{
$this->process = $process;
}
}
非常感谢您提供的任何帮助。
谢谢,
亚当。