mirror of https://github.com/dahall/Vanara.git
218 lines
9.1 KiB
C#
218 lines
9.1 KiB
C#
using NUnit.Framework;
|
|
using NUnit.Framework.Internal;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using static Vanara.PInvoke.PortableDeviceApi;
|
|
|
|
namespace Vanara.PInvoke.Tests
|
|
{
|
|
[TestFixture]
|
|
public class PortableDeviceApiTests
|
|
{
|
|
IPortableDevice device = null;
|
|
IPortableDeviceManager manager;
|
|
static readonly Ole32.PROPERTYKEY eventNameProp = new(Guid.NewGuid(), 1);
|
|
|
|
[OneTimeSetUp]
|
|
public void _Setup()
|
|
{
|
|
manager = new();
|
|
var deviceId = manager.GetDevices().FirstOrDefault();
|
|
if (deviceId is null) throw new ArgumentNullException("Device");
|
|
device = new();
|
|
device.Open(deviceId, GetClientInfo());
|
|
}
|
|
|
|
[OneTimeTearDown]
|
|
public void _TearDown()
|
|
{
|
|
device?.Close();
|
|
device = null;
|
|
manager = null;
|
|
}
|
|
|
|
[Test]
|
|
public void DeviceEventing()
|
|
{
|
|
string evtName = nameof(DeviceEventing);
|
|
EventWaitHandle evt = new(false, EventResetMode.ManualReset, evtName);
|
|
EventCallback callback = new();
|
|
IPortableDeviceValues vals = new();
|
|
vals.SetStringValue(eventNameProp, evtName);
|
|
device.Advise(0, callback, vals, out var cookie);
|
|
TestContext.WriteLine($"Fired = {evt.WaitOne(200)}");
|
|
device.Unadvise(cookie);
|
|
}
|
|
|
|
class EventCallback : IPortableDeviceEventCallback
|
|
{
|
|
void IPortableDeviceEventCallback.OnEvent(IPortableDeviceValues pEventParameters)
|
|
{
|
|
var evtName = pEventParameters.GetStringValue(eventNameProp);
|
|
if (EventWaitHandle.TryOpenExisting(evtName, out var evt))
|
|
evt.Set();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EnumDevices()
|
|
{
|
|
try
|
|
{
|
|
var devices = manager.GetDevices();
|
|
TestContext.WriteLine("{0} Windows Portable Device(s) found in the system", devices.Length);
|
|
|
|
foreach (var device in devices)
|
|
{
|
|
TestContext.WriteLine(device +
|
|
"\n\tManufacturer: " + manager.GetDeviceManufacturer(device) +
|
|
"\n\tDescription: " + manager.GetDeviceDescription(device) +
|
|
"\n\tFriendly Name: " + manager.GetDeviceFriendlyName(device));
|
|
}
|
|
}
|
|
catch (COMException exception)
|
|
{
|
|
Console.WriteLine("COM exception: code {1}, {0}", exception.Message, exception.ErrorCode);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Console.WriteLine(exception.Message);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EnumDeviceProps()
|
|
{
|
|
var content = device.Content();
|
|
|
|
foreach (var s in RecursiveEnumerate(WPD_DEVICE_OBJECT_ID, content).Take(20))
|
|
TestContext.WriteLine(s);
|
|
|
|
static IEnumerable<string> RecursiveEnumerate(string objId, IPortableDeviceContent content)
|
|
{
|
|
var enumObjs = content.EnumObjects(0, objId);
|
|
foreach (var sobj in enumObjs.Enumerate())
|
|
{
|
|
yield return sobj;
|
|
foreach (var s in RecursiveEnumerate(sobj, content))
|
|
yield return s;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EnumDeviceFuncCats()
|
|
{
|
|
var caps = device.Capabilities();
|
|
|
|
foreach (var catid in caps.GetFunctionalCategories().Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid.Value))
|
|
{
|
|
TestContext.WriteLine(GetPI(catid, "WPD_FUNCTIONAL_CATEGORY_")?.Name ?? catid.ToString());
|
|
foreach (var type in caps.GetSupportedContentTypes(catid).Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid.Value))
|
|
{
|
|
TestContext.WriteLine(" t: " + (GetPI(type, "WPD_CONTENT_TYPE_")?.Name ?? type.ToString()));
|
|
foreach (var fmt in caps.GetSupportedFormats(type).Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid.Value))
|
|
TestContext.WriteLine(" f: " + (GetPI(fmt, "WPD_OBJECT_FORMAT_")?.Name ?? type.ToString()));
|
|
}
|
|
foreach (var obj in caps.GetFunctionalObjects(catid).Enumerate().Where(pv => pv.VarType == VarEnum.VT_LPWSTR).Select(pv => pv.pwszVal))
|
|
TestContext.WriteLine(" o: " + obj);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EnumDeviceCmds()
|
|
{
|
|
var caps = device.Capabilities();
|
|
|
|
foreach (var cmd in caps.GetSupportedCommands().Enumerate())
|
|
{
|
|
TestContext.WriteLine(GetPI(cmd, "WPD_COMMAND_")?.Name ?? cmd.ToString());
|
|
Write(caps.GetCommandOptions(cmd), "WPD_OPTION_", " ");
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EnumDeviceResources()
|
|
{
|
|
var content = device.Content();
|
|
var res = content.Transfer();
|
|
foreach (var cmd in res.GetSupportedResources(WPD_DEVICE_OBJECT_ID).Enumerate())
|
|
{
|
|
Write(res.GetResourceAttributes(WPD_DEVICE_OBJECT_ID, cmd), "WPD_");
|
|
}
|
|
}
|
|
|
|
private void Write(IPortableDeviceValues vals, string lookupFilter, string prefix = "")
|
|
{
|
|
foreach (var val in vals.Enumerate())
|
|
{
|
|
var name = GetPI(val.Item1, lookupFilter)?.Name ?? val.Item1.ToString();
|
|
TestContext.WriteLine($"{prefix}{name} = {val.Item2.Value}");
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void CallDeviceCmd()
|
|
{
|
|
var pkey = WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED;
|
|
Assert.IsTrue(device.Capabilities().GetSupportedCommands().Enumerate().Contains(pkey));
|
|
var objId = device.Content().EnumObjects(0, WPD_DEVICE_OBJECT_ID).Enumerate().First();
|
|
IPortableDeviceValues vals = new();
|
|
vals.SetCommandPKey(pkey);
|
|
vals.SetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, objId);
|
|
IPortableDeviceKeyCollection result = new();
|
|
vals.SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, result);
|
|
device.SendCommand(0, vals);
|
|
foreach (var key in result.Enumerate())
|
|
TestContext.WriteLine(key);
|
|
}
|
|
|
|
[Test]
|
|
public void EnumDeviceEvents()
|
|
{
|
|
var caps = device.Capabilities();
|
|
|
|
foreach (var evt in caps.GetSupportedEvents().Enumerate().Where(pv => pv.VarType == VarEnum.VT_CLSID).Select(pv => pv.puuid.Value))
|
|
{
|
|
TestContext.WriteLine(GetPI(evt, "WPD_EVENT_")?.Name ?? evt.ToString());
|
|
foreach (var opt in caps.GetEventOptions(evt).Enumerate())
|
|
TestContext.WriteLine($" {(GetPI(opt.Item1, "WPD_EVENT_OPTION_")?.Name ?? opt.Item1.ToString())} = {opt.Item2.Value}");
|
|
}
|
|
}
|
|
|
|
private static PropertyInfo GetPI<T>(T t, string prefix) =>
|
|
typeof(PortableDeviceApi).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).
|
|
FirstOrDefault(pi => pi.Name.StartsWith(prefix) && pi.PropertyType.Equals(typeof(T)) && t.Equals(pi.GetValue(null)));
|
|
|
|
private IPortableDeviceValues GetClientInfo(bool readOnly = true)
|
|
{
|
|
// Client information is optional. The client can choose to identify itself, or
|
|
// to remain unknown to the driver. It is beneficial to identify yourself because
|
|
// drivers may be able to optimize their behavior for known clients. (e.g. An
|
|
// IHV may want their bundled driver to perform differently when connected to their
|
|
// bundled software.)
|
|
|
|
// CoCreate an IPortableDeviceValues interface to hold the client information.
|
|
IPortableDeviceValues clientInformation = new();
|
|
|
|
// Attempt to set all bits of client information
|
|
clientInformation.SetStringValue(WPD_CLIENT_NAME, "Test");
|
|
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, 1);
|
|
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, 0);
|
|
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_REVISION, 2);
|
|
if (readOnly)
|
|
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS, ACCESS_MASK.GENERIC_READ);
|
|
|
|
// Some device drivers need to impersonate the caller in order to function correctly. Since our application does not
|
|
// need to restrict its identity, specify SECURITY_IMPERSONATION so that we work with all devices.
|
|
clientInformation.SetUnsignedIntegerValue(WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, (uint)FileFlagsAndAttributes.SECURITY_IMPERSONATION);
|
|
|
|
return clientInformation;
|
|
}
|
|
}
|
|
} |