getElementById on element within an iframe - c#

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.

Related

SCardEstablishContext memory leak

We suddenly have problems with the smart card api on some windows installations.
There seem to be a memory leak while calling the SCardEstablishContext function.
The problem can be reproduced in a console application with the code sample available at
http://www.pinvoke.net/default.aspx/winscard.scardestablishcontext
class Program
{
#region Win32
// WinSCard APIs to be imported.
[DllImport("WinScard.dll")]
static extern int SCardEstablishContext(uint dwScope,
IntPtr notUsed1,
IntPtr notUsed2,
out IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardReleaseContext(IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardConnect(IntPtr hContext,
string cReaderName,
uint dwShareMode,
uint dwPrefProtocol,
ref IntPtr phCard,
ref IntPtr ActiveProtocol);
[DllImport("WinScard.dll")]
static extern int SCardDisconnect(IntPtr hCard, int Disposition);
[DllImport("WinScard.dll", EntryPoint = "SCardListReadersA", CharSet = CharSet.Ansi)]
static extern int SCardListReaders(
IntPtr hContext,
byte[] mszGroups,
byte[] mszReaders,
ref UInt32 pcchReaders);
#endregion
static void Main(string[] args)
{
while (true)
{
SmartCardInserted();
System.Threading.Thread.Sleep(10);
}
}
internal static bool SmartCardInserted()
{
bool cardInserted = false;
IntPtr hContext = IntPtr.Zero;
try
{
List<string> readersList = new List<string>();
int ret = 0;
uint pcchReaders = 0;
int nullindex = -1;
char nullchar = (char)0;
// Establish context.
ret = SCardEstablishContext(2, IntPtr.Zero, IntPtr.Zero, out hContext);
// First call with 3rd parameter set to null gets readers buffer length.
ret = SCardListReaders(hContext, null, null, ref pcchReaders);
byte[] mszReaders = new byte[pcchReaders];
// Fill readers buffer with second call.
ret = SCardListReaders(hContext, null, mszReaders, ref pcchReaders);
// Populate List with readers.
ASCIIEncoding ascii = new ASCIIEncoding();
string currbuff = ascii.GetString(mszReaders);
int len = (int)pcchReaders;
if (len > 0)
{
while (currbuff[0] != nullchar)
{
nullindex = currbuff.IndexOf(nullchar); // Get null end character.
string reader = currbuff.Substring(0, nullindex);
readersList.Add(reader);
len = len - (reader.Length + 1);
currbuff = currbuff.Substring(nullindex + 1, len);
}
}
// We have list of readers, check for cards.
IntPtr phCard = IntPtr.Zero;
IntPtr ActiveProtocol = IntPtr.Zero;
int result = 0;
foreach (string readerName in readersList)
{
try
{
result = SCardConnect(hContext, readerName, 2, 3, ref phCard, ref ActiveProtocol);
if (result == 0)
{
cardInserted = true;
break;
}
}
finally
{
SCardDisconnect(phCard, 0);
}
}
}
finally
{
SCardReleaseContext(hContext);
}
return cardInserted;
}
}
To test, we call the method SmartCardInserted() in an infinite loop with a small delay => the memory grows constantly and new hadles are allocated.
We see this problem on systems runing Windows 10 or Windows Server 2012, but not on Windows Server 2008.
Any ideas are greatly appreciated!
The problem seems to have been released with v1709 of Windows 10. The shortest amount of code to reproduce the bug is
while(true) {
ret = SCardEstablishContext(2, IntPtr.Zero, IntPtr.Zero, out hContext);
SCardReleaseContext(hContext);
}
It leaks ~264 bytes of memory each time a context is established and released.
If you maintain hContext outside of the loop and only create a context if it's IntPtr.Zero you should be able to avoid the leak. Then when you call SCardListReaders, check to see if you get SCARD_E_INVALID_HANDLE back and invalidate your hContext.
class Program
{
#region Win32
// WinSCard APIs to be imported.
[DllImport("WinScard.dll")]
static extern int SCardEstablishContext(uint dwScope,
IntPtr notUsed1,
IntPtr notUsed2,
out IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardReleaseContext(IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardConnect(IntPtr hContext,
string cReaderName,
uint dwShareMode,
uint dwPrefProtocol,
ref IntPtr phCard,
ref IntPtr ActiveProtocol);
[DllImport("WinScard.dll")]
static extern int SCardDisconnect(IntPtr hCard, int Disposition);
[DllImport("WinScard.dll", EntryPoint = "SCardListReadersA", CharSet = CharSet.Ansi)]
static extern int SCardListReaders(
IntPtr hContext,
byte[] mszGroups,
byte[] mszReaders,
ref UInt32 pcchReaders);
#endregion
static void Main(string[] args)
{
IntPtr hContext = IntPtr.Zero;
while (true)
{
SmartCardInserted(hContext);
System.Threading.Thread.Sleep(10);
}
SCardReleaseContext(hContext);
}
internal static bool SmartCardInserted(IntPtr hContext)
{
bool cardInserted = false;
try
{
List<string> readersList = new List<string>();
int ret = 0;
uint pcchReaders = 0;
int nullindex = -1;
char nullchar = (char)0;
// Establish context.
if(hContext == IntPtr.Zero)
ret = SCardEstablishContext(2, IntPtr.Zero, IntPtr.Zero, out hContext);
// First call with 3rd parameter set to null gets readers buffer length.
ret = SCardListReaders(hContext, null, null, ref pcchReaders);
if(ret == 0x80100003) // SCARD_E_INVALID_HANDLE = 0x80100003, // The supplied handle was invalid
{
try
{
SCardReleaseContext(hContext);
}
catch {}
finally
{
hContext = IntPtr.Zero;
}
return false;
}
byte[] mszReaders = new byte[pcchReaders];
// Fill readers buffer with second call.
ret = SCardListReaders(hContext, null, mszReaders, ref pcchReaders);
// Populate List with readers.
ASCIIEncoding ascii = new ASCIIEncoding();
string currbuff = ascii.GetString(mszReaders);
int len = (int)pcchReaders;
if (len > 0)
{
while (currbuff[0] != nullchar)
{
nullindex = currbuff.IndexOf(nullchar); // Get null end character.
string reader = currbuff.Substring(0, nullindex);
readersList.Add(reader);
len = len - (reader.Length + 1);
currbuff = currbuff.Substring(nullindex + 1, len);
}
}
// We have list of readers, check for cards.
IntPtr phCard = IntPtr.Zero;
IntPtr ActiveProtocol = IntPtr.Zero;
int result = 0;
foreach (string readerName in readersList)
{
try
{
result = SCardConnect(hContext, readerName, 2, 3, ref phCard, ref ActiveProtocol);
if (result == 0)
{
cardInserted = true;
break;
}
}
finally
{
SCardDisconnect(phCard, 0);
}
}
}
return cardInserted;
}
}
It's a workaround until the Winscard.dll API is fixed.

C# Interface to DLL and ExecutionEngineException

Newbie to C#. I have written a WDF driver and DLL that work. I am creating an application in C# to access the hardware through the DLL. There is a specific function that is causing an ExecutionEngineException soon after it is first called. Here is the function definition from the DLL:
DECLDIR int ReadDatagram(int channel, unsigned long *msgID, unsigned int *msgType, int *msgLen, unsigned int *data);
In my C# application code, I import this function with the following lines:
[DllImport("pcmcanDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
internal static extern int ReadDatagram(int channel, ref uint msgID, ref uint msgType, ref int msgLen, uint[] data);
When I start the application and open a channel, this function is periodically called by a timer. After a short indefinite time, I get the following exception message. If I comment out he call of this function, the application never has an issue.
Mesage: An unhandled exception of type 'System.ExecutionEngineException' occurred in mscorlib.dll
My application code is here. I believe I am handling the pointer arguments correctly because occasionally this will work a few times and the data is good in those ceases. Appreciate any insights.
private void rcvTimer_Tick(object sender, EventArgs e)
{
int channel = 1;
String dsplyString = "Packet Received\n";
uint msgID = 0, msgType = 0;
int msgLen = 0;
uint[] data = new uint[8];
ErrorTypes dllReturn = ErrorTypes.RCV_BUFFER_EMPTY;
do
{
dllReturn = (ErrorTypes)NativeMethods.ReadDatagram(channel, ref msgID, ref msgType, ref msgLen, data);
if (dllReturn != ErrorTypes.SUCCESS && dllReturn != ErrorTypes.RCV_BUFFER_EMPTY)
{
MessageBox.Show("Error receiving packet.", "Receipt Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
else if (dllReturn == ErrorTypes.SUCCESS)
{
dsplyString = String.Format("{0} {1} {2} {3}\n", channel, msgID, msgType, msgLen);
}
} while (dllReturn != ErrorTypes.RCV_BUFFER_EMPTY);
}
Try following
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
}
}
public enum ErrorTypes : int
{
RCV_BUFFER_EMPTY = 0,
SUCCESS = 1
}
public class Test
{
[DllImport("pcmcanDLL.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
internal static extern int ReadDatagram(int channel, ref uint msgID, IntPtr msgType, ref int msgLen, IntPtr dataPtr);
private void rcvTimer_Tick(object sender, EventArgs e)
{
int channel = 1;
String dsplyString = "Packet Received\n";
uint msgID = 0;
uint msgType = 0;
int msgLen = 0;
uint[] data = new uint[8];
ErrorTypes dllReturn = ErrorTypes.RCV_BUFFER_EMPTY;
IntPtr dataPtr = Marshal.AllocHGlobal(Marshal.SizeOf(data));
IntPtr msgTypePtr = Marshal.AllocHGlobal(Marshal.SizeOf(msgType));
do
{
Marshal.StructureToPtr(msgType, msgTypePtr, true);
Marshal.StructureToPtr(data, dataPtr, true);
dllReturn = (ErrorTypes)ReadDatagram(channel, ref msgID, msgTypePtr, ref msgLen, dataPtr);
if (dllReturn != ErrorTypes.SUCCESS && dllReturn != ErrorTypes.RCV_BUFFER_EMPTY)
{
MessageBox.Show("Error receiving packet.", "Receipt Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
else if (dllReturn == ErrorTypes.SUCCESS)
{
dsplyString = String.Format("{0} {1} {2} {3}\n", channel, msgID, msgType, msgLen);
}
} while (dllReturn != ErrorTypes.RCV_BUFFER_EMPTY);
Marshal.FreeHGlobal(dataPtr);
Marshal.FreeHGlobal(msgTypePtr);
}
}
}

Call to IExtractImage.GetLocation() is failing with NullReference Exception

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.

Programmatically reading callable dll functions

My project needs to inspect .c and .dll files. it combines those informations to determine what it is supposed to call and then calls that.
I need to inspect the dlls to find which dll has which function. I have gotten so far as to map the dll to memory without initialising it. now i need to map the header to something so i can read out the section that has the callable names in it.
how can i do that? this is the code so far:
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
public static string[] GetFKTNames(string dll)
{
IntPtr lib = LoadLibraryEx(dll, IntPtr.Zero, LoadLibraryFlags.DONT_RESOLVE_DLL_REFERENCES);
//INDICATES WHAT I WANT TO DO, BUT DOES NOT WORk
//Header header = GetHeader(lib);
//Unload(lib);
//return header.Names;
}
EDIT #2:
i made a little bit of progress and quit it for today... there are 4 free days coming up here in germany...
i am not entirely sure if that marshalling is correct - i had no way to test it. I would love to read a book on that topic - so please comment if you know a good book that explains how that headerstuff works and what different headers are out there.
private static List<string> ListDLLFunctions(string sADllName)
{
List<string> names = new List<string>();
IntPtr LoadedImage = LoadLibraryEx(sADllName, IntPtr.Zero, LoadLibraryFlags.DONT_RESOLVE_DLL_REFERENCES);
IMAGE_NT_HEADERS header = (IMAGE_NT_HEADERS) Marshal.PtrToStructure(libPtr, typeof(IMAGE_NT_HEADERS));
// ImageExportDirectory = (_IMAGE_EXPORT_DIRECTORY*)
// ImageDirectoryEntryToData(LoadedImage.MappedAddress,
// false, IMAGE_DIRECTORY_ENTRY_EXPORT, &cDirSize);
// if (ImageExportDirectory != NULL)
// {
// dNameRVAs = (DWORD *)ImageRvaToVa(LoadedImage.FileHeader,
// LoadedImage.MappedAddress,
// ImageExportDirectory->AddressOfNames, NULL);
// for(size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++)
// {
// sName = (char *)ImageRvaToVa(LoadedImage.FileHeader,
// LoadedImage.MappedAddress,
// dNameRVAs[i], NULL);
// slListOfDllFunctions.push_back(sName);
// }
// }
FreeLibrary(LoadedImage);
return names;
}
static void Main(string[] args)
{
List<string> names = ListDLLFunctions("KERNEL32.DLL");
}
There isn't a "one call" function that can do it for you.
You have to load the dll and look for the names in the export table.
There is a post with some code to do it in c++ - maybe you can try to port it to c#
Non from the code, but from console I usually use
DumpBin utility to see Dll's exports.
I think you can use that tool with Process.Start(..) and parse its output in order to gain an information you need.
Hope this helps.
I solved the problem for my usecase:
I load the lib into momory, copy it into a bytearray and then investigate it using PE information.
There are a couple of useful ressources for this topic that helped me a lot:
c# import examples for relevant structures
msdn article on PE format
zotteljedi article on PE format
nunu brito article on PE format
simon cooper article on PE format
joachim bauch article on loading dll
In retrospec i get how i could have used the other approaches to get my information. The Class that i have written is great to learn PE and explore some dlls, i recommand rewriting it if you want to get the grip of how the PE works.
Here is the Class:
public class DLLHelper
{
private byte[] dllInMemory;
private UInt32 PESizeOfImage;
private UInt32 VA_PE;
[DllImport("kernel32.dll")]
static public extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
[DllImport("kernel32.dll")]
static public extern bool FreeLibrary(IntPtr hModule);
public enum LoadLibraryFlags : uint
{
DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008
}
public DLLHelper(string dllpath)
{
PESizeOfImage = GetDllSizeInMemory(dllpath);
dllInMemory = GetDLLCopy(dllpath, PESizeOfImage);
UInt32 VA_p_PE = 0x3C;
VA_PE = Get4ByteFromLocation(VA_p_PE, dllInMemory);
}
private byte[] GetDLLCopy(string dllpath, uint PESizeOfImage)
{
IntPtr libPtr = LoadLibraryEx(dllpath, IntPtr.Zero, LoadLibraryFlags.DONT_RESOLVE_DLL_REFERENCES);
byte[] dllInMemory = new byte[PESizeOfImage];
Marshal.Copy(libPtr, dllInMemory, 0, (int)PESizeOfImage);
FreeLibrary(libPtr);
return dllInMemory;
}
private UInt32 GetDllSizeInMemory(string dllpath)
{
byte[] dllpreload = File.ReadAllBytes(dllpath);
UInt32 pp_PE = 0x3C;
UInt32 p_PE = Get4ByteFromLocation(pp_PE, dllpreload);
UInt32 p_PEOPTIONALHEADER = p_PE + 0x18;
UInt32 p_PESizeOfImage = p_PEOPTIONALHEADER + 0x38;
return Get4ByteFromLocation(p_PESizeOfImage, dllpreload);
}
public void DumpToFile(String filename)
{
File.WriteAllBytes(filename, dllInMemory);
}
public string GetDLLName()
{
UInt32 VAExport = GetVAExport(VA_PE, dllInMemory);
UInt32 VAName = GetVAName(VAExport, dllInMemory);
String Name = GetString(VAName, dllInMemory);
return Name;
}
public List<String> GetFunctionNames()
{
List<String> fkts = new List<String>();
UInt32 VAExport = GetVAExport(VA_PE, dllInMemory);
UInt32 VA_p_firstFKT = GetVA_p_firstFKT(VAExport, dllInMemory);
UInt32 VA_p_lastFKT = GetVA_p_lastFKT(VAExport, dllInMemory);
for (UInt32 VA_p_fkt = VA_p_firstFKT; VA_p_fkt <= VA_p_lastFKT; VA_p_fkt += sizeof(UInt32))
{
UInt32 VA_fkt = Get4ByteFromLocation(VA_p_fkt, dllInMemory);
fkts.Add(GetString(VA_fkt, dllInMemory));
}
return fkts;
}
private UInt32 GetVA_p_lastFKT(UInt32 VAExport, byte[] dllInMemory)
{
UInt32 first = GetVA_p_firstFKT(VAExport, dllInMemory);
UInt32 count = GetfktCount(VAExport, dllInMemory);
UInt32 last = first + (count - 1) * sizeof(UInt32);
return last;
}
private UInt32 GetfktCount(UInt32 VAExport, byte[] dllInMemory)
{
UInt32 RVA_Count = 0x14;
UInt32 VA_Count = VAExport + RVA_Count;
return Get4ByteFromLocation(VA_Count, dllInMemory);
}
private UInt32 GetVA_p_firstFKT(UInt32 VAExport, byte[] dllInMemory)
{
UInt32 RVA_p_FIRST = 0x20;
UInt32 VA_p_FIRST = VAExport + RVA_p_FIRST;
return Get4ByteFromLocation(VA_p_FIRST, dllInMemory);
}
private UInt32 GetVAName(UInt32 VAExport, byte[] dllInMemory)
{
UInt32 RVA_p_NAME = 0x0C;
UInt32 VA_p_NAME = VAExport + RVA_p_NAME;
return Get4ByteFromLocation(VA_p_NAME, dllInMemory);
}
private UInt32 GetVAExport(UInt32 VAPE, byte[] dllInMemory)
{
UInt32 RVA_p_EXPORT = 0x78;
UInt32 VA_p_EXPORT = VAPE + RVA_p_EXPORT;
return Get4ByteFromLocation(VA_p_EXPORT, dllInMemory);
}
string GetString(UInt32 location, byte[] dll)
{
int length = 0;
while (dll[location + length] != 0x00)
{
length++;
}
if (location > int.MaxValue) throw new Exception("uncastable");
return Encoding.UTF8.GetString(dll, (int)location, length);
}
private UInt32 Get4ByteFromLocation(UInt32 location, byte[] dll)
{
if (!(BitConverter.IsLittleEndian))
{
byte[] partial = GetByteSubset(4, location, dll);
Array.Reverse(partial);
return BitConverter.ToUInt32(partial, 0);
}
return BitConverter.ToUInt32(dll, (int)location);
}
private byte[] GetByteSubset(int size, UInt32 location, byte[] dll)
{
byte[] val = new byte[size];
for (int i = 0; i < size; i++)
{
val[i] = dll[location + i];
}
return val;
}
}

Recognize Windows Shell Special Folder (i.e. get its CSIDL) via its pIDL (Now determine if pIDLs are equal with C#)

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.
}

Categories