I have the following code to import the IExtractImage interface.
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1")]
public interface IExtractImage
{
[PreserveSig]
Int32 GetLocation([MarshalAs(UnmanagedType.LPWStr)] out StringBuilder pszPathBuffer,
int cch,
ref int pdwPriority,
SIZE prgSize,
int dwRecClrDepth,
ref int pdwFlags);
[PreserveSig]
Int32 Extract(out IntPtr phBmpThumbnail);
}
I also have the IShellFolder imported, which I am not mentioning here for brevity. My intention is to access the thumbnail of a file using the shellfolder. So here's my code to retrieve the thumbnail. But my call to IExtractImage.GetLocation() is failing with a NullReference exception, stating
"An exception of type 'System.NullReferenceException' occurred in CCDash.exe but was not handled in user code
Additional information: Object reference not set to an instance of an object."
Can someone please help me identify what is it that I am missing?
public Bitmap GetThumbNail(string mediaFileName)
{
IShellFolder shellDesktop;
SHGetDesktopFolder(out shellDesktop);
IntPtr pidlRoot;
uint attribute = 0;
string mediaPath = "C:\\Users\\<user>\\Videos"; // I have hard-coded the path for now
uint pchEaten = 0;
// Get the pidl of the media folder
shellDesktop.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, mediaPath, ref pchEaten, out pidlRoot, ref attribute);
Guid mediaFolderGuid = new Guid("000214E6-0000-0000-C000-000000000046");
shellDesktop.BindToObject(pidlRoot, IntPtr.Zero, mediaFolderGuid, out shellMediaFolder);
IntPtr pidlMediaFolder;
// Get the pidl of the media file
shellMediaFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, mediaFileName, ref pchEaten, out pidlMediaFolder, ref attribute);
Guid mediaFileImgGuid = new Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1");
uint rfgRes = 0;
IExtractImage extractImage;
shellMediaFolder.GetUIObjectOf(IntPtr.Zero, 1, out pidlMediaFolder, mediaFileImgGuid, ref rfgRes, out extractImage);
SIZE size = new SIZE
{
cx = 40,
cy = 40
};
int flags = 0x40 | 0x40;
StringBuilder location = new StringBuilder(260, 260);
int priority = 0;
int requestedColourDepth = 0x20;
IntPtr hBmp = IntPtr.Zero;
// Now get the image
extractImage.GetLocation(out location, location.Capacity, ref priority, size, requestedColourDepth, ref flags);
extractImage.Extract(out hBmp);
Bitmap thumbnail = Image.FromHbitmap(hBmp);
return thumbnail;
}
EDIT:
I have now modified my code as below. Not very different from the first version, but with a little more error handling and better documentation and variable names that can help us understand my code better. Here's the modified code:
public Bitmap GetThumbNail(string mediaFileName)
{
Bitmap thumbnail = null;
//Step 1: Use SHGetDesktopFolder to get the desktop folder.
IShellFolder shellDesktop;
SHGetDesktopFolder(out shellDesktop);
if (shellDesktop != null)
{
IntPtr pidlMediaFolder;
try
{
uint attribute = 0;
string mediaPath = Path.GetDirectoryName(mediaFileName);
uint pchEaten = 0;
// Step 2: Using the desktop's IShellFolder, pass the file's parent folder path name into ParseDisplayName to get its PIDL.
shellDesktop.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, mediaPath, ref pchEaten, out pidlMediaFolder, ref attribute);
}
catch (Exception)
{
Marshal.ReleaseComObject(shellDesktop);
return null;
}
if (pidlMediaFolder != IntPtr.Zero)
{
Guid mediaFolderGuid = new Guid("000214E6-0000-0000-C000-000000000046");
IShellFolder shellMediaFolder;
// Step 3: Using the desktop's IShellFolder, pass the PIDL into the BindToObject method
// and get the IShellFolder interface of the file's parent folder.
try
{
shellDesktop.BindToObject(pidlMediaFolder, IntPtr.Zero, mediaFolderGuid, out shellMediaFolder);
}
catch (Exception)
{
Marshal.ReleaseComObject(shellDesktop);
return null;
}
if (shellMediaFolder != null)
{
IntPtr pidlMediaFile;
uint attribute = 0;
uint pchEaten = 0;
// Step 4: Using the parent folder's IShellFolder, pass the file name into ParseDisplayName to get its PIDL.
int ret = shellMediaFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, mediaFileName, ref pchEaten, out pidlMediaFile, ref attribute);
Guid mediaFileImgGuid = new Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1");
uint rfgRes = 0;
IExtractImage extractImage;
// Step 5: Using the parent folder's IShellFolder, pass the file's PIDL
// into the GetUIObjectOf. method to get the IExtractImage interface.
ret = shellMediaFolder.GetUIObjectOf(IntPtr.Zero, 1, out pidlMediaFile, mediaFileImgGuid, ref rfgRes, out extractImage);
SIZE size = new SIZE
{
cx = 40,
cy = 40
};
uint flags = 0x0200;
StringBuilder location = new StringBuilder(260, 260);
int priority = 0;
int requestedColourDepth = 0x20;
IntPtr hBmp = IntPtr.Zero;
// Now get the image
extractImage.GetLocation(out location, location.Capacity, ref priority, size, requestedColourDepth, ref flags);
extractImage.Extract(out hBmp);
thumbnail = Image.FromHbitmap(hBmp);
}
}
}
return thumbnail;
}
I see that at step 4, the pidlMediaFile is not retrieved correctly and it's value is still 0 after the ParseDisplayName() call. This is where the problem begins. I am not sure why the pidl for the filename is not retrieved whereas it is retrieved successfully for the file's parent folder.
It looks like extractImage is being returned null from GetUIObjectOf(). Try checking the HRESULT return value from GetUIObjectOf() to find out why a null value is being returned.
EDIT:
From http://msdn.microsoft.com/en-us/library/windows/desktop/aa378137%28v=vs.85%29.aspx:
E_INVALIDARG One or more arguments are not valid 0x80070057
According to the documentation, the third argument is an [in] argument, not an [out] argument.
Related
Info:
.Net 4.5
Tested on:
Win7 64 bit
Win10 64 bit (Virtual Box)
I am trying to get a list of handles of an external process and return their names as string so I can close a specific one afterwards. Therefore i wrote this function using the Win32API which will check if the handle is the handle i want to close: `
const int CNST_SYSTEM_HANDLE_INFORMATION = 16;
const uint STATUS_INFO_LENGTH_MISMATCH = 0xc0000004;
public static string getObjectTypeName(Win32API.SYSTEM_HANDLE_INFORMATION shHandle, Process process)
{
IntPtr m_ipProcessHwnd = Win32API.OpenProcess(Win32API.ProcessAccessFlags.All, false, process.Id);
IntPtr ipHandle = IntPtr.Zero;
var objBasic = new Win32API.OBJECT_BASIC_INFORMATION();
IntPtr ipBasic = IntPtr.Zero;
var objObjectType = new Win32API.OBJECT_TYPE_INFORMATION();
IntPtr ipObjectType = IntPtr.Zero;
IntPtr ipObjectName = IntPtr.Zero;
string strObjectTypeName = "";
int nLength = 0;
int nReturn = 0;
IntPtr ipTemp = IntPtr.Zero;
if (!Win32API.DuplicateHandle(m_ipProcessHwnd, shHandle.Handle,
Win32API.GetCurrentProcess(), out ipHandle,
0, false, Win32API.DUPLICATE_SAME_ACCESS))
return null;
ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic));
Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectBasicInformation,
ipBasic, Marshal.SizeOf(objBasic), ref nLength);
objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType());
Marshal.FreeHGlobal(ipBasic);
ipObjectType = Marshal.AllocHGlobal(objBasic.TypeInformationLength);
nLength = objBasic.TypeInformationLength;
while ((uint)(nReturn = Win32API.NtQueryObject(
ipHandle, (int)Win32API.ObjectInformationClass.ObjectTypeInformation, ipObjectType,
nLength, ref nLength)) ==
Win32API.STATUS_INFO_LENGTH_MISMATCH)
{
Marshal.FreeHGlobal(ipObjectType);
ipObjectType = Marshal.AllocHGlobal(nLength);
}
objObjectType = (Win32API.OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectType, objObjectType.GetType());
if (Is64Bits())
{
ipTemp = new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32);
}
else
{
ipTemp = objObjectType.Name.Buffer;
}
strObjectTypeName = Marshal.PtrToStringUni(ipTemp, objObjectType.Name.Length >> 1);
Marshal.FreeHGlobal(ipObjectType);
Win32API.CloseHandle(ipHandle);
return strObjectTypeName;
}`
The problem however is that this code works in Win7 64bit, not in Win10! --> In Win 10 strObjectTypeName = Marshal.PtrToStringUni(); throws a AcessViolationException (Last few lines in the code)
System.AccessViolationException Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Am I missing something here about how unmanaged memory has to be accessed in win10?
I have just come across the same issue. I haven't tried Win7, but when you run the code on Win10(x64) as 32bit (e.g. set the "Prefer 32-bit flag" of your application) it should work.
When the exception happens, drag&drop the variable "ipTemp" to the "memory window" of Visual Studio, if it displays only question marks or an error message you don't have a valid pointer.
As far as I figured out, there are (more) padding bytes in the 64bit versions of the structs that are used by this API:
OBJECT_TYPE_INFORMATION contains a UNICODE_STRING and UNICODE_STRING has 4 padding bytes before the Buffer-field in 64bit Mode.
My workaraound was this:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UNICODE_STRING
{
private IntPtr _dummy; // the two ushorts seem to be padded with 4 bytes in 64bit mode only
/// <summary>
/// The length, in bytes, of the string stored in Buffer. If the string is null-terminated, Length does not include the trailing null character.
/// </summary>
public ushort Length
{
get { return (ushort)Marshal.ReadInt16(this, 0); }
}
/// <summary>
/// The length, in bytes, of Buffer.
/// </summary>
public ushort MaximumLength
{
get { return (ushort)Marshal.ReadInt16(this, 2); }
}
public IntPtr Buffer;
}
During my research I found so many questions regarding this topic and basically two kinds of sample code
that have been copied all over the Internet that I am considering to create an open-source library named WinKernelObjectsDotNet.
Update: The library is now available here. It supports finding the process that is locking a file or a serial-port (COM) with a single line of code.
I suggest to change UNICODE_STRING structure in a such way.
public struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
[MarshalAs(UnmanagedType.LPWStr)] public string Buffer;
}
So getObjectTypeName method will be something like this and will work for both 32/64:
public static string getObjectTypeName(SYSTEM_HANDLE_INFORMATION shHandle, Process process) {
IntPtr ipProcessHwnd = OpenProcess(ProcessAccessFlags.All, false, process.Id);
if (!DuplicateHandle(ipProcessHwnd, shHandle.Handle, GetCurrentProcess(), out IntPtr ipHandle, 0, false, DUPLICATE_SAME_ACCESS)) {
return null;
}
OBJECT_BASIC_INFORMATION objBasicInformation = new OBJECT_BASIC_INFORMATION();
IntPtr ipBasicInformation = Marshal.AllocHGlobal(Marshal.SizeOf(objBasicInformation));
int iBasicInformationLength = 0;
NtQueryObject(ipHandle, (int) ObjectInformationClass.ObjectBasicInformation, ipBasicInformation, Marshal.SizeOf(objBasicInformation), ref iBasicInformationLength);
objBasicInformation = (OBJECT_BASIC_INFORMATION) Marshal.PtrToStructure(ipBasicInformation, typeof(OBJECT_BASIC_INFORMATION));
Marshal.FreeHGlobal(ipBasicInformation);
int iObjectTypeInformationLength = objBasicInformation.TypeInformationLength;
IntPtr ipObjectTypeInformation = Marshal.AllocHGlobal(iObjectTypeInformationLength);
while (Win32API.STATUS_INFO_LENGTH_MISMATCH == (uint) (NtQueryObject(ipHandle, (int) ObjectInformationClass.ObjectTypeInformation, ipObjectTypeInformation, iObjectTypeInformationLength, ref iObjectTypeInformationLength))) {
Marshal.FreeHGlobal(ipObjectTypeInformation);
ipObjectTypeInformation = Marshal.AllocHGlobal(iObjectTypeInformationLength);
}
CloseHandle(ipHandle);
OBJECT_TYPE_INFORMATION objObjectType = (OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectTypeInformation, typeof(OBJECT_TYPE_INFORMATION));
Marshal.FreeHGlobal(ipObjectTypeInformation);
return objObjectType.Name.Buffer;
}
My current code works on elements outside of an iframe. How should I approach fetching elements within an iframe using getElementById? My end goal is to write text within the the <body id="tinymce"><p>...</p></body> tags. I am not using a webBrowser control - this is for an external instance of iexplore
HTML Sample
Code Sample
foreach (InternetExplorer ie in new ShellWindowsClass())
{
if (ie.LocationURL.ToString().IndexOf("intranet_site_url") != -1)
{
IWebBrowserApp wb = (IWebBrowserApp)ie;
while (wb.Busy) { Thread.Sleep(100); }
HTMLDocument document = ((HTMLDocument)wb.Document);
// FETCH BY ID
IHTMLElement element;
HTMLInputElementClass hitem;
element = document.getElementById("tinymce");
hitem = (HTMLInputElementClass)element;
hitem.value = first_name;
// FETCH BY ID in IFRAME
IHTMLFramesCollection2 hframes = document.frames;
for (int i = 0; i < hframes.length; i++)
{
object ref_index = i;
IHTMLWindow2 currentFrame = (IHTMLWindow2)hframes.item(ref ref_index);
if (currentFrame != null)
{
MessageBox.Show(currentFrame.name);
// what to do from here?
}
else
MessageBox.Show("Null");
}
}
}
- update idea
Chance of adapting my idea below?
if (currentFrame != null)
{
MessageBox.Show(currentFrame.name);
HTMLDocument document_sub = ((HTMLDocument)currentFrame.document);
IHTMLElement element_sub;
HTMLInputElementClass hitem_sub;
element_sub = (document_sub.getElementById("tinymce"));
hitem_sub = (HTMLInputElementClass)element_sub;
try
{
hitem_sub.value = first_name;
// the above will produce...
// InvalidCastException: Unable to cast COM object of type 'mshtml.HTMLBodyCLass' to class type 'mshtml.HTMLInputElementClass'
}
catch { }
}
This answer was inspired by some research I recently did on using eval to inject script into an out-of-proc instance of Internet Explorer.
The idea is to bypass MSHTML DOM interop interfaces and use dynamic JavaScript to obtain a DOM object of interest. There are some implications:
execScript method is deprecated in upcoming IE11.
Unmanaged IDispatchEx::InvokeEx has to be used to access dynamic JavaScript objects inside HTML window object out-of-proc (in case of in-proc WebBrowser control, standard Type.Invoke, HtmlDocument.InvokeScript or dynamic invocation work just fine with this technique).
To address the question itself, it should be quite easy to get the desired body element, using the approach illustrated below:
var tinymceBody = DispExInvoker.Invoke(ie.Document.parentWindow, "eval", "document.getElementById('decrpt_ifr').contentWindow.document.getElementById('tinymce')");
Here's a sample which executes alert(document.URL) in the context of a child frame of jsfiddle.net, by automating an out-of-proc instance of InternetExplorer.Application:
private async void Form1_Load(object sender, EventArgs ev)
{
SHDocVw.InternetExplorer ie = new SHDocVw.InternetExplorer();
ie.Visible = true;
var documentCompleteTcs = new TaskCompletionSource<bool>();
ie.DocumentComplete += delegate
{
if (documentCompleteTcs.Task.IsCompleted)
return;
documentCompleteTcs.SetResult(true);
};
ie.Navigate("http://jsfiddle.net/");
await documentCompleteTcs.Task;
// inject __execScript code into the top window
var execScriptCode = "(window.__execScript = function(exp) { return eval(exp); }, window.self)";
var window = DispExInvoker.Invoke(ie.Document.parentWindow, "eval", execScriptCode);
// inject __execScript into a child iframe
var iframe = DispExInvoker.Invoke(window, "__execScript",
String.Format("document.all.tags('iframe')[0].contentWindow.eval('{0}')", execScriptCode));
// invoke 'alert(document.URL)' in the context of the child frame
DispExInvoker.Invoke(iframe, "__execScript", "alert(document.URL)");
}
/// <summary>
/// Managed wrapper for calling IDispatchEx::Invoke
/// https://stackoverflow.com/a/18349546/1768303
/// </summary>
public class DispExInvoker
{
// check is the object supports IsDispatchEx
public static bool IsDispatchEx(object target)
{
return target is IDispatchEx;
}
// invoke a method on the target IDispatchEx object
public static object Invoke(object target, string method, params object[] args)
{
var dispEx = target as IDispatchEx;
if (dispEx == null)
throw new InvalidComObjectException();
var dp = new System.Runtime.InteropServices.ComTypes.DISPPARAMS();
try
{
// repack arguments
if (args.Length > 0)
{
// should be using stackalloc for DISPPARAMS arguments, but don't want enforce "/unsafe"
int size = SIZE_OF_VARIANT * args.Length;
dp.rgvarg = Marshal.AllocCoTaskMem(size);
ZeroMemory(dp.rgvarg, size); // zero'ing is equal to VariantInit
dp.cArgs = args.Length;
for (var i = 0; i < dp.cArgs; i++)
Marshal.GetNativeVariantForObject(args[i], dp.rgvarg + SIZE_OF_VARIANT * (args.Length - i - 1));
}
int dispid;
dispEx.GetDispID(method, fdexNameCaseSensitive, out dispid);
var ei = new System.Runtime.InteropServices.ComTypes.EXCEPINFO();
var result = Type.Missing;
dispEx.InvokeEx(dispid, 0, DISPATCH_METHOD, ref dp, ref result, ref ei, null);
return result;
}
finally
{
if (dp.rgvarg != IntPtr.Zero)
{
for (var i = 0; i < dp.cArgs; i++)
VariantClear(dp.rgvarg + SIZE_OF_VARIANT * i);
Marshal.FreeCoTaskMem(dp.rgvarg);
}
}
}
// interop declarations
[DllImport("oleaut32.dll", PreserveSig = false)]
static extern void VariantClear(IntPtr pvarg);
[DllImport("Kernel32.dll", EntryPoint = "RtlZeroMemory", SetLastError = false)]
static extern void ZeroMemory(IntPtr dest, int size);
const uint fdexNameCaseSensitive = 0x00000001;
const ushort DISPATCH_METHOD = 1;
const int SIZE_OF_VARIANT = 16;
// IDispatchEx interface
[ComImport()]
[Guid("A6EF9860-C720-11D0-9337-00A0C90DCAA9")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IDispatchEx
{
// IDispatch
int GetTypeInfoCount();
[return: MarshalAs(UnmanagedType.Interface)]
System.Runtime.InteropServices.ComTypes.ITypeInfo GetTypeInfo([In, MarshalAs(UnmanagedType.U4)] int iTInfo, [In, MarshalAs(UnmanagedType.U4)] int lcid);
void GetIDsOfNames([In] ref Guid riid, [In, MarshalAs(UnmanagedType.LPArray)] string[] rgszNames, [In, MarshalAs(UnmanagedType.U4)] int cNames, [In, MarshalAs(UnmanagedType.U4)] int lcid, [Out, MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
void Invoke(int dispIdMember, ref Guid riid, uint lcid, ushort wFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams, out object pVarResult, ref System.Runtime.InteropServices.ComTypes.EXCEPINFO pExcepInfo, IntPtr[] pArgErr);
// IDispatchEx
void GetDispID([MarshalAs(UnmanagedType.BStr)] string bstrName, uint grfdex, [Out] out int pid);
void InvokeEx(int id, uint lcid, ushort wFlags,
[In] ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pdp,
[In, Out] ref object pvarRes,
[In, Out] ref System.Runtime.InteropServices.ComTypes.EXCEPINFO pei,
System.IServiceProvider pspCaller);
void DeleteMemberByName([MarshalAs(UnmanagedType.BStr)] string bstrName, uint grfdex);
void DeleteMemberByDispID(int id);
void GetMemberProperties(int id, uint grfdexFetch, [Out] out uint pgrfdex);
void GetMemberName(int id, [Out, MarshalAs(UnmanagedType.BStr)] out string pbstrName);
[PreserveSig]
[return: MarshalAs(UnmanagedType.I4)]
int GetNextDispID(uint grfdex, int id, [In, Out] ref int pid);
void GetNameSpaceParent([Out, MarshalAs(UnmanagedType.IUnknown)] out object ppunk);
}
}
Try this:
Windows.Forms.HtmlWindow frame = WebBrowser1.Document.GetElementById("decrpt_ifr").Document.Window.Frames["decrpt_ifr"];
HtmlElement body = frame.Document.GetElementById("tinymce");
body.InnerHtml = "Hello, World!";
That gets the frame and treats it as a different document (because it is) and then it tries to get the element from its id. Good luck.
Edit: This should do the trick taking advantage of the dynamic datatype, and InternetExplorer interface:
private void Form1_Load(object sender, EventArgs e)
{
foreach (InternetExplorer ie in new ShellWindows())
{
if (ie.LocationURL.ToString().IndexOf("tinymce") != -1)
{
IWebBrowserApp wb = (IWebBrowserApp)ie;
wb.Document.Frames.Item[0].document.body.InnerHtml = "<p>Hello, World at </p> " + DateTime.Now.ToString();
}
}
}
Explore the sandbox attribute.
http://www.w3schools.com/tags/att_iframe_sandbox.asp
Also another way would be to get url of that iframe and load that into your browser.
For me accepted solution is giving "Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))" error.
I'm trying to get ACL for a shared folder. The code to get security descriptor is following:
private static SECURITY_DESCRIPTOR GetSecurityDescriptor(string path)
{
var sdUtil = new ADsSecurityUtility();
Byte[] temp = (Byte[])sdUtil.GetSecurityDescriptor(path, (int)ADS_PATHTYPE_ENUM.ADS_PATH_FILESHARE, (int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_RAW);
IntPtr ptr = (IntPtr)0;
SECURITY_DESCRIPTOR sd;
try
{
ptr = Marshal.AllocHGlobal(temp.Length);
Marshal.Copy(temp, 0, ptr, temp.Length);
sd = (SECURITY_DESCRIPTOR)Marshal.PtrToStructure(ptr, typeof(SECURITY_DESCRIPTOR));
return sd;
}
catch (Exception)
{
throw new Exception("Couldn't get security descriptor");
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
SD is ok, I have no problem with that.
Then I'm trying to get DACL and SACL from the SD.
private static List<ACL> GetAcls(SECURITY_DESCRIPTOR sd)
{
List<ACL> result = new List<ACL>(2);
ACL temp = new ACL();
int daclPresent = 0;
int daclDefaulted = 0;
try
{
int res = PInvoke.GetSecurityDescriptorDacl(ref sd, ref daclPresent, ref temp, ref daclDefaulted);
result.Add(temp);
temp = new ACL();
}
catch (Exception) { }
try
{
int res = PInvoke.GetSecurityDescriptorSacl(ref sd, ref daclPresent, ref temp, ref daclDefaulted);
result.Add(temp);
}
catch (Exception) { }
return result;
}
External functions are defined as following:
[DllImport("advapi32.dll")]
public static extern int GetSecurityDescriptorDacl(
[MarshalAs(UnmanagedType.Struct)] ref SECURITY_DESCRIPTOR pSecurityDescriptor,
ref int lpbDaclPresent,
[MarshalAs(UnmanagedType.Struct)] ref ACL pDacl,
ref int lpbDaclDefaulted
);
[DllImport("advapi32.dll")]
public static extern int GetSecurityDescriptorSacl(
[MarshalAs(UnmanagedType.Struct)] ref SECURITY_DESCRIPTOR pSecurityDescriptor,
ref int lpbDaclPresent,
[MarshalAs(UnmanagedType.Struct)] ref ACL pDacl,
ref int lpbDaclDefaulted
);
When I check properties of SD instance I see following:
sd.Dacl
{Permission.ACL}
AceCount: 83886080
AclRevision: 169
AclSize: 1281
Sbz1: 0
Sbz2: 21
sd.Sacl
{Permission.ACL}
AceCount: 6
AclRevision: 20
AclSize: 9961474
Sbz1: 0
Sbz2: 2359297
In total ACL contains 6 ACEs. So it seems SACL contains all of them. However it's not recommended by MS to use these properties. Instead GetSecurityDescriptorDacl and GetSecurityDescriptorSacl should be used. So I use them. And see that count of ACEs in DACL is 0 and count of ACEs in SACL is also 0.
So the question is: how to get properly all ACEs from the security descriptor?
You must treat a SECURITY_DESCRIPTOR as an opaque handle. You can't cast to one as you have done on the line:
sd = (SECURITY_DESCRIPTOR)Marshal.PtrToStructure(ptr,
typeof(SECURITY_DESCRIPTOR));
When you did the above cast you lost all the Owner, Group, DACL and SACL information since you have a self-relative SECURITY_DESCRIPTOR but you are not marshaling the data along with your definition of the structure.
Simply change your declarations of the various API calls (i.e. GetSecurityDescriptorDacl, etc.) to take a byte[] rather than a ref SECURITY_DESCRIPTOR and pass in the byte[] that you received from the ADsSecurityUtility.
I am working on a program that is writing to an HID device and am getting the error 87, Invalid parameter on the WriteFile function. I got the functions from Jan Axelson's USB Complete so I'm not sure why I am getting the error.
I am using this to find my device:
private void USBInit()
{
IntPtr deviceInfoSet;
Int32 memberIndex = 0;
SP_DEVICE_INTERFACE_DATA MyDeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
Int32 bufferSize = 0;
IntPtr detailDataBuffer;
Boolean success = false;
deviceFound = false;
HidD_GetHidGuid(ref hidGuid); // Get the GUID
deviceInfoSet = SetupDiGetClassDevs // Get pointer to a device info set
(ref hidGuid,
IntPtr.Zero,
IntPtr.Zero,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
do
{
MyDeviceInterfaceData.cbSize = Marshal.SizeOf(MyDeviceInterfaceData); // Identify Device Interface
success = SetupDiEnumDeviceInterfaces
(deviceInfoSet,
IntPtr.Zero,
ref hidGuid,
memberIndex,
ref MyDeviceInterfaceData);
success = SetupDiGetDeviceInterfaceDetail // Request Structure with Device Path Name
(deviceInfoSet,
ref MyDeviceInterfaceData,
IntPtr.Zero,
0,
ref bufferSize,
IntPtr.Zero);
detailDataBuffer = Marshal.AllocHGlobal(bufferSize);
Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);
success = SetupDiGetDeviceInterfaceDetail
(deviceInfoSet,
ref MyDeviceInterfaceData,
detailDataBuffer,
bufferSize,
ref bufferSize,
IntPtr.Zero);
IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt32() + 4);
devicePathName = Marshal.PtrToStringAuto(pDevicePathName);
Marshal.FreeHGlobal(detailDataBuffer);
/* Request Communications Handle */
deviceHandle = CreateFile
(devicePathName,
(GENERIC_WRITE | GENERIC_READ),
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
0);
/* Get Vendor ID and Product ID */
DeviceAttributes.Size = Marshal.SizeOf(DeviceAttributes);
success = HidD_GetAttributes(deviceHandle, ref DeviceAttributes);
// Compare Vendor ID and Product ID.
if ((DeviceAttributes.VendorID == myVendorID) && (DeviceAttributes.ProductID == myProductID))
{
MessageBoxResult res = System.Windows.MessageBox.Show("Device Found", "K-IX", MessageBoxButton.OK);
deviceFound = true;
/* Get pointer to capabilities */
success = HidD_GetPreparsedData(deviceHandle, ref preparsedData);
/* Get Device Capabilities */
Int32 result = 0;
result = HidP_GetCaps(preparsedData, ref Capabilities);
return;
}
else
{
// Not my device
memberIndex++;
deviceHandle.Close();
if (memberIndex == 128)
{ break; }
}
} while (!deviceFound);
}
And this is the code I am using to try to send to the device:
private void SendUSB()
{
Int32 numberOfBytesWritten = 0;
Byte[] outputReportBuffer = null;
Boolean success;
Boolean success2;
// Set size of the Output report buffer.
Array.Resize(ref outputReportBuffer, Capabilities.InputReportByteLength);
// Store Report ID in first byte of header.
outputReportBuffer[0] = 0;
// Store report data following the Report ID.
outputReportBuffer[1] = 0x01;
//outputReportBuffer[2] = 0x02;
// outputReportBuffer[3] = 0x03;
// Send Report
success = WriteFile
(deviceHandle,
outputReportBuffer,
outputReportBuffer.Length,
ref numberOfBytesWritten,
IntPtr.Zero);
if (!success)
{
Int32 lastError = Marshal.GetLastWin32Error();
}
success2 = HidD_FreePreparsedData(preparsedData);
}
I have verified that my report length expected for the device is 2, but am not sure where to go from here as USB and HID programming are new for me.
You specified FILE_FLAG_OVERLAPPED in the CreateFile() call but pass a null for the lpOverlapped argument in the WriteFile() call. That's not legal.
Remove the FILE_FLAG_OVERLAPPED option.
I have a situation where I want to perform special processing on some Windows shell special folders (those corresponding to values in the CSIDL enum.) (My solution must be WinXP compatible.) The problem I'm having is that when I encounter IShellFolders as I work my way down the heirarchy, I cannot find a way to match up the IShellFolders to their CSIDL.
This is my current approach:
Initialize a static one-to-one data structure (csidlToFromFullPIdl) of all CSIDLs to their pIDLs returned by SHGetSpecialFolderLocation.
foreach (CSIDL csidl in Enum.GetValues(typeof(CSIDL))
{
IntPtr fullPIdl = IntPtr.Zero;
int hResult = ShellApi.SHGetSpecialFolderLocation(IntPtr.Zero, csidl, ref fullPIdl);
if (hResult != 0)
Marshal.ThrowExceptionForHR(hResult);
csidlToFromFullPIdl.Add(csidl, fullPIdl);
}
Start the heirarchy with the Desktop IShellFolder:
int hResult = ShellApi.SHGetDesktopFolder(ref _shellFolder);
hResult = ShellApi.SHGetSpecialFolderLocation(IntPtr.Zero, CSIDL.CSIDL_DESKTOP, ref _fullPIdl);
Retrieve children like so:
hResult = _shellFolder.EnumObjects(IntPtr.Zero, SHCONTF.SHCONTF_FOLDERS, out pEnum);
// Then repeatedly call:
pEnum.Next(1, out childRelativePIdl, out numberGotten);
Construct new fully-qualified pIDLs for the children like so:
_fullPIdl = ShellApi.ILCombine(parentFullPIdl, childRelativePIdl);
(Finally, retrieve the IShellFolder for the child using:)
hResultUint = parentShellItem.ShellFolder.BindToObject(childRelativePIdl, IntPtr.Zero, ShellApi.IID_IShellFolder, out _shellFolder);
The problem I'm having is that neither the childRelativePIdl nor the _fullPIdl correspond to any pIDLs in csidlToFromFullPIdl.
TIA.
FYI on Vista machines the GUID corresponding to KNOWNFOLDERIDs may be a solution (but not for me as I must be WinXP compatible.)
I should also say that I think using the paths of the special folders (via SHGetSpecialFolderPath) is insufficient because several of the special folders in which I'm interested do not have paths. (E.g., CSIDL_DRIVES and CSIDL_NETWORK.)
I'm trying two approaches to this. The first is to use SHGetDataFromIDList to retrieve the Clsid, which I can then compare to known Clsids:
public static Guid GetClsidFromFullPIdl(IntPtr fullPIdl)
{
// Get both parent's IShellFolder and pIDL relative to parent from full pIDL
IntPtr pParentShellFolder;
IntPtr relativePIdl = IntPtr.Zero;
int hResultInt = ShellApi.SHBindToParent(fullPIdl, ShellGuids.IID_IShellFolder, out pParentShellFolder, ref relativePIdl);
if (hResultInt != (int)HRESULT.S_OK)
Marshal.ThrowExceptionForHR(hResultInt);
object parentShellFolderObj = System.Runtime.InteropServices.Marshal.GetTypedObjectForIUnknown(
pParentShellFolder, typeof(IShellFolder));
IShellFolder parentShellFolder = (IShellFolder)parentShellFolderObj;
SHDESCRIPTIONID descriptionId = new SHDESCRIPTIONID();
IntPtr pDescriptionId = MarshalToPointer(descriptionId);
// Next line returns hResult corresponding to NotImplementedException
hResultInt = ShellApi.SHGetDataFromIDList(parentShellFolder, ref relativePIdl, SHGDFIL.SHGDFIL_DESCRIPTIONID, pDescriptionId,
Marshal.SizeOf(typeof(SHDESCRIPTIONID)));
if (hResultInt != (int)HRESULT.S_OK)
Marshal.ThrowExceptionForHR(hResultInt);
if (parentShellFolder != null)
Marshal.ReleaseComObject(parentShellFolder);
return descriptionId.Clsid;
}
static IntPtr MarshalToPointer(object data)
{
IntPtr pData = Marshal.AllocHGlobal(Marshal.SizeOf(data));
Marshal.StructureToPtr(data, pData, false);
return pData;
}
The problem with this approach is that the call to SHGetDataFromIDList returns an hResult that corresponds to throwing a NotImplementedException. Does this mean that SHGetDataFromIDList is unavailable on my system? (WinXP SP3.)
My second approach is to compare the item identifier lists referenced by two pointers to item identifier lists and see if they are equal. I'm implementing a technique coded here in C. This is what I have so far:
Per Raymond Chen: ITEMIDLISTs can be equivalent without being byte-for-byte identical. Use IShellFolder::CompareIDs to test equivalence.
static bool pIdlsAreEquivalent(IntPtr pIdl1, IntPtr pIdl2)
{
if (pIdl1 == pIdl2) return true;
if (pIdl1 == IntPtr.Zero || pIdl2 == IntPtr.Zero) return false;
int pIdl1Size = GetItemIDSize(pIdl1);
if (pIdl1Size != GetItemIDSize(pIdl2)) return false;
byte[] byteArray1 = new byte[pIdl1Size];
byte[] byteArray2 = new byte[pIdl1Size];
Marshal.Copy(pIdl1, byteArray1, 0, pIdl1Size);
Marshal.Copy(pIdl2, byteArray2, 0, pIdl1Size);
for (int i = 0; i < pIdl1Size; i++)
{
if (byteArray1[i] != byteArray2[i])
return false;
}
return true;
}
static int GetItemIdSize(IntPtr pIdl)
{
if (pIdl == IntPtr.Zero) return 0;
int size = 0;
// Next line throws AccessViolationException
ITEMIDLIST idl = (ITEMIDLIST)Marshal.PtrToStructure(pIdl, typeof(ITEMIDLIST));
while (idl.mkid.cb > 0)
{
size += idl.mkid.cb;
pIdl = (IntPtr)((int)pIdl + idl.mkid.cb);
idl = (ITEMIDLIST)Marshal.PtrToStructure(pIdl, typeof(ITEMIDLIST));
}
return size;
}
public struct ITEMIDLIST
{
public SHITEMID mkid;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SHITEMID
{
public ushort cb; // The size of identifier, in bytes, including cb itself.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] abID; // A variable-length item identifier.
}