How to take the snapshot of a IE webpage through a BHO (C#) - bho

I am trying to build an IE BHO in C# for taking the snapshot of a webpage loaded in the IE browser. Here is what I'm trying to do:
public class ShowToolbarBHO : BandObjectLib.IObjectWithSite
{
IWebBrowser2 webBrowser = null;
public void SetSite (Object site)
{
.......
if (site != null)
{
......
webBrowser = (IWebBrowser2)site;
......
}
}
}
Also, I p/invoke the following COM methods:
[Guid("0000010D-0000-0000-C000-000000000046")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
[ComImportAttribute()]
public interface IViewObject
{
void Draw([MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd, IntPtr hdcTargetDev, IntPtr hdcDraw, [MarshalAs(UnmanagedType.LPStruct)] ref COMRECT lprcBounds, [In] IntPtr lprcWBounds, IntPtr pfnContinue, int dwContinue);
int GetColorSet([MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd, IntPtr hicTargetDev, [Out] IntPtr ppColorSet);
int Freeze([MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, out IntPtr pdwFreeze);
int Unfreeze([MarshalAs(UnmanagedType.U4)] int dwFreeze);
int SetAdvise([MarshalAs(UnmanagedType.U4)] int aspects, [MarshalAs(UnmanagedType.U4)] int advf, [MarshalAs(UnmanagedType.Interface)] IAdviseSink pAdvSink);
void GetAdvise([MarshalAs(UnmanagedType.LPArray)] out int[] paspects, [MarshalAs(UnmanagedType.LPArray)] out int[] advf, [MarshalAs(UnmanagedType.LPArray)] out IAdviseSink[] pAdvSink);
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public class COMRECT
{
public int left;
public int top;
public int right;
public int bottom;
public COMRECT()
{
}
public COMRECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
}
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisibleAttribute(true)]
[GuidAttribute("0000010F-0000-0000-C000-000000000046")]
[ComImportAttribute()]
public interface IAdviseSink
{
void OnDataChange([In]IntPtr pFormatetc, [In]IntPtr pStgmed);
void OnViewChange([MarshalAs(UnmanagedType.U4)] int dwAspect, [MarshalAs(UnmanagedType.I4)] int lindex);
void OnRename([MarshalAs(UnmanagedType.Interface)] object pmk);
void OnSave();
void OnClose();
}
Now When I take the snapshot:
I make a call CaptureWebScreenImage((IHTMLDocument2) webBrowser.document);
public static Image CaptureWebScreenImage(IHTMLDocument2 myDoc) {
int heightsize = (int)getDocumentAttribute(myDoc, "scrollHeight");
int widthsize = (int)getDocumentAttribute(myDoc, "scrollWidth");
Bitmap finalImage = new Bitmap(widthsize, heightsize);
Graphics gFinal = Graphics.FromImage(finalImage);
COMRECT rect = new COMRECT();
rect.left = 0;
rect.top = 0;
rect.right = widthsize;
rect.bottom = heightsize;
IntPtr hDC = gFinal.GetHdc();
IViewObject vO = myDoc as IViewObject;
vO.Draw(1, -1, (IntPtr)0, (IntPtr)0, (IntPtr)0, (IntPtr)hDC, ref rect, (IntPtr)0, (IntPtr)0, 0);
gFinal.ReleaseHdc();
gFinal.Dispose();
return finalImage;
}
I am not getting the image of the webpage. Rather I am getting an image with black background. I am not sure if this is the right way of doing it, but I found over the web that IViewObject::Draw method is used for taking the image of a webpage in IE.
I was earlier doing the image capture using the Native PrintWindow() method as mentioned in the following codeproject's page - http://www.codeproject.com/KB/graphics/IECapture.aspx
But the image size is humongous! I was trying to see if I can reduce the size by using other techniques. It would be great if someone can point out the mistakes (I am sure there would be many) in my code above.
Thanks,
Kapil

You can try to query IHTMLElementRender interface for the document element (which is available through IHTMLDocument3 interface) and use DrawToDC method to render page to a bitmap.

1 IHTMLElementRender::DrawToDC is deprecated, according to Microsoft's documentation. And it does not draw object and canvas, like flash, as far as I can see.
It's better to cal IViewObject::Draw() or ::OleDraw().
2 I dont know how to do it in C#, but I know how to do it in win32 program. In win32 program, you need to call IOleInplaceObject::SetObjectRect to set IWebBrowser's position and size.
Or SetClientSite(), I am sure one of them does the magic.
CComQIPtr oleObject = browser;
CComQIPtr<IOleInPlaceObject> inPlaceObject(mOleObject);
inPlaceObject->SetObjectRects(&rcClient, &rcClient);
oleObject ->SetClientSite(clientSite);
Hope it helps

Related

Hooking NtCreateFile API from ntdll.dll with EasyHook (c#)

This is the first time I try to hook windows API. My goal is to monitor all files that a process is going to create/open/read/write.
In order to be the most verbose possible, I decided to hook the ntdll.dll API such as NtCreateFile() and NtOpenFile(). So, in order to acheive this goal, I went on EasyHook, which seems easy and robust.
I've essetially followed the FileMon example, changing what I really wanted: the Hooked function.
When I try to read information about the file that is going to be opened, I try to read information from the OBJECT_ATTRIBUTES structure, such as
the ObjectName. Those are integer pointers, so I expected to use the function Marshal.PtrToStringAuto(attributes.objectName) in order to get the string value. However, the result is I can only have bad strings, without any meaning. Also, the File access seems to be not working. I guess there's something wrong with this
code, maybe in the DllImport signatures. Be adviced I had to replace SafeHandle with IntPtr, because of EasyHook was complaining about marshaling them.
Can someone help me?
Here's my specific code of the injected DLL:
Here's the Run method code
public void Run(RemoteHooking.IContext InContext, String inChannelName)
{
// First of all, install all the hooks
try
{
// NtCreateFile
fileCreationHook = LocalHook.Create(
LocalHook.GetProcAddress("ntdll.dll", "NtCreateFile"),
new CreateFileDelegate(CreateFile_Hooked),
this
);
fileCreationHook = LocalHook.Create(
LocalHook.GetProcAddress("ntdll.dll", "NtOpenFile"),
new OpenFileDelegate(OpenFile_Hooked),
this
);
fileCreationHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
remoteIf.Log("File creation Hook correctly installed on pid "+RemoteHooking.GetCurrentProcessId());
}
catch (Exception e)
{
remoteIf.Log(e.Message);
remoteIf.Log(e.StackTrace);
return;
}
// Wake up the process
remoteIf.Log("Waiking up process...");
RemoteHooking.WakeUpProcess();
while (true)
{
Thread.Sleep(500);
if (queue.Count > 0)
{
String[] package = null;
lock (queue)
{
package = queue.ToArray();
queue.Clear();
}
remoteIf.OnCreateFile(RemoteHooking.GetCurrentProcessId(), package);
}
else
remoteIf.Ping();
}
}
Here's the contructor code:
public InjectedDLL(RemoteHooking.IContext InContext, String inChannelName)
{
// Create the structure which will contain all the messages
queue = new Stack<string>();
// Initiate the connection to the Injector process, getting back its interface
remoteIf = RemoteHooking.IpcConnectClient<IPCInterface>(inChannelName);
// Try invocating a method to test the connection.
remoteIf.Ping();
}
Here there are the Hook delegate and the hook function
public delegate int CreateFileDelegate(out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
ref long allocSize,
uint fileAttributes,
System.IO.FileShare share,
uint createDisposition,
uint createOptions,
IntPtr eaBuffer,
uint eaLength);
public int CreateFile_Hooked(
out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
ref long allocSize,
uint fileAttributes,
System.IO.FileShare share,
uint createDisposition,
uint createOptions,
IntPtr eaBuffer,
uint eaLength)
{
//string s = Marshal.PtrToStringAuto(objectAttributes.ObjectName);
int res = NtCreateFile(out handle, access,ref objectAttributes,out ioStatus, ref allocSize,fileAttributes, share,createDisposition,createOptions,eaBuffer,eaLength);
return res;
}
Here there are the NtDll.Dll native functions:
[DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true)]
public static extern int NtCreateFile(
out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
ref long allocSize,
uint fileAttributes,
System.IO.FileShare share,
uint createDisposition,
uint createOptions,
IntPtr eaBuffer,
uint eaLength);
[DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true)]
public static extern int NtOpenFile(
out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
System.IO.FileShare share,
uint openOptions
);
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct OBJECT_ATTRIBUTES
{
public Int32 Length;
public IntPtr RootDirectory;
public IntPtr ObjectName;
public uint Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct IO_STATUS_BLOCK
{
public uint status;
public IntPtr information;
}
ObjectName is a pointer to a UNICODE_STRING struct. A managed equivalent looks like this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
[MarshalAs(UnmanagedType.LPWStr)]
public String Buffer;
}
You need to use marshalling to get a managed copy of the struct.
var us = Marshal.PtrToStructure<UNICODE_STRING>(objectAttributes.ObjectName);
Once you have the managed struct you can access the Buffer field to get the name of the object.

How can I get the localized name of a 'special' windows folder (Recycle bin etc.)?

I'm trying to find out the 'correct' windows API for finding out the localized name of 'special' folders, specifically the Recycle Bin. I want to be able to prompt the user with a suitably localized dialog box asking them if they want to send files to the recycle bin or delete them directly.
I've found lots on the internet (and on Stackoverflow) about how to do the actual deletion, and it seems simple enough, I just really want to be able to have the text localized.
Read this article for code samples and usage:
http://www.codeproject.com/KB/winsdk/SpecialFolders.aspx
Also there is an article on MSDN that helps you Identify the Location of Special Folders with API Calls
I actually didn't find the CodeProject article terribly helpful, so I thought I'd answer this question with the actual code that I used to retrieve the localized name of the recycle bin.
This sample also tries to behave correctly with regard to freeing resources. Any comments are welcome, especially if you spot an error with my resource management!
public static string GetLocalizedRecycleBinName()
{
IntPtr relative_pidl, parent_ptr, absolute_pidl;
PInvoke.SHGetFolderLocation(IntPtr.Zero, PInvoke.CSIDL.BitBucket,
IntPtr.Zero, 0, out absolute_pidl);
try
{
PInvoke.SHBindToParent(absolute_pidl,
ref PInvoke.Guids.IID_IShellFolder,
out parent_ptr, out relative_pidl);
PInvoke.IShellFolder shell_folder =
Marshal.GetObjectForIUnknown(parent_ptr)
as PInvoke.IShellFolder;
// Release() for this object is called at finalization
if (shell_folder == null)
return Strings.RecycleBin;
PInvoke.STRRET strret = new PInvoke.STRRET();
StringBuilder sb = new StringBuilder(260);
shell_folder.GetDisplayNameOf(relative_pidl, PInvoke.SHGNO.Normal,
out strret);
PInvoke.StrRetToBuf(ref strret, relative_pidl, sb, 260);
string name = sb.ToString();
return String.IsNullOrEmpty(name) ? Strings.RecycleBin : name;
}
finally { PInvoke.ILFree(absolute_pidl); }
}
static class PInvoke
{
[DllImport("shell32.dll")]
public static extern int SHGetFolderLocation(IntPtr hwndOwner,
CSIDL nFolder, IntPtr hToken, uint dwReserved, out IntPtr ppidl);
[DllImport("shell32.dll")]
public static extern int SHBindToParent(IntPtr lpifq, [In] ref Guid riid,
out IntPtr ppv, out IntPtr pidlLast);
[DllImport("shlwapi.dll")]
public static extern Int32 StrRetToBuf(ref STRRET pstr, IntPtr pidl,
StringBuilder pszBuf, uint cchBuf);
[DllImport("shell32.dll")]
public static extern void ILFree([In] IntPtr pidl);
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214E6-0000-0000-C000-000000000046")]
public interface IShellFolder
{
[PreserveSig]
Int32 CompareIDs(Int32 lParam, IntPtr pidl1, IntPtr pidl2);
void ParseDisplayName(IntPtr hwnd, IntPtr pbc, String pszDisplayName,
UInt32 pchEaten, out IntPtr ppidl, UInt32 pdwAttributes);
void EnumObjects(IntPtr hwnd, int grfFlags,
out IntPtr ppenumIDList);
void BindToObject(IntPtr pidl, IntPtr pbc, [In] ref Guid riid,
out IntPtr ppv);
void BindToStorage(IntPtr pidl, IntPtr pbc, [In] ref Guid riid,
out IntPtr ppv);
void CreateViewObject(IntPtr hwndOwner, [In] ref Guid riid,
out IntPtr ppv);
void GetAttributesOf(UInt32 cidl,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
IntPtr[] apidl, ref uint rgfInOut);
void GetUIObjectOf(IntPtr hwndOwner, UInt32 cidl,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
IntPtr[] apidl, [In] ref Guid riid, UInt32 rgfReserved,
out IntPtr ppv);
void GetDisplayNameOf(IntPtr pidl, SHGNO uFlags, out STRRET pName);
void SetNameOf(IntPtr hwnd, IntPtr pidl, string pszName,
int uFlags, out IntPtr ppidlOut);
}
public enum CSIDL
{
BitBucket = 0x000a,
}
public enum SHGNO
{
Normal = 0x0000, ForParsing = 0x8000,
}
[StructLayout(LayoutKind.Explicit, Size = 520)]
public struct STRRETinternal
{
[FieldOffset(0)] public IntPtr pOleStr;
[FieldOffset(0)] public IntPtr pStr;
[FieldOffset(0)] public uint uOffset;
}
[StructLayout(LayoutKind.Sequential)]
public struct STRRET
{
public uint uType;
public STRRETinternal data;
}
public class Guids
{
public static Guid IID_IShellFolder =
new Guid("{000214E6-0000-0000-C000-000000000046}");
}
}

Removing horizontal scrollbar from list control in report mode [duplicate]

I've got a ListView control in Details mode with a single column. It's on a form that is meant to only be used with the keyboard, mostly with the up/down arrows for scrolling and enter to select. So I don't really need to have the scroll bars and would just like them to not show for a cleaner look. However, when I set the ListView.Scrollable property to false, I can still move the selected item up and down, but as soon as it moves to an item not currently in view, the list won't move to show that item. I've tried using EnsureVisible to programmatically scroll the list, but it does nothing when in this mode.
Is there any way to manually move the list up and down to scroll, but without having the scrollbar present?
It's not easy but it can be done. If you try to hide the scroll bar through ShowScrollBar, the ListView will simply put it back again. So you have to do something more devious.
You will have to intercept the WM_NCCALCSIZE message, and in there, turn off the vertical scroll style. Whenever the listview tries to turn it on again, you will turn it off again in this handler.
public class ListViewWithoutScrollBar : ListView
{
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case 0x83: // WM_NCCALCSIZE
int style = (int)GetWindowLong(this.Handle, GWL_STYLE);
if ((style & WS_VSCROLL) == WS_VSCROLL)
SetWindowLong(this.Handle, GWL_STYLE, style & ~WS_VSCROLL);
base.WndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
const int GWL_STYLE = -16;
const int WS_VSCROLL = 0x00200000;
public static int GetWindowLong(IntPtr hWnd, int nIndex) {
if (IntPtr.Size == 4)
return (int)GetWindowLong32(hWnd, nIndex);
else
return (int)(long)GetWindowLongPtr64(hWnd, nIndex);
}
public static int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong) {
if (IntPtr.Size == 4)
return (int)SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
else
return (int)(long)SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
}
[DllImport("user32.dll", EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)]
public static extern IntPtr GetWindowLong32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", CharSet = CharSet.Auto)]
public static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, int dwNewLong);
}
This will give you a ListView without scroll bars that still scrolls when you use the arrow keys to change selection.
Since ShowScrollBar didn't work, maybe this helps:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
private const int WM_VSCROLL = 0x115;
private const int SB_LINEDOWN = 1;
public Form1()
{
InitializeComponent();
for (int i = 0; i < 100; i++)
listView1.Items.Add("foo" + i);
listView1.Scrollable = false;
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
SendMessage(listView1.Handle, WM_VSCROLL, SB_LINEDOWN, 0);
}
i did something more easy. i left scrollable to true and used a custom slider(colorSlider) that i found on codeproject and i drawed the slider over the position where the vscroller would appear and then used the ensureVisible function.
Call the ShowScrollBar API method.
If ShowScrollBar doesn't work, I'm not sure how to do it.
You could put the ListView in a panel and make the ListView wider than the panel so that the scrollbar is cut off (check SystemInformation.VerticalScrollBarWidth), but that's a horrifyingly ugly hack.
You can use ListView.Scrollable Property. Set it to false and Scroll bars won't appear!

JSFL - Responding to the “Replace Existing Item” dialog

I am adding a new version of a custom component to the fla using jsfl.
This prompts the user to choose whether to replace the existing components or not.
If possible, I'd like to respond to this prompt through jsfl.
Thanks.
I would write a program (with a delay) to confirm the 'Resolve Library Conflict'-dialog and call it with FLfile.runCommandLine before calling fl.componentsPanel.addItemToDocument.
I know how to do this in Windows but not on a Mac.
(In windows the program to confirm the replacement could be:
See Microsoft - Dev Center - Desktop > Samples > Enumerate top level windows fast in C#
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace FlashHack
{
class Program
{
protected delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
protected static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
[DllImport("user32.dll")]
protected static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
internal static extern IntPtr GetParent(IntPtr hwnd);
[DllImport("User32.dll", EntryPoint = "SetForegroundWindow")]
private static extern IntPtr SetForegroundWindowNative(IntPtr hWnd);
protected static bool AnswerResolveLibraryConflictDlg(IntPtr hWnd, IntPtr lParam)
{
int size = GetWindowTextLength(hWnd);
if (size++ > 0 && IsWindowVisible(hWnd))
{
StringBuilder sb = new StringBuilder(size);
GetWindowText(hWnd, sb, size);
if (sb.ToString() == "Resolve Library Conflict")
{
IntPtr parent = GetParent(hWnd);
Console.WriteLine(sb.ToString());
if (parent != null)
{
SetForegroundWindowNative(parent);
SendKeys.SendWait("{TAB} {ENTER}");
}
}
}
return true;
}
static void Main(string[] args)
{
System.Threading.Thread.Sleep(1000); // Must wait for the 'Resolve Library Conflict'-dlg
Console.WriteLine("Now!");
EnumWindows(new EnumWindowsProc(AnswerResolveLibraryConflictDlg), IntPtr.Zero);
}
}
}
And in jsfl this program could be started with:
FLfile.runCommandLine("start ConfirmReplace.exe");
fl.componentsPanel.addItemToDocument( ... );
)

get all handles

How can I get all handles of a window via Win32? I found this code but I don't know how is it work!
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
private List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}

Resources