I am fairly new to C# as well as windows programming and I am attempting to establish communication between a USB HID device and an app written in c# on a windows xp pc. I have obtained the device path successfully and have used this to establish a valid Handle on the device:
IntPtr drive = CreateFile(devicePath, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero,
OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, IntPtr.Zero);
This proved to be a valid handle that yields a positive integer when tested. After creating the handle I call 'HidD_GetPreparsedData' and 'HidP_GetCaps' which yield a struct previously imported (HIDP_CAPS) that stores specific data related to the device attached. Next, I try to call writefile():
bool success = WriteFile(drive, ref outputReportBuffer,
caps.OutputReportByteLength, ref numberOfBytesWritten,
IntPtr.Zero);
drive: The Handle of the device
outputReportBuffer:The array of bytes to write [65]
caps.OutputReportByteLength: The amount of bytes to write, caps is the struct of HIDP_CAPS
numberOfBytesWritten: hardcoded to 0
IntPtr.Zero: Everything I have read passes this last parameter as I have done. In the formal parameters of 'writefile' the last parameter is listed as 'IntPtr ipOverlapped', but I have not been able to figure out what that is, except that It should be passed as some form of 0
How WriteFile is imported:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteFile(IntPtr hFile,
ref byte[] lpBuffer,
uint nNumberOfBytesToWrite,
ref uint lpNumberOfBytesWritten,
IntPtr ipOverlapped);
When writefile is executed it simply does nothing. I get no error, but when tested, it reveals that communication was never established and it was not successful. If anyone could share any knowledge or provide any resources to help I would really appreciate it. Thank you for reading.
Regards.
Remove the ref in your WriteFile declaration.
While http://www.pinvoke.net/ is a great resource for P/Invoke declarations, it's a wiki-style website and many declarations are wrong (often they won't work on a x64 machine).
Related
I have a C# win form app. In it I'm getting a unique serial of HDD by using this method.
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool GetVolumeInformation(string Volume, StringBuilder VolumeName,
uint VolumeNameSize, out uint SerialNumber, uint SerialNumberLength,
uint flags, StringBuilder fs, uint fs_size);
with Volume variable as Path.GetPathRoot(Environment.CurrentDirectory)
I get the serial as SerialNumber.ToString("X")
Works fine but the situation is;
I have Shared the app folder. I get XXXXXXXX as serial when I run the EXE
When another person run the app from my shared path he gets 00000000 as serial
But when that another user Mapped this folder and run the app he gets XXXXXXXX as serial
So what is the cause of this problem? Is there any better way to use?
I referred WMI but didn't fit to my need.
I need to find open process or application textbox and change its value. But i wanna do it with c# way. If anyone knows could you please share with me? Or do i have to use c++ and how?
Thanks for your advices.
Like another said, UIAutomation is the way to go. http://msdn.microsoft.com/en-us/library/ms753107.aspx
The following code will open Notepad.exe, open its File dialog, then type in some text into the file name field.
Process notepad = Process.Start("notepad");
Thread.Sleep(5000);
SendKeys.SendWait("^o"); //ctrl+o to open the File Dialog
var notepadTextBox = AutomationElement.RootElement.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.AutomationIdProperty, "1148"));
object valuePattern = null;
if (notepadTextBox.TryGetCurrentPattern(ValuePattern.Pattern, out valuePattern))
{
((ValuePattern)valuePattern).SetValue("THERE YOU GO"); // this text will be entered in the textbox
}
else
{
//ERROR
}
So this is really a combination of sending keystrokes to control the UI (bring up the File Open dialog) & UIAutomation, but you could change it to drive the Menu like a user would if you need to.
Also you might be wondering where the magic string "1148" comes from - that is the "Automation Id" of the file name entry field in Notepad. I used inspect.exe (included in the Windows SDK) to find the automation Id, you will need that for your application to see its AutomationIds, if it has any.
One way to do this is if the application is out of your control in terms of using libraries and wrappers:
Process[] Procs = Process.GetProcessesByName("NameofProcess");
that will give you the process in question. Now this is where it will get tricky and depend upon what exactly you need to do.
You would eventually need to find where the strings are stored in memory, you could use a memory profiler to do this or something like CheatEngine to find the values, not going to get into what you use CheatEngine for or how you use it, but it is just a simple way of finding memory locations.
You could then read/write to the memory locations using something like this:
[DllImport("kernel32.dll")]
public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten);
public static byte[] ReadMem(IntPtr MemAddy, uint bytestoread, Process Proc)
{
//
//Create new Memory buffer and pointer to that buffer
//
byte[] buffer = new byte[bytestoread];
IntPtr bufferptr;
//
//Read Process Memory and output to buffer
//
ReadProcessMemory(Proc.Handle, MemAddy, buffer, bytestoread, out bufferptr);
//
//Return the buffer
//
return buffer;
}
public static bool WriteMem(IntPtr MemAddy, byte[] buffer, Process Proc)
{
int NumWriten;
WriteProcessMemory(Proc.Handle, MemAddy, buffer, (uint)buffer.Length, out NumWriten);
if (NumWriten != buffer.Length)
{
return false;
}
else return true;
}
These two functions would allow you to read and write to some arbitrary processes memory locations.
if you want the window in question you could use:
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
Like So:
IntPtr HWND = FindWindow(null, "WinName");
Which will give you the handle to the window in question.
Another way to do this would be to find the window and then pass some events to it, like bring the window to focus and then tab through the text boxes programmatically. However without more information about what exactly you are trying to do I am not sure what else to say here.
The tool you are looking for is UI Automation. It will let you see the other program's controls and send text to those controls. I have done this in the past where I had to export data from a corrupted database and I had to click OK on a dialog every time it hit a corrupted record.
The topic is too complex to go in to depth on how to do it in the space of a SO answer, but here is a tutorial I found on CodePlex that goes over how to do it.
There are also 3rd party wrapper libraries to make it easier to do. My personal favorite is White.
I want to automate a program called Spotify from C#, the best way (I think) to do this is by triggering fake keypresses. I want to program to pause playback, and I don't know enough about this stuff to find another way than keypresses. So I use Visual Studio's Spy++ to see what message Spotify gets when pressing the play button on my keyboard, I copy the data from that message into my Console Application and run it, when I run I can see the PostMessage in Spy++'s Message Logging, so this is working but it doesn't pause/play my music. I guess this is because I also have to send another PostMessage with another destination, but how do I know what else to send?
Post Message call:
MessageHelper.PostMessage((int)hwndSpotify, 0x100, 0x000000B3, 0x01000001);
I hope someone is familiar with this and can help me out.
To automate Spotify, first you have to get the handle of the window with the following class name: SpotifyMainWindow (using FindWindow()).
Then, you can use the SendMessage() method to send a WM_APPCOMMAND message to the Spotify's window.
Following a simple code to do that:
internal class Win32
{
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
internal class Constants
{
internal const uint WM_APPCOMMAND = 0x0319;
}
}
public enum SpotifyAction : long
{
PlayPause = 917504,
Mute = 524288,
VolumeDown = 589824,
VolumeUp = 655360,
Stop = 851968,
PreviousTrack = 786432,
NextTrack = 720896
}
For instance, to play or pause the current track:
Win32.SendMessage(hwndSpotify, Win32.Constants.WM_APPCOMMAND, IntPtr.Zero, new IntPtr((long)SpotifyAction.PlayPause));
Pressing the "play buttion" results in a virtual key code - for an official list see http://msdn.microsoft.com/en-us/library/dd375731%28v=VS.85%29.aspx .
There you find for example VK_VOLUME_UP VK_MEDIA_PLAY_PAUSE VK_ZOOM .
Even some Remotes translate to these codes to be as compatible as possible with existing software.
These were introduced back in the day when Windows ME (!) came out and are still in use - at least when I checked the registry of my Windows 2008 R2 !
Basically Windows translates certain VK* into WM_APPCOMMAND messages with certain codes which the applications listen to...
If the key has something to do with launching an app to do (i.e. Mail, Browser etc.) then the magic happens via Windows Explorer which reads the mapping (either by association or direct exec) from the registry at Software\ Microsoft\ Windows\ CurrentVersion\ Explorer\ AppKey - either HKLM or HKCU.
Some links with old but as it seems still valid information:
http://msdn.microsoft.com/en-us/windows/hardware/gg463446.aspx
http://msdn.microsoft.com/en-us/windows/hardware/gg462991
http://msdn.microsoft.com/en-us/windows/hardware/gg463372
My Problem
I'm using PInvoked Windows API functions to verify if a user is part of the local administrators group. I'm utilizing GetCurrentProcess, OpenProcessToken, GetTokenInformationand LookupAccountSid to verify if the user is a local admin.
GetTokenInformation returns a TOKEN_GROUPS struct with an array of SID_AND_ATTRIBUTES structs. I iterate over the collection and compare the user names returned by LookupAccountSid.
My problem is that, locally (or more generally on our in-house domain), this works as expected. The builtin\Administrators is located within the group membership of the current process token and my method returns true. On another domain of another developer the function returns false.
The LookupAccountSid functions properly for the first 2 iterations of the TOKEN_GROUPS struct, returning None and Everyone, and then craps out complaining that "A Parameter is incorrect."
What would cause only two groups to work correctly?
The TOKEN_GROUPS struct indicates that there are 14 groups. I'm assuming it's the SID that is invalid.
Everything that I have PInvoked I have taken from an example on the PInvoke website. The only difference is that with the LookupAccountSid I have changed the Sid parameter from a byte[] to a IntPtr because SID_AND_ATTRIBUTESis also defined with an IntPtr. Is this ok since LookupAccountSid is defined with a PSID?
LookupAccountSid PInvoke
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool LookupAccountSid(
string lpSystemName,
IntPtr Sid,
StringBuilder lpName,
ref uint cchName,
StringBuilder ReferencedDomainName,
ref uint cchReferencedDomainName,
out SID_NAME_USE peUse);
Where the code falls over
for (int i = 0; i < usize; i++)
{
accountCount = 0;
domainCount = 0;
//Get Sizes
LookupAccountSid(null, tokenGroups.Groups[i].SID, null, ref accountCount, null,
ref domainCount, out snu);
accountName2.EnsureCapacity((int) accountCount);
domainName.EnsureCapacity((int) domainCount);
if (!LookupAccountSid(null, tokenGroups.Groups[i].SID, accountName2, ref accountCount, domainName,
ref domainCount, out snu))
{
//Finds its way here after 2 iterations
//But only in a different developers domain
var error = Marshal.GetLastWin32Error();
_log.InfoFormat("Failed to look up SID's account name. {0}", new Win32Exception(error).Message);
continue;
}
If more code is needed let me know. Any help would be greatly appreciated.
It sounds like you're trying to duplicate the functionality of NetUserGetLocalGroups. You can also use NetUserGetInfo with an information level of 1, and check the value of usri1_priv in the USER_INFO_1 for USER_PRIV_ADMIN.
I'm not sure if NetUserGetLocalGroups knows about deny SIDs (If you need to verify if the current process (not the user account!) is in the admin group, you have to handle deny SIDs)
If you only need to support 2000 and later, PInvoke CheckTokenMembership (That MSDN page has a IsUserAdmin example function)
On NT4 you need to get a TokenGroups array from GetTokenInformation, but you don't call LookupAccountSid, you just call EqualSid on every item and compare it to a admin group SID you create with AllocateAndInitializeSid(...,SECURITY_BUILTIN_DOMAIN_RID,...)
I know that the following should work:
Environment.GetEnvironmentVariable("windir", EnvironmentVariableTarget.Machine)
My problem with this call is that if for some reason someone decided to remove the "windir" Env Var , this won't work.
Is there an even more secure way to get the System drive?
string windir = Environment.SystemDirectory; // C:\windows\system32
string windrive = Path.GetPathRoot(Environment.SystemDirectory); // C:\
Note: This property internally uses the GetSystemDirectory() Win32 API. It doesn't rely on environment variables.
This one returns the path to the system directory (system32).
Environment.GetFolderPath(Environment.SpecialFolder.System)
You may be able to use that, then you don't need to rely on environment variables.
One thing i actually maybe misunderstand is that you want the System Drive, but by using "windir" you'll get the windows folder. So if you need a secure way to get the windows folder, you should use the good old API function GetWindowsDirectory.
Here is the function prepared for C# usage. ;-)
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint GetWindowsDirectory(StringBuilder lpBuffer, uint uSize);
private string WindowsDirectory()
{
uint size = 0;
size = GetWindowsDirectory(null, size);
StringBuilder sb = new StringBuilder((int)size);
GetWindowsDirectory(sb, size);
return sb.ToString();
}
So if you really need the drive on which windows is running, you could afterwards call
System.IO.Path.GetPathRoot(WindowsDirectory());
You can use the GetWindowsDirectory API to retrieve the windows directory.
Never read environment variables (any script or user can change them !)
The official method (MS internal, used by Explorer) is a Win32 api FAQ for decades (see Google groups, Win32, System api)
Theres an environment variable called SystemDrive
C:\>SET SystemDrive
SystemDrive=C: