Impersonating user in asp.net core - asp.net-core

I have the following piece of code from a regular mvc app which uploads a file by impersonating a user
public class PublicController : Controller
{
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
WindowsImpersonationContext impersonationContext;
[DllImport("advapi32.dll")]
public static extern int LogonUserA(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
public SomeActionMethod(model containing file)
{
if (ImpersonateValidUser(userName: "someuserwithpowertoupload", domain: "", password: "somepassword"))
{
path = "Somepath";
file.SaveAs(path);
}
}
private bool ImpersonateValidUser(String userName, String domain, String password)
{
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, impersonationLevel: 2, hNewToken: ref tokenDuplicate) != 0)
{
using (tempWindowsIdentity = new WindowsIdentity(tokenDuplicate))
{
this.impersonationContext = tempWindowsIdentity.Impersonate();
if (this.impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
}
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (tokenDuplicate != IntPtr.Zero)
{
CloseHandle(tokenDuplicate);
}
return false;
}
Problem here is that WindowsImpersonationContext doesnt exist in .net core. Can anyone provide a code snippet which impersonates a user? Microsoft docs here https://learn.microsoft.com/en-us/dotnet/standard/security/impersonating-and-reverting isnt very helpful.
Thank you.

From the docs:
ASP.NET Core doesn't implement impersonation. Apps run with the app's identity for all requests, using app pool or process identity. If the app should perform an action on behalf of a user, use WindowsIdentity.RunImpersonated or RunImpersonatedAsync in a terminal inline middleware in Startup.Configure.
app.Run(async (context) =>
{
try
{
var user = (WindowsIdentity)context.User.Identity;
await context.Response
.WriteAsync($"User: {user.Name}\tState: {user.ImpersonationLevel}\n");
WindowsIdentity.RunImpersonated(user.AccessToken, () =>
{
var impersonatedUser = WindowsIdentity.GetCurrent();
var message =
$"User: {impersonatedUser.Name}\t" +
$"State: {impersonatedUser.ImpersonationLevel}";
var bytes = Encoding.UTF8.GetBytes(message);
context.Response.Body.Write(bytes, 0, bytes.Length);
});
}
catch (Exception e)
{
await context.Response.WriteAsync(e.ToString());
}
});

Related

Access to file on network drive asp net core

I open file by asp core endpoint successfully:
[HttpGet("files/{fileName}")]
public IActionResult GetFile(string fileName)
{
var filePath = _hostingEnvironment.ContentRootPath + "\\Files\\" + fileName;
if (filePath == null) return NotFound();
return PhysicalFile(filePath, MimeTypes.GetMimeType(filePath));
}
The file is in the server local folder:
C:\inetpub\wwwroot\myProject\files
I want to do the same with network folder mounted as disk R:\:
var filePath = "R:\\Files\\" + fileName;
R:\files
But this do not works with error 500.
I also can open file from R:\ directly by windows explorer with login & password.
So how to get access to the network drive file?
You'll need to get the network address which is mapped to R: Drive, then you can use it as the file/folder path in your code:
Since you require a password to access the files in this shared drive, you've got two options as explained in this SO answer
1: Set AppPool user
The "right" way to do this is to run the webserver's AppPool as the
identity that can access the share. That way, the only credential
storage is done securely in the IIS config (rather than in your code
or in readable config files). Putting the webserver and fileserver in
the same Windows domain (or different domains with trust) is the
easiest way, but the "same username/password" thing should work there
as well.
2: P/Invoke to WNetAddConnection2
This has a good implementation here How To Access Network Drive Using C#
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Net;
public class ConnectToSharedFolder: IDisposable
{
readonly string _networkName;
public ConnectToSharedFolder(string networkName, NetworkCredential credentials)
{
_networkName = networkName;
var netResource = new NetResource
{
Scope = ResourceScope.GlobalNetwork,
ResourceType = ResourceType.Disk,
DisplayType = ResourceDisplaytype.Share,
RemoteName = networkName
};
var userName = string.IsNullOrEmpty(credentials.Domain)
? credentials.UserName
: string.Format(#"{0}\{1}", credentials.Domain, credentials.UserName);
var result = WNetAddConnection2(
netResource,
credentials.Password,
userName,
0);
if (result != 0)
{
throw new Win32Exception(result, "Error connecting to remote share");
}
}
~ConnectToSharedFolder()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
WNetCancelConnection2(_networkName, 0, true);
}
[DllImport("mpr.dll")]
private static extern int WNetAddConnection2(NetResource netResource,
string password, string username, int flags);
[DllImport("mpr.dll")]
private static extern int WNetCancelConnection2(string name, int flags,
bool force);
[StructLayout(LayoutKind.Sequential)]
public class NetResource
{
public ResourceScope Scope;
public ResourceType ResourceType;
public ResourceDisplaytype DisplayType;
public int Usage;
public string LocalName;
public string RemoteName;
public string Comment;
public string Provider;
}
public enum ResourceScope : int
{
Connected = 1,
GlobalNetwork,
Remembered,
Recent,
Context
};
public enum ResourceType : int
{
Any = 0,
Disk = 1,
Print = 2,
Reserved = 8,
}
public enum ResourceDisplaytype : int
{
Generic = 0x0,
Domain = 0x01,
Server = 0x02,
Share = 0x03,
File = 0x04,
Group = 0x05,
Network = 0x06,
Root = 0x07,
Shareadmin = 0x08,
Directory = 0x09,
Tree = 0x0a,
Ndscontainer = 0x0b
}
}
public string networkPath = #"\\{Your IP or Folder Name of Network}\Shared Data";
NetworkCredential credentials = new NetworkCredential(#"{User Name}", "{Password}");
public string myNetworkPath = string.Empty;
public byte[] DownloadFileByte(string DownloadURL)
{
byte[] fileBytes = null;
using (new ConnectToSharedFolder(networkPath, credentials))
{
var fileList = Directory.GetDirectories(networkPath);
foreach (var item in fileList) { if (item.Contains("ClientDocuments")) { myNetworkPath = item; } }
myNetworkPath = myNetworkPath + DownloadURL;
try
{
fileBytes = File.ReadAllBytes(myNetworkPath);
}
catch (Exception ex)
{
string Message = ex.Message.ToString();
}
}
return fileBytes;
}

How to keep user logged in after browser is closed

Every time I close the browser I need to log in again into this app. It is developed in .NET Core 2.0. I'm trying to let it logged in, like every other regular site.
I checked this post that may be useful, but since the code is quite different from this application I decided to create this post.
This is my security code:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
namespace Petito.Common
{
public interface IActivityContext
{
string ActivityID { get; }
IPrincipal User { get; }
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityIdentity : IIdentity
{
private string _name = null;
[JsonIgnore()]
private bool _isAuthenticated = false;
[JsonIgnore()]
private string _authenticationType = "";
public ActivityIdentity()
{
}
public ActivityIdentity(string name) : this(name, false, "")
{
}
internal ActivityIdentity(string name, bool isAuthenticated, string authenticationType)
{
this._name = name;
this._isAuthenticated = isAuthenticated;
this._authenticationType = authenticationType;
}
public string Name { get => _name; }
public bool IsAuthenticated { get => _isAuthenticated; }
public string AuthenticationType { get => _authenticationType; }
public static ActivityIdentity Unathenticated => new ActivityIdentity();
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityPrincipal : IPrincipal
{
private ActivityIdentity _activityIdentity = null;
private string[] _roles = null;
public ActivityPrincipal() : this(ActivityIdentity.Unathenticated, null)
{
}
public ActivityPrincipal(ActivityIdentity activityIdentity, params string[] roles)
{
_activityIdentity = activityIdentity;
_roles = roles;
}
public ActivityIdentity Identity => _activityIdentity;
IIdentity IPrincipal.Identity => _activityIdentity;
public bool IsInRole(string role)
{
if (_roles != null && _roles.Length > 0)
{
return _roles.Contains(role);
}
return false;
}
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityContext : IDisposable, IActivityContext
{
private string _activityID = Guid.NewGuid().ToString();
private DateTime _startDate = DateTime.UtcNow;
private DateTime? _endDate = null;
private ActivityPrincipal _activityPrincipal = null;
public ActivityContext() : this(null)
{
}
public ActivityContext(IPrincipal principal)
{
_activityPrincipal = Convert(principal);
}
private ActivityPrincipal Convert(IPrincipal principal)
{
if (principal == null)
{
return new ActivityPrincipal();
}
var activityPrincipal = principal as ActivityPrincipal;
if (activityPrincipal != null)
{
return activityPrincipal;
}
var claimsPrincipal = principal as ClaimsPrincipal;
if (claimsPrincipal != null)
{
var roles = claimsPrincipal.Claims.Select(x => x.Value);
var p = new ActivityPrincipal(
new ActivityIdentity(claimsPrincipal.Identity.Name, claimsPrincipal.Identity.IsAuthenticated, claimsPrincipal.Identity.AuthenticationType)
, roles.ToArray()
);
return p;
}
throw new NotSupportedException($"Converting {principal.GetType()} not supported");
}
public void Dispose()
{
if (!_endDate.HasValue)
{
_endDate = DateTime.UtcNow;
}
}
public string ActivityID { get => _activityID; }
public DateTime StartDate { get => _startDate; }
public DateTime? EndDate { get => _endDate; }
public IPrincipal User
{
get
{
return _activityPrincipal;
}
}
}
}
Of course, I'll still try to figure out what's wrong with the code. Please if you need another part of the code other from that I posted let me know.
Thanks!

Weblogic Providers

I have created a custom authentication provider that checks if a user exists in a datasource and allows it to login or not.
Now I also have to check the roles of that user, but I don't understand if the same provider can take care of Authentication and Role mapping or if I have to do another provider.
I had tried to created another provider, for the role mapping, but I can't find it, or not looking in the right place to configurate it, but my MBean type also doesn't any configs to be inserted.
Can anyone help me with this?
I tried to find examples of role mapping, with no luck.
Thanks
Have a look at the Oracle Guide: How to Develop a Custom Role Mapping Provider
The process is very similiar to creating an authentication Provider, the only difference are the interfaces you have to implement.
Now for my Implementation (I assume knowledge about MBean Provider Creation using the WebLogicMBeanMaker, since you already created an Authentication Provider):
You need 3 Files, a XML File with the configuration, the Provider and the Implementation of a Role.
The Config File:
<?xml version="1.0" ?>
<!DOCTYPE MBeanType SYSTEM "commo.dtd">
<MBeanType
Name = "MYRoleMapper"
DisplayName = "MYRoleMapper"
Package = "MY.security"
Extends = "weblogic.management.security. authorization.RoleMapper"
PersistPolicy = "OnUpdate"
>
<MBeanAttribute
Name = "ProviderClassName"
Type = "java.lang.String"
Writeable = "false"
Preprocessor = "weblogic.management.configuration.LegalHelper.checkClassName(value)"
Default = ""MY.security.MYRoleMapperProviderImpl""
/>
<MBeanAttribute
Name = "Description"
Type = "java.lang.String"
Writeable = "false"
Default = ""MY RM provider ""
/>
<MBeanAttribute
Name = "Version"
Type = "java.lang.String"
Writeable = "false"
Default = ""1.2""
/>
</MBeanType>
The Actual Provider MYRoleMapperProviderImpl.java:
public class MYRoleMapperProviderImpl implements RoleProvider, RoleMapper {
private String description;
private static final Map<String, SecurityRole> NO_ROLES = Collections.unmodifiableMap(new HashMap<String, SecurityRole>(1));
private final static String RESSOURCE_URL = "<url>";
private final static String RESSOURCE_EJB = "<ejb>";
private enum rollen {
READER;
}
#Override
public void initialize(ProviderMBean mbean, SecurityServices services) {
description = mbean.getDescription() + "\n" + mbean.getVersion();
}
#Override
public String getDescription() {
return description;
}
#Override
public void shutdown() {
}
#Override
public RoleMapper getRoleMapper() {
return this;
}
#Override
public Map<String, SecurityRole> getRoles(Subject subject, Resource resource, ContextHandler handler) {
Map<String, SecurityRole> roles = new HashMap<String, SecurityRole>();
Set<Principal> principals = subject.getPrincipals();
for (Resource res = resource; res != null; res = res.getParentResource()) {
getRoles(res, principals, roles);
}
if (roles.isEmpty()) {
return NO_ROLES;
}
return roles;
}
private void getRoles(Resource resource, Set<Principal> principals, Map<String, SecurityRole> roles) {
if (resource.getType() == RESSOURCE_URL || resource.getType() == RESSOURCE_EJB) {
roles.put(rollen.READER.toString(), new MYSecurityRoleImpl(rollen.READER.toString(), "READER Rolle"));
}
}
}
And an absolute simple Role Implementation:
package MY.security;
import weblogic.security.service.SecurityRole;
public class MYSecurityRoleImpl implements SecurityRole {
private String _roleName;
private String _description;
private int _hashCode;
public MYSecurityRoleImpl(String roleName, String description)
{
_roleName = roleName;
_description = description;
_hashCode = roleName.hashCode() + 17;
}
public boolean equals(Object secRole)
{
if (secRole == null)
{
return false;
}
if (this == secRole)
{
return true;
}
if (!(secRole instanceof MYSecurityRoleImpl))
{
return false;
}
MYSecurityRoleImpl anotherSecRole = (MYSecurityRoleImpl)secRole;
if (!_roleName.equals(anotherSecRole.getName()))
{
return false;
}
return true;
}
public String toString () { return _roleName; }
public int hashCode () { return _hashCode; }
public String getName () { return _roleName; }
public String getDescription () { return _description; }
}

Unit test - httpcontext is null, websecurity.CurrentUserId not being populated either

I have an MVC 4 application that I'm building unit tests for. In my GameController, I have an Action, JoinGame, that requires the current userid. I get this with WebSecurity.CurrentUserId inside the controller.
When I run the unit test for JoinGame, UserId is not being populated. Obviously during a unit test there is no 'current' user. I'm trying to figure out how to mock one.
The first error I got was was System.ArgumentNullException: Value cannot be null. Parameter name; httpContext
After much searching, I found
How to mock httpcontext so that it is not null from a unit test?.
I followed the guidance in that link (created a HttpContextFactory,
which mocked httpcontext and also set the current controller context
to the mocked data). This didn't have any effect.
I then found this Mocking WebSecurity provider
I created a wrapper interface & class for websecurity, and mocked the wrapper & injected the websecuritywrapper into the gamecontroller. This solved the httpContext (though I presently don't really understand why this worked and the HttpContextFactory didn't), however the CurrentUserId returned by the websecuritywrapper is always 0. Even if I hardcode it to 1 insider the websecuritywrapper class (public int CurrentUserId{ get { return 1; }}
Obviously I'm doing something wrong, just not sure what. I've posted code for the unit test, the controller and the wrapper below.
public RedirectToRouteResult JoinGame(int gameid)
{
//_wr is the websecuritywrapper
int UserID = _wr.CurrentUserId; //WebSecurity.CurrentUserId;
// get userteam for this user and this game
UserTeam ut = _UserTeamRepository.GetUserTeam(userteamid:0, gameid: gameid, userid: UserID);
int intUTID = 0;
if (ut == null)
{
// no userteam found, create it
OperationStatus opStatus = _UserTeamRepository.CreateUserTeam(UserID, gameid);
if (opStatus.Status) intUTID = (int)opStatus.OperationID;
}
else {intUTID = ut.Id; }
if (intUTID > 0)
{
return RedirectToAction("Index", "ViewPlayers", new { id = intUTID });
}
else
{
return RedirectToAction("Index", "Game");
}
}
[Test]
public void Game_JoinGame_Returns_RedirectToAction()
{
UserProfile creator = new UserProfile();
UserProfile user = new UserProfile();
Game game = new Game();
ICollection<UserTeam> uteams = null;
UserTeam ut = new UserTeam();
ICollection<UserTeam_Player> utp = null;
List<Game> games = new List<Game>
{
new Game { Id = 1, CreatorId = 1, Name = "Game1", Creator = creator, UserTeams=uteams},
};
List<UserTeam> userteams = new List<UserTeam>
{
new UserTeam {Id=1, UserId = 1, GameId=1, User=user, Game = game, UserTeam_Players=utp}
};
Mock<IGameRepository> mockGameRepository = new Mock<IGameRepository>();
Mock<IUserTeamRepository> mockUserTeamRepository = new Mock<IUserTeamRepository>();
Mock<IWebSecurityWrapper> mockWSW = new Mock<IWebSecurityWrapper>();
mockUserTeamRepository.Setup(mr => mr.GetAllUserTeams()).Returns(userteams);
mockUserTeamRepository.Setup(mr => mr.GetUserTeam(0,1,1)).Returns(ut);
mockUserTeamRepository.Setup(mr => mr.CreateUserTeam(1, 1));
//Arrange
GameController Controller = new GameController(mockGameRepository.Object, mockUserTeamRepository.Object, mockWSW.Object);
// This didn't work
//HttpContextFactory.SetFakeAuthenticatedControllerContext(Controller);
//Act
RedirectToRouteResult result = Controller.JoinGame(1);
Assert.AreEqual("Index", result.RouteValues["action"]);
}
public class WebSecurityWrapper : IWebSecurityWrapper
{
public int CurrentUserId{ get { return WebSecurity.CurrentUserId; }}
public string CurrentUserName { get { return "admin_user"; } } // WebSecurity.CurrentUserName;
public bool HasUserId { get { return WebSecurity.HasUserId; } }
public bool Initialized { get { return WebSecurity.Initialized; } }
public bool IsAuthenticated { get { return WebSecurity.IsAuthenticated; } }
public bool ChangePassword(string userName, string currentPassword, string newPassword){return WebSecurity.ChangePassword(userName, currentPassword, newPassword);}
public bool ConfirmAccount(string accountConfirmationToken) { return WebSecurity.ConfirmAccount(accountConfirmationToken); }
public bool ConfirmAccount(string userName, string accountConfirmationToken) { return WebSecurity.ConfirmAccount(userName,accountConfirmationToken); }
public string CreateAccount(string userName, string password, bool requireConfirmationToken = false) { return WebSecurity.CreateAccount(userName, password, requireConfirmationToken = false); }
public string CreateUserAndAccount(string userName, string password, object propertyValues = null, bool requireConfirmationToken = false) { return WebSecurity.CreateUserAndAccount(userName, password, propertyValues = null, requireConfirmationToken = false); }
public string GeneratePasswordResetToken(string userName, int tokenExpirationInMinutesFromNow = 1440) { return WebSecurity.GeneratePasswordResetToken(userName, tokenExpirationInMinutesFromNow = 1440); }
public DateTime GetCreateDate(string userName) { return WebSecurity.GetCreateDate(userName); }
public DateTime GetLastPasswordFailureDate(string userName){ return WebSecurity.GetLastPasswordFailureDate(userName); }
public DateTime GetPasswordChangedDate(string userName) { return WebSecurity.GetPasswordChangedDate(userName); }
public int GetPasswordFailuresSinceLastSuccess(string userName) { return WebSecurity.GetPasswordFailuresSinceLastSuccess(userName);}
public int GetUserId(string userName){ return WebSecurity.GetUserId(userName);}
public int GetUserIdFromPasswordResetToken(string token) { return WebSecurity.GetUserIdFromPasswordResetToken(token); }
public void InitializeDatabaseConnection(string connectionStringName, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables) { WebSecurity.InitializeDatabaseConnection(connectionStringName, userTableName, userIdColumn, userNameColumn, autoCreateTables); }
public void InitializeDatabaseConnection(string connectionString, string providerName, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables) { WebSecurity.InitializeDatabaseConnection(connectionString, providerName, userTableName, userIdColumn, userNameColumn, autoCreateTables); }
public bool IsAccountLockedOut(string userName, int allowedPasswordAttempts, int intervalInSeconds) { return WebSecurity.IsAccountLockedOut(userName, allowedPasswordAttempts, intervalInSeconds); }
public bool IsAccountLockedOut(string userName, int allowedPasswordAttempts, TimeSpan interval) { return WebSecurity.IsAccountLockedOut(userName, allowedPasswordAttempts, interval); }
public bool IsConfirmed(string userName){ return WebSecurity.IsConfirmed(userName); }
public bool IsCurrentUser(string userName) { return WebSecurity.IsCurrentUser(userName); }
public bool Login(string userName, string password, bool persistCookie = false) { return WebSecurity.Login(userName, password, persistCookie = false); }
public void Logout() { WebSecurity.Logout(); }
public void RequireAuthenticatedUser() { WebSecurity.RequireAuthenticatedUser(); }
public void RequireRoles(params string[] roles) { WebSecurity.RequireRoles(roles); }
public void RequireUser(int userId) { WebSecurity.RequireUser(userId); }
public void RequireUser(string userName) { WebSecurity.RequireUser(userName); }
public bool ResetPassword(string passwordResetToken, string newPassword) { return WebSecurity.ResetPassword(passwordResetToken, newPassword); }
public bool UserExists(string userName) { return WebSecurity.UserExists(userName); }
}
The reason that you're getting 0 back when you hard code 1 is because of this line:
Mock<IWebSecurityWrapper> mockWSW = new Mock<IWebSecurityWrapper>();
The version of the IWebSecurityWrapper you're getting is a mock (since you injected it as such). Adding
mockSW.Setup(x=>x.CurrentUserId).Returns(1);
Should get you what you need. Since we're now telling the mock to return 1 when asked for the CurrentUserId
The reason HttpContextFactory didn't work is because the HttpContextFactory implementations I've seen deal with properties on the controller and I suspect your dependency on HttpContext was inside the WebSecurity class itself, hence why you need the wrapper.

Options for HIDAPI on OS X

I have been working with HIDAPI with C and trying to get Mono to communicate with the HIDAPI using interop. I have done a lot of searching and have not been able to find anyone who has gotten HIDAPI to work with Mono on OS X.
Does anyone know if I can redirect the output from a HID device using HIDAPI to a local virtual serial port and then have Mono just read from the serial port?
Another option, would anyone know if I could use something like Arduino leonardo or Circuits#Home USB Host Shield?
At least until I can sort out PInvoke on Mono.
Thanks
I found and adapted this code from elsewhere, though I can't for the life of me find the source. If someone knows, please let me know so I can properly attribute it and link to it. It's been working well for me on both Windows and OS X. You do have to build hidapi for each platform, obviously.
using System;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace HidApiCommunicationLayer
{
internal class HidApiInteropCommLayer
{
#region Interop
#if WINDOWS
private const string HIDAPI_DLL = "hidapi.dll";
#else
private const string HIDAPI_DLL = "hidapi.dylib";
#endif
protected IntPtr _device;
private Object _locker = new object();
public bool IsOpen()
{
return _device != IntPtr.Zero;
}
public void Open(ushort vid, ushort hid, string serial)
{
if (_device != IntPtr.Zero) throw new Exception("a device is already opened; close it first.");
IntPtr ret = hid_open(vid, hid, serial);
_device = ret;
//if (_device != IntPtr.Zero)
// hid_set_nonblocking(_device, true);
}
public int Read(byte[] buffer, int length)
{
lock (_locker)
{
AssertValidDev();
int ret = hid_read_timeout(_device, buffer, (uint)length, 1);
if (ret < 0)
throw new Exception("Failed to Read.");
return ret;
}
}
public void Close()
{
AssertValidDev();
hid_close(_device);
_device = IntPtr.Zero;
}
public int ExitHidAPI()
{
return hid_exit();
}
public String GetProductString()
{
AssertValidDev();
byte[] buf = new byte[1000];
int ret = HidApiInteropCommLayer.hid_get_product_string(_device, buf, (uint)(buf.Length / 4) - 1);
if (ret < 0)
throw new Exception("failed to receive product string");
return EncodeBuffer(buf);
}
public String GetManufacturerString()
{
AssertValidDev();
byte[] buf = new byte[1000];
int ret = HidApiInteropCommLayer.hid_get_manufacturer_string(_device, buf, (uint)(buf.Length / 4) - 1);
if (ret < 0)
throw new Exception("failed to receive manufacturer string");
return EncodeBuffer(buf);
}
public int GetFeatureReport(byte[] buffer, int length)
{
AssertValidDev();
int ret = hid_get_feature_report(_device, buffer, (uint)length);
if (ret < 0)
throw new Exception("failed to get feature report");
return ret;
}
public int SendFeatureReport(byte[] buffer)
{
int ret = hid_send_feature_report(_device, buffer, (uint)buffer.Length);
//if (ret < 0)
// throw new Exception ("failed to send feature report");
return ret;
}
public int Write(byte[] buffer)
{
lock (_locker)
{
AssertValidDev();
int ret = hid_write(_device, buffer, HID_MAX_PACKET_SIZE + 1);
//if (ret < 0)
// Custom logging
return ret;
}
}
public String Error()
{
AssertValidDev();
IntPtr ret = hid_error(_device);
return Marshal.PtrToStringAuto(ret);
}
public string GetIndexedString(int index)
{
AssertValidDev();
byte[] buf = new byte[1000];
int ret = HidApiInteropCommLayer.hid_get_indexed_string(_device, index, buf, (uint)(buf.Length / 4) - 1);
if (ret < 0)
throw new Exception("failed to receive indexed string");
return EncodeBuffer(buf);
}
public string GetSerialNumberString()
{
AssertValidDev();
byte[] buf = new byte[1000];
int ret = HidApiInteropCommLayer.hid_get_serial_number_string(_device, buf, (uint)(buf.Length / 4) - 1);
if (ret < 0)
throw new Exception("failed to receive serial number string");
return EncodeBuffer(buf);
}
private string EncodeBuffer(byte[] buffer)
{
return Encoding.Unicode.GetString(buffer).Trim('\0');
}
private void AssertValidDev()
{
if (_device == IntPtr.Zero) throw new Exception("No device opened");
}
#region DllImports
[DllImport(HIDAPI_DLL)]
private static extern int hid_read(IntPtr device, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);
[DllImport(HIDAPI_DLL)]
private static extern int hid_read_timeout(IntPtr device, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length, int timeout);
[DllImport(HIDAPI_DLL)]
private static extern IntPtr hid_open(ushort vid, ushort pid, [MarshalAs(UnmanagedType.LPWStr)] string serial);
[DllImport(HIDAPI_DLL)]
private static extern void hid_close(IntPtr device);
[DllImport(HIDAPI_DLL)]
private static extern int hid_init();
[DllImport(HIDAPI_DLL)]
private static extern int hid_exit();
[DllImport(HIDAPI_DLL)]
private static extern int hid_get_product_string(IntPtr device, [Out] byte[] _string, uint length);
[DllImport(HIDAPI_DLL)]
private static extern int hid_get_manufacturer_string(IntPtr device, [Out] byte[] _string, uint length);
[DllImport(HIDAPI_DLL)]
private static extern int hid_get_feature_report(IntPtr device, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);
[DllImport(HIDAPI_DLL)]
private static extern int hid_get_serial_number_string(IntPtr device, [Out] byte[] serial, uint maxlen);
[DllImport(HIDAPI_DLL)]
private static extern int hid_get_indexed_string(IntPtr device, int string_index, [Out] byte[] _string, uint maxlen);
[DllImport(HIDAPI_DLL)]
private static extern IntPtr hid_error(IntPtr device);
[DllImport(HIDAPI_DLL)]
private static extern int hid_send_feature_report(IntPtr device, [In, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);
[DllImport(HIDAPI_DLL)]
private static extern int hid_set_nonblocking(IntPtr device, [In, MarshalAs(UnmanagedType.SysInt)] bool nonblock);
[DllImport(HIDAPI_DLL)]
private static extern int hid_write(IntPtr device, [In, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);
[DllImport(HIDAPI_DLL)]
private static extern IntPtr hid_open_path([In, MarshalAs(UnmanagedType.LPStr)] string path);
#endregion DllImports
#endregion Interop
#region Constructors
public static HidApiInteropCommLayer GetDevice(ushort vid, ushort pid)
{
try
{
HidApiInteropCommLayer layer = new HidApiInteropCommLayer();
layer.Open(vid, pid, null);
return layer._device == IntPtr.Zero ? null : layer;
}
catch (System.BadImageFormatException fx)
{
//Custom logging
return null;
}
catch (Exception ex)
{
//Custom logging
return null;
}
}
#endregion Constructors
private const int HID_MAX_PACKET_SIZE = 1024;
#region ICommunicationLayer
public void Init()
{
try
{
if (IsOpen())
{
ContinueReadProcessing = true;
ReadThread = new Thread(new ThreadStart(ReadLoop));
ReadThread.Name = "HidApiReadThread";
ReadThread.Start();
}
else
{
Disconnect();
}
}
catch (Exception ex)
{
//Custom logging
throw;
}
}
public bool SendData(byte[] data)
{
try
{
MemoryStream stream = new MemoryStream(HID_MAX_PACKET_SIZE + 1);
stream.WriteByte(0);
stream.Write(data, 0, HID_MAX_PACKET_SIZE);
int ret = Write(stream.ToArray());
if (ret >= 0)
return true;
else
{
return false;
}
}
catch (Exception ex)
{
//Custom logging
return false;
}
}
public event EventHandler<DataEventArgs> DataReceived;
public event EventHandler Disconnected;
public void Start()
{
ContinueReadProcessing = true;
}
public void Stop()
{
Disconnect();
}
#endregion ICommunicationLayer
private Thread ReadThread = null;
protected volatile bool ContinueReadProcessing = true;
private void ReadLoop()
{
var culture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
while (ContinueReadProcessing)
{
try
{
byte[] report = new byte[HID_MAX_PACKET_SIZE];
var result = Read(report, HID_MAX_PACKET_SIZE);
if (result > 0)
{
DataReceived(this, new DataEventArgs(report));
}
else if (result < 0)
{
Disconnect();
}
}
catch (Exception ex)
{
Disconnect();
}
Thread.Sleep(1);
}
}
private void Disconnect()
{
ContinueReadProcessing = false;
Disconnected(this, EventArgs.Empty);
}
#region IDisposable Members
public void Dispose()
{
ContinueReadProcessing = false;
ReadThread.Join(500);
if (ReadThread.IsAlive)
{
ReadThread.Abort();
}
if (IsOpen())
Close();
int res = ExitHidAPI();
}
#endregion IDisposable Members
}
internal class Utf32Marshaler : ICustomMarshaler
{
private static Utf32Marshaler instance = new Utf32Marshaler();
public static ICustomMarshaler GetInstance(string s)
{
return instance;
}
public void CleanUpManagedData(object o)
{
}
public void CleanUpNativeData(IntPtr pNativeData)
{
Marshal.FreeHGlobal(pNativeData);
//UnixMarshal.FreeHeap(pNativeData);
}
public int GetNativeDataSize()
{
return IntPtr.Size;
}
public IntPtr MarshalManagedToNative(object obj)
{
string s = obj as string;
if (s == null)
return IntPtr.Zero;
return Marshal.StringToHGlobalAuto(s);
}
public object MarshalNativeToManaged(IntPtr pNativeData)
{
return Marshal.PtrToStringAuto(pNativeData);
}
}
public class DataEventArgs : EventArgs
{
public DataEventArgs(byte[] data)
{
Data = data;
}
public byte[] Data { get; private set; }
}
}
Arduino Leonardo can't act as a USB Host.
In principle though, yes, you could make a device act as a CDC Serial Port on the device side, and have it act as Host USB to a HID device.
Alternatively, you could skip the HIDAPI step entirely and use HidSharp. :) It'll P/Invoke directly into the MacOS X native APIs.
Hope this helps
James