I am trying to make an external map for a computer game.
Therefore I have made a Forms Application with a picture box, that contains my map image.
Now I want to draw little squares onto the map using GDI. I allready got that working using Graphics.DrawRectangle.
Now I want to update the position of the rectangle every 0.2s.
How do I do that?
My current source (i wnt to replace the button with an auto-update):
public partial class Form1 : Form
{
//choords local player
int localX;
int localY;
int running;
const int Basex = 0x05303898;
const int Basey = 0x05303894;
const string Game = "ac_client";
//map drawing
Pen aPen = new Pen(Color.Black);
Graphics localp;
//choords enemy
//permission to read process memory
const int PROCESS_VM_READ = 0x0010; //needed for reading memory
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(int hProcess,
int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
if (Process.GetProcessesByName(Game).Length > 0)
{
Process process = Process.GetProcessesByName(Game)[0];
IntPtr procHandle = OpenProcess(PROCESS_VM_READ, false, process.Id);
int bytesRead = 0;
byte[] buffer = new byte[24]; //'Hello World!' takes 12*2 bytes because of Unicode
// 0x0046A3B8 is the address where I found the string, replace it with what you found
ReadProcessMemory((int)procHandle, Basex, buffer, buffer.Length, ref bytesRead);
localX = BitConverter.ToInt32(buffer, 0);
LBlocalx.Text = Convert.ToString(Math.Ceiling(Convert.ToDecimal(localX)));
ReadProcessMemory((int)procHandle, Basey, buffer, buffer.Length, ref bytesRead);
localY = BitConverter.ToInt32(buffer, 0);
LBlocaly.Text = Convert.ToString(Math.Ceiling(Convert.ToDecimal(localY)));
localp = pictureBox1.CreateGraphics();
localp.DrawRectangle(aPen, (Convert.ToInt32(Convert.ToString(Math.Ceiling(Convert.ToDecimal(localX))))/1000), (Convert.ToInt32(Convert.ToString(Math.Ceiling(Convert.ToDecimal(localY))))/1000), 10, 10);
}
else
{
MessageBox.Show("Error! Process not running.");
}
}
How about you store two time variables(DateTime) one that has the time when you started checking for that 2 second difference, another with current time and on the beginning of every iteration you verify if the difference of both times is 2 seconds. Remember, the first variable is the one that has the time when the difference was 2 seconds or when you first started checking for that difference.
You could also use the Timer class and set a timer that on ever 2secs do something.
Timer class reference:
https://msdn.microsoft.com/en-us/library/system.timers.timer%28v=vs.110%29.aspx
Related
what i need to do is that I need to control another application installed on the same machine using my custom application. For example if i needed to use the standard windows calculator I would simply send the input events to the calculator. I have used some code snippets to make this possible and I have now triggered both mouse and keyboard events. but the problem is that i can be sure that the keyboard event will hit the target application because it has the process handle. but i cannot be sure about the mouse. and also if the target application goes into background, i cannot initiate mouse clicks on it. I need help to find a way to make sure that the mouse click is done on the application only.
I need to send mouse co-ordinates and click as well. for example "sendMouseClick("Notepad", 100, 400); which will send a click to Notepad, even though it stays minimized.
IMPORTANT NOTE
A similar question is answered previously but that is in reference to first finding the state of the other application and then sending the inputs either keyboard or mouse, what i need to do is to send an application a set of instructions that must work whether the application is in foreground or not.
For "The other Guys":: if you dont want to help or cant help, thats okay but please do know that i havent stolen the question or anything. I simply want to achieve this task in C#.
The code I have to simulate keyboard key Press is:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;
namespace SimulateKeyPress
{
partial class Form1 : Form
{
private Button button1 = new Button();
private Button button2 = new Button();
private Button button3 = new Button();
[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
public Form1()
{
button1.Location = new Point(10, 10);
button1.TabIndex = 1;
button1.Text = "Click to automate Calculator";
button1.AutoSize = true;
button1.Click += new EventHandler(button1_Click);
button2.Location = new Point(150, 140);
button2.TabIndex = 0;
button2.Text = "Click to Exit Calculator";
button2.AutoSize = true;
button2.Location = new Point(80, 80);
button2.TabIndex = 2;
button2.Text = "Click to Run Calculator";
button2.AutoSize = true;
button2.Click += new EventHandler(button2_Click);
this.DoubleClick += new EventHandler(Form1_DoubleClick);
this.Controls.Add(button1);
this.Controls.Add(button2);
// this.Controls.Add(button3);
}
// Get a handle to an application window.
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,
string lpWindowName);
// Activate an application window.
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
// Send a series of key presses to the Calculator application.
private void button1_Click(object sender, EventArgs e)
{
// Get a handle to the Calculator application. The window class
// and window name were obtained using the Spy++ tool.
IntPtr calculatorHandle = FindWindow("CalcFrame", "Calculator");
//Process firstProc = new Process();
//firstProc.StartInfo.FileName = "calc.exe";
//firstProc.EnableRaisingEvents = true;
//firstProc.Start();
// Verify that Calculator is a running process.
if (calculatorHandle == IntPtr.Zero)
{
MessageBox.Show("Calculator is not running.");
return;
}
// Make Calculator the foreground application and send it
// a set of calculations.
SetForegroundWindow(calculatorHandle);
SendKeys.SendWait("1024");
SendKeys.SendWait("*");
SendKeys.SendWait("32");
SendKeys.SendWait("=");
}
private void button2_Click(object sender, EventArgs e)
{
System.Diagnostics.Process.Start("calc.exe");
}
private void button3_Click(object sender,EventArgs e)
{
Process [] proc =Process.GetProcessesByName("Calculator");
proc[0].Kill();
}
// Send a key to the button when the user double-clicks anywhere
// on the form.
private void Form1_DoubleClick(object sender, EventArgs e)
{
// Send the enter key to the button, which raises the click
// event for the button. This works because the tab stop of
// the button is 0.
SendKeys.Send("{ENTER}");
}
}
}
the previous help on stack overflow, msdn and other sites provides the code to simulate a mouse click in the same application. But i need to send mouse hits to another application.
Maybe this could help you
Code
The task
Getting the mouse's current position
Sending the mouse event
Windows forms
...
using System.Runtime.InteropServices;
namespace yournamespace
{
public partial class yourclassname
{
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
int X = Cursor.Position.X;
int Y = Cursor.Position.Y;
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, X, Y, 0, 0);
}
}
WPF
Things are a bit harder in WPF
double mousePointX;
double mousePointY;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out POINT lpPoint);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
private void WritePoint(object sender, RoutedEventArgs e)
{
POINT p;
if (GetCursorPos(out p))
{
System.Console.WriteLine(Convert.ToString(p.X) + ";" + Convert.ToString(p.Y));
}
}
[DllImport("User32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
private Point ConvertPixelsToUnits(int x, int y)
{
// get the system DPI
IntPtr dDC = GetDC(IntPtr.Zero); // Get desktop DC
int dpi = GetDeviceCaps(dDC, 88);
bool rv = ReleaseDC(IntPtr.Zero, dDC);
// WPF's physical unit size is calculated by taking the
// "Device-Independant Unit Size" (always 1/96)
// and scaling it by the system DPI
double physicalUnitSize = (1d / 96d) * (double)dpi;
Point wpfUnits = new Point(physicalUnitSize * (double)x,
physicalUnitSize * (double)y);
return wpfUnits;
}
private void WriteMouseCoordinatesInWPFUnits()
{
POINT p;
if (GetCursorPos(out p))
{
Point wpfPoint = ConvertPixelsToUnits(p.X, p.Y);
System.Console.WriteLine(Convert.ToString(wpfPoint.X) + ";" + Convert.ToString(wpfPoint.Y));
mousePointY = wpfPoint.Y;
mousePointX = wpfPoint.X
}
}
Now the most important part of the code
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
...
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, Convert.ToUInt32(mousePointX), Convert.ToUInt32(mousePointY), 0, 0);
...
Warning
The code is tested
The code is not a "copy & paste code
i want to disable the scrolling feature of richtextbox in c#. i just want to make richtextbox to allow user to enter only in its size area, means no vertical scrolling for user. just like MS-word or open Office Pages.thanx in advance.
You should override WndProc and block WM_SETFOCUS.
protected override void WndProc(ref Message m)
{
if(m.Msg != WM_SETFOCUS)
base.WndProc(ref m);
}
Here is a tutorial about this : How to: C# - Prevent RichTextBox from auto scrolling
This worked for me.
First thing as you may have seen in other posts you need access to user32.dll from C#.
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hwndLock,Int32 wMsg,Int32 wParam, ref Point pt);
We need to make some constant declaration to make the SendMessage calls properly.
private const int WM_USER = 0x400;
private const int EM_HIDESELECTION = WM_USER + 63;
private const int WM_SETREDRAW = 0x000B;
private const int EM_GETSCROLLPOS = WM_USER + 221;
private const int EM_SETSCROLLPOS = WM_USER + 222;
Then, some public static methods to be used whenever we need to stop scrolling.
public static void Suspend(Control control)
{
Message msgSuspendUpdate = Message.Create(control.Handle, WM_SETREDRAW, IntPtr.Zero,
IntPtr.Zero);
NativeWindow window = NativeWindow.FromHandle(control.Handle);
window.DefWndProc(ref msgSuspendUpdate);
}
public static void Resume(Control control)
{
// Create a C "true" boolean as an IntPtr
IntPtr wparam = new IntPtr(1);
Message msgResumeUpdate = Message.Create(control.Handle, WM_SETREDRAW, wparam,
IntPtr.Zero);
NativeWindow window = NativeWindow.FromHandle(control.Handle);
window.DefWndProc(ref msgResumeUpdate);
control.Invalidate();
}
public static Point GetScrollPoint(Control control) {
Point point = new Point();
SendMessage(control.Handle, EM_GETSCROLLPOS, 0, ref point);
return point;
}
public static void SetScrollPoint(Control control, Point point)
{
SendMessage(control.Handle, EM_SETSCROLLPOS, 0, ref point);
}
The Suspend method stops the Control to make a redraw on the screen. The Resume method restarts redraws on the screen for the given Control.
The GetScrollPoint method gets the actual Point where the scroll caret is located. The SetScrollPoint puts the scroll caret at the given point.
How to use these methods? First, given a Control you need to stop autoscroll, make the call to Suspend, then to GetScrollPoint, (make what you need to do with the control, like highlight or append text) then SetScrollPoint and finally Resume.
In my case, I wanted to copy the entire line of a RichTextBox at any time when the cursor moves from line to line. (Doing so produce a scroll on long lines).
This is my working method:
private int intLastLine = -1;
private void richTextBoxSwitch_SelectionChanged(object sender, EventArgs e)
{
try
{
if (this.richTextBoxSwitch.TextLength > 0)
{
ControlBehavior.Suspend(this.richTextBoxSwitch);
Point point = ControlBehavior.GetScrollPoint(this.richTextBoxSwitch);
int intSelectionStartBackup = this.richTextBoxSwitch.SelectionStart;
int intSelectionLengthBackup = this.richTextBoxSwitch.SelectionLength;
int intCharIndex = this.richTextBoxSwitch.GetFirstCharIndexOfCurrentLine();
int intLine = this.richTextBoxSwitch.GetLineFromCharIndex(intCharIndex);
this.richTextBoxSwitch.SuspendLayout();
if (intLastLine != intLine)
{
intLastLine = intLine;
int intLength = this.richTextBoxSwitch.Lines[intLine].Length;
this.richTextBoxSwitch.Select(intCharIndex, intLength);
this.richTextBoxSwitch.BackColor = ColorMessageBackground;
strData = this.richTextBoxSwitch.SelectedText;
this.textBoxMessageSelected.Text = strData.Trim();
this.richTextBoxSwitch.Select(intSelectionStartBackup, intSelectionLengthBackup);
}
this.richTextBoxSwitch.ResumeLayout();
ControlBehavior.SetScrollPoint(this.richTextBoxSwitch, point);
ControlBehavior.Resume(this.richTextBoxSwitch);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Hope this helps!
This question already has answers here:
How do you simulate Mouse Click in C#?
(7 answers)
Closed 2 years ago.
I'd like to perform a click in a windows application, without using the real mouse (so I can minimize it). Much like a bot would behave.
How would I do this?
I think the function you're looking for is PostMessage
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostMessage(int hWnd, uint Msg, int wParam, int lParam);
You can read more about it here on codeproject, and download a demo project, which sends keystrokes.
This method posts messages directly on the input queue associated with the program, based on the process handle you use (hWnd)
You can also use this function to send mouse clicks with it, by posting button events, like so:
PostMessage(hWnd, WM_LBUTTONDBLCLK, 0, l);
More information about those button events can be found here on MSDN.
I'm sure if you search around the internet for samples for PostMessage mouse events you'll find plenty
You can use a timer, or handle key press, and create in the timer tick or key press function simulate a mouse click, using the user32.dll file (better be in a form shape so you can handle timer interval...):
using System;
using System.Windows.Forms;
namespace Clicker
{
public partial class Form1 : Form
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
private const int MOUSEEVENT_LEFTDOWN = 0x02;
private const int MOUSEEVENT_LEFTUP = 0x04;
private const int MOUSEEVENT_MIDDLEDOWN = 0x20;
private const int MOUSEEVENT_MIDDLEUP = 0x40;
private const int MOUSEEVENT_RIGHTDOWN = 0x08;
private const int MOUSEEVENT_RIGHTUP = 0x10;
private int count = 0;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
timer1.Start();
label2.Text = "Timer is on";
}
private void button2_Click(object sender, EventArgs e)
{
timer1.Stop();
label2.Text = "Timer is off";
}
private void timer1_Tick(object sender, EventArgs e)
{
mouse_event(MOUSEEVENT_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENT_LEFTUP, 0, 0, 0, 0);
count++;
label3.Text = count + " amount of clicks";
}
}
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
public const int MOUSEEVENTF_LEFTDOWN = 0x02;
public const int MOUSEEVENTF_LEFTUP = 0x04;
public const int MOUSEEVENTF_RIGHTDOWN = 0x08;
public const int MOUSEEVENTF_RIGHTUP = 0x10;
public void MouseClick()
{
int x = 100;
int y = 100;
mouse_event(MOUSEEVENTF_LEFTDOWN, x, y, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, x, y, 0, 0);
}
I found this at http://social.msdn.microsoft.com/forums/en-US/winforms/thread/86dcf918-0e48-40c2-88ae-0a09797db1ab/.
You'll need the resolution of the machine that it's on, use the System.Windows.Forms.Screen class, here: SO
You will then need to move the mouse to that location, or avoiding that, you may need to hook to the program that's running, and send it an event that causes it to minimize.
It's going to be hard to get something like this to work with C#, as you'll need to inject that DLL into the program. A lower level language like C may be helpful.
Here's a brief explanation / question
You can use Window Automation to find Minimize button in window and perform click. I find it easy and used a lot. here is the link to understand the whole concept.
https://msdn.microsoft.com/en-us/library/windows/desktop/ff486375(v=vs.85).aspx
I'm building a WPF application in which I need to display document previews such as what is achievable with a DocumentViewer and DocumentPaginator. However, converting the report to XPS and loading it into a DocumentViewer has proven to be very slow when the report is large (as a common report I'll need to display is).
This lead me to start thinking that there is probably some way to start showing the first few pages of the report while the rest of the pages are being 'loaded' into the DocumentViewer -- basically loading/showing the pages as they're created.
Does anyone know if something like this is possible? And, if so, how would you suggest I get started trying to make it work? I've spent a few hours looking around online for a solution to display the report faster, but haven't come up with anything.
For the sake of full disclosure, in this case the report I need to display is being created in HTML. I know that I need to convert it to XPS in order to use the DocumentViewer, but I bring this up because if anyone has a fast way of displaying the HTML, please feel free to bring that up too. I can't use a WebBrowser control as I have to have the display in a 'print preview' type of mode. A good algorithm for deciding how to 'paginate' an HTML site would probably lead me to a solution to this problem as well as then I could create a custom control to display it. I'd use a DocumentPaginator, but then the outputted file is XPS and then I'm back to the DocumentViewer issue.
Again, any help is greatly appreciated. Thank you!
Ok, I think I've got something...
Once again I found a better URL to reference. This one wasn't loading for me straight up so I grabbed it from the Google cache: http://webcache.googleusercontent.com/search?q=cache:LgceMCkJBrsJ:joshclose.net/%3Fp%3D247
Define the IViewObject interface as described in each article:
[ComVisible(true), ComImport()]
[GuidAttribute("0000010d-0000-0000-C000-000000000046")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IViewObject
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int Draw(
[MarshalAs(UnmanagedType.U4)] UInt32 dwDrawAspect,
int lindex,
IntPtr pvAspect,
[In] IntPtr ptd,
IntPtr hdcTargetDev,
IntPtr hdcDraw,
[MarshalAs(UnmanagedType.Struct)] ref Rectangle lprcBounds,
[MarshalAs(UnmanagedType.Struct)] ref Rectangle lprcWBounds,
IntPtr pfnContinue,
[MarshalAs(UnmanagedType.U4)] UInt32 dwContinue);
[PreserveSig]
int GetColorSet([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect,
int lindex, IntPtr pvAspect, [In] IntPtr ptd,
IntPtr hicTargetDev, [Out] IntPtr ppColorSet);
[PreserveSig]
int Freeze([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect,
int lindex, IntPtr pvAspect, [Out] IntPtr pdwFreeze);
[PreserveSig]
int Unfreeze([In, MarshalAs(UnmanagedType.U4)] int dwFreeze);
void SetAdvise([In, MarshalAs(UnmanagedType.U4)] int aspects,
[In, MarshalAs(UnmanagedType.U4)] int advf,
[In, MarshalAs(UnmanagedType.Interface)] IAdviseSink pAdvSink);
void GetAdvise([In, Out, MarshalAs(UnmanagedType.LPArray)] int[] paspects,
[In, Out, MarshalAs(UnmanagedType.LPArray)] int[] advf,
[In, Out, MarshalAs(UnmanagedType.LPArray)] IAdviseSink[] pAdvSink);
}
Create an HtmlPaginator class that screenshots the browser's document (as described) but then crops it into pages / frames:
class HtmlPaginator
{
public event EventHandler<PageImageEventArgs> PageReady;
protected virtual void OnPageReady(PageImageEventArgs e)
{
EventHandler<PageImageEventArgs> handler = this.PageReady;
if (handler != null)
handler(this, e);
}
public class PageImageEventArgs : EventArgs
{
public Image PageImage { get; set; }
public int PageNumber { get; set; }
}
public void GeneratePages(string doc)
{
Bitmap htmlImage = RenderHtmlToBitmap(doc);
int pageWidth = 800;
int pageHeight = 600;
int xLoc = 0;
int yLoc = 0;
int pages = 0;
do
{
int remainingHeightOrPageHeight = Math.Min(htmlImage.Height - yLoc, pageHeight);
int remainingWidthOrPageWidth = Math.Min(htmlImage.Width - xLoc, pageWidth);
Rectangle cropFrame = new Rectangle(xLoc, yLoc, remainingWidthOrPageWidth, remainingHeightOrPageHeight);
Bitmap page = htmlImage.Clone(cropFrame, htmlImage.PixelFormat);
pages++;
PageImageEventArgs args = new PageImageEventArgs { PageImage = page, PageNumber = pages };
OnPageReady(args);
yLoc += pageHeight;
if (yLoc > htmlImage.Height)
{
xLoc += pageWidth;
if (xLoc < htmlImage.Width)
{
yLoc = 0;
}
}
}
while (yLoc < htmlImage.Height && xLoc < htmlImage.Width);
}
private static Bitmap RenderHtmlToBitmap(string doc)
{
Bitmap htmlImage = null;
using (var webBrowser = new WebBrowser())
{
webBrowser.ScrollBarsEnabled = false;
webBrowser.ScriptErrorsSuppressed = true;
webBrowser.DocumentText = doc;
while (webBrowser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
webBrowser.Width = webBrowser.Document.Body.ScrollRectangle.Width;
webBrowser.Height = webBrowser.Document.Body.ScrollRectangle.Height;
htmlImage = new Bitmap(webBrowser.Width, webBrowser.Height);
using (Graphics graphics = Graphics.FromImage(htmlImage))
{
var hdc = graphics.GetHdc();
var rect1 = new Rectangle(0, 0, webBrowser.Width, webBrowser.Height);
var rect2 = new Rectangle(0, 0, webBrowser.Width, webBrowser.Height);
var viewObject = (IViewObject)webBrowser.Document.DomDocument;
viewObject.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, hdc, ref rect1, ref rect2, IntPtr.Zero, 0);
graphics.ReleaseHdc(hdc);
}
}
return htmlImage;
}
}
Call it like so:
WebBrowser browser = new WebBrowser();
browser.Navigate("http://www.stackoverflow.com");
while (browser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
HtmlPaginator pagr = new HtmlPaginator();
pagr.PageReady += new EventHandler<HtmlPaginator.PageImageEventArgs>(pagr_PageReady);
pagr.GeneratePages(browser.DocumentText);
To test it I implemented a basic form with a button and a picture box and a List collection. I add pages to the collection as they're ready from the HtmlPaginator and use the button to add the next image to the picturebox.
The magic numbers are your desired width and height. I used 800x600 but you probably have different dimensions you want.
The downside here is you're still waiting for the WebBrowser to render the HTML but I really don't see how an alternate solution is going to reduce that time - something has to interpret and draw the HTML in the first place. Write your own web browser I guess. :)
I did try playing with IViewObject.Draw to see if I could just have it render the page frames directly rather than have the cropping loop, but it wasn't working for me.
I am trying to write a hello world type program for using virtual channels in the windows terminal services client.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
IntPtr mHandle = IntPtr.Zero;
private void Form1_Load(object sender, EventArgs e)
{
mHandle = NativeMethods.WTSVirtualChannelOpen(IntPtr.Zero, -1, "TSCRED");
if (mHandle == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
private void button1_Click(object sender, EventArgs e)
{
uint bufferSize = 1024;
StringBuilder buffer = new StringBuilder();
uint bytesRead;
NativeMethods.WTSVirtualChannelRead(mHandle, 0, buffer, bufferSize, out bytesRead);
if (bytesRead == 0)
{
MessageBox.Show("Got no Data");
}
else
{
MessageBox.Show("Got data: " + buffer.ToString());
}
}
protected override void Dispose(bool disposing)
{
if (mHandle != System.IntPtr.Zero)
{
NativeMethods.WTSVirtualChannelClose(mHandle);
}
base.Dispose(disposing);
}
}
internal static class NativeMethods
{
[DllImport("Wtsapi32.dll")]
public static extern IntPtr WTSVirtualChannelOpen(IntPtr server,
int sessionId, [MarshalAs(UnmanagedType.LPStr)] string virtualName);
//[DllImport("Wtsapi32.dll", SetLastError = true)]
//public static extern bool WTSVirtualChannelRead(IntPtr channelHandle, long timeout,
// byte[] buffer, int length, ref int bytesReaded);
[DllImport("Wtsapi32.dll")]
public static extern bool WTSVirtualChannelClose(IntPtr channelHandle);
[DllImport("Wtsapi32.dll", EntryPoint = "WTSVirtualChannelRead")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool WTSVirtualChannelRead(
[In()] System.IntPtr hChannelHandle
, uint TimeOut
, [Out()] [MarshalAs(UnmanagedType.LPStr)]
System.Text.StringBuilder Buffer
, uint BufferSize
, [Out()] out uint pBytesRead);
}
I am sending the data from the MSTSC COM object and ActiveX controll.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
rdp.Server = "schamberlainvm";
rdp.UserName = "TestAcct";
IMsTscNonScriptable secured = (IMsTscNonScriptable)rdp.GetOcx();
secured.ClearTextPassword = "asdf";
rdp.CreateVirtualChannels("TSCRED");
rdp.Connect();
}
private void button1_Click(object sender, EventArgs e)
{
rdp.SendOnVirtualChannel("TSCRED", "Hello World!");
}
}
//Designer code
//
// rdp
//
this.rdp.Enabled = true;
this.rdp.Location = new System.Drawing.Point(12, 12);
this.rdp.Name = "rdp";
this.rdp.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("rdp.OcxState")));
this.rdp.Size = new System.Drawing.Size(1092, 580);
this.rdp.TabIndex = 0;
I am getting a execption every time NativeMethods.WTSVirtualChannelRead runs
Any help on this would be greatly appreciated.
EDIT -- mHandle has a non-zero value when the function runs. updated code to add that check.
EDIT2 -- I used the P/Invoke Interop Assistant and generated a new sigiture
[DllImport("Wtsapi32.dll", EntryPoint = "WTSVirtualChannelRead")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool WTSVirtualChannelRead(
[In()] System.IntPtr hChannelHandle
, uint TimeOut
, [Out()] [MarshalAs(UnmanagedType.LPStr)]
StringBuilder Buffer
, uint BufferSize
, [Out()] out uint pBytesRead);
it now receives the text string (Yea!) but it only gets the first letter of my test string(Boo!). Any ideas on what is going wrong?
EDIT 3 ---
After the call that should of read the hello world;
BytesRead = 24
Buffer.Length = 1; Buffer.Capacity = 16; Buffer.m_StringValue = "H";
Well the issue is you are sending a 16bit unicode string in the sending side and reading out a ansi string on the other so the marshalling layer is terminating the string buffer at the first NUL character. You could either changing the UnmanagedType.LPStr to UnmanagedType.LPWStr or marshal it as a byte array and then convert to a string using a Unicode Encoding class.
Something like this might work (NOTE: untested code as I don't have a server to test on):
public static extern int WTSVirtualChannelRead(IntPtr hChannel,
uint Timeout,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=3)] byte[] Buffer,
uint BufferSize,
out uint BytesRead);
string DoRead(IntPtr hChannel)
{
byte[] buf = new byte[1024];
uint bytesRead;
if (WTSVirtualChannelRead(hChannel, 0, buf, (uint)buf.Length, out bytesRead) != 0)
{
return Encoding.Unicode.GetString(buf, 0, (int)bytesRead);
}
else
{
return "";
}
}
I feel like taking a shower after writing this but...
private void button1_Click(object sender, EventArgs e)
{
uint bufferSize = 2;
StringBuilder buffer = new StringBuilder();
StringBuilder final = new StringBuilder();
uint bytesRead;
NativeMethods.WTSVirtualChannelRead(mHandle, 0, buffer, bufferSize, out bytesRead);
while (bytesRead != 0)
{
final.Append(buffer);
NativeMethods.WTSVirtualChannelRead(mHandle, 0, buffer, bufferSize, out bytesRead);
}
MessageBox.Show("Got data: " + final.ToString());
}
If anyone else can provide a better solution to the only one character transmitting problem I will gladly accept that instead of this.