How to get the PIDL of any folder to use in RegisterChangeNotify - c#

I want to watch a folder (for example 'c:\test') for changes.
I found the following piece of code but I don't know how to get to the FolderPIDL for any given folder.
public ulong RegisterChangeNotify(IntPtr hWnd, IntPtr FolderPIDL, bool Recursively)
{
if(notifyid != 0) return(0);
SHChangeNotifyEntry changeentry = new SHChangeNotifyEntry();
changeentry.pIdl = FolderPIDL;
changeentry.Recursively = Recursively;
notifyid = SHChangeNotifyRegister(
hWnd,
SHCNF.SHCNF_TYPE | SHCNF.SHCNF_IDLIST,
SHCNE.SHCNE_ALLEVENTS | SHCNE.SHCNE_INTERRUPT,
WM_SHNOTIFY,
1,
ref changeentry);
return(notifyid);
}
Any ideas?
Thanks

You can find a PIDL from a path with the ILCreateFromPath method (for a real file/folder path):
IntPtr pidl = ILCreateFromPath(#"c:\path\file.ext");
if (pidl != IntPtr.Zero)
try
{
// do something
}
finally
{
Marshal.FreeCoTaskMem(pidl);
}

Related

In Metro, how do I get a list of all countries?

In Metro, the CultureInfo.GetCultures method doesn't exist anymore. What alternatives are available, if I am to retrieve a full list of strings with all the countries available?
Please try this out:
public class LocalesRetrievalException: Exception
{
public LocalesRetrievalException(string message): base(message)
{
}
}
#region Windows API
private delegate bool EnumLocalesProcExDelegate(
[MarshalAs(UnmanagedType.LPWStr)]String lpLocaleString,
LocaleType dwFlags, int lParam);
[DllImport(#"kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool EnumSystemLocalesEx(EnumLocalesProcExDelegate pEnumProcEx,
LocaleType dwFlags, int lParam, IntPtr lpReserved);
private enum LocaleType : uint
{
LocaleAll = 0x00000000, // Enumerate all named based locales
LocaleWindows = 0x00000001, // Shipped locales and/or replacements for them
LocaleSupplemental = 0x00000002, // Supplemental locales only
LocaleAlternateSorts = 0x00000004, // Alternate sort locales
LocaleNeutralData = 0x00000010, // Locales that are "neutral" (language only, region data is default)
LocaleSpecificData = 0x00000020, // Locales that contain language and region data
}
#endregion
public enum CultureTypes : uint
{
SpecificCultures = LocaleType.LocaleSpecificData,
NeutralCultures = LocaleType.LocaleNeutralData,
AllCultures = LocaleType.LocaleWindows
}
public static IReadOnlyCollection<CultureInfo> GetCultures(
CultureTypes cultureTypes)
{
List<CultureInfo> cultures = new List<CultureInfo>();
EnumLocalesProcExDelegate enumCallback = (locale, flags, lParam) =>
{
try
{
cultures.Add(new CultureInfo(locale));
}
catch (CultureNotFoundException)
{
// This culture is not supported by .NET (not happened so far)
// Must be ignored.
}
return true;
};
if (EnumSystemLocalesEx(enumCallback, (LocaleType)cultureTypes, 0,
(IntPtr)0) == false)
{
int errorCode = Marshal.GetLastWin32Error();
throw new LocalesRetrievalException("Win32 error " + errorCode +
" while trying to get the Windows locales");
}
// Add the two neutral cultures that Windows misses
// (CultureInfo.GetCultures adds them also):
if (cultureTypes == CultureTypes.NeutralCultures ||
cultureTypes == CultureTypes.AllCultures)
{
cultures.Add(new CultureInfo("zh-CHS"));
cultures.Add(new CultureInfo("zh-CHT"));
}
return new ReadOnlyCollection<CultureInfo>(cultures);
}
Reference: Replacement for CultureInfo.GetCultures in .NET Windows Store apps

Get SPF records from a Domain

What are the ways to Check SPF records on a domain?
There is a website where i can do it manually using - http://www.mxtoolbox.com/SuperTool.aspx
How can i do it via ASP.NET and C#? Basically i want to verify/check SPF records on a domain if its supporting out own mail web server.
I have the same problem, and managed to find two three solutions:
The nslookup solution
You can get the SPF by typing the following command in the command line:
nslookup -type=TXT <hostname>
You can automate this in C# using System.Diagonstics.Process, as described in this blog post.
The DNS.NET Resolver project
I found this CodeProject article about DNS resolution. It comes with demo project. I ran the project and got the following result for stackexchange.com:
Note: Make sure that the QType field is set to TXT before you press the Send button
The section highlighted in yellow represents the SPF record. I haven't yet dug into the code to see how it's done, but this seems like a good alternative to the nslookup solution above.
[Update] The ARSoft.Tools.Net project
If all you need to do is check whether a domain supports a mail server, you can use the ARSoft.Tools.Net library (also available as a NuGet Package).
After installing the package, I managed to perform the SPF check with this code:
var spfValidator = new ARSoft.Tools.Net.Spf.SpfValidator();
var mailIpAddress = IPAddress.Parse("X.X.X.X");
var domain = "example.com";
var senderAddress = "sender#example.com";
ARSoft.Tools.Net.Spf.SpfQualifier result =
spfValidator.CheckHost(mailIpAddress, domain, senderAddress);
Even though .NET has a lot of support for networking including doing host name to address mapping it lacks a general way to query DNS.
However, you can use P/Invoke to call the DnsQuery function directly. The API is somewhat cumbersome but it is not impossible to create the correct P/Invoke signature for your requirement.
A SPF record is stored as a TXT record in DNS. The corresponding structure you will have to work with is the DNS_TXT_DATA structure. If you can find an example of querying a MX record you can reuse the code and use DNS_TYPE_TEXT for the query type and unmarshal the data to a DNS_TXT_DATA structure.
Or you could just use this code:
using System.ComponentModel;
using System.Runtime.InteropServices;
public String DnsGetTxtRecord(String name) {
const Int16 DNS_TYPE_TEXT = 0x0010;
const Int32 DNS_QUERY_STANDARD = 0x00000000;
const Int32 DNS_ERROR_RCODE_NAME_ERROR = 9003;
const Int32 DNS_INFO_NO_RECORDS = 9501;
var queryResultsSet = IntPtr.Zero;
try {
var dnsStatus = DnsQuery(
name,
DNS_TYPE_TEXT,
DNS_QUERY_STANDARD,
IntPtr.Zero,
ref queryResultsSet,
IntPtr.Zero
);
if (dnsStatus == DNS_ERROR_RCODE_NAME_ERROR || dnsStatus == DNS_INFO_NO_RECORDS)
return null;
if (dnsStatus != 0)
throw new Win32Exception(dnsStatus);
DnsRecordTxt dnsRecord;
for (var pointer = queryResultsSet; pointer != IntPtr.Zero; pointer = dnsRecord.pNext) {
dnsRecord = (DnsRecordTxt) Marshal.PtrToStructure(pointer, typeof(DnsRecordTxt));
if (dnsRecord.wType == DNS_TYPE_TEXT) {
var lines = new List<String>();
var stringArrayPointer = pointer
+ Marshal.OffsetOf(typeof(DnsRecordTxt), "pStringArray").ToInt32();
for (var i = 0; i < dnsRecord.dwStringCount; ++i) {
var stringPointer = (IntPtr) Marshal.PtrToStructure(stringArrayPointer, typeof(IntPtr));
lines.Add(Marshal.PtrToStringUni(stringPointer));
stringArrayPointer += IntPtr.Size;
}
return String.Join(Environment.NewLine, lines);
}
}
return null;
}
finally {
const Int32 DnsFreeRecordList = 1;
if (queryResultsSet != IntPtr.Zero)
DnsRecordListFree(queryResultsSet, DnsFreeRecordList);
}
}
[DllImport("Dnsapi.dll", EntryPoint = "DnsQuery_W", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
static extern Int32 DnsQuery(String lpstrName, Int16 wType, Int32 options, IntPtr pExtra, ref IntPtr ppQueryResultsSet, IntPtr pReserved);
[DllImport("Dnsapi.dll")]
static extern void DnsRecordListFree(IntPtr pRecordList, Int32 freeType);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct DnsRecordTxt {
public IntPtr pNext;
public String pName;
public Int16 wType;
public Int16 wDataLength;
public Int32 flags;
public Int32 dwTtl;
public Int32 dwReserved;
public Int32 dwStringCount;
public String pStringArray;
}
Building on the answer by Martin Liversage, I've added some comments that explain what's going on, and adjusted to return multiple records if they exist.
My example also concatenates multiple strings in a TXT record rather than separating by line breaks.
I don't know if the if (dnsRecord.wType == DNS_TYPE_TEXT) line is really necessary considering that constraint is in the arguments to the DnsQuery function, but I preserved it from Martin's answer anyway.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
namespace Util
{
/// <summary>
/// Based on https://stackoverflow.com/a/11884174 (Martin Liversage)
/// </summary>
class DnsInterop
{
private const short DNS_TYPE_TEXT = 0x0010;
private const int DNS_QUERY_STANDARD = 0x00000000;
private const int DNS_ERROR_RCODE_NAME_ERROR = 9003;
private const int DNS_INFO_NO_RECORDS = 9501;
public static IEnumerable<string> GetTxtRecords(string domain)
{
var results = new List<string>();
var queryResultsSet = IntPtr.Zero;
DnsRecordTxt dnsRecord;
try
{
// get all text records
// pointer to results is returned in queryResultsSet
var dnsStatus = DnsQuery(
domain,
DNS_TYPE_TEXT,
DNS_QUERY_STANDARD,
IntPtr.Zero,
ref queryResultsSet,
IntPtr.Zero
);
// return null if no records or DNS lookup failed
if (dnsStatus == DNS_ERROR_RCODE_NAME_ERROR
|| dnsStatus == DNS_INFO_NO_RECORDS)
{
return null;
}
// throw an exception if other non success code
if (dnsStatus != 0)
throw new Win32Exception(dnsStatus);
// step through each result
for (
var pointer = queryResultsSet;
pointer != IntPtr.Zero;
pointer = dnsRecord.pNext)
{
dnsRecord = (DnsRecordTxt)
Marshal.PtrToStructure(pointer, typeof(DnsRecordTxt));
if (dnsRecord.wType == DNS_TYPE_TEXT)
{
var builder = new StringBuilder();
// pointer to array of pointers
// to each string that makes up the record
var stringArrayPointer = pointer + Marshal.OffsetOf(
typeof(DnsRecordTxt), "pStringArray").ToInt32();
// concatenate multiple strings in the case of long records
for (var i = 0; i < dnsRecord.dwStringCount; ++i)
{
var stringPointer = (IntPtr)Marshal.PtrToStructure(
stringArrayPointer, typeof(IntPtr));
builder.Append(Marshal.PtrToStringUni(stringPointer));
stringArrayPointer += IntPtr.Size;
}
results.Add(builder.ToString());
}
}
}
finally
{
if (queryResultsSet != IntPtr.Zero)
{
DnsRecordListFree(queryResultsSet,
(int)DNS_FREE_TYPE.DnsFreeRecordList);
}
}
return results;
}
[DllImport("Dnsapi.dll", EntryPoint = "DnsQuery_W",
ExactSpelling = true, CharSet = CharSet.Unicode,
SetLastError = true)]
static extern int DnsQuery(string lpstrName, short wType, int options,
IntPtr pExtra, ref IntPtr ppQueryResultsSet, IntPtr pReserved);
[DllImport("Dnsapi.dll")]
static extern void DnsRecordListFree(IntPtr pRecordList, int freeType);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct DnsRecordTxt
{
public IntPtr pNext;
public string pName;
public short wType;
public short wDataLength;
public int flags;
public int dwTtl;
public int dwReserved;
public int dwStringCount;
public string pStringArray;
}
enum DNS_FREE_TYPE
{
DnsFreeFlat = 0,
DnsFreeRecordList = 1,
DnsFreeParsedMessageFields = 2
}
}
}
You basically need to do a DNS request asking for the MX/SPF record of the domain. There's a few examples of doing this in C# around. There's a library at http://mailsystem.codeplex.com/ that has a Validator class with GetMxRecords to do this that you might find useful
For what is worth - MailBee's .NET Objects support this as well. I only say this because we already owned this component and I was about to implement something else when I found this goodie already baked into what we had.
http://www.afterlogic.com/mailbee-net/docs/filter_spam_with_dns.html
We tried to use #martin-liversage's answer, but after some time running on hundrets of domains it failed on some memory problem. (Maybe there was some invalid/another type DNS record?)
So i studied this exact WINAPI functions and structures used in this case and edited the solution acordingly.
Links to WINAPI documentation are included in code.
So here's our improved code, that's working 100% even in our case:
public String GetSpfRecord(String domain)
{
// Definition of DNS params
const Int16 DNS_TYPE_TXT = 0x0010;
const Int32 DNS_QUERY_STANDARD = 0x00000001;
const Int32 DNS_ERROR_RCODE_NAME_ERROR = 9003;
const Int32 DNS_INFO_NO_RECORDS = 9501;
DnsRecordA dnsRecord;
var queryResultsSet = IntPtr.Zero;
try
{
var dnsStatus = DnsQuery(
domain,
DNS_TYPE_TXT,
DNS_QUERY_STANDARD,
IntPtr.Zero,
ref queryResultsSet,
IntPtr.Zero
);
if (dnsStatus == DNS_ERROR_RCODE_NAME_ERROR || dnsStatus == DNS_INFO_NO_RECORDS)
return null;
if (dnsStatus != 0)
throw new Win32Exception(dnsStatus);
for (IntPtr pointer = queryResultsSet; pointer != IntPtr.Zero; pointer = dnsRecord.pNext)
{
// Copies data from memory (size of DnsRecordA) from adress pointer to new alocated memory and creates instance of pointer to this place.
dnsRecord = (DnsRecordA)Marshal.PtrToStructure(pointer, typeof(DnsRecordA));
// pokud se jedná o typ TXT
if (dnsRecord.wType == DNS_TYPE_TXT)
{
// get pointer to informations in "Data" property (https://learn.microsoft.com/en-us/windows/win32/api/windns/ns-windns-dns_recorda)
var dataPointer = pointer + Marshal.SizeOf(typeof(DnsRecordA));
// Get the txtData
var txtData = (DNS_TXT_DATAA)Marshal.PtrToStructure(dataPointer, typeof(DNS_TXT_DATAA));
if (txtData.dwStringCount >= 1)
{
string line = Marshal.PtrToStringUni(txtData.pStringArray[0]);
// only if record starts with "v=spf" (Getting only SPF records)
// Getting only first (here is always maximum of 1 record) and returning whole line
if (line.StartsWith("v=spf") && string.IsNullOrEmpty(result))
{
return line;
}
}
}
}
// no SPF record - returning null
return null;
}
finally
{
const Int32 DnsFreeRecordList = 1;
// always release the memory alocated for list of dns records
if (queryResultsSet != IntPtr.Zero)
DnsRecordListFree(queryResultsSet, DnsFreeRecordList);
}
}
// https://learn.microsoft.com/en-us/windows/win32/api/windns/nf-windns-dnsquery_a
[DllImport("Dnsapi.dll", EntryPoint = "DnsQuery_W", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
static extern Int32 DnsQuery(String lpstrName, Int16 wType, Int32 options, IntPtr pExtra, ref IntPtr ppQueryResultsSet, IntPtr pReserved);
// https://learn.microsoft.com/en-us/windows/win32/api/windns/nf-windns-dnsrecordlistfree
[DllImport("Dnsapi.dll")]
static extern void DnsRecordListFree(IntPtr pRecordList, Int32 freeType);
// https://learn.microsoft.com/en-us/windows/win32/api/windns/ns-windns-dns_recorda
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct DnsRecordA
{
public IntPtr pNext;
public String pName;
public Int16 wType;
public Int16 wDataLength;
public Int32 flags;
public Int32 dwTtl;
public Int32 dwReserved;
// Commented, because i'm getting this value dynamicaly (it can also be another structure type which might cause some problems)
//public DNS_TXT_DATA Data;
}
// https://learn.microsoft.com/en-us/windows/win32/api/windns/ns-windns-dns_txt_dataa
[StructLayout(LayoutKind.Sequential)]
struct DNS_TXT_DATAA
{
/// DWORD->unsigned int
public uint dwStringCount;
/// PSTR[1]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = UnmanagedType.SysUInt)]
internal IntPtr[] pStringArray;
}
Based on DnsDig project I created a DLL which can be used on any .net (vb , c#, forms, web. etc..) project
https://devselz.com/software/devselz_dnsdig_dns-txt-etc-query-domain-register.zip
Download the DLL , unzip, and Add as reference to your project (if website place on root/bin folder):
DnsDig.dll
nunit.framework.dll
(126KB in total)
Then use this example as for an ASP.Ne website (vb.net code)
Imports DnsDig
Imports Heijden.DNS
Partial Class lib_u_l_Default
Inherits System.Web.UI.Page
Public Resolver As Resolver
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
Resolver = New Resolver
Dim SW As New System.Diagnostics.Stopwatch
SW.Start()
Dim DNSResponse As Heijden.DNS.Response = Resolver.Query(Request.QueryString("d"), QType.SPF, QClass.ANY)
SW.Stop()
If DNSResponse.header.ANCOUNT > 0 Then
For Each answerRR As AnswerRR In DNSResponse.Answers
Response.Write("<br/>" & answerRR.ToString)
Next
End If
End Sub
End Class
RESULTS:
https://yourwebsiteusingabovedlls.com/anyplacewhereabovecode/?d=goodyes.com
will write
goodyes.com. 3535 IN TXT "google-site-verification=IMw-tL0VWgMJbtcRgt_bu5UaVwpbNb94dvcOSObooa4"
goodyes.com. 3535 IN TXT "v=spf1 include:_spf.buzondecorreo.com ~all"
One option is to use the package DnsClient.NET (Github-Repo).
This library is able to do high performant DNS lookups as well as lookups for SPF-entries.
Example code for SPF-lookup:
var domainName = "example.domain";
var lookup = new LookupClient();
var result = await lookup.QueryAsync(domainName, QueryType.TXT).ConfigureAwait(false);
var records = result.Answers.OfType<TxtRecord>().ToList();
Funny how all the websites has this wrong
SPF is not TXT
you can have a txt record with no SPF and SPF with no TXT, so a TXT lookup will not show the SPF

Searching for tree item when I don't know exact tree hierarchy

I am trying to select a tree item in a Coded UI Test, but I don't know the entire hierarchy.
Example:
mssql connection
Tables
Unknown
FirstName
Is there a way to search for this FirstName tree item and specify that it is so many levels deep, without specifying the entire path?
It doesn't look like any of the Search Configuration properties will do this.
If FirstName is unique in the tree, then you can use PInvoke, and you won't need to specify the depth:
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
var result = new List<IntPtr>();
var listHandle = GCHandle.Alloc(result);
try
{
var childProc = new User32.EnumWindowsProc(EnumWindow);
User32.EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
var gch = GCHandle.FromIntPtr(pointer);
var list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
// Modify this to check to see if you want to cancel the operation, then return a null here
return true;
}
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowsProc callback, IntPtr i);
// sample usage:
public void findWindowUser32()
{
foreach (IntPtr child in GetChildWindows(User32.FindWindow(null, "Untitled - Notepad")))
{
StringBuilder sb = new StringBuilder(100);
User32.GetClassName(child, sb, sb.Capacity);
if (sb.ToString() == "Edit")
{
uint wparam = 0 << 29 | 0;
User32.PostMessage(child, WindowsConstants.WM_KEYDOWN, (IntPtr)Keys.H, (IntPtr)wparam);
}
}
}
When your control was mapped in the UI map, perhaps the full hierarchy was used, eg
mssql connection
-Tables
--Unknown1
---FirstName
resulted in 4 mapped controls.
You can edit the uimap .xml file manually carefully removing the -Unknown1 element, and making sure that MatchExactHierarchy is turned off.
That way the search will originally fail, move on to using heuristics to look for elements deeper in the tree than the immediate children and should find your control.
public static UITestControl GetTreeItem(UITestControl TreeControl, string ItemName, bool ContainsTrue = true)
{
AutomationElement tree = AutomationElement.FromHandle(TreeControl.WindowHandle);
System.Windows.Automation.ControlType controlType = tree.Current.ControlType;
//Get collection of tree nodes.
AutomationElementCollection treeNodeCollection = null;
treeNodeCollection = tree.FindAll(TreeScope.Descendants,
new System.Windows.Automation.PropertyCondition(AutomationElement.ControlTypeProperty,
System.Windows.Automation.ControlType.TreeItem));
UITestControl ReqTreeItem = new UITestControl();
foreach (AutomationElement item in treeNodeCollection)
{
if ((item.Current.Name == ItemName) && (!ContainsTrue))
{
ReqTreeItem = UITestControlFactory.FromNativeElement(item, "UIA");
break;
}
if ((item.Current.Name.Contains(ItemName)) && (ContainsTrue))
{
ReqTreeItem = UITestControlFactory.FromNativeElement(item, "UIA");
break;
}
}
return ReqTreeItem;
}

Get selected items of folder with WinAPI

I try to get the selected files of a folder which the user is using. I have the following code which is already running, but only on desktop files:
private string selectedFiles()
{
// get the handle of the desktop listview
IntPtr vHandle = WinApiWrapper.FindWindow("Progman", "Program Manager");
vHandle = WinApiWrapper.FindWindowEx(vHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
vHandle = WinApiWrapper.FindWindowEx(vHandle, IntPtr.Zero, "SysListView32", "FolderView");
//IntPtr vHandle = WinApiWrapper.GetForegroundWindow();
//Get total count of the icons on the desktop
int vItemCount = WinApiWrapper.SendMessage(vHandle, WinApiWrapper.LVM_GETITEMCOUNT, 0, 0);
//MessageBox.Show(vItemCount.ToString());
uint vProcessId;
WinApiWrapper.GetWindowThreadProcessId(vHandle, out vProcessId);
IntPtr vProcess = WinApiWrapper.OpenProcess(WinApiWrapper.PROCESS_VM_OPERATION | WinApiWrapper.PROCESS_VM_READ |
WinApiWrapper.PROCESS_VM_WRITE, false, vProcessId);
IntPtr vPointer = WinApiWrapper.VirtualAllocEx(vProcess, IntPtr.Zero, 4096,
WinApiWrapper.MEM_RESERVE | WinApiWrapper.MEM_COMMIT, WinApiWrapper.PAGE_READWRITE);
try
{
for (int j = 0; j < vItemCount; j++)
{
byte[] vBuffer = new byte[256];
WinApiWrapper.LVITEM[] vItem = new WinApiWrapper.LVITEM[1];
vItem[0].mask = WinApiWrapper.LVIF_TEXT;
vItem[0].iItem = j;
vItem[0].iSubItem = 0;
vItem[0].cchTextMax = vBuffer.Length;
vItem[0].pszText = (IntPtr)((int)vPointer + Marshal.SizeOf(typeof(WinApiWrapper.LVITEM)));
uint vNumberOfBytesRead = 0;
WinApiWrapper.WriteProcessMemory(vProcess, vPointer,
Marshal.UnsafeAddrOfPinnedArrayElement(vItem, 0),
Marshal.SizeOf(typeof(WinApiWrapper.LVITEM)), ref vNumberOfBytesRead);
WinApiWrapper.SendMessage(vHandle, WinApiWrapper.LVM_GETITEMW, j, vPointer.ToInt32());
WinApiWrapper.ReadProcessMemory(vProcess,
(IntPtr)((int)vPointer + Marshal.SizeOf(typeof(WinApiWrapper.LVITEM))),
Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0),
vBuffer.Length, ref vNumberOfBytesRead);
string vText = Encoding.Unicode.GetString(vBuffer, 0,
(int)vNumberOfBytesRead);
string IconName = vText;
//Check if item is selected
var result = WinApiWrapper.SendMessage(vHandle, WinApiWrapper.LVM_GETITEMSTATE, j, (int)WinApiWrapper.LVIS_SELECTED);
if (result == WinApiWrapper.LVIS_SELECTED)
{
return vText;
}
}
}
finally
{
WinApiWrapper.VirtualFreeEx(vProcess, vPointer, 0, WinApiWrapper.MEM_RELEASE);
WinApiWrapper.CloseHandle(vProcess);
}
return String.Empty;
}
I tried to get the window handle with GetForegroundWindow() and then call the SHELLDLL_DefView without success.
So how can I change the first 3 rows to get me the handle of the current folder in use?
That's a lot of hacking to do something that is explicitly supported by the various shell objects and interfaces. Granted the documentation doesn't make it easily discoverable, but the functionality is there. Raymond Chen wrote a great article about using these interfaces. There doesn't appear to be a way to get the "current" folder, though I guess you could get the HWNDs and see if any is the foreground window.
thank you very much. You gave me the right direction. It is possible to get the selected files of a folder:
/// <summary>
/// Get the selected file of the active window
/// </summary>
/// <param name="handle">Handle of active window</param>
/// <returns></returns>
public String getSelectedFileOfActiveWindow(Int32 handle)
{
try
{
// Required ref: SHDocVw (Microsoft Internet Controls COM Object)
ShellWindows shellWindows = new SHDocVw.ShellWindows();
foreach (InternetExplorer window in shellWindows)
{
if (window.HWND == handle)
return ((Shell32.IShellFolderViewDual2)window.Document).FocusedItem.Path;
}
}
catch (Exception)
{
return null;
}
return null;
}

Win32 API function to programmatically enable/disable device

I am writing a small C# app to disable a device (my laptop touchpad) whenever another mouse device is detected, and enable the touchpad again if a mouse is not detected. I am not even able to disable the touchpad in device manager (it is running on the default mouse class driver).
I am getting into device driver development so I thought maybe I could write a little filter driver that would just accept IOCTLs to enable and disable passing mouse event messages up the device stack, and get messages from user mode via a raw PDO. However, I asked that question and somebody has suggested that I can do this in usermode via the SetupDi.. functions. That would be really good, because this raw PDO communication method is a PITA to work with.
I have only used SetupDiGetClassDevs before, and there are so many of them, can someone with more experience with this part of the Win32 API just tell me quickly what one I should call to stop/disable a mouse device or its interface or if there is something somewhere in the dark corners of the framework that will do this (maybe in WMI?).
Update (24/9/09) I figured out how to do this with a filter driver and posted how I did it on my original question. I still want to know if it is possible to enable or disable devices directly from Win32 and if so, how - so I will leave this question open.
You can enable/disable devices from Win32 (and hence from C# via P/Invoke) using the SetupDi APIs but not all devices are "disable-able" in this way.
The problem you'll run into trying to disable your touchpad from Win32 (or WMI or any other API which calls down into the SetupDi* family of functions) is that the default mouse driver which is in most laptops with a touchpad ("PS/2 compatible mouse") doesn't support being disabled using SetupDi API's. I suspect this may be because actual old mice using PS/2 connectors can't be hot-detached without hosing the hardware.
To verify that you can't disable, go into Device Manager and right-click on your mouse driver. If you see a disable option, you can use SetupDi to disable it. If no disable option, you're out of luck... welcome to IOCTL-land!
If you do see a disable option, then the code below (ported to C# from a VB sample I found here) should let you disable and re-enable the device.
Here's the code to call the library:
public static void EnableMouse(bool enable)
{
// every type of device has a hard-coded GUID, this is the one for mice
Guid mouseGuid = new Guid("{4d36e96f-e325-11ce-bfc1-08002be10318}");
// get this from the properties dialog box of this device in Device Manager
string instancePath = #"ACPI\PNP0F03\4&3688D3F&0";
DeviceHelper.SetDeviceEnabled(mouseGuid, instancePath, enable);
}
Here's the library itself, adapted from here.
using System;
using System.Text;
using System.Collections.Generic;
using DisableDevice;
using System.Runtime.InteropServices;
using System.ComponentModel;
using Microsoft.Win32.SafeHandles;
using System.Security;
using System.Runtime.ConstrainedExecution;
using System.Management;
namespace DisableDevice
{
[Flags()]
internal enum SetupDiGetClassDevsFlags
{
Default = 1,
Present = 2,
AllClasses = 4,
Profile = 8,
DeviceInterface = (int)0x10
}
internal enum DiFunction
{
SelectDevice = 1,
InstallDevice = 2,
AssignResources = 3,
Properties = 4,
Remove = 5,
FirstTimeSetup = 6,
FoundDevice = 7,
SelectClassDrivers = 8,
ValidateClassDrivers = 9,
InstallClassDrivers = (int)0xa,
CalcDiskSpace = (int)0xb,
DestroyPrivateData = (int)0xc,
ValidateDriver = (int)0xd,
Detect = (int)0xf,
InstallWizard = (int)0x10,
DestroyWizardData = (int)0x11,
PropertyChange = (int)0x12,
EnableClass = (int)0x13,
DetectVerify = (int)0x14,
InstallDeviceFiles = (int)0x15,
UnRemove = (int)0x16,
SelectBestCompatDrv = (int)0x17,
AllowInstall = (int)0x18,
RegisterDevice = (int)0x19,
NewDeviceWizardPreSelect = (int)0x1a,
NewDeviceWizardSelect = (int)0x1b,
NewDeviceWizardPreAnalyze = (int)0x1c,
NewDeviceWizardPostAnalyze = (int)0x1d,
NewDeviceWizardFinishInstall = (int)0x1e,
Unused1 = (int)0x1f,
InstallInterfaces = (int)0x20,
DetectCancel = (int)0x21,
RegisterCoInstallers = (int)0x22,
AddPropertyPageAdvanced = (int)0x23,
AddPropertyPageBasic = (int)0x24,
Reserved1 = (int)0x25,
Troubleshooter = (int)0x26,
PowerMessageWake = (int)0x27,
AddRemotePropertyPageAdvanced = (int)0x28,
UpdateDriverUI = (int)0x29,
Reserved2 = (int)0x30
}
internal enum StateChangeAction
{
Enable = 1,
Disable = 2,
PropChange = 3,
Start = 4,
Stop = 5
}
[Flags()]
internal enum Scopes
{
Global = 1,
ConfigSpecific = 2,
ConfigGeneral = 4
}
internal enum SetupApiError
{
NoAssociatedClass = unchecked((int)0xe0000200),
ClassMismatch = unchecked((int)0xe0000201),
DuplicateFound = unchecked((int)0xe0000202),
NoDriverSelected = unchecked((int)0xe0000203),
KeyDoesNotExist = unchecked((int)0xe0000204),
InvalidDevinstName = unchecked((int)0xe0000205),
InvalidClass = unchecked((int)0xe0000206),
DevinstAlreadyExists = unchecked((int)0xe0000207),
DevinfoNotRegistered = unchecked((int)0xe0000208),
InvalidRegProperty = unchecked((int)0xe0000209),
NoInf = unchecked((int)0xe000020a),
NoSuchHDevinst = unchecked((int)0xe000020b),
CantLoadClassIcon = unchecked((int)0xe000020c),
InvalidClassInstaller = unchecked((int)0xe000020d),
DiDoDefault = unchecked((int)0xe000020e),
DiNoFileCopy = unchecked((int)0xe000020f),
InvalidHwProfile = unchecked((int)0xe0000210),
NoDeviceSelected = unchecked((int)0xe0000211),
DevinfolistLocked = unchecked((int)0xe0000212),
DevinfodataLocked = unchecked((int)0xe0000213),
DiBadPath = unchecked((int)0xe0000214),
NoClassInstallParams = unchecked((int)0xe0000215),
FileQueueLocked = unchecked((int)0xe0000216),
BadServiceInstallSect = unchecked((int)0xe0000217),
NoClassDriverList = unchecked((int)0xe0000218),
NoAssociatedService = unchecked((int)0xe0000219),
NoDefaultDeviceInterface = unchecked((int)0xe000021a),
DeviceInterfaceActive = unchecked((int)0xe000021b),
DeviceInterfaceRemoved = unchecked((int)0xe000021c),
BadInterfaceInstallSect = unchecked((int)0xe000021d),
NoSuchInterfaceClass = unchecked((int)0xe000021e),
InvalidReferenceString = unchecked((int)0xe000021f),
InvalidMachineName = unchecked((int)0xe0000220),
RemoteCommFailure = unchecked((int)0xe0000221),
MachineUnavailable = unchecked((int)0xe0000222),
NoConfigMgrServices = unchecked((int)0xe0000223),
InvalidPropPageProvider = unchecked((int)0xe0000224),
NoSuchDeviceInterface = unchecked((int)0xe0000225),
DiPostProcessingRequired = unchecked((int)0xe0000226),
InvalidCOInstaller = unchecked((int)0xe0000227),
NoCompatDrivers = unchecked((int)0xe0000228),
NoDeviceIcon = unchecked((int)0xe0000229),
InvalidInfLogConfig = unchecked((int)0xe000022a),
DiDontInstall = unchecked((int)0xe000022b),
InvalidFilterDriver = unchecked((int)0xe000022c),
NonWindowsNTDriver = unchecked((int)0xe000022d),
NonWindowsDriver = unchecked((int)0xe000022e),
NoCatalogForOemInf = unchecked((int)0xe000022f),
DevInstallQueueNonNative = unchecked((int)0xe0000230),
NotDisableable = unchecked((int)0xe0000231),
CantRemoveDevinst = unchecked((int)0xe0000232),
InvalidTarget = unchecked((int)0xe0000233),
DriverNonNative = unchecked((int)0xe0000234),
InWow64 = unchecked((int)0xe0000235),
SetSystemRestorePoint = unchecked((int)0xe0000236),
IncorrectlyCopiedInf = unchecked((int)0xe0000237),
SceDisabled = unchecked((int)0xe0000238),
UnknownException = unchecked((int)0xe0000239),
PnpRegistryError = unchecked((int)0xe000023a),
RemoteRequestUnsupported = unchecked((int)0xe000023b),
NotAnInstalledOemInf = unchecked((int)0xe000023c),
InfInUseByDevices = unchecked((int)0xe000023d),
DiFunctionObsolete = unchecked((int)0xe000023e),
NoAuthenticodeCatalog = unchecked((int)0xe000023f),
AuthenticodeDisallowed = unchecked((int)0xe0000240),
AuthenticodeTrustedPublisher = unchecked((int)0xe0000241),
AuthenticodeTrustNotEstablished = unchecked((int)0xe0000242),
AuthenticodePublisherNotTrusted = unchecked((int)0xe0000243),
SignatureOSAttributeMismatch = unchecked((int)0xe0000244),
OnlyValidateViaAuthenticode = unchecked((int)0xe0000245)
}
[StructLayout(LayoutKind.Sequential)]
internal struct DeviceInfoData
{
public int Size;
public Guid ClassGuid;
public int DevInst;
public IntPtr Reserved;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PropertyChangeParameters
{
public int Size;
// part of header. It's flattened out into 1 structure.
public DiFunction DiFunction;
public StateChangeAction StateChange;
public Scopes Scope;
public int HwProfile;
}
internal class NativeMethods
{
private const string setupapi = "setupapi.dll";
private NativeMethods()
{
}
[DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiCallClassInstaller(DiFunction installFunction, SafeDeviceInfoSetHandle deviceInfoSet, [In()]
ref DeviceInfoData deviceInfoData);
[DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiEnumDeviceInfo(SafeDeviceInfoSetHandle deviceInfoSet, int memberIndex, ref DeviceInfoData deviceInfoData);
[DllImport(setupapi, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeDeviceInfoSetHandle SetupDiGetClassDevs([In()]
ref Guid classGuid, [MarshalAs(UnmanagedType.LPWStr)]
string enumerator, IntPtr hwndParent, SetupDiGetClassDevsFlags flags);
/*
[DllImport(setupapi, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiGetDeviceInstanceId(SafeDeviceInfoSetHandle deviceInfoSet, [In()]
ref DeviceInfoData did, [MarshalAs(UnmanagedType.LPTStr)]
StringBuilder deviceInstanceId, int deviceInstanceIdSize, [Out()]
ref int requiredSize);
*/
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiGetDeviceInstanceId(
IntPtr DeviceInfoSet,
ref DeviceInfoData did,
[MarshalAs(UnmanagedType.LPTStr)] StringBuilder DeviceInstanceId,
int DeviceInstanceIdSize,
out int RequiredSize
);
[SuppressUnmanagedCodeSecurity()]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet);
[DllImport(setupapi, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiSetClassInstallParams(SafeDeviceInfoSetHandle deviceInfoSet, [In()]
ref DeviceInfoData deviceInfoData, [In()]
ref PropertyChangeParameters classInstallParams, int classInstallParamsSize);
}
internal class SafeDeviceInfoSetHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeDeviceInfoSetHandle()
: base(true)
{
}
protected override bool ReleaseHandle()
{
return NativeMethods.SetupDiDestroyDeviceInfoList(this.handle);
}
}
public sealed class DeviceHelper
{
private DeviceHelper()
{
}
/// <summary>
/// Enable or disable a device.
/// </summary>
/// <param name="classGuid">The class guid of the device. Available in the device manager.</param>
/// <param name="instanceId">The device instance id of the device. Available in the device manager.</param>
/// <param name="enable">True to enable, False to disable.</param>
/// <remarks>Will throw an exception if the device is not Disableable.</remarks>
public static void SetDeviceEnabled(Guid classGuid, string instanceId, bool enable)
{
SafeDeviceInfoSetHandle diSetHandle = null;
try
{
// Get the handle to a device information set for all devices matching classGuid that are present on the
// system.
diSetHandle = NativeMethods.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, SetupDiGetClassDevsFlags.Present);
// Get the device information data for each matching device.
DeviceInfoData[] diData = GetDeviceInfoData(diSetHandle);
// Find the index of our instance. i.e. the touchpad mouse - I have 3 mice attached...
int index = GetIndexOfInstance(diSetHandle, diData, instanceId);
// Disable...
EnableDevice(diSetHandle, diData[index], enable);
}
finally
{
if (diSetHandle != null)
{
if (diSetHandle.IsClosed == false)
{
diSetHandle.Close();
}
diSetHandle.Dispose();
}
}
}
private static DeviceInfoData[] GetDeviceInfoData(SafeDeviceInfoSetHandle handle)
{
List<DeviceInfoData> data = new List<DeviceInfoData>();
DeviceInfoData did = new DeviceInfoData();
int didSize = Marshal.SizeOf(did);
did.Size = didSize;
int index = 0;
while (NativeMethods.SetupDiEnumDeviceInfo(handle, index, ref did))
{
data.Add(did);
index += 1;
did = new DeviceInfoData();
did.Size = didSize;
}
return data.ToArray();
}
// Find the index of the particular DeviceInfoData for the instanceId.
private static int GetIndexOfInstance(SafeDeviceInfoSetHandle handle, DeviceInfoData[] diData, string instanceId)
{
const int ERROR_INSUFFICIENT_BUFFER = 122;
for (int index = 0; index <= diData.Length - 1; index++)
{
StringBuilder sb = new StringBuilder(1);
int requiredSize = 0;
bool result = NativeMethods.SetupDiGetDeviceInstanceId(handle.DangerousGetHandle(), ref diData[index], sb, sb.Capacity, out requiredSize);
if (result == false && Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
{
sb.Capacity = requiredSize;
result = NativeMethods.SetupDiGetDeviceInstanceId(handle.DangerousGetHandle(), ref diData[index], sb, sb.Capacity, out requiredSize);
}
if (result == false)
throw new Win32Exception();
if (instanceId.Equals(sb.ToString()))
{
return index;
}
}
// not found
return -1;
}
// enable/disable...
private static void EnableDevice(SafeDeviceInfoSetHandle handle, DeviceInfoData diData, bool enable)
{
PropertyChangeParameters #params = new PropertyChangeParameters();
// The size is just the size of the header, but we've flattened the structure.
// The header comprises the first two fields, both integer.
#params.Size = 8;
#params.DiFunction = DiFunction.PropertyChange;
#params.Scope = Scopes.Global;
if (enable)
{
#params.StateChange = StateChangeAction.Enable;
}
else
{
#params.StateChange = StateChangeAction.Disable;
}
bool result = NativeMethods.SetupDiSetClassInstallParams(handle, ref diData, ref #params, Marshal.SizeOf(#params));
if (result == false) throw new Win32Exception();
result = NativeMethods.SetupDiCallClassInstaller(DiFunction.PropertyChange, handle, ref diData);
if (result == false)
{
int err = Marshal.GetLastWin32Error();
if (err == (int)SetupApiError.NotDisableable)
throw new ArgumentException("Device can't be disabled (programmatically or in Device Manager).");
else if (err >= (int)SetupApiError.NoAssociatedClass && err <= (int)SetupApiError.OnlyValidateViaAuthenticode)
throw new Win32Exception("SetupAPI error: " + ((SetupApiError)err).ToString());
else
throw new Win32Exception();
}
}
}
}
Note that when you get an Index-Out-Of-Bounds exception on the line int index = GetIndexOfInstance(diSetHandle, diData, instanceId);, you might have used the wrong classGuid for the device or the wrong instanceId.
Also note that when you run this code on a 64-bit Windows platform, you should target the 64-bit platform when you build your application. Otherwise - i.e. when running your application as a 32-bit process on a 64-bit Windows platform - you will get an SetupAPI error InWow64 (ERROR_IN_WOW64).
When targetting a 64-bit Windows platform, you might also have to make changes to other parts of your application, e.g. when doing pointer arithmetic, in order to prevent overflows.
One way might be to use the Windows Management Instrumentation layer. There seems to be a few device related classes defined in this layer.
You can consider the below mentioned Win APIs
CM_Request_Device_EjectW function: The CM_Request_Device_Eject function prepares a local device instance for safe removal. If the device is removable.
CM_Query_And_Remove_SubTreeW function: The CM_Query_And_Remove_SubTree function checks whether a device instance and its children can be removed and if so, it removes them.
And the same can be found here: CM_Request_Device_EjectW and CM_Query_And_Remove_SubTreeW
In case you wanted to toggle the device enabled status, you can adapt #JustinGrant's amazing solution by replacing SetDeviceEnabled with the following:
public static void ToggleDeviceEnabled(Guid classGuid, string instanceId)
{
SafeDeviceInfoSetHandle diSetHandle = null;
try
{
(diSetHandle, var did) = GetDeviceInfo(classGuid, instanceId);
// according to https://stackoverflow.com/a/13105326/308451
// the answer might lie in CM_Get_DevNode_Status. Let's try.
int result = NativeMethods.CM_Get_DevNode_Status(out uint devNodeStatus, out uint probNum, did.DevInst);
// return value meanings are in Cfgmgr32.h., but CR_SUCCESS is one of them
const int CR_SUCCESS = 0;
if (result != CR_SUCCESS)
throw new Win32Exception();
// devNodeStatus now contains status bit flags: any combination of the DN_- prefixed bit flags defined in Cfg.h.
// one of the flags is
const uint DN_STARTED = 0x00000008; // Is currently configured
bool currentlyEnabled = HasFlag(devNodeStatus, DN_STARTED);
bool enable = !currentlyEnabled;
// toggle
EnableDevice(diSetHandle, did, enable);
}
finally
{
if (diSetHandle != null)
{
if (diSetHandle.IsClosed == false)
{
diSetHandle.Close();
}
diSetHandle.Dispose();
}
}
}
static bool HasFlag(uint flags, uint flag)
{
return (flags & flag) == flag;
}
private static (SafeDeviceInfoSetHandle, DeviceInfoData) GetDeviceInfo(Guid classGuid, string instanceId)
{
// Get the handle to a device information set for all devices matching classGuid that are present on the system.
var diSetHandle = NativeMethods.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, SetupDiGetClassDevsFlags.Present);
// Get the device information data for each matching device.
DeviceInfoData[] diData = GetDeviceInfoData(diSetHandle);
// Find the index of our instance. i.e. the touchpad mouse - I have 3 mice attached...
int index = GetIndexOfInstance(diSetHandle, diData, instanceId);
return (diSetHandle, diData[index]);
}
[DllImport("cfgmgr32.dll", SetLastError = true)]
public static extern int CM_Get_DevNode_Status(out uint status, out uint probNum, int devInst, int flags = 0);

Categories