Windows Live Writer托管Internet Explorer控件进行编辑,但没有缩放控件。我希望能够向它发送缩放命令。由于其他应用程序托管IE浏览器,我认为可以将缩放命令发送到特定IE浏览器实例的实用程序非常方便,但我还没找到。
我看到有一个命令,OLECMDID_OPTICAL_ZOOM,但我不确定如何将命令发送给它。理想情况下,我想从C#或Powershell中做到这一点。
注意:问题是询问如何控制正在运行的应用程序中的Web浏览器控件中的缩放,我没有创建,主要示例是Windows Live Writer中的编辑器表面。
答案 0 :(得分:5)
简短版:我不完全确定你可以做你想做的事。
我有一些代码实际上可以处理WLW窗口内的HTML文档,这样可行,但我发现我实际上无法获得对文档父窗口的引用。我不是Windows的本地人,但我在我的时间里做了一些PInvoke。可能只是因为我缺乏本地Windows知识阻止我弥合最后的差距。
从我的内容来看,获取IE窗口引用的一般过程是:
拥有该父窗口后,可以调用IWebBrowser2.ExecWB方法来运行OLE缩放命令。
问题是当您尝试访问IHTMLDocument2.parentWindow属性时,似乎总是抛出InvalidCastException 。从what我read,当您尝试从运行文档的线程以外的线程中获取父窗口时,这就是交易。
所以,无论如何,我会删除获取HTML文档参考的代码,如果你可以跨越那个微小的最后一步,那么你将得到你的答案。我自己也搞不清楚了。
这是一款控制台应用。您需要为IHTMLDocument2接口引用Microsoft.mshtml。
using System;
using System.Runtime.InteropServices;
namespace ControlInternetExplorerServer
{
////////////////////////
// LOTS OF PINVOKE STUFF
////////////////////////
[Flags]
public enum SendMessageTimeoutFlags : uint
{
SMTO_NORMAL = 0x0,
SMTO_BLOCK = 0x1,
SMTO_ABORTIFHUNG = 0x2,
SMTO_NOTIMEOUTIFNOTHUNG = 0x8
}
public static class NativeMethods
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(
string lpClassName,
string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(
IntPtr hwndParent,
IntPtr hwndChildAfter,
string lpszClass,
string lpszWindow);
[DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetWindow", SetLastError = true)]
public static extern IntPtr GetNextWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.U4)] int wFlag);
[DllImport("oleacc.dll", PreserveSig = false)]
[return: MarshalAs(UnmanagedType.Interface)]
public static extern object ObjectFromLresult(
IntPtr lResult,
[MarshalAs(UnmanagedType.LPStruct)] Guid refiid,
IntPtr wParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint RegisterWindowMessage(string lpString);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
IntPtr windowHandle,
uint Msg,
IntPtr wParam,
IntPtr lParam,
SendMessageTimeoutFlags flags,
uint timeout,
out IntPtr result);
public static IntPtr FindWindowRecursive(IntPtr parent, string windowClass, string windowCaption)
{
var found = FindWindowEx(parent, IntPtr.Zero, windowClass, windowCaption);
if (found != IntPtr.Zero)
{
return found;
}
var child = FindWindowEx(parent, IntPtr.Zero, null, null);
while (child != IntPtr.Zero)
{
found = FindWindowRecursive(child, windowClass, windowCaption);
if (found != IntPtr.Zero)
{
return found;
}
child = GetNextWindow(child, 2);
}
return IntPtr.Zero;
}
}
//////////////////////
// THE INTERESTING BIT
//////////////////////
public class Program
{
public static void Main(string[] args)
{
// First parameter is the class name of the window type - retrieved from Spy++
// Second parameter is the title of the window, which you'll
// probably want to take in via command line args or something.
var wlwWindow = NativeMethods.FindWindow("WindowsForms10.Window.8.app.0.33c0d9d", "Untitled - Windows Live Writer");
if (wlwWindow == IntPtr.Zero)
{
Console.WriteLine("Unable to locate WLW window.");
return;
}
// Since you don't know where in the tree it is, you have to recursively
// search for the IE window. This will find the first one it comes to;
// ostensibly there's only one, right? RIGHT?
var ieWindow = NativeMethods.FindWindowRecursive(wlwWindow, "Internet Explorer_Server", null);
if (ieWindow == IntPtr.Zero)
{
Console.WriteLine("Unable to locate IE window.");
return;
}
// Get a handle on the document inside the IE window.
IntPtr smResult;
var message = NativeMethods.RegisterWindowMessage("WM_HTML_GETOBJECT");
NativeMethods.SendMessageTimeout(ieWindow, message, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 1000, out smResult);
if (smResult== IntPtr.Zero)
{
Console.WriteLine("Unable to locate the HTML document object.");
}
// Cast the document to the appropriate interface.
var htmlDoc = (mshtml.IHTMLDocument2)NativeMethods.ObjectFromLresult(smResult, typeof(mshtml.IHTMLDocument2).GUID, IntPtr.Zero);
// Here's where you would normally get htmlDoc.parentWindow and call ExecWB
// to execute the zoom operation, but htmlDoc.parentWindow throws an InvalidCastException.
}
}
}
答案 1 :(得分:1)
这很容易......
您需要通过创建派生类来覆盖正常的Web浏览器控件。
然后,该派生类用于访问父Web浏览器控件的底层activeX实现,此时您可以访问“ExecWB”方法,遗憾的是,该方法未在标准对象中公开显示。
一旦你拥有了这个底层对象,你就可以简单地提供你想要的任何公共方法,比如缩放,然后直接将它们传递给activeX接口。
我写了一个smaple winforms程序来展示它是如何完成的,我已将项目上传到我的MSN天空驱动器:
答案 2 :(得分:1)
我一直在考虑这个问题,我发现我们可以使用Win32天的旧技术(Pre OLE,COM +和所有爵士乐)称为“Sendkeys”
基本上你需要做的是使用win32 API找到实时编写器主窗口的窗口句柄,然后从那里遍历应用程序的对象层次结构,直到找到Web控件。
一旦你拥有了该对象的句柄(间谍++将告诉你它是否可访问),理论上你应该能够发送你喜欢的任何组合键,然后由应用程序消息泵处理好像按键源自应用程序本身。
我确实看过尝试这种方法,但它确实依赖于应用程序本身实际处理并按下按键以在其代码中执行所需的功能。正如我通过测试发现的那样,父应用程序中似乎没有对缩放作出反应的处理代码,因此我不确定该方法是否有效。
然而,稍微扩展一下,一旦你有了SDK,为现场编写器编写插件并不困难,所以我认为探索解决这个问题的途径将是以下的混合:
实时编写器插件接受从远程代理发送的命令
远程代理使用与发送密钥相同的方法将消息发送到插件
插件中的代码,根据我的示例项目中的代码找到原位浏览器控件,然后在需要时调用它的缩放方法。
我现在没有时间仔细阅读此内容: - (