• C#搞工控的一些代码

    2019-01-08 16:55:10
    1、字节转化为单精度浮点数 2、单精度转字节 3、使用结构体 4、使用动态链接库 5、ASCCII码字符转16进制数 6、字符转ASCII码,ASCII码转字符 7、字符串转字符数组 8、整形数据与字节数组相互转换 9、ASCII码的...
    首先工控项目都会用到:
    using System.Runtime.InteropServices;
    1、字节转化为单精度浮点数
    2、单精度转成字节
    3、使用结构体
    4、使用动态链接库
    5、ASCCII码字符转成16进制数
    6、字符转ASCII码,ASCII码转字符
    7、字符串转成字符数组
    8、整形数据与字节数组相互转换
    9、ASCII码的使用,适用于串口通信
    10、c#获得时间和路径 
     
     
     
     
    1、字节转化为单精度浮点数
               //byte[] retdata = new byte[] { 0x00, 0x00, 0x7A, 0x43 }; //43 7A 00 00 是250,在这里要倒过来写
                byte[] r;
                r=new byte[4];
                r[3] = 0x43;
                r[2] = 0xac;
                r[1] = 0x00;
                r[0] = 0x00;
                textBox1.Text = BitConverter.ToSingle(r, 0).ToString ();
     
    2、单精度转成字节
                float floatvalue;
                floatvalue = 2500;
                byte[] b = BitConverter.GetBytes(floatvalue );
                textBox1.Text =Convert.ToString(b[3],16)+" "+Convert.ToString(b[2],16)+" "+Convert.ToString(b[1],16)+" "+Convert.ToString(b[0],16);
     
    3、使用结构体
       (1)先引用命名空间
            using System.Runtime.InteropServices;
       (2)在public partial class Form1 : Form
              { 
            中声明结构体类型。例如://注意代码的位置
            [StructLayout(LayoutKind.Sequential)]  //不写这个也行,默认的内存排列就是Sequential,也就是按成员的先后顺序排列.
            public struct PVCI_INIT_CONFIG
            {
                public uint AccCode;
                public uint AccMask;
                public uint Reserved;
                public byte Filter;
                public byte kCanBaud;
                public byte Timing0;
                public byte Timing1;
                public byte Mode;
                public byte CanRx_IER;
            }
        (3)在需要使用的地方定义:
             形式1:s x;
                    x.a = 0x55;
                    x.b = 0x66;
             形式2:PVCI_INIT_CONFIG[] config = new PVCI_INIT_CONFIG[1];  //定义一个结构体数组
                    config[0].AccCode = 0x80000008;
                    config[0].AccMask = 0xFFFFFFFF;
                    config[0].Reserved = 204;
                    config[0].Filter = 0;
                    config[0].kCanBaud = 12;  //500k
                    config[0].Timing0 = 0x00;
                    config[0].Timing1 = 0x1C;
                    config[0].CanRx_IER = 1; //Enable CAN reveive
                    config[0].Mode = 0;
     
    4、使用动态链接库
            有些DLL有依赖文件,例如:吉阳CANUSB的,和DLL相关的有两个VCI_CAN文件和一个SiUSBXp.dll文件。将所有相关文件拷贝到bin\debug中和exe文件在一个文件中。
            using System.Runtime.InteropServices;
            在窗体类中public partial class Form1 : Form
            声明就可以用   
            [DllImport("VCI_CAN.dll",EntryPoint = "VCI_OpenDevice")]
            public static extern int VCI_OpenDevice(uint Devtype, uint Devindex, uint Reserved);
     
     
    5、ASCCII码字符转成16进制数
                     //这个在串口通信有实际应用
    //如9的ASCCII码是57,将9的ASCCII传进函数,然后57-0x30就是数字9
    //再如F的ASCCI码是70,70-'A'=70-65=5,然后5+10就是15也就是F表示的数字
            public int HexChar(char c)
            {
                if ((c >= '0') && (c <= '9'))
                    return c - 0x30;
                else if ((c >= 'A') && (c <= 'F'))
                    return c - 'A' + 10;
                else if ((c >= 'a') && (c <= 'f'))
                    return c - 'a' + 10;
                else
                    return 0x10;
            }
            //这个将FE之类的字符串转成数字
            public int Str2Hex(string str)
            {
                int len = str.Length;
                if (len == 2)
                {
                    int a = HexChar(str[0]);
                    int b = HexChar(str[1]);
                    if (a == 16 || b == 16)
                    {
                        MessageBox.Show("format error!");
                        return 256;
                    }
                    else
                    {
                        return a * 16 + b;
     
                    }
     
                }
                else
                {
                    MessageBox.Show("len must be 2");
                    return 256;
                }
            }
           //可以这样使用,强制转换为字节型,并以字符串显示为254
           textBox1.Text = Convert.ToString((byte)Str2Hex("FE"));
           //这样就显示FE了
           textBox1.Text = Convert.ToString((byte)Str2Hex("FE"),16);
     
    6、字符转ASCII码,ASCII码转字符 
          public static int Asc(string character)
      {
       if (character.Length == 1)
       {
        System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();
        int intAsciiCode = (int)asciiEncoding.GetBytes(character)[0];
        return (intAsciiCode);
       }
       else
       {
        throw new Exception("Character is not valid.");
       } 
     
      } 
     
    ASCII码转字符: 
    单个字符
    public static string Chr(int asciiCode)
      {
       if (asciiCode >= 0 && asciiCode <= 255)
       {
        System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();
        byte[] byteArray = new byte[] { (byte)asciiCode };
        string strCharacter = asciiEncoding.GetString(byteArray);
        return (strCharacter);
       }
       else
       {
        throw new Exception("ASCII Code is not valid.");
       }
      } 
    Excel专用
    /// <summary>
            /// ASCII码转字符串(转换为Excel列的形式:A/B/C...AA/AB/AC...BA/BB/......)
            /// </summary>
            /// <param name="asciiCode">最大数字255(即Excel最末列IV)</param>
            /// <returns></returns>
            public static string Chr(int asciiCode)
            {
                if (asciiCode > 0 && asciiCode <= 255)
                {
                    System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();
                    string strCharacter = string.Empty;
                    byte[] byteArray = null;
                    int division = (asciiCode - 64) / 26;
                    int mod = (asciiCode - 64) % 26;
                    if (mod == 0)
                    {
                        division = division - 1;
                        mod = 26;
                    }
     
                    if ((division == 0) && (mod <= 26))
                    {
                        byteArray = new byte[] { (byte)(mod + 64) };
                        strCharacter = strCharacter + asciiEncoding.GetString(byteArray);
                    }
                    else
                    {
                        byteArray = new byte[] { (byte)(division + 64) };
                        strCharacter = strCharacter + asciiEncoding.GetString(byteArray);
     
                        byteArray = new byte[] { (byte)(mod + 64) };
                        strCharacter = strCharacter + asciiEncoding.GetString(byteArray);
                    }
     
                    return strCharacter;
                }
                else
                {
                    return "ASCII Code is not valid.";
                }
            }
     
    7、字符串转成字符数组
                string ss;
                ss = "hello world";
                char[] sx=ss.ToCharArray();
                int i;
                for (i = 0; i < sx.Length; i++)
                {
                    textBox1.Text = textBox1.Text + " " + sx[i];
                }
     
     
    8、整形数据与字节数组相互转换
                
                例如:吉阳CAN的ID号移位问题
                int id;
                id = 0x088090C1<<3;         
                byte[] sID = BitConverter.GetBytes(id);
                textBox1.Text = Convert.ToString(sID[3], 16) + " " + Convert.ToString(sID[2], 16) + " " + Convert.ToString(sID[1], 16) + " " + Convert.ToString(sID[0], 16) +" ";
                //将字节数组组合成32位整形
                int rID=0x00000000;
                rID = (sID[3] << 24) | (sID[2] << 16)|(sID[1]<<8)|sID[0];
                rID = rID >> 3;
                 
    9、ASCII码的使用,适用于串口通信
                 char x = (char)59;     //ASCII码是59
                textBox1.Text = "hello" + x.ToString()+(char)32+"world";  //asccii码32是空格
                //写成数组形式
                char[] str = { (char)45,(char)48,(char)100 };  //(char)77 就是ASCII码77的 强制转为字符
                textBox1.Text = str[0].ToString() + str[1].ToString() + str[2].ToString(); //用在串口发送上比较合适
     
     
    10、c#获得时间和路径             
    //  textBox1.Text = DateTime.Now .ToString ()+":"+DateTime.Now.Millisecond .ToString() ; //时间,精确到毫秒
                //  textBox1.Text = Environment.CurrentDirectory.ToString();  //当前路径
                //  textBox1.Text = Environment.TickCount.ToString();  //获得系统启动经过的毫秒数
                //  System.Threading.Thread.Sleep(1000);  //暂停1秒

     

    展开全文
  • VS2015 C#程序打包.exe,步骤超详细 前言:当我们写好一个程序后,提供给用户都以安装包方式给对方。那么我们就需要将程序打包。接下来是我亲测可行的且超好用的打包方法,废话就不多说了,直接上图。 步骤一 ...

    VS2015 C#程序打包成.exe之installshield使用教程

    个人总结:
    经过测试,本方法适用于多个版本(VS2010、VS2013、VS2015、VS2019),这篇博客非常不错。分享给大家共同学习。
    同时该打包方法不仅可以打包C#的winform、WPF程序,还可以打包MFC、QT等程序,可以说是非常好用的打包方法之一。

    前言

    从Visual Studio 2012开始,微软就把自家原来的安装与部署工具彻底废掉了,转而让大家去安装使用第三方的打包工具“InstallShield Limited Edition for Visual Studio”,注意这个版本是免费的,只需要邮件注册下,就会有要注册码。虽然不知道微软这是何用意,但是既然他叫我们用,那就去用吧。(其实,早在VS2010中,微软就把InstallShield Limited Edition for Visual Studio放在里面了,只是那个时候自家产品还在)。

    由于InstallShield Limited Edition for Visual Studio的教程、资料太少,所以我今天才决定写这个文章,专门针对C#项目打包,包括打包集成Microsoft .NET Framework等。大家从官方下载的最新版InstallShield Limited Edition for Visual Studio,是支持VS2010、VS2012、VS2013的(小牛在WIN7、WIN8中安装正常,但是WIN8.1死活都安装不进去……)

    如何下载注册:

    http://go.microsoft.com/fwlink/?LinkID=239620&lang=1033&ver=ult

    用你的邮箱注册,会免费发注册码的,不需要破解

    具体步骤:

    在这里插入图片描述

    1、新建了一个基本的三层项目,用来测试打包

    在这里插入图片描述

    2、根据实际情况,填写程序基本信息

    在这里插入图片描述

    3、这一步非常重要,看上图 :

    1)设置为简体中文,否则安装路径有中文的话就会出问题
    2)设置默认安装路径
    3)修改默认字体
    4)每次升级,重新打包,只需要点击这一行右侧的“…”按钮,就会重新生成Code,安装时就会自动覆盖老版本
    在这里插入图片描述

    4、选择我要的.NET Framework 4.0

    在这里插入图片描述

    5、这里什么也不做

    在这里插入图片描述

    6、添加要打包的文件、程序

    在这里插入图片描述

    7、右键——属性(Properties)

    在这里插入图片描述

    8、非常重要:

    1)如果你的是.NET项目程序DLL、EXE,那就按照默认的设置,不要去改,否则出错
    2)如果你的是OCX或者ActiveX等需要注册的DLL,那么选择“Self-registration”
    在这里插入图片描述

    9、设置快捷方式

    在这里插入图片描述

    10、注册表配置:

    1)打开这个
    2)现在这个测试项目,不需要写注册表信息,所以我什么都不改。如果你的项目要写注册表,那就自己填。
    在这里插入图片描述

    11、安装访谈(安装界面对话框配置):

    1)打开这个
    2)我就不修改了,大家根据自己的需求调整。
    3)如果要修改,点击这个
    在这里插入图片描述

    12、修改安装步骤、对话框、背景等

    在这里插入图片描述

    13、到这这一步,就剩下最重要的问题:如何把.NET Framework一起打包进程序去:

    1)解决方案,点击“Specify Application Data”–双击“Redistributables”–勾选“Microsoft .NET Framework 4.0 Full”–勾选之后,它会自动联网下载,下载速度是比较慢的,下完之后,右侧就会变成“Installed Locally”

    A).NET 4.0:如果大家嫌慢,那就去网上下载:dotNetFx40_Full_x86_x64.exe,然后放到这个路径:
    C:\Program Files (x86)\InstallShield\2013LE\SetupPrerequisites\Microsoft .net\4.0\Full
    这样的话就能节省很多时间了,不过除了这个,它还需要下载其他东西的,只是你可以节省这部分时间

    B).NET 3.5 SP1:下载dotnetfx35.exe,然后放到这个路径:
    C:\Program Files (x86)\InstallShield\2013LE\SetupPrerequisites\Microsoft .net\3.5 SP1\Full
    在这里插入图片描述

    14、最后的设置:

    1)解决方案,点击“Prepare for Release”–双击“Releases”–点击选中“SingleImage”–选项卡点击“Setup.exe”–找到“InstallShield Prerequisites Location”,把它设置为“Extract From Setup.exe”
    在这里插入图片描述

    15、打包完成了,运行效果如上图:

    1)打包后的程序放在这个地方:XXX\Express\SingleImage\DiskImages\DISK1\setup.exe (70.9MB,比较大)
    本文采用 CC协议 发布,转载请注明:转载自 吾乐吧软件站

    本文链接地址:http://www.wuleba.com/?p=23892

    本人算是好学之人,学过JAVA、LUA、Python、C#、C++语言,从事过KUKA、EPSON、ABB机器人编程和图像识别处理引导机器人做高精度复杂的工作。搞过嵌入式软件开发,目前正在做C++(QT)程序开发与C#(winform、WPF)程序开发,欢迎志同道合之人加本人QQ1334551779共同讨论学习(请注明来自博客)。

    展开全文
  • C# WinForm WebBrowser (一) MSDN资料  1、主要用途:使用户可以在窗体中导航网页。 2、注意:WebBrowser 控件会占用大量资源。使用完该控件后一定要调用 Dispose 方法,以便确保及时释放所有资源。必须在...

    C# WinForm WebBrowser () MSDN资料 

    1主要用途:使用户可以在窗体中导航网页。

    2注意WebBrowser 控件会占用大量资源。使用完该控件后一定要调用 Dispose 方法,以便确保及时释放所有资源。必须在附加事件的同一线程上调用 Dispose 方法,该线程应始终是消息或用户界面 (UI) 线程。

    3WebBrowser 使用下面的成员可以将控件导航到特定 URL、在导航历史记录列表中向后和向前移动,还可以加载当前用户的主页和搜索页:

    1.URL属性:可读、可写,用于获取或设置当前文档的 URL。 

    WebBrowser 控件维护浏览会话期间访问的所有网页的历史记录列表。设置Url属性时,WebBrowser 控件导航到指定的 URL 并将该 URL 添加到历史记录列表的末尾。

    WebBrowser 控件在本地硬盘的缓存中存储最近访问过的站点的网页。每个页面都可以指定一个到期日期,指示页面在缓存中保留的时间。当控件定位到某页时,如果该页具有缓存的版本,则直接显示缓存中的内容而不必重新下载该页,从而节省了时间。使用 Refresh 方法强制 WebBrowser控件通过下载来重新加载当前页,从而确保控件显示最新版本。

    注意:即使已请求了另一个文档,该属性也包含当前文档的 URL。如果设置该属性的值,然后立即再次检索该值,要是 WebBrowser 控件尚未来得及加载新文档,则检索到的值可能与设置的值不同。

    2.Navigate方法将指定位置的文档加载到 WebBrowser 控件中。

    3.GoBack方法:如果导航历史记录中的上一页可用,则将 WebBrowser 控件导航到该页。

    如果导航成功,则返回true;如果导航历史记录中的上一页不可用,则返回false

     WebBrowser 控件维护浏览会话期间访问的所有网页的历史记录列表。可以使用GoForward方法实现一个后退按钮。

    使用 CanGoBack 属性确定导航历史记录是否可用以及是否包含上一页。处理 CanGoBackChanged 事件,在 CanGoBack 属性值更改时接收通知。

    4.GoForward方法:如果导航历史记录中的下一页可用,则将 WebBrowser 控件导航到该页。

    如果导航成功,则返回true;如果导航历史记录中的下一页不可用,则返回false

    WebBrowser 控件维护浏览会话期间访问的所有网页的历史记录列表。可以使用 GoForward 方法实现一个前进按钮.

    使用 CanGoForward 属性确定导航历史记录是否可用以及是否包含当前页之后的页。处理 CanGoForwardChanged 事件,在 CanGoForward 属性值更改时接收通知 

      5.GoHome方法:将 WebBrowser 控件导航到当前用户的主页。

      6.GoSearch方法:将 WebBrowser 控件导航到当前用户的默认搜索页。 

        默认搜索页存储在注册表的 HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\Search Page 注册表项下。

        若要使用其他搜索页而不是默认搜索页,请调用 Navigate 方法或指定 Url 属性。

      7.Refresh方法:重新加载当前显示在 WebBrowser 控件中的文档。

      8.Stop方法:取消所有挂起的导航并停止所有动态页元素(如背景声音和动画)。

    如果导航不成功,则显示一页指示出现的问题。使用这些成员中的任何一个进行导航都会导致在导航的不同阶段发生 NavigatingNavigated DocumentCompleted 事件。


    4ObjectForScripting 属性:获取或设置一个对象,该对象可由显示在 WebBrowser 控件中的网页所包含的脚本代码访问。

    使用该属性启用 WebBrowser 控件承载的网页与包含 WebBrowser 控件的应用程序之间的通信。使用该属性可以将动态 HTML (DHTML) 代码与客户端应用程序代码集成在一起。为该属性指定的对象可作为 window.external 对象(用于主机访问的内置 DOM 对象)用于网页脚本。

    可以将此属性设置为希望其公共属性和方法可用于脚本代码的任何 COM 可见的对象。可以通过使用 ComVisibleAttribute 对类进行标记使其成为 COM 可见的类。

    若要从客户端应用程序代码调用网页中定义的函数,请使用可从 Document 属性检索的 HtmlDocument 对象的 HtmlDocument.InvokeScript 方法

    5AllowNavigation属性:获取或设置一个值,该值指示控件在加载其初始页之后是否可以导航到其他页。

    6AllowWebBrowserDrop属性:获取或设置一个值,该值指示 WebBrowser 控件是否导航到拖放到它上面的文档。

    7WebBrowserShortcutsEnabled属性:是否启用WebBrowser自带的快捷键。

    8ScriptErrorsSuppressed 属性:获取或设置一个值,该值指示出现脚本错误时,WebBrowser 控件是否显示错误对话框。

    9IsWebBrowserContextMenuEnabled属性:是否启用右键菜单。


    源:MSDN http://msdn.microsoft.com/zh-cn/library/system.windows.forms.webbrowser(v=vs.80).aspx

    C#WinForm WebBrowser (实用方法总结 

    实用方法1:获取状态栏信息

    void webBrowser1_StatusTextChanged(object sender, EventArgs e)
    {
        label1.Text = webBrowser1.StatusText;
    }

    实用方法2:页面跳转后改变地址栏地址

    //Navigated事件处理函数中改变地址栏地址是最恰当的:
    private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
    {
        textBox1.Text = webBrowser1.Url.ToString();
    }

    实用方法3:设置单选框

    //建议使用执行单击事件的方式来设置单选框,而不是修改属性:
    webBrowser1.Document.GetElementById("RBT_A").InvokeMember("click");

    实用方法4:设置联动型下拉列表

    //比较常见的联动型多级下拉列表就是省/市县选择了,这种情况下直接设置选择项的属性不会触发联动,需要在最后执行触发事件函数才能正常工作:

    foreach (HtmlElement f in s.GetElementsByTagName("option"))
    {
        if (f.InnerText == "北京")
        {
            f.SetAttribute("selected""selected");
        }
        else
        {
            f.SetAttribute("selected""");
        }
    }
    s.RaiseEvent("onchange");

    以上四种方法转于:http://www.cnblogs.com/SkyD/archive/2009/04/23/1441696.html

    实用方法一:在WinForm中相应Web事件
    假设HTML源代码如下:

    <html> 
    <body> 
    <input type="button" id="btnClose" value="关闭" /> 
    </body> 
    </html>

      

    HtmlDocument htmlDoc = webBrowser.Document; 
    HtmlElement btnElement = htmlDoc.All["btnClose"]; 
    if (btnElement != null

        btnElement.click += new HtmlElementEventHandler(HtmlBtnClose_Click); 
    }


    //很简单吧?那么稍稍高级一点的——我们都知道一个HTML元素可能有很多各种各样的事件,而HtmlElement这个类只给出最常用、共通的几个。那么,如何响应其他事件呢?这也很简单,只需要调用HtmlElementAttachEventHandler就可以了:

    btnElement.AttachEventHandler("onclick", new EventHandler(HtmlBtnClose_Click));  
    //这一句等价于上面的btnElement.click += new HtmlElementEventHandler(HtmlBtnClose_Click); 

    对于其他事件,把"onclick"换成该事件的名字就可以了。例如:

    formElement.AttachEventHandler("onsubmit"new EventHandler(HtmlForm_Submit)); 

      

    实用方法二:模拟表单自动填写和提交

    假设有一个最简单的登录页面,输入用户名密码,点登录按钮即可登录。已知用户名输入框的id(或Name,下同)是username,密码输入框的idpassword登录按钮的idsubmitbutton,那么我们只需要在webBrowserDocumentCompleted事件中使用下面的代码即可:

    HtmlElement btnSubmit = webBrowser.Document.All["submitbutton"]; 
    HtmlElement tbUserid = webBrowser.Document.All["username"]; 
    HtmlElement tbPasswd = webBrowser.Document.All["password"]; 

    if (tbUserid == null || tbPasswd == null || btnSubmit == null
        return

    tbUserid.SetAttribute("value""smalldust"); 
    tbPasswd.SetAttribute("value""12345678"); 

    btnSubmit.InvokeMember("click");

    关于表单的提交,的确还有另一种方法就是获取form元素而不是button,并用form元素的submit方法:

    HtmlElement formLogin = webBrowser.Document.Forms["loginForm"];  
    //……  
    formLogin.InvokeMember("submit"); 

    本文之所以没有推荐这种方法,是因为现在的网页,很多都在submit按钮上添加onclick事件,以对提交的内容做最基本的验证。如果直接使用formsubmit方法,这些验证代码就得不到执行,有可能会引起错误。 

    实用方法三:调用脚本

    首先是调用Web页面的脚本中已经定义好的函数。假设HTML中有如下Javascript

    function DoAdd(a, b) {
        return a + b;
    }

    那么,我们要在WinForm调用它,只需如下代码即可:

    object oSum = webBrowser.Document.InvokeScript("DoAdd"new object[] { 12 });
    int sum = Convert.ToInt32(oSum);

     

    其次,如果我们想执行一段Web页面中原本没有的脚本,该怎么做呢?这次.Net的类没有提供,看来还要依靠COM了。IHTMLWindow2可以将任意的字符串作为脚本代码来执行。

    string scriptline01 = @"function ShowPageInfo() {";
    string scriptline02 = @"     var numLinks = document.links.length; ";
    string scriptline03 = @"     var numForms = document.forms.length; ";
    string scriptline04 = @"     var numImages = document.images.length; ";
    string scriptline05 = @"     var numScripts = document.scripts.length; ";
    string scriptline06 = @"     alert('网页的统计结果:\r\n链接数:' + numLinks + ";
    string scriptline07 = @"        '\r\n表单数:' + numForms + ";
    string scriptline08 = @"        '\r\n图像数:' + numImages + ";
    string scriptline09 = @"        '\r\n脚本数:' + numScripts);}";
    string scriptline10 = @"ShowPageInfo();";

    string strScript = scriptline01 + scriptline02 + scriptline03 + scriptline04 + scriptline05 +
                       scriptline06 + scriptline07 + scriptline08 + scriptline09 + scriptline10;

    IHTMLWindow2 win = (IHTMLWindow2)webBrowser.Document.Window.DomWindow;
    win.execScript(strScript, "Javascript");
     

    以上三种实用方法转于:http://www.cnblogs.com/smalldust/archive/2006/03/08/345561.html

    最后:在脚本中调用WinForm里的代码,这个可能吗? 呵呵,当然是可能的。
    下面的代码示例演示如何使用 ObjectForScripting 属性。在该示例中,ObjectForScripting 属性被设置为当前窗体。

    view sourceprint?

    using System; 

    using System.Windows.Forms; 

    using System.Security.Permissions; 

      

    [PermissionSet(SecurityAction.Demand, Name="FullTrust")] 

    [System.Runtime.InteropServices.ComVisibleAttribute(true)] 

    public class Form1 : Form 

        private WebBrowser webBrowser1 = new WebBrowser(); 

        private Button button1 = new Button(); 

      

        [STAThread] 

        public static void Main() 

        { 

            Application.EnableVisualStyles(); 

            Application.Run(new Form1()); 

        } 

      

        public Form1() 

        { 

            button1.Text = "call script code from client code"; 

            button1.Dock = DockStyle.Top; 

            button1.Click += new EventHandler(button1_Click); 

            webBrowser1.Dock = DockStyle.Fill; 

            Controls.Add(webBrowser1); 

            Controls.Add(button1); 

            Load += new EventHandler(Form1_Load); 

        } 

      

        private void Form1_Load(object sender, EventArgs e) 

        { 

            webBrowser1.AllowWebBrowserDrop = false; 

            webBrowser1.IsWebBrowserContextMenuEnabled = false; 

            webBrowser1.WebBrowserShortcutsEnabled = false; 

            webBrowser1.ObjectForScripting = this; 

            // Uncomment the following line when you are finished debugging. 

            //webBrowser1.ScriptErrorsSuppressed = true; 

      

            webBrowser1.DocumentText = 

                "<html><head><script>" 

    C# WinForm WebBrowser (编辑模式 

    一、启用编辑模式、 浏览模式 及 自动换行

     
            /// <summary>
            /// 编辑模式
            /// </summary>
            public void EditMode()
            {
                if (this.webBrowser.Document != null)
                {
                    mshtml.IHTMLDocument2 doc = this.webBrowser.Document.DomDocument as mshtml.IHTMLDocument2;
                    if (doc != null)
                    {
                        doc.designMode = "on";
                    }
                }
            }

            /// <summary>
            /// 启用浏览模式
            /// </summary>
            public void BrowseMode()
            {
                if (this.webBrowser.Document != null)
                {
                    mshtml.IHTMLDocument2 doc = this.webBrowser.Document.DomDocument as mshtml.IHTMLDocument2;
                    if (doc != null)
                    {
                        doc.designMode = "off";
                    }
                }
            }

            /// <summary>
            /// 设置自动换行
            /// </summary>
            /// <param name="value"></param>
            public void SetAutoWrap(bool value)
            {
                mshtml.HTMLDocument doc = this.webBrowser.Document.DomDocument as mshtml.HTMLDocument;
                if (doc != null)
                {
                    mshtml.HTMLBody body = doc.body as mshtml.HTMLBody;
                    if (body != null)
                    {
                        body.noWrap = !value;
                    }
                }
            }

    在编辑模式下,可以使用:

      this.webBrowser.Document.ExecCommand([string],[bool],[object]);

    方法来操作WebBrowser中的HTML
    其中第一个字符串类型的参数为:要执行的命令的名称 
    第二个布尔类型的参数为: 是否向用户显示命令特定的对话框或消息框

    第三个Object类型的参数为:要使用该命令分配的值。并非适用于所有命令

    常见的命令有:

            private const string HTML_COMMAND_BOLD = "Bold";                       //加粗
            private const string HTML_COMMAND_UNDERLINE = "Underline";             //下划线
            private const string HTML_COMMAND_ITALIC = "Italic";                   //斜体
            private const string HTML_COMMAND_SUBSCRIPT = "Subscript";             //下标
            private const string HTML_COMMAND_SUPERSCRIPT = "Superscript";         //上标
            private const string HTML_COMMAND_STRIKE_THROUGH = "StrikeThrough";    //删除线
            private const string HTML_COMMAND_FONT_NAME = "FontName";              //字体
            private const string HTML_COMMAND_FONT_SIZE = "FontSize";              //字号
            private const string HTML_COMMAND_FORE_COLOR = "ForeColor";            //字体前景色
            private const string HTML_COMMAND_BACK_COLOR = "BackColor";            //字体背景色
            private const string HTML_COMMAND_INSERT_FORMAT_BLOCK = "FormatBlock"//加粗
            private const string HTML_COMMAND_REMOVE_FORMAT = "RemoveFormat";      //清楚样式
            private const string HTML_COMMAND_JUSTIFY_LEFT = "JustifyLeft";        //文本左对齐
            private const string HTML_COMMAND_JUSTIFY_CENTER = "JustifyCenter";    //文本中间对齐
            private const string HTML_COMMAND_JUSTIFY_RIGHT = "JustifyRight";      //文本右对齐
            private const string HTML_COMMAND_JUSTIFY_FULL = "JustifyFull";        //文本两端对齐
            private const string HTML_COMMAND_INDENT = "Indent";                   //增大缩进量
            private const string HTML_COMMAND_OUTDENT = "Outdent";                 //减小缩进量
            private const string HTML_COMMAND_INSERT_LINE = "InsertHorizontalRule";//插入分割符
            private const string HTML_COMMAND_INSERT_LIST = "Insert{0}List"// replace with (Un)Ordered 插入项目符号或项目编号
            private const string HTML_COMMAND_INSERT_IMAGE = "InsertImage";         //插入图像
            private const string HTML_COMMAND_INSERT_LINK = "CreateLink";           //插入链接
            private const string HTML_COMMAND_REMOVE_LINK = "Unlink";               //移除链接
            private const string HTML_COMMAND_TEXT_CUT = "Cut";                     //剪切
            private const string HTML_COMMAND_TEXT_COPY = "Copy";                   //复制
            private const string HTML_COMMAND_TEXT_PASTE = "Paste";                 //粘贴
            private const string HTML_COMMAND_TEXT_DELETE = "Delete";               //删除
            private const string HTML_COMMAND_TEXT_UNDO = "Undo";                   //撤销
            private const string HTML_COMMAND_TEXT_REDO = "Redo";                   //恢复
            private const string HTML_COMMAND_TEXT_SELECT_ALL = "SelectAll";        //全选
            private const string HTML_COMMAND_TEXT_UNSELECT = "Unselect";           //取消选择
            private const string HTML_COMMAND_TEXT_PRINT = "Print";                 // 打印
            private const string HTML_COMMAND_EDITMODE = "EditMode";                // 编辑模式
            private const string HTML_COMMAND_BROWSEMODE = "BrowseMode";            // 浏览模式
            private const string HTML_COMMAND_OVERWRITE = "OverWrite";             //转换插入、覆写模式

    // 更多的命令请参见:

    http://msdn.microsoft.com/en-us/library/ms533049.aspx

    C# WinForm WebBrowser (自定义操作【转】 

    ————————————————————————————————————————————————————————————————
      由于本人在开发中经常要在程序中嵌入浏览器,为了符合自己的需求经常要对浏览器进行扩展和定制, 解决这些问题需在网上找资料和学习的过程,我想可能很多开发者或许会遇到同样的问题,特写此文,以供大家参考。 


    MFC中使用浏览器

      在MFC中微软为我们提供了CHtmlViewCDHtmlDialog类让我们的程序很方便的嵌入浏览器和进行浏览器的二次开发,这比直 接使用WebBrowser控件要方便很多,所以本文中讨论的浏览器的问题都是针对CHtmlView来讨论的。文中将提到一个类CLhpHtmlView, 它是CHtmlView的派生类,文中提及的扩展或定制都将在CLhpHtmlView(或派生类)上实现。


    怎样扩展或定制浏览器

      浏览器定义了一些扩展接口(如IDocHostUIHandler可以定制浏览器界面有关的行为),以便开发者进行定制和扩展。浏览 器会在需要的时候向他的控制站点查询这些接口,在控制站点里实现相应的接口就可以进行相应的扩展。在MFC7.01类 库中,CHtmlView使用的控制站点是CHtmlControlSite的,在CHtmlControlSite类中 只实现了接口IDocHostUIHandler, 而要实现更多的扩展接口,必须用自定义的控制站类来取代CHtmlControlSite,在下文中提及的类CDocHostSite即为自定义 的控制站类。 

    关于接口的介绍请参考: 

    http://dev.csdn.net/develop/article/48/48483.shtm 

    如何使自定义的控制站点来替换默认的控制站点呢?在MFC7.0中只需重载CHtmlView的虚函数CreateControlSite即可: 

    BOOL CLhpHtmlView::CreateControlSite(COleControlContainer * pContainer, 

    COleControlSite ** ppSite, UINT /*nID*/, REFCLSID /*clsid*/)

    {

    *ppSite = new CDocHostSite(pContainer, this);// 创建自己的控制站点实例

    return (*ppSite) ? TRUE : FALSE;

    }

    VC6.0要替换控制站要复杂的多,这里就不讨论了,如需要6.0版本的请给我发邮件到yourshine@21cn.com


    定制鼠标右键弹出出菜单

      要定制浏览器的鼠标右键弹出菜单,必须在自定义的控制站点类中实现IDocHostUIHandler2接口,并且IE的 版本是5.5或以上。在接口IDocHostUIHandler2ShowContextMenu方法中调用浏览器类的OnShowContextMenu虚函数,我们 在浏览器类的派生类重载此虚函数即可实现右键菜单的定制,参见代码 

    HRESULT CDocHostSite::XDocHostUIHandler::ShowContextMenu(DWORD dwID,

                                                             POINT * ppt,

                                                             IUnknown * pcmdtReserved,

                                                             IDispatch * pdispReserved)

    {

    METHOD_PROLOGUE(CDocHostSite, DocHostUIHandler);

    return pThis->m_pView->OnShowContextMenu( dwID, ppt, pcmdtReserved,pdispReserved );

    }

    HRESULT CLhpHtmlView::OnShowContextMenu(DWORD dwID, 

                                            LPPOINT ppt,

                                            LPUNKNOWN pcmdtReserved, 

                                            LPDISPATCH pdispReserved)

    {

    HRESULT result = S_FALSE;

    switch(m_ContextMenuMode)

    {

    case NoContextMenu:// 无菜单

    result=S_OK;

    break;

    case DefaultMenu:// 默认菜单

    break;

    case TextSelectionOnly:// 仅文本选择菜单

    if(!(dwID == CONTEXT_MENU_TEXTSELECT || dwID == CONTEXT_MENU_CONTROL))

    result=S_OK;

    break;

    case CustomMenu:// 自定义菜单

    if(dwID!=CONTEXT_MENU_TEXTSELECT)

    result=OnShowCustomContextMenu(ppt,pcmdtReserved,pdispReserved);

    break;

    }

    return result;

    }

    CLhpHtmlView中定义的枚举类型CONTEXT_MENU_MODE举出了定制右键弹出菜单的四种类型 

    enum CONTEXT_MENU_MODE// 上下文菜单

    {

    NoContextMenu,// 无菜单

    DefaultMenu,// 默认菜单

    TextSelectionOnly,// 仅文本选择菜单

    CustomMenu// 自定义菜单

    };

    通过CLhpHtmlView的函数SetContextMenuMode来设置右键菜单的类型。如果设定的右键弹出菜单是自定义菜单类型, 我们只要在CLhpHtmlView的派生类中重载OnShowCustomContextMenu虚函数即可,如下代码 CDemoViewCLhpHtmlView的派生类 

    HRESULT CDemoView::OnShowCustomContextMenu(LPPOINT ppt, LPUNKNOWN pcmdtReserved,LPDISPATCH pdispReserved)

    {

    if ((ppt==NULL)||(pcmdtReserved==NULL)||(pcmdtReserved==NULL))

    return S_OK;

    HRESULT hr=0;

    IOleWindow *oleWnd=NULL;

        hr=pcmdtReserved->QueryInterface(IID_IOleWindow, (void**)&oleWnd);

    if((hr != S_OK)||(oleWnd == NULL))

    return S_OK;

    HWND hwnd=NULL;

    hr=oleWnd->GetWindow(&hwnd);

    if((hr!=S_OK)||(hwnd==NULL))

    {

    oleWnd->Release();

    return S_OK;

    }

    IHTMLElementPtrpElem=NULL;

    hr = pdispReserved->QueryInterface(IID_IHTMLElement, (void**)&pElem);

    if(hr != S_OK)

    {

    oleWnd->Release();

    return S_OK;

    }

    IHTMLElementPtrpParentElem=NULL;

    _bstr_ttagID;

    BOOL go=TRUE;

    pElem->get_id(&tagID.GetBSTR());

    while(go && tagID.length()==0)

    {

    hr=pElem->get_parentElement(&pParentElem);

    if(hr==S_OK && pParentElem!=NULL)

    {

    pElem->Release();

    pElem=pParentElem;

    pElem->get_id(&tagID.GetBSTR());

    }

    else

    go=FALSE;

    };

    if(tagID.length()==0)

    tagID="no id";

    CMenu Menu,SubMenu;

    Menu.CreatePopupMenu();

    CString strTagID = ToStr(tagID);

    if(strTagID == "red")

    Menu.AppendMenu(MF_BYPOSITION, ID_RED, "您点击的是红色");

    else if(strTagID == "green")

    Menu.AppendMenu(MF_BYPOSITION, ID_GREEN, "您点击的是绿色");

    else if(strTagID == "blue")

    Menu.AppendMenu(MF_BYPOSITION, ID_BLUE, "您点击的是蓝色");

    else

    Menu.AppendMenu(MF_BYPOSITION, ID_NONE, "你点了也白点,请在指定的地方点击");

    int MenuID=Menu.TrackPopupMenu(TPM_RETURNCMD|TPM_LEFTALIGN|TPM_RIGHTBUTTON,ppt->x, ppt->y, this);

    switch(MenuID)

    {

    case ID_RED:

    MessageBox("红色");

    break;

    case ID_GREEN:

    MessageBox("红色");

    break;

    case ID_BLUE:

    MessageBox("红色");

    break;

    case ID_NONE:

    MessageBox("haha");

    break;

    }

    oleWnd->Release();

    pElem->Release();

    return S_OK;

    }


    实现脚本扩展(很重要的external接口)

      在你嵌入了浏览器的工程中,如果网页的脚本中能调用C++代码,那将是一件很惬意的事情,要实现这种交互,就必须实现脚本扩展。实现脚本扩展就是在程序中实现一个IDispatch接口,通过CHtmlView类的OnGetExternal虚函数返回此接口指针,这样就可以在脚本中通过window.external.XXX(关键字window可以省略)来 引用接口暴露的方法或属性(XXX为方法或属性名)。在MFC中从CCmdTarget派生的类都可以实现自动化,而不必在MFC工程中引入繁杂的ATL。从CCmdTarget派生的类实现自动化接口的时候不要忘了在构造函数中调用EnableAutomation函数。
      要使虚函数OnGetExternal发挥作用必须在 自定义的控制站点类中实现IDocHostUIHandler,在接口IDocHostUIHandlerGetExternal方法中调用浏览器类的OnGetExternal虚函数,我们在浏览器类的派生类重载OnGetExternal虚函数, 通过参数lppDispatch返回一个IDispatch指针,这样脚本中引用window.external时就是引用的返回的接口,参见代码 

    HRESULT CDocHostSite::XDocHostUIHandler::GetExternal(IDispatch ** ppDispatch)

    {

    METHOD_PROLOGUE(CDocHostSite, DocHostUIHandler);

    return pThis->m_pView->OnGetExternal( ppDispatch );

    }

    CLhpHtmlView::CLhpHtmlView(BOOL isview)

    {

    ......

    EnableAutomation();// 允许自动化

    }

    HRESULT CLhpHtmlView::OnGetExternal(LPDISPATCH *lppDispatch)

    {

    *lppDispatch = GetIDispatch(TRUE);// 返回自身的IDispatch接口

    return S_OK;

    }      

    请注意上面代码中,在OnGetExternal返回的是自身IDispatch接口, 这样就不比为脚本扩展而另外写一个从CCmdTarget派生的新类, CLhpHtmlView本身就是从CCmdTarget派生,直接在上面实现接口就是。 

    下用具体示例来说明怎样实现脚本扩展 

    示例会在网页上点击一个按钮而使整个窗口发生抖动

    CLhpHtmlView派生一个类CDemoView,在类中实现IDispatch, 并通过IDispatch暴露方法WobbleWnd 

    ---------------------------------------------------------------------------

    文件 DemoView.h

    ---------------------------------------------------------------------------

    .......

    class CDemoView : public CLhpHtmlView

    {

    ......

    DECLARE_DISPATCH_MAP() // 构建dispatch映射表以暴露方法或属性

    ......

    void WobbleWnd();// 抖动窗口

    };

    ---------------------------------------------------------------------------

    文件 DemoView.cpp

    ---------------------------------------------------------------------------

    ......

    // 把成员函数映射到Dispatch映射表中,暴露方法给脚本

    BEGIN_DISPATCH_MAP(CDemoView, CLhpHtmlView)

    DISP_FUNCTION(CDemoView, "WobbleWnd", WobbleWnd, VT_EMPTY, VTS_NONE)

    END_DISPATCH_MAP()

    ......

    void CDemoView::WobbleWnd()

    {

    // 在这里实现抖动窗口

    ......

    }

    ---------------------------------------------------------------------------

    文件 Demo.htm

    ---------------------------------------------------------------------------

    ...... οnclick="external.WobbleWnd()" ......

    这里我要介绍一下DISP_FUNCTION宏,它的作用是将一个函数映射到Dispatch映射表中,我们看 

    DISP_FUNCTION(CDemoView, "WobbleWnd", WobbleWnd, VT_EMPTY, VTS_NONE)

    CDemoView是宿主类名, "WobbleWnd"是暴露给外面的名字(脚本调用时使用的名字), VT_EMPTY是返回值得类型为空,VTS_NONE说明此方法没有参数,如果要映射的函数有返回值和参数该 如何映射,通过下面举例来说明 

    DISP_FUNCTION(CCalendarView,"TestFunc",TestFunc,VT_BOOL,VTS_BSTR VTS_I4 VTS_I4)

    BOOL TestFunc(LPCSTR param1, int param2, int param3)

    {

    .....

    }

    参数表VTS_BSTR VTS_I4 VTS_I4是用空格分隔,他们的类型映射请参考MSDN,这要提醒的是不要把VTS_BSTRCString对应,而应与LPCSTR对应。


    C++代码中如何调用网页脚本中的函数

      IHTMLDocument2::scripts属性表示HTML文档中所有脚本对象。使用脚本对象的IDispatch接口的GetIDsOfNames方法可以得到脚本函数的 DispID,得到DispID后,使用IDispatchInvoke函数可以调用对应的脚本函数。CLhpHtmlView提供了方便的调用JavaScript的函数,请参考CLhpHtmlView中有关键字“JScript”的代码。


    定制消息框的标题

      我们在脚本中调用alert弹出消息框时,消息框的标题是微软预定义的“Microsoft Internet Explorer”,如下图:



      在自定义的控制站点类中实现IDocHostShowUI接口,在接口的ShowMessage方法中调用浏览器的OnShowMessage,我们重载 OnShowMessage虚函数即可定制消息框的标题,实现代码如下: 

    // 窗口标题"Microsoft Internet Explorer"的资源标识

    #define IDS_MESSAGE_BOX_TITLE 2213

    HRESULT CLhpHtmlView::OnShowMessage(HWND hwnd,

                                        LPOLESTR lpstrText,

                                        LPOLESTR lpstrCaption,

                                        DWORD dwType,

                                        LPOLESTR lpstrHelpFile,

                                        DWORD dwHelpContext,

                                        LRESULT * plResult)

    {

        //载入Shdoclc.dll 和IE消息框标题字符串

        HINSTANCE hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL"));

        if (hinstSHDOCLC == NULL)

            return S_FALSE;

    CString strBuf,strCaption(lpstrCaption);

    strBuf.LoadString(hinstSHDOCLC, IDS_MESSAGE_BOX_TITLE);

        // 比较IE消息框标题字符串和lpstrCaption

        // 如果相同,用自定义标题替换

    if(strBuf==lpstrCaption)

            strCaption = m_DefaultMsgBoxTitle;

        // 创建自己的消息框并且显示

        *plResult = MessageBox(CString(lpstrText), strCaption, dwType);

        //卸载Shdoclc.dll并且返回

        FreeLibrary(hinstSHDOCLC);

        return S_OK;

    }

    从代码中可以看到通过设定m_DefaultMsgBoxTitle的值来改变消息宽的标题,修改此值是同过SetDefaultMsgBoxTitle来实现 

    void CLhpHtmlView::SetDefaultMsgBoxTitle(CString strTitle)

    {

    m_DefaultMsgBoxTitle=strTitle;

    }


    怎样定制、修改浏览器向Web服务器发送的HTTP请求头

      在集成了WebBrowser控件的应用中,Web服务器有时可能希望客户端(浏览器)发送的HTTP请求中附带一些额外的信息或自定义的 HTTP头字段,这样就必须在浏览器中控制向Web服务器发送的HTTP请求。 下面是捕获的一个普通的用浏览器发送的HTTP请求头: 

    GET /text7.htm HTTP/1.0

    Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, \

    application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*

    Referer: http://localhost

    Accept-Language: en-us

    User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Poco 0.31; LHP Browser 1.01; \

    .NET CLR 1.1.4322)

    Host: localhost

    Connection: Keep-Alive

    CHtmlView的 

    void Navigate2(

       LPCTSTR lpszURL,

       DWORD dwFlags = 0,

       LPCTSTR lpszTargetFrameName = NULL,

       LPCTSTR lpszHeaders = NULL,

       LPVOID lpvPostData = NULL,

       DWORD dwPostDataLen = 0 

    );

    函数参数lpszHeaders可以指定HTTP请求头,示例如下

    Navigate2(_T("http://localhost"),NULL,NULL, "MyDefineField: TestValue");

    我们捕获的HTTP头如下: 





    怎样修改浏览器标识

      在HTTP请求头中User-Agent字段表明了浏览器的版本以及操作系统的版本等信息。WEB服务器经常需要知道用户请求页面时是来自IE还是来自自己的客户端中的WebBrowser控件, 以便分开处理,而WebBrowser控件向WEB服务器发送的浏览器标识(User-Agent字段)跟用IE发送的是一样的,怎样区分自己的浏览器和IE呢? 微软没有提供现成的方法,要自己想法解决。 前面讨论的定制HTTP请求头就是为这一节准备的。 思路是这样的: 在自己的浏览器里处理每一个U页面请求,把请求头User-Agent改成自己想要的。 在CHtmlViewOnBeforeNavigate2虚函数里来修改HTTP请求是再好不过了, 

    #define WM_NVTO(WM_USER+1000)

    class NvToParam

    {

    public:

    CString URL;

    DWORD Flags;

    CString TargetFrameName;

    CByteArray PostedData;

    CString Headers;

    };

    void CDemoView::OnBeforeNavigate2(LPCTSTR lpszURL, 

                                      DWORD nFlags, 

                                      LPCTSTR lpszTargetFrameName, 

                                      CByteArray& baPostedData, 

                                      LPCTSTR lpszHeaders, 

                                      BOOL* pbCancel)

    {

    CString strHeaders(lpszHeaders);

    if(strHeaders.Find("User-Agent:LHPBrowser 1.0") < 0)// 检查头里有没有自定义的User-Agent串

    {

    *pbCancel = TRUE;// 没有,取消这次导航

    if(!strHeaders.IsEmpty())

    strHeaders += "\r\n";

    strHeaders += "User-Agent:LHPBrowser 1.0";// 加上自定义的User-Agent串

    NvToParam* pNvTo = new NvToParam;

    pNvTo->URL = lpszURL;

    pNvTo->Flags = nFlags;

    pNvTo->TargetFrameName = lpszTargetFrameName;

    baPostedData.Copy(pNvTo->PostedData);

    pNvTo->Headers = strHeaders;

    // 发送一个自定义的导航消息,并把参数发过去

    PostMessage(WM_NVTO,(WPARAM)pNvTo);

    return;

    }

    CHtmlView::OnBeforeNavigate2(lpszURL, 

                                 nFlags, 

                                 lpszTargetFrameName, 

                                 baPostedData, 

                                 lpszHeaders, 

                                 pbCancel);

    }

    LRESULT CDemoView::OnNvTo(WPARAM wParam, LPARAM lParam)

    {

    NvToParam* pNvTo = (NvToParam*)wParam;

    Navigate2((LPCTSTR)pNvTo->URL, 

               pNvTo->Flags, 

               pNvTo->PostedData, 

               (LPCTSTR)pNvTo->TargetFrameName, 

               (LPCTSTR)pNvTo->Headers);

    delete pNvTo;

    return 1;

    }

    OnBeforeNavigate2中如果发现没有自定义的User-Agent串,就加上这个串,并取消本次导航,再Post一个消息(一定要POST,让OnBeforeNavigate2跳出以后再进行导航 ),在消息中再次导航,再次导航时请求头已经有了自己的标识,所以能正常的导航。 


    去掉讨厌的异常警告 

      在程序中使用了CHtmlView以后,我们在调整窗口大小的时候经常会看到输出窗口输出的异常警告: ReusingBrowser.exe 中的 0x77e53887 处最可能的异常: Microsoft C++ exception: COleException @ 0x0012e348 。 

    Warning: constructing COleException, scode = DISP_E_MEMBERNOTFOUND($80020003).

    这是由于CHtmlView在处理WM_SIZE消息时的一点小问题引起的,采用如下代码处理WM_SIZE消息就不会有此警告了 

    void CLhpHtmlView::OnSize(UINT nType, int cx, int cy)

    {

    CFormView::OnSize(nType, cx, cy);

    if (::IsWindow(m_wndBrowser.m_hWnd)) 

    CRect rect; 

    GetClientRect(rect); 

    // 就这一句与CHtmlView的不同

    ::AdjustWindowRectEx(rect, GetStyle(), FALSE, WS_EX_CLIENTEDGE);

    m_wndBrowser.SetWindowPos(NULL, 

                              rect.left, 

                              rect.top, 

                              rect.Width(), 

                              rect.Height(), 

                              SWP_NOACTIVATE | SWP_NOZORDER); 

    }


    怎样处理浏览器内的拖放 

      有时可能有这样的需求,我们希望在资源管理器里托一个文件到浏览器而做出相应的处理,甚至是将文件拖到某一个网页元素上来做出相应的处理,而浏览器默认的处理拖放文件操作是将文件打开,但WebBrowser控件给了我们一个自己处理拖放的机会。 那就是在自定义的控制站点类中实现IDocHostUIHandler,在接口IDocHostUIHandlerGetDropTarget方法中调用 浏览器类的OnGetDropTarget虚函数。要处理网页内的拖放,必需在OnGetDropTarget函数中返回一个自己定义的IDropTarget接口指针, 所以我们自己写一个类CMyOleDropTargetCOleDropTarget类派生,并且在实现IDropTarget接口,此类的代码在这就不列出了,请下载演示 程序,参考文件MyOleDropTarget.hMyOleDropTarget.cpp。我们看CLhpHtmlViewOnGetDropTarget的代码 

    HRESULT CLhpHtmlView::OnGetDropTarget(LPDROPTARGET pDropTarget, LPDROPTARGET* ppDropTarget )

    {

    m_DropTarget.SetIEDropTarget(pDropTarget);

    LPDROPTARGET pMyDropTarget;

    pMyDropTarget = (LPDROPTARGET)m_DropTarget.GetInterface(&IID_IDropTarget);

    if(pMyDropTarget)

    {

    *ppDropTarget = pMyDropTarget;

    pMyDropTarget->AddRef();

    return S_OK;

    }

    return S_FALSE;

    }

    m_DropTarget即为自定义的处理拖放的对象。这样就能通过在从CLhpHtmlView派生的类中重载OnDragEnterOnDragOver、 OnDropOnDragLeave虚函数来处理拖放了。在这里顺带讲一下视图是怎样处理拖放的。 要使视图处理拖放,首先在视图里添加一个COleDropTarget(或派生类)成员变量,如CLhpHtmlView中的“CMyOleDropTarget m_DropTarget;”,再在 视图创建时调用COleDropTarget对象的Register,即把视图与COleDropTarget对象关联起来,如CLhpHtmlView中的“m_DropTarget.Register(this);”,再对拖放 触发的事件进行相应的处理, OnDragEnter 把某对象拖入到视图时触发,在此检测拖入的对象是不是视图想接受的对象,如是返回“DROPEFFECT_MOVE”表示接受此对象,如 

    if(pDataObject->IsDataAvailable(CF_HDROP))// 被拖对象是文件吗?

    return DROPEFFECT_MOVE;

    OnDragOver 被拖对象在视图上移动,同OnDragEnter一样检测拖入对象,如果要接受此对象返回“DROPEFFECT_MOVE”。 OnDrop 拖着被拖对象在视图上放开鼠标,在这里对拖入对象做出处理; OnDragLeave 拖着被拖对象离开视图。 C++的代码写好了,但事情还没完,还必须在网页里用脚本对拖放事件进行处理, 即页面里哪个元素要接受拖放对象哪个元素就要处理ondragenterondragoverondrop,代码其实很简单,让事件的返回值为false即可,这样 C++的代码才有机会处理拖放事件,代码如下: 

    ......

    <td οndragenter="event.returnValue = false" οndragοver="event.returnValue = false" \

    οndrοp="event.returnValue = false">

    ......

    如果要使整个视图都接受拖放,则在Body元素中处理此三个事件。 注意:别忘了让工程对OLE的支持即在初始化应用程序时调用AfxOleInit()


    怎样禁止网页元素的选取

      用网页做界面时多数情况下是不希望网页上的元素是能够被鼠标选中的, 要使网页元素不能被选中做法是:给浏览器的宿主信息标记加上DOCHOSTUIFLAG_DIALOG标记。

    宿主信息标记N个标记位来控制浏览器的许多性质,如: 

    · 禁用浏览器的3D的边缘; 

    · 禁止滚动条; 

    · 禁用脚本; 

    · 定义双击处理的方式; 

    · 禁用浏览器的自动完成功能; 

    ...... 更多详情请参考MSDNDOCHOSTUIFLAG帮助。

    怎样修改宿主信息标记

    CDocHostSite中实现IDocHostUIHandler, 在GetHostInfo方法中调用浏览器的OnGetHostInfo虚函数,在虚函数OnGetHostInfo中便可指定宿主信息标记,如:

    HRESULT CLhpHtmlView::OnGetHostInfo(DOCHOSTUIINFO * pInfo)

    {

    pInfo->cbSize = sizeof(DOCHOSTUIINFO);

    pInfo->dwFlags = DOCHOSTUIFLAG_DIALOG | 

                        DOCHOSTUIFLAG_THEME  | 

                        DOCHOSTUIFLAG_NO3DBORDER | 

                        DOCHOSTUIFLAG_SCROLL_NO;

    pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;

    return S_OK;

    }

    用脚本也可实现: 在Head中加入脚本

    document.onselectstart=new Function(''return false'');

    或者 

    <body onselectstart="return false">。


    其它 

    CLhpHtmlView中还提供了几个函数, 修改网页元素的内容: 

    BOOL PutElementHtml(CString ElemID,CString Html);

    取表单元素的值: 

    BOOL GetElementValue(CString ElemID,CString& Value);

    设置表单元素的值: 

    BOOL PutElementValue(CString ElemID,CString Value);

    给表单元素设置焦点: 

    void ElementSetFocus(CString EleName);

    转载:http://www.vckbase.com/document/viewdoc/?id=1486

    ————————————————————————————————————————————————————————————————

    自定义浏览器

    本教程提供了自定义浏览器控件的行为和外观的一些方法。你将看到高级的宿主接口,IDocHostUIHandler, IDocHostUIHandler2, IDocHostShowUI, 和ICustomDoc。本文也讨论其他自定义方法,例如在宿主的IDispatch实现中处理DISPID_AMBIENT_DLCONTROL来进行下载控制;以及使用IHostDialogHelper。

    本文分为如下章节

    · 前提和需求

    · 介绍

    · 浏览器自定义架构

    · IDocHostUIHandler

    · IDocHostUIHandler2

    · GetOptionKeyPath 和 GetOverrideKeyPath比较

    · 控制导航

    · IDocHostShowUI

    · 控制下载和执行

    · IHostDialogHelper

    · 控制新的窗口

    · 显示证书对话框(New!) 

    · 信息栏(New!)

    · 结论

    前提和需求

    为了理解和使用本教程,你需要

    · 对C++和COM的深入了解

    · 

    · 熟悉活动模板库 (ATL)

    · 

    · 安装了Microsoft(R) Internet Explorer (IE)6 或更高版本

    · 

    · 开发环境具有用于IE6或更高版本的头文件和库文件;特别是Mshtmhst.h.译者注:可以在http://www.microsoft.com/msdownload/platformsdk/sdkupdate/ 这里下载最新的Internet Development SDK

    许多自定义特性是在IE5或者5.5版本就可以使用的,但是有几个特性需要IE6。一些特性需要IE6的Windows XP SP2版本。使用某个特性之前,应该检查参考文档以获得版本信息。

    介绍

    集成浏览器控件是快速软件开发的强有力的工具。通过成为浏览器的宿主,你可以利用便于使用的Dynamic HTML (DHTML), HTML, 和Extensible Markup Language (XML)来显示信息和开发一个用户界面。但是,浏览器控件的行为可能不确切符合你的需求。例如,默认的状态允许用户通过快捷菜单的查看源代码选项查看一个显示的页面的源代码,你可能需要禁用或者干脆去掉这个选项。你可能更进一步,需要用你自己的快捷菜单替换默认的快捷菜单。

    在刚刚提到的自定义特性之外,高级宿主特性允许

    · 在显示的页面上的按钮和其他控件可以调用你的应用程序的内建方法,有效地扩展DHTML对象模型(DOM)。

    · 改变拖放的行为

    · 限制浏览器的导航,例如,限制于指定的页面/域,或者站点

    · 捕获用户键入,并且在需要的时候处理。比如说,你可能需要捕获CTRL+O来阻止用户在新的IE中打开网页而不是使用你的程序打开,

    · 改变默认字体和显示设置

    · 控制下载内容,以及当下载完成之后浏览器的处理。例如,你可能禁用视频的播放,脚本的执行,点击链接时打开新的窗口,或者Microsoft(R) ActiveX 控件的下载和执行。

    · 限制查看源代码

    · 捕获搜索

    · 捕获导航错误

    · 替代/修改快捷菜单或者禁用,替代,自定义,或者添加快捷菜单项

    · 为你的应用程序改变注册表设定

    · 控制和修改浏览器控件显示的消息框

    · 控制新窗口的创建方式

    在下列中,我们将会看到多数,但是不是全部的这些可能性而且讨论该如何实现他们。

    浏览器自定义架构

    介绍 IDocHostUIHandler , IDocHostUIHander2 , IDocHostShowUI 和 ICustomDoc

    下面三个接口是浏览器控件用户界面的自定义核心:IDocHostUIHandler ,IDocHostUIHandler2 和 IDocHostShowUI。当你修改浏览器控件的时候 , 这些是你在你的应用程序中实现的接口。也有一些服务接口。 ICustomDoc 被MSHTML实现并且提供一个方法在某些情况下能够自定义浏览器控件。IHostDialogHelper提供一个方法打开可信对话框,没有像IE对话框那样为他们(译者注:在标题栏上)作标记。

    除了使用这些接口,你还可以做其他件事。一,你能通过在IDispatch实现中拦截环境特性的变化来控制下载其次,你能通过在IDispatch实现中拦截DISPID_NEWWINDOW2控制窗口的创建方式

    译者注:MFC7中的DHTML类,例如CHtmlView和CDHtmlDialog实现了这些接口,但是对于使用其他的类库的程序员,可能需要自己实现这些接口。

    如何工作

    当一个容器提供对ActiveX 控件支持时候 , 浏览器控件自定义机制被设计为被自动化。当浏览器控件被实例化的时候,如果可能的话,它尝试找来自宿主的 IDocHostUIHandler , IDocHostUIHandler2 和 IDocHostShowUI 实现浏览器控件通过调用宿主的IOleClientSite接口的一个QueryInterface方法来查找。

    译者注:IE5.5有个Bug,没有查询IDocHostUIHandler2 接口的实现,这使得宿主程序不能覆盖默认的参数。需要更多信息的话,参考微软知识库文章 Q272968 BUG:IDocHostUIHandler2 没有在浏览器控件中调用。

    这一个结构为一个实现一个IOleClientSite接口的应用程序自动地工作,通过调用浏览器的IOleObject::SetClientSite方法传递给浏览器控件一个IOleClientSite接口。浏览器控件的一个典型的实例化可能看起来像这样:

    例子

    //为了明确起见,省略错误检查

    CComPtr<IOleObject> spOleObj;

    //创建 WebBrowser--类成员变量 m_spWebBrowser中保存指针

    CoCreateInstance(CLSID_WebBrowser, NULLCLSCTX_INPROCIID_IWebBrowser2, (void**)&m_spWebBrowser);

    // 查询WebBrowserIOleObject接口

    m_spWebBrowser->QueryInterface(IID_IOleObject, (void**)&spOleObj);

    //设置用户站点

    spOleObj->SetClientSite(this);

    //本地激活浏览器控件

    RECT rcClient

    GetClientRect(&rcClient);

    spOleObj->DoVerb(OLEIVERB_INPLACEACTIVATENULLthis, 0, GetTopLevelWindow(), &rcClient);

    //容器拦截浏览器事件的注册

    AtlAdvise(m_spWebBrowser,GetUnknown(), DIID_DWebBrowserEvents2,&m_dwCookie);

    //导航到启动页

    m_spWebBrowser->Navigate(L"res://webhost.exe/startpage.htm", NULLNULLNULLNULL);

    然而,如果你的应用程序没有一个IOleClientSite接口,你并没失去全部希望。IE提供ICustomDoc接口,这样你能自己传递你的IDocHostUIHandler接口给浏览器。你不能使用IDocHostUIHandler2和 IDocHostShowUI接口而不提供一个浏览器控件宿主的IOleClientSite接口。

    译者注:

    MFC7中引入的类COleControlContainer和一大堆DHTML类曾经搞得我晕头转向,最后我不得不放弃了自己对IOleClientSite的实现,而通过ICustomDoc来显式地设置IDocHostUIHandler接口。这样必须在第一个页面下载完成之后才能够开始自定义浏览器,因为暴露ICustomDoc接口的对象只有在第一个页面下载完成之后才可用。一个ICustomDoc的示例可以在CSDN文档中心找到,网址是http://www.csdn.net/develop/Read_Article.asp?Id=8813

    当浏览器控件获得了对这些接口之中的任何一个的一个指针的时候,接口的方法在适当的时候在浏览器控件的生命期中被调用。举例来说, 当用户右击在浏览器控件的客户区的任何地点时,在IE显示它的默认快捷菜单之前,你的IDocHostUIHandler::ShowContextMenu的实现将会被调用。这给你一个机会显示你自己的快捷菜单而且取消IE的快捷菜单显示。

    译者注:一些屏蔽快捷菜单的示例可以在CSDN文档中心找到,网址是http://www.csdn.net/develop/article/18/18541.shtm

    当初始化浏览器控件的时候 ,记住几个重点。你的应用程序应该使用 OleInitialize而不是CoInitialize启动COM。OleInitialize启用剪贴簿支持,拖放,对象连接与嵌入(OLE)和本地激活。当你的应用程序结束的时候使用OleUninitialize关闭COM库。

    ATL COM 向导使用 CoInitialize而不是OleInitialize打开COM库。 如果你使用这一个向导建立一个可运行的程序,你需要将 CoInitialize 和 CoUninitialize 调用换成 OleInitialize 和 OleUninitialize。对于一个微软基础类 (MFC) 应用程序, 确定你的应用程序调用 AfxOleInit, 它在它的初始化程序中调用OleInitialize。

    如果你不需要在你的应用程序中支持拖放,你可以调用IWebBrowser2::RegisterAsDropTarget,传递VARIANT_TRUE(译者注:原文如此,按照接口文档,似乎应该传递VARIANT_FALSE), 避免任何在你的浏览器控件实例上的拖放操作。

    一个浏览器控件宿主应用程序也需要IOleInPlaceSite的一个实现, 由于 IOleInPlaceSite派生自IOleWindow,应用程序将需要IOleWindow的一个实现。你需要这些实现使得你的应用程序具有一个窗口,显示浏览器控件,以及处理它的显示设置。

    这些接口和IOleClientSite的实现在许多情况可能是最小的或不存在的。IOleClientSite的所有方法都可以返回E_NOTIMPL。 一些IOleInPlaceSite和IOleWindow的方法需要一个实现来覆盖返回值。可以在示例代码中查看IOleInPlaceSite和IOleWindow的最小实现的样例代码。

    既然我们已经完成了初始化的准备,让我们看一看浏览器控件自定义的每一个接口。

    IDocHostUIHandler

    IDocHostUIHandler自IE5以后已经是可用的。它提供15个方法。大体上,一些较重要的方法是IDocHostUIHandler::GetExternal, IDocHostUIHandler::GetHostInfo, IDocHostUIHandler::GetOptionKeyPath, IDocHostUIHandler::ShowContextMenu, 和 IDocHostUIHandler::TranslateAccelerator。当然,方法对你的重要性将会依赖于你的应用程序。

    IDocHostUIHandler::GetHostInfo

    你使用IDocHostUIHandler::GetHostInfo告诉MSHTML有关你的应用程序的能力和需求。通过它你能控制很多东西, 举例来说。

    · 你能禁用浏览器的3D的边缘。

    · 你能避免滚动条或改变他们的外观

    · 你能禁用脚本

    · 你能定义双击处理的方式

    · 你能禁用浏览器的自动完成功能。

    IDocHostUIHandler::GetHostInfo有一个参数,被 MSHTML分配的DOCHOSTUIINFO 结构的一个指针。你的工作要将结构中填充你传给MSHTML的信息。

    DOCHOSTUIINFO结构有四个成员。第一个成员是 cbSize,是结构的大小。你应该自己像下面的示例代码那样设置。第二个成员是dwFlags,由来自DOCHOSTUIFLAG枚举的数值位与组成。第三成员是dwDoubleClick,来自DOCHOSTUIDBLCLK枚举的一个数值。第四个成员是pchHostCss。你可以将pchHostCss设定为浏览器控件显示的页面中应用的全局样式表(CSS)规则的一个字符串的指针。DOCHOSTUIINFO 的最后一个成员是pchHostNs。你可以设置为你提供的分号分隔的命名空间列表字符串。在你正在浏览器控件中显示的页上使用自定义标签的时候使用这一个成员。这样你能声明一个全局的命名空间列表,而不需要在每个显示声明他们。

    确定使用CoTaskMemAlloc为pchHostCss或pchHostNS分配字符串译者注:看起来调用者用CoTaskMemFree释放这些字符串)。

    例子

    HRESULT GetHostInfo( DOCHOSTUIINFO* pInfo)

    {

        WCHAR* szCSS = L"BODY {background-color:#ffcccc}";

        WCHAR* szNS = L"IE;MyTags;MyTags2='www.microsoft.com'";

        #define CCHMAX 256

        size_t cchLengthCSS,cchLengthszNS;

        HRESULT hr=StringCchLengthW(szCSS, CCHMAX,&cchLengthCSS)

        //TODO: 在这里处理错误。

        OLECHAR* pCSSBuffer=(OLECHAR*) CoTaskMemAlloc((cchLengthCSS+1)*sizeof(OLECHAR));

        //TODO: 在这里处理错误确定内存成功地被分配

        hr=StringCchLengthW(szNS, CCHMAX,&cchLengthszNS)

        //TODO: 在这里处理错误。

        OLECHAR* pNSBuffer=(OLECHAR*) CoTaskMemAlloc((cchLengthszNS+1)*sizeof(OLECHAR));

        //TODO: 在这里处理错误确定内存成功地被分配

        hr=StringCchCopyW(pCSSBuffer , cchLengthCSS+1,szCSS)

        //TODO: 在这里处理错误。

        hr=StringCchCopyW(pNSBuffer , cchLengthszNS+1,szNS)

        //TODO: 在这里处理错误。

        pInfo-> cbSize= sizeof(DOCHOSTUIINFO)

        pInfo-> dwFlags=DOCHOSTUIFLAG_NO3DBORDER|DOCHOSTUIFLAG_SCROLL_NO|DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE;

        pInfo-> dwDoubleClick= DOCHOSTUIDBLCLK_DEFAULT;

        pInfo-> pchHostCss= pCSSBuffer;

        pInfo-> pchHostNS= pNSBuffer;

        return S_OK;

    }

    如果你没有什么需要告诉MSHTML,你可以在这个方法中返回E_NOTIMPL 。

    IDocHostUIHandler::ShowContextMenu

    通过实现这一个方法, 你获得一个用户右击被浏览器控件显示的快捷菜单的控制。你能通过这个方法返回S_OK 阻止IE显示它的默认快捷菜单。返回一些其他的数值 , 像S_FALSE或E_NOTIMPL,允许IE继续执行它的默认快捷菜单行为。

    如果你仅仅在这个方法中返回S_OK, 你能避免任何浏览器控件的右击行为。 这可能是你在许多场合中的全部需求,但是你能做更多。通常,你使用这一个方法在返回 S_OK 之前产生并且显示你自己的快捷菜单。如果你知道浏览器控件显示的菜单的资源,而且它如何选择他们,你能也有效地自定义默认的浏览器控件快捷菜单。让我们看看它如何工作

    浏览器控件由Shdoclc.dll获得它的快捷菜单资源。这个知识和一些 #define给予你一个机会操纵浏览器的菜单。让我们举例来说,假定你对默认菜单感到满意,除了你想要除去查看代码之外。下列代码载入来自Shdoclc.dll的浏览器控件快捷菜单资源,根据环境选择正确的菜单,移除IDM_VIEWSOURCE命令应的菜单项,然后显示菜单

    例子

    HRESULT CBrowserHost::ShowContextMenu(DWORD dwID,

        POINT *ppt,

        IUnknown *pcmdTarget,

        IDispatch *pdispObject)

    {

        #define IDR_BROWSE_CONTEXT_MENU 24641

        #define IDR_FORM_CONTEXT_MENU 24640

        #define SHDVID_GETMIMECSETMENU 27

        #define SHDVID_ADDMENUEXTENSIONS 53

        HRESULT hr;

        HINSTANCE hinstSHDOCLC;

        HWND hwnd;

        HMENU hMenu;

        CComPtr<IOleCommandTarget> spCT;

        CComPtr<IOleWindow> spWnd;

        MENUITEMINFO mii={0};

        CComVariant var, var1, var2;

        hr = pcmdTarget->QueryInterface(IID_IOleCommandTarget, (void**)&spCT);

        hr = pcmdTarget->QueryInterface(IID_IOleWindow, (void**)&spWnd);

        hr = spWnd->GetWindow(&hwnd);

        hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL"));

        if (hinstSHDOCLC == NULL)

        {

            // 载入模块错误 -- 尽可能安全地失败

            return;

        }

        hMenu=LoadMenu(hinstSHDOCLC,

            MAKEINTRESOURCE(IDR_BROWSE_CONTEXT_MENU));

        hMenu=GetSubMenu(hMenu,dwID);

        //获得语言子菜单

        hr = spCT->Exec(&CGID_ShellDocViewSHDVID_GETMIMECSETMENU, 0, NULL, &var);

        mii.cbSize = sizeof(mii);

        mii.fMask = MIIM_SUBMENU;

        mii.hSubMenu = (HMENU) var.byref;

        //加入语言子菜单到编码上下文菜单

        SetMenuItemInfo(hMenu, IDM_LANGUAGE, FALSE, &mii);

        //插入来自注册表的快捷菜单扩展

        V_VT(&var1) = VT_INT_PTR;

        V_BYREF(&var1) = hMenu;

        V_VT(&var2) = VT_I4;

        V_I4(&var2) = dwID;

        hr = spCT->Exec(&CGID_ShellDocViewSHDVID_ADDMENUEXTENSIONS, 0, &var1, &var2);

        //删除查看源代码

        DeleteMenu(hMenu, IDM_VIEWSOURCE, MF_BYCOMMAND);

        //显示快捷菜单

        int iSelection = ::TrackPopupMenu(hMenu,

            TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,

            ppt->x,

            ppt->y,

            0,

            hwnd,

            (RECT*)NULL);

        //发送选定的快捷菜单项目指令到外

        LRESULT lr = ::SendMessage(hwnd, WM_COMMAND, iSelection, NULL);

        FreeLibrary(hinstSHDOCLC);

        return S_OK;

    }

    安全警告不正确地使用LoadLibrary能载入错误的动态链接库(DLL)来威胁你的应用程序的安全关于该如何正确地用微软Windows 的不同版本载入DLL的信息,参照LoadLibrary的文档

    IDocHostUIHandler::GetExternal: 扩充文档对象模型

    IDocHostUIHandler 提供一个让你用在你自己的应用程序中实现你自己的对象,方法和特性扩充IE文档对象模型 (DOM)的方法。你的实现是提供给MSHTML一个IDispatch接口指针,指向你自定义的COM自动化对象,实现你自定义的对象、属性和方法。这些对象,特性和方法之后可以在浏览器控件显示的任何页面中通过文档的外部对象访问

    这一个方法的实现可以是非常简单的, 假定你的IDispatch接口在实现IDocHostUIHandler的相同对象上。

    HRESULT CBrowserHost::GetExternal(IDispatch **ppDispatch)

    {

        *ppDispatch = this;

        return S_OK;

    }

    只要 MSHTML有对你的 IDispatch 的一个指针,MSHTML将会传递网页上对任何外部对象的调用你的应用程序的自动化方法:

    <SCRIPT language="JScript">

    function MyFunc(iSomeData)

    {

        external.MyCustomMethod("Some text", iSomeData);

    }

    </SCRIPT>

    你也能使用这技术传递整个对象一个页。为了实现它,在你的IDispatch实现中创建一个方法,传递回你的网页可以用的对象。

    <SCRIPT language="JScript">

    function MyFunc(iSomeData)

    {

        var oCustCalendarObj;

        external.GetCustomCalender(oCustCalenderObj);

        oCustCalerdarObj.doStuffWithIt();

    }

    </SCRIPT>

    可以看看示例代码中使用 ATLIDispatch自动化实现的一个例子 。

    译者注:IE也扩展了浏览器的文档对象模型,使得你在脚本中可以通过扩展对象menuArguments属性访问当前窗口对象。

    IDocHostUIHandler::GetOptionKeyPath

    IDocHostUIHandler::GetOptionKeyPath是自定义浏览器控件的一个非常有力的工具。 许多浏览器控件显示行为设定被储存在注册表中HKEY_CURRENT_USER键的下面。IDocHostUIHandler::GetOptionKeyPath给你一个机会为你的浏览器控件的特定实例覆盖这些注册表设定。它通过提供一个替代的注册表位置来实现,浏览器控件将会在这里读取注册表设置

    IDocHostUIHandler::GetOptionKeyPath的一个实现传递给浏览器控件读取注册表设置的位置的一个字符串。浏览器控件将会找寻在HKEY_CURRENT_USER键下面的这一个键。

    例子

    HRESULT CBrowserHost::GetOptionKeyPath(LPOLESTR *pchKey,

        DWORD dwReserved)

    {

        HRESULT hr;

        #define CCHMAX 256

        size_t cchLength;

        if (pchKey)

        {

            WCHAR* szMyKey = L"Software\MyCompany\MyApp";

            hr = StringCchLengthW(szMyKey, CCHMAX, &cchLength);

            //TODO: 在这里处理错误。

            *pchKey = (LPOLESTR)CoTaskMemAlloc((cchLength + 1) * sizeof(WCHAR));

            if (*pchKey)

                hr = StringCchCopyW(*pchKey, cchLength + 1, szKey);

            //TODO: 在这里处理错误。

            hr = (*pchKey) ? S_OK : E_OUTOFMEMORY;

        }

        else

            hr = E_INVALIDARG;

        return hr;

    }

    IDocHostUIHandler::GetHostInfo一样,确保为你的字符串使用 CoTaskMemAlloc分配内存。

    告诉浏览器控件该哪里找寻你的注册表设置实际上是第一步——就程序运行来说第二步。 你的程序必须在被IDocHostUIHandler::GetOptionKeyPath告诉的位置设置一个注册表,这样浏览器控件才可以去读取。有多种方法来完成这个步骤一个方法是当应用程序被安装的时候执行一个注册表脚本。另外的一个方法当应用程序启动的时候,用代码来完成。这里是改变默认值字体,大小和颜色的一个设定。

    例子

    HRESULT SetSomeKeys()

    {

        HKEY hKey = NULL;

        HKEY hKey2 = NULL;

        HKEY hKey3 = NULL;

        DWORD dwDisposition = NULL;

        LONG lResult = NULL;

        #define CBMAX 256

        size_t cbLength;

        RegCreateKeyEx(HKEY_CURRENT_USER, _T("Software\MyCompany\MyApp"),

            NULLNULLREG_OPTION_NON_VOLATILEKEY_SET_VALUE,

            NULL, &hKey, &dwDisposition);

        RegCreateKeyEx(hKey, _T("Main"), NULLNULLREG_OPTION_NON_VOLATILE,

            KEY_SET_VALUENULL, &hKey2, &dwDisposition);

        RegSetValueEx(hKey2, _T("Use_DlgBox_Colors"), NULLREG_SZ,

            (CONST BYTE*)_T("no"), sizeof(_T("no")));

        RegCloseKey(hKey2);

        RegCreateKeyEx(hKey, _T("Settings"), NULLNULLREG_OPTION_NON_VOLATILE,

            KEY_SET_VALUENULL, &hKey2, &dwDisposition);

        RegSetValueEx(hKey2, _T("Anchor Color"), NULLREG_SZ,

            (CONST BYTE*)_T("0,255,255"), sizeof(_T("0,255,255")));

        RegSetValueEx(hKey2, _T("Text Color"), NULLREG_SZ,

            (CONST BYTE*)_T("255,0,255"), sizeof(_T("255,0,255")));

        RegCloseKey(hKey2);

        RegCreateKeyEx(hKey, _T("International\Scripts"), NULLNULL,

            REG_OPTION_NON_VOLATILEKEY_SET_VALUENULL,

            &hKey2, &dwDisposition);

        BYTE bDefaultScript = 0x3;

        RegSetValueEx(hKey2, _T("Default_Script"), NULLREG_BINARY,

            &bDefaultScript, sizeof(bDefaultScript));

        RegCreateKeyEx(hKey2, _T("3"), NULLNULLREG_OPTION_NON_VOLATILE,

            KEY_SET_VALUENULL, &hKey3, &dwDisposition);

        BYTE bSize = 0x4; // Value from 0 - 4. 2 is medium.

        TCHAR* szFontName = _T("Comic Sans MS");

        TCHAR* szFixedFontName = _T("Courier");

        HRESULT hr = StringCbLength(szFontName, CBMAX, &cbLength);

        //TODO: 在这里处理错误。

        RegSetValueEx(hKey3, _T("IEPropFontName"), NULLREG_SZ,

            (CONST BYTE*)szFontName, cbLength + sizeof(TCHAR));

        hr = StringCbLength(szFixedFontName, CBMAX, &cbLength);

        //TODO: 在这里处理错误。

        RegSetValueEx(hKey3, _T("IEFixedFontName"), NULLREG_SZ,

            (CONST BYTE*)szFixedFontName, cbLength + sizeof(TCHAR));

        RegSetValueEx(hKey3, _T("IEFontSize"), NULLREG_BINARY, &bSize, sizeof(bSize));

        RegCloseKey(hKey3);

        RegCloseKey(hKey2);

        RegCloseKey(hKey);

        return S_OK;

    }

    IDocHostUIHandler2

    IDocHostUIHandler2 只有一个方法,IDocHostUIHandler2::GetOverrideKeyPath。它运行非常类似于IDocHostUIHandler::GetOptionKeyPath的一个功能。它指出你修改自默认注册表设置的集成浏览器使用的注册表设置的位置。IDocHostUIHandler2::GetOverrideKeyPath 的一个实现看起来会很类似于IDocHostUIHandler::GetOptionKeyPath的一个实现。

    GetOptionKeyPath 和 GetOverrideKeyPath 的比较

    你或许没看到IDocHostUIHandler::GetOptionKeyPath和IDocHostUIHandler2::GetOverrideKeyPath之间的任何不同。在他们之间的不同是微妙的, 但是重要的。如果你实现 IDocHostUIHandler::GetOptionKeyPath,你的浏览器控件实例将会忽略任何IE用户设定。这些设定被储存在注册表HKEY_CURRENT_USER/Software/Microsoft/Internet Explorer下面。如果你实现IDocHostUIHandler2::GetOverrideKeyPath,你的浏览器控件实例将会合并任何的用户设定—字体设定,菜单扩展,诸如此类——到显示和行为中

    举例说明在IDocHostUIHandler::GetOptionKeyPath和IDocHostUIHandler2::GetOverrideKeyPath之间的不同,让我们重新看看IDocHostUIHandler::ShowContextMenu那段的示例代码记住这一行:

    spCT->Exec(&CGID_ShellDocView, SHDVID_ADDMENUEXTENSIONS, 0, &var1, &var2);

    如果你已经实现IDocHostUIHandler::GetOptionKeyPath,因为菜单扩展信息被储存在当前用户的注册表信息中,所以这一加入任何自定义项目到快捷菜单。如果你已经实现IDocHostUIHandler2::GetOverrideKeyPath, 这一个添加在HKEY_CURRENT_USER/Software/Microsoft/Internet Explorer/MenuExt面定义的任何目前用户定义的菜单扩展, 除非你明确地在你的自定义注册信息位置提供一个空的或替代的MenuExt键。

    控制导航

    你可能想知道在IDocHostUIHandler那一节为什么不提到 IDocHostUIHandler::TranslateUrl,作为希望制页面导航实现方法。原因是这一个方法不是控制导航通用的技术。 除非你直接地集成MSHTML,这一个方法将没有控制导航的效果。作为替代,通过实现IDispatch::Invoke,处理DISPID_BEFORENAVIGATE2,你可以控制导航。例如,下列代码避免导航一个特别的网址,如果使用者尝试这么做,会显示 "没有允许导航" 错误页。

    例子

    case DISPID_BEFORENAVIGATE2:

    {

        CComBSTR url = ((*pDispParams).rgvarg)[5].pvarVal->bstrVal;

        if (url == "http://www.adatum.com" || url == "http://www.adatum.com/")

        {

            CComPtr<IWebBrowser2> spBrowser;

            CComPtr<IDispatch> spDisp = ((*pDispParams).rgvarg)[6].pdispVal;

            spDisp->QueryInterface(IID_IWebBrowser2, (void**)&spBrowser);

            spBrowser->Stop();

            CComBSTR newURL = "L"res://webhost.exe/nonavigate.htm";

            spBrowser->Navigate(newURL, NULLNULLNULLNULL);

            ((*pDispParams).rgvarg)[0].boolVal = TRUE;

        }

        break;

    }

    IDocHostShowUI

    这个接口给你对浏览器控件显示信息对话框和帮助的控制。它工作机理和IDocHostUIHandler和IDocHostUIHandler2一样,你实现它,这样在浏览器控件显示它自己的任何的信息或帮助之前 ,能调用你的IDocHostShowUI方法。这给你一个机会阻止浏览器控件显示任何东西,而且使你能够改为显示你自己的自定义信息或帮助。 IDocHostShowUI有两个方法,IDocHostShowUI::ShowMessage和IDocHostShowUI::ShowHelp。

    IDocHostShowUI::ShowMessage

    返回 S_OK禁用浏览器控件信息对话框。任何其他的返回数值,像S_FALSE或E_NOTIMPL,允许浏览器控件显示它的信息对话框

    通过这个方法能做的一件事情是为你的应用程序自定义信息框标题,替代 "Microsoft Internet Explorer" 。你能通过比较lpstrCaption和储存在Shdoclc.dll中的IE使用的字符串资源来完成它。它的ID是IDS_MESSAGE_BOX_TITLE,数值是2213。下列示例代码演示你可能需要做的工作

    例子

    HRESULT CBrowserHost::ShowMessage(HWND hwnd,

        LPOLESTR lpstrText,

        LPOLESTR lpstrCaption,

        DWORD dwType,

        LPOLESTR lpstrHelpFile,

        DWORD dwHelpContext,

        LRESULT *plResult)

    {

        USES_CONVERSION;

        TCHAR pBuffer[50];

        // 窗口标题"Microsoft Internet Explorer"的资源标识

        #define IDS_MESSAGE_BOX_TITLE 2213

        //载入Shdoclc.dll IE消息框标题字符串

        HINSTANCE hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL"));

        if (hinstSHDOCLC == NULL)

        {

            // 载入模块错误 -- 尽可能安全地失败

            return;

        }

        LoadString(hinstSHDOCLC, IDS_MESSAGE_BOX_TITLE, pBuffer, 50);

        // 比较IE消息框标题字符串和lpstrCaption

        // 如果相同,用自定义标题替换

        if (_tcscmp(OLE2T(lpstrCaption), pBuffer) == 0)

            lpstrCaption = L"Custom Caption";

        // 创建自己的消息框并且显示

            *plResult = MessageBox(OLE2T(lpstrText), OLE2T(lpstrCaption), dwType);

        //卸载Shdoclc.dll并且返回

        FreeLibrary(hinstSHDOCLC);

        return S_OK;

    }

    安全警告不正确地使用LoadLibrary能载入错误的动态链接库(DLL)来威胁你的应用程序的安全关于该如何正确地用微软Windows的不同版本载入DLL的信息,参照 LoadLibrary的文档

    IDocHostShowUI::ShowHelp

    这一个方法当IE需要显示帮助时被调用,举例来说当 F1 键被按下时,而且工作方式和IDocHostShowUI::ShowMessage类似。返回S_OK覆盖IE的帮助,或另外的HRESULT值IE执行自己的帮助

    控制下载和执行

    浏览器控件给你它下载,显示设置和执行的控制。 为了要得到这控制,你实现你的宿主的IDispatch接口,使得它处理DISPID_AMBIENT_DLCONTROL。当浏览器控件被实例化的时候,它将会以这一个ID调用你的IDispatch::Invoke。将pvarResult设置为下列的标识的一个位与的组合,指明你的配置

    · DLCTL_DLIMAGES , DLCTL_VIDEOS 和 DLCTL_BGSOUNDS: 如果这些标识被设定图像,视频背景音乐将会被从服务器下载并且显示或播放,否则将不被下载显示。

    · DLCTL_NO_SCRIPTS 和 DLCTL_NO_JAVA: 脚本Java程序将不被运行。

    · DLCTL_NO_DLACTIVEXCTLS 和 DLCTL_NO_RUNACTIVEXCTLS: ActiveX 控件将不被下载或者运行。

    · DLCTL_DOWNLOADONLY: 网页只将会被下载,不显示。

    · DLCTL_NO_FRAMEDOWNLOAD:浏览器控件将会下载并且解析框架集页面,但是不会下载和解析框架集中单独的框架。

    · DLCTL_RESYNCHRONIZE 和 DLCTL_PRAGMA_NO_CACHE: 这些标志导致Internet缓冲的刷新。通过 DLCTL_RESYNCHRONIZE,服务器将会被请求更新状态。如果服务器指出缓存信息是最新的,将会使用 缓存文件。通过DLCTL_PRAGMA_NO_CACHE,不管文件的更新状态如何,文件都会被从服务器重新下载。

    · DLCTL_NO_BEHAVIORS: 行为不被下载并且在文件中被禁用。

    · DLCTL_NO_METACHARSET_HTML: 忽略META元素中指明的字符集

    · DLCTL_URL_ENCODING_DISABLE_UTF8 和 DLCTL_URL_ENCODING_ENABLE_UTF8: 这些标志的功能类似于IDocHostUIHandler::GetHostInfo使用DOCHOSTUIFLAG_URL_ENCODING_DISABLE_UTF8DOCHOSTUIFLAG_URL_ENCODING_ENABLE_UTF8标志。不同是只有浏览器控件被初始化的时候,DOCHOSTUIFLAG标志才会被检查。这里的环境特性变化下载标志每当浏览器控件需要运行一个下载被检查。

    · DLCTL_NO_CLIENTPULL: 不运行客户端重定位页面操作(译者注:例如<meta http-equiv="refresh" content="30"> 的默认行为)

    · DLCTL_SILENT: 在下载期间没有用户界面显示。

    · DLCTL_FORCEOFFLINE: 浏览器控件总是在脱机模式中操作。

    · DLCTL_OFFLINEIFNOTCONNECTED 和 DLCTL_OFFLINE: 这些标志是相同的。如果不连接到英特网浏览器控件将会在脱机模式中操作。

    DISPID_AMBIENT_DLCONTROL和标志数值在mshtmdid.h被定义

    最初,当对IDispatch::Invoke调用开始的时候, pvarResult参数指向的VARIANTVT_EMPTY类型。 你必须为任何有效的设定设置它为VT_I4类型。你可以在VARIANT的lVal成员中存储标志数值。

    大部份标志数值有否定的效果,也就是说,他们避免行为正常地发生。举例来说,如果你自定义浏览器控件行为,那么通常脚本会被执行。 但是如果你设定DLCTL_NOSCRIPTS 标志,脚本将不会在控制的那个实例中运行。然而,三标志— DLCTL_DLIMAGES , DLCTL_VIDEOS 和 DLCTL_BGSOUNDS的作用正好相反。你必须全部设置标志,使得浏览器控件以它的默认行为执行关于图像,视频和声音的处理。

    下列示例代码使得一个浏览器控件实例下载并且显示图像和视频,但是不处理背景音乐,因为DLCTL_BGSOUNDS没有被明确地设定。浏览器控件显示的页上脚本运行被禁用

    STDMETHODIMP CAtlBrCon::Invoke(DISPID dispidMember, REFIID riid,

        LCID lcid, WORD wFlags,

        DISPPARAMS* pDispParams,

        VARIANT* pvarResult,

        EXCEPINFO* pExcepInfo,

        UINT* puArgErr)

    {

        switch (dispidMember)

        {

            case DISPID_AMBIENT_DLCONTROL:

                pvarResult->vt = VT_I4;

                pvarResult->lVal = DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_NO_SCRIPTS;

                break;

            default:

                return DISP_E_MEMBERNOTFOUND;

        }

        return S_OK;

    }

    IHostDialogHelper

    IHostDialogHelper是一个你能根据你的爱好创建对话框的接口。这一个接口有一个方法,IHostDialogHelper::ShowHTMLDialog。这一个方法提供如同功能ShowHTMLDialog一般的服务,但是使用起来稍微比较容易一点

    为了要使用IHostDialogHelper,你从头产生对话框辅助对象。在这里是你使用CoCreateInstance的方式创建它。接口和ID在 mshtmhst.h 被定义。

    例子

    IHostDialogHelper* pHDH;

    IMoniker* pUrlMoniker;

    BSTR bstrOptions = SysAllocString(L"dialogHeight:30;dialogWidth:40");

    BSTR bstrPath = SysAllocString(L"c:\dialog.htm");

    CreateURLMoniker(NULL, bstrPath, &pUrlMoniker);

    // 创建对话框辅助对象

    CoCreateInstance(CLSID_HostDialogHelper,

        NULL,

        CLSCTX_INPROC,

        IID_IHostDialogHelper,

        (void**)&pHDH);

    //调用ShowHTMLDialog 创建对话框

    pHDH->ShowHTMLDialog(NULL,

        pUrlMoniker,

        NULL,

        bstrOptions,

        NULL,

        NULL);

    //释放资源

    SysFreeString(bstrPath);

    SysFreeString(bstrOptions);

    pUrlMoniker->Release();

    pHDH->Release();

    译者注:如果要使用对话框来获得用户输入,你可能需要传递两个参数到ShowHTMLDialog。关于ShowHTMLDialog参数的说明,参见Platform SDK文档。ShowHTMLDialog和ShowHTMLDialogEx 似乎一直是MSHTML.DLL导出的两个函数,微软把它封装为接口,可能是在为未来的兼容性作准备。

    控制新的窗口

    控制浏览器控件的一个重要的方法控制导航。你在前面已经看如何在IDispatch::Invoke中拦截DISPID_BEFORENAVIGATE2实现控制你的浏览器控件导航位置。另外一个导航的重要的方面要控制导航发生方式, 尤其打开新的窗口的时候。让我们举例来说, 使用者右击一个链接,选择 "在新窗囗中打开" 或一页包含像这样的脚本:

    window.open("www.msn.com")

    默认地,浏览器控件行代码的处理通过打开IE的一个新的实例来显示页。这可能正好是你的应用程序需要的但是也可能不是。也许你需要在当前的浏览器控件实例中打开所有链接,你将在你控制下的浏览器控件的一个新的实例——具有你的用户界面和你的商标——打开链接

    可以在你的IDispatch实现中拦截一个事件——DWebBrowserEvents2::NewWindow2——来控制。你的控制需要连接到DWebBrowserEvents2连接点拦截这一个事件。

    你连接到DWebBrowserEvents2之后,实现你的IDispatch::Invoke以处理 DISPID_NEWWINDOW2。在为DISPID_NEWWINDOW2IDispatch::Invoke函数调用中,数组pDispParams包含两个参数。第一个,序号是零, 是一个布尔类型的数值,告诉浏览器控件是否取消新的窗囗。默认它是假值,而且将会打开一个新的窗囗。如果你要完全取消新窗囗的创建, 设定标志到真值。

    序号为一的参数是一个IDispatch接口的指针。你可以将这一个参数设定为你已经创建的浏览器控件的IDispatch。当你传回这样一个IDispatch之后,MSHTML将会使用你给出的控件打开链接

    译者注:MFC中的DHTML类和类向导默认支持这个事件。需要更多信息的话,参见MSJ1998年7月份的文章Keeping an Eye on Your Browser by Monitoring Internet Explorer 4.0 Events,以及 微软知识库文章 Q184876 HOWTO: Use the WebBrowser Control NewWindow2 Event

    显示一个正数对话框

    IE6或者更高版本中,你可以在用户浏览一个合法的安全超文本传输协议(HTTPS)站点的时候显示证书对话框。这和用户点击IE中的锁图标效果相同。你可以通过 DWebBrowserEvents2::SetSecureLockIcon事件来显示你自己的图标。

    #define SHDVID_SSLSTATUS 33

    IOleCommandTarget *pct;

    if (SUCCEEDED(pWebBrowser2->QueryInterface(IID_IOleCommandTarget, (void **) &pct)))

    {

       pct->Exec(&CGID_ShellDocView, SHDVID_SSLSTATUS, 0, NULL, NULL);

       pct->Release();

    }

    信息栏

    Windows XP SP2 中的Internet Explorer 6 引入了一个新的安全用户界面元素,称为信息栏。在特定操作被阻止的时候,信息栏给用户显示一个用户界面元素。特别的,它会在以下操作被阻止的时候显示。

    · 弹出窗口初始化(参见 弹出窗口杀手)

    · 

    · 文件下载 (see 文件下载的限制)

    · 

    · 安装ActiveX 控件(see ActiveX 的限制)

    · 

    · ActiveX控件安全提示的原因是用户安全设置或者是控件未标记为脚本安全的。

    · 

    · 文件的扩展名和多用途因特网邮件扩展类型(MIME)不符的(参见 MIME 处理)

    · 

    · 网络协议锁死的内容(参见 协议)

    · 

    信息栏是Windows XP SP2 中的Internet Explorer 6引入的安全特性之一。和其他安全特性控制一样,可以通过一个注册表键来控制:(FEATURE_SECURITYBAND). 默认情况下IE(iexplorer.exe) 和Windows 资源管理器(explorer.exe) 在这个安全特性控制下。下面显示注册表键和启用过程:

    HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)

    SOFTWARE

    Microsoft

    Internet Explorer

    Main

    FeatureControl

    FEATURE_SECURITYBAND

    iexplorer.exe= 0x00000001

    explorer.exe= 0x00000001

    process name.exe=0x00000001

    这个FEATURE_SECURITYBAND 安全特性控制影响IE是否显示信息栏,信息栏在一个操作被阻止的时候提示用户。它不控制操作的阻止过程。

    一个集成浏览器控件的程序可以通过将其进程添加到这个注册表键来启用信息栏。这可以通过调用CoInternetSetFeatureEnabled函数来在运行时执行。如果一个应用程序并未在这个安全特性控制下,那么浏览器控件的行为和Internet Explorer 6 SP1b中的一样.

    没有方法通过脚本来访问这个特性。

    在FEATURE_SECURITYBAND及相关安全特性控制下的应用程序可以使用信息栏应用程序编程接口(API)来在一个URL 操作被禁止时自定义显示的用户界面。为信息栏引入了很多新的OLECMDID命令。头三个是属于CGID_DocHostCommandHandler组。宿主应用程序应该在它们的IDocHostUIHandler实现的同一个对象中实现IOleCommandTarget ,以接受来自浏览器控件IOleCommandTarget::Exec调用。

    · OLECMDID_PAGEACTIONBLOCKED

    · 

    · OLECMDID_PAGEACTIONUIQUERY

    · 

    · OLECMDID_FOCUSVIEWCONTROLS

    · 

    宿主应用程序可以使用下面两个新的OLECMDID 命令来执行浏览器控件IOleCommandTarget::Exec调用。

    · OLECMDID_FOCUSVIEWCONTROLSQUERY

    · 

    · OLECMDID_SHOWPAGEACTIONMENU

    · 

    这个示例使用IWebBrowser2::ExecWB 来执行OLECMDID_SHOWPAGEACTIONMENU 命令。

       POINT pt = { 0 };

       GetCursorPos(&pt);

       CComVariant varHwnd((LONG)hwnd);

       CComVariant varX(pt.x);

       CComVariant varY(pt.y);

       SAFEARRAY* psa = SafeArrayCreateVector(VT_VARIANT, 0, 3);

       LONG lIndex = 0;

       SafeArrayPutElement(psa, &lIndex, &varHwnd);

       lIndex++;

       SafeArrayPutElement(psa, &lIndex, &varX);

       lIndex++;

       SafeArrayPutElement(psa, &lIndex, &varY);

       CComVariant varArgIn;

       V_VT(&varArgIn) = VT_ARRAY | VT_I4;

       V_ARRAY(&varArgIn) = psa;

       pBrowser->ExecWB(OLECMDID_SHOWPAGEACTIONMENU, (OLECMDEXECOPT)dwPageActionFlags, &varArgIn, NULL);

    另外,应用程序可以实现IInternetSecurityManager来重载默认的安全区域设置,参见创建一个自定义URL安全管理器以获得更多信息.

    结论

    你现在有许多技术,可以根据你的处理来自定义浏览器控件。这个文章决不是没有遗漏的,但是希望现在可以自行发现超越本文的技术。检查IE注册表设置中那些你可以用IDocHostUIHandler::GetOptionKeyPath或IDocHostUIHandler2::GetOverrideKeyPath修改的信息。记住许多注册表设置相互依赖。你可能必须做一些实验发现注册表设置可以多么的有效地自定义;如果需要控制浏览器控件的拖放行为,你也可以去看看IDocHostUIHandler::GetDropTarget。

    C# WinForm WebBrowser (讨厌的问题 

    WebBrowse 编辑模式 中几个讨厌的问题:

    1、当设置DocumentText属性值时会一直弹出一个可恶的保存对话框

    现我已知的较好的策略有:
    1)在设置两个DocumentText属性值之间使用webBrowser1.Document.OpenNew(true)方法,但这个方法会引发一些问题。详细内容见下。
    2)在设置DocumentText属性之前将编辑模式改为浏览模式,设置完后再将浏览模式改为编辑模式。


    2、监控Html内容的改变。

    监控 WebBrowser 控件内容的改变


    3WebBrowseMouse事件。

    WebBrowse本身没有Mouse的相关事件,但是我们可以借助WebBrowse中的Body元素来模拟一些简单的相关事件。

    //WinForm中注册Web事件
    //假设HTML源代码如下:
    <html> 
    <body> 
    <input type="button" id="btnClose" value="关闭" /> 
    </body> 
    </html>
     
    // WinForm中注册Web事件
    HtmlDocument htmlDoc = webBrowser.Document; 
    HtmlElement btnElement = htmlDoc.All["btnClose"]; 
    if (btnElement != null

        btnElement.click += new HtmlElementEventHandler(HtmlBtnClose_Click); 
    }

    WebBrowse中更多的实用方法参见:C#WinForm WebBrowser (实用方法总结


    4webBrowser1.Document.OpenNew(true)Bug

    相关链接:
    1追踪导致 WebBrowser控件 编辑模式下 Ctrl + Z 失效的原因 【求大侠佐证,这算不算是微软的Bug呢?】

    追踪导致 WebBrowser控件 编辑模式下 Ctrl + Z 失效的原因 【求大侠佐证,这算不算是微软的Bug呢?】 


    导致Ctrl  + Z失效的原因由以下2点连锁引发而导致:

    1、为了解决 WebBrowser 控件导航时弹出保存对话框,使用了 this.webBrowser.Document.OpenNew(true); // 防止 弹出保存对话框, 该方法指示新的文本改变将会在新窗口中打开。

    2、 由原因1导致 webBrowser 控件的编辑模式失效, 表面上看上去还是可以编辑的,但实际上新窗口内部已经不支持编辑了。

    注:这里涉及到了WebBrowser控件的特殊性,它是由三层控件嵌套而成的,外面的两层是大概负责容器、 及 响应用户操作的, 而最内层的则是承载HTML标记,并通过渲染引擎展示HTML内容。用黑盒测试的方法推断,当使用webBrowser.Document.OpenNew(true);  方法时,最内层控件应该是一个新的实例, 表面上看上去还是可以编辑的,但实际上内部的新窗口已经不支持编辑了,进而导致了Ctrl + Z的失效!

    测试代码如下:

    public partial class FrmTest : Form
    {
    // 界面上有一个WebBrowser 和 4Button

    private string strUrl = "http://www.cnblogs.com/08shiyan";

    public FrmTest()
    {
    InitializeComponent();
    }

    /// <summary>
    /// 编辑模式
    /// </summary>
    public void EditMode()
    {
    if (this.webBrowser1.Document != null)
    {
    mshtml.IHTMLDocument2 doc = this.webBrowser1.Document.DomDocument as mshtml.IHTMLDocument2;
    doc.designMode = "on";
    }
    }

    /// <summary>
    /// 启用浏览模式
    /// </summary>
    public void BrowseMode()
    {
    if (this.webBrowser1.Document != null)
    {
    mshtml.IHTMLDocument2 doc = this.webBrowser1.Document.DomDocument as mshtml.IHTMLDocument2;
    doc.designMode = "off";
    }
    }

    // 请确保该按钮是应用程序启动后第一次被点击
    private void button1_Click(object sender, EventArgs e)
    {
    this.webBrowser1.DocumentText = string.Empty;
    this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>"this.strUrl));
    this.EditMode();

    this.webBrowser1.Document.OpenNew(true);
    this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>"this.strUrl));

    // 注意此时Ctrl + Z 撤销操作将会失效
    }

    // 请确保该按钮是应用程序启动后第一次被点击
    private void button2_Click(object sender, EventArgs e)
    {
    this.webBrowser1.DocumentText = string.Empty;
    this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>"this.strUrl));
    this.EditMode();

    this.webBrowser1.Document.OpenNew(true);
    this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>"this.strUrl));

    this.EditMode(); // button1的差别是再次启用编辑模式 // 注意此时Ctrl + Z 撤销操作也会失效
    }

    // 请确保该按钮是应用程序启动后第一次被点击
    private void button3_Click(object sender, EventArgs e)
    {
    this.webBrowser1.DocumentText = string.Empty;
    this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>"this.strUrl));
    this.EditMode();

    this.webBrowser1.Document.OpenNew(true);
    this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>"this.strUrl));

    this.BrowseMode(); // button2 的区别是 先关闭编辑模式,再启用编辑模式
    this.EditMode();

    // 此时 Ctrl + Z 可以使用
    }

    // 重启应用程序
    private void button4_Click(object sender, EventArgs e)
    {
    Application.Restart();
    }

    }

    根据以上得出结论:
    编辑模式下: this.webBrowser.Document.OpenNew(true); 方法会打开一个新的内部窗口,而新窗口中编辑模式出现问题,导致Ctrl + Z ,Ctrl +Y操作失效。 此时 需要先关闭 编辑模式” 然后再打开编辑模式” Ctrl + Z, Ctrl +Y 才能正常使用。

    求佐证。。。

    原创 转载请标明出处: http://www.cnblogs.com/08shiyan

    2http://topic.csdn.net/u/20110318/14/e3744686-3dae-4152-a1d8-c0011e8f355c.html



    更多的关于WebBrowse的问题。

    展开全文
  • 封装Lua for C#

    2016-04-14 14:34:37
    为了懂LUA在我们的GDEX中到底怎么用,我决定研究一下如何比较好的在WPF里封装一个基于lua的APP framework。   今天先对Lua for C#进行了一次简单的封装。 在C#下用过Lua的人都知道,用C#实现一个函数之后和...

    为了搞懂LUA在我们的GDEX中到底怎么用,我决定研究一下如何比较好的在WPF里封装一个基于lua的APP  framework。

     

    今天先对Lua for C#进行了一次简单的封装。

    在C#下用过Lua的人都知道,用C#实现一个函数之后和LUA绑定,需要用到Lua类的RegisterFunction方法。

     

    在函数很少的情况下很好用,但是若需要绑定C#里成百上千个函数,则麻烦了,添加一个函数,至少每次需要修改两个地方:函数实现,函数绑定(RegisterFunction)。并且如果在lua中绑定的名字和C#中不一样,则更麻烦,还需要维护一个函数映射。

     

    今天翻了一下google,翻出GameDev.net上一篇老外的文章,叫《Using Lua with C#》,看了一下,它的方法不错。(改天考虑翻译这篇文章),不过他的示例代码实在是太太太冗长了,大部分是生成函数介绍和函数帮助文档等,直接忽略。把它最核心的东西拿过来,然后自己封装了一下,用起来感觉不错。

     

    基本思想是,使用C#的Attribute来标记函数,实现自动绑定。

     

    核心部分代码如下(LuaFramework.cs):

     

    1. using System;  
    2. using System.Collections.Generic;  
    3. using System.Linq;  
    4. using System.Text;  
    5. using System.Windows;  
    6. using System.Reflection;  
    7. using LuaInterface;  
    8.   
    9. namespace WPFLuaFramework  
    10. {  
    11.     /// <summary>  
    12.     /// Lua函数描述特性类  
    13.     /// </summary>  
    14.     public class LuaFunction : Attribute  
    15.     {  
    16.         private String FunctionName;  
    17.   
    18.         public LuaFunction(String strFuncName)  
    19.         {  
    20.             FunctionName = strFuncName;  
    21.         }  
    22.   
    23.         public String getFuncName()  
    24.         {  
    25.             return FunctionName;  
    26.         }  
    27.     }  
    28.   
    29.     /// <summary>  
    30.     /// Lua引擎  
    31.     /// </summary>  
    32.     class LuaFramework  
    33.     {  
    34.         private Lua pLuaVM = new Lua();//lua虚拟机  
    35.   
    36.         /// <summary>  
    37.         /// 注册lua函数  
    38.         /// </summary>  
    39.         /// <param name="pLuaAPIClass">lua函数类</param>  
    40.         public void BindLuaApiClass( Object pLuaAPIClass )  
    41.         {  
    42.             foreach (MethodInfo mInfo in pLuaAPIClass.GetType().GetMethods())  
    43.             {  
    44.                 foreach (Attribute attr in Attribute.GetCustomAttributes(mInfo))  
    45.                 {  
    46.                     string LuaFunctionName = (attr as LuaFunction).getFuncName();  
    47.                     pLuaVM.RegisterFunction(LuaFunctionName, pLuaAPIClass, mInfo);  
    48.                 }  
    49.             }  
    50.         }  
    51.   
    52.         /// <summary>  
    53.         /// 执行lua脚本文件  
    54.         /// </summary>  
    55.         /// <param name="luaFileName">脚本文件名</param>  
    56.         public void ExecuteFile(string luaFileName)  
    57.         {  
    58.             try  
    59.             {  
    60.                 pLuaVM.DoFile(luaFileName);  
    61.             }  
    62.             catch (Exception e)  
    63.             {  
    64.                 MessageBox.Show(e.ToString());  
    65.             }  
    66.         }  
    67.   
    68.         /// <summary>  
    69.         /// 执行lua脚本  
    70.         /// </summary>  
    71.         /// <param name="luaCommand">lua指令</param>  
    72.         public void ExecuteString(string luaCommand)  
    73.         {  
    74.             try  
    75.             {  
    76.                 pLuaVM.DoString(luaCommand);  
    77.             }  
    78.             catch (Exception e)  
    79.             {  
    80.                 MessageBox.Show(e.ToString());  
    81.             }  
    82.         }  
    83.     }  
    84. }  

     

    我的LUA API类如下,用于实现C# for lua的函数(LuaAPI.cs)

     

    1. using System;  
    2. using System.Collections.Generic;  
    3. using System.Linq;  
    4. using System.Text;  
    5. using System.Windows;  
    6.   
    7. namespace WPFLuaFramework  
    8. {  
    9.     class LuaAPI  
    10.     {  
    11.         [LuaFunction("lua1")]  
    12.         public void a1()  
    13.         {  
    14.             MessageBox.Show("a1 called");  
    15.         }  
    16.   
    17.         [LuaFunction("lua2")]  
    18.         public int a2()  
    19.         {  
    20.             MessageBox.Show("a2 called");  
    21.             return 0;  
    22.         }  
    23.   
    24.         [LuaFunction("lua3")]  
    25.         public void a3(string s)  
    26.         {  
    27.             MessageBox.Show("a3 called");  
    28.         }  
    29.     }  
    30. }  

     

    最后看调用代码,是不是很简单

     

    1. LuaFramework test = new LuaFramework();  
    2. test.BindLuaApiClass(new LuaAPI());  
    3. test.ExecuteFile("test.lua");  
    4. test.ExecuteString("lua1()");  

     

    LUA代码如下

     

    lua1();
    lua2();
    lua3("test");

     

    展开全文
  • C#--微软.NET的第一语言 本书着重介绍语言本身,比较少涉及应用,不错的入门书,从头讲起,不怕不明白。 <<page 1>> page begin==================== 目 目目 目 录 录录 录 第一部分 C#语言概述.4 ...
  • C#一步步写串口通信 标签: c#textboxbuttonobjectstring 2012-08-10 14:57 18916人阅读 评论(38) 收藏 举报  分类:   C#(2) 项目经验(1)  版权声明:本文为博主原创文章,...

     用C#一步步写串口通信

    标签: c#textboxbuttonobjectstring
     18916人阅读 评论(38) 收藏 举报
     分类:
     
     

       附言:

    1. 有网友反应我写的这篇文章还不错,索性就将它置顶了,希望对大家串口编程的学习有所帮助。

    2.在此吐槽一下东家CSDN的编辑框,非常难用,刚才排版还好好的,现在打开又是一团糟,对你造成的阅读不便我带csdn向您道歉!

    以下是正文:
    ===============================================================================================================================

      

          最近在公司让用C#写一个串口调试的工具,要求向串口中输入16进制数据或字符串。下面我将这次遇到的问题和解决方法奉献出来,希望对工作中需要的朋友有所帮助!
          
    我们来看具体的实现步骤。

       公司要求实现以下几个功能:
    1):实现两台计算机之前的串口通信,以16进制形式和字符串两种形式传送和接收。
    2):根据需要设置串口通信的必要参数。
    3):定时发送数据。
    4):保存串口设置。

          看着好像挺复杂,其实都是纸老虎,一戳就破,前提是你敢去戳。我尽量讲的详细一些,争取说到每个知识点。

          在编写程序前,需要将你要测试的COM口短接,就是收发信息都在本地计算机,短接的方式是将COM口的2、3号针接起来。COM口各针的具体作用,度娘是这么说的:COM口。记住2、3针连接一定要连接牢固,我就是因为接触不良,导致本身就不通,白白花掉了一大半天时间调试代码。

    下面给出主要的操作界面,如下:



    顺便,我将所有控件对应的代码名字也附上了,相信对初学者来说,再看下面的代码会轻松很多。控件名字命名的方法是“控件名+作用”的形式,例如“打开串口”的开关按钮,其名字是btnSwitch  (btn就是button的简写了)。我认为这种命名控件的方式比较好,建议大家使用,如果你有好的命名方式,希望你能告诉我!

    下面我们将各个功能按照从主到次的顺序逐个实现。(我分块给出代码实现,代码下载见文章最后。)


    一、获取计算机的COM口总个数,将它们列为控件cbSerial的候选项,并将第一个设为cbSerial的默认选项。

    这部分是在窗体加载时完成的。请看代码:
    (很多信息代码的注释里讲的很清楚,我就不赘述了。)

    1. //检查是否含有串口  
    2.            string[] str = SerialPort.GetPortNames();  
    3.            if (str == null)  
    4.            {  
    5.                MessageBox.Show("本机没有串口!""Error");  
    6.                return;  
    7.            }  
    8.   
    9.            //添加串口项目  
    10.            foreach (string s in System.IO.Ports.SerialPort.GetPortNames())  
    11.            {//获取有多少个COM口  
    12.                cbSerial.Items.Add(s);  
    13.            }  
    14.   
    15.            //串口设置默认选择项  
    16.            cbSerial.SelectedIndex = 0;         //设置cbSerial的默认选项  

    二、“串口设置”
    这面我没代码编程,直接从窗体上按照串口信息设置就行。我们仅设置它们的默认选项,但这里我用到了ini文件,暂时不讲,我们先以下面形式设置默认。

     
    1. cbBaudRate.SelectedIndex = 5;  
    2.  cbDataBits.SelectedIndex = 3;  
    3.  cbStop.SelectedIndex = 0;  
    4.  cbParity.SelectedIndex = 0;  
    5.  radio1.Checked = true;  //发送数据的“16进制”单选按钮(这里我忘了改名,现在看着很不舒服!)  
    6.  rbRcvStr.Checked = true;  

    三、打开串口
    在发送信息之前,我们需要根据选中的选项设置串口信息,并设置一些控件的属性,最后将串口打开。
    private void btnSwitch_Click(object sender, EventArgs e)        { //sp1是全局变量。 SerialPort>
    [csharp] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1.  private void btnSwitch_Click(object sender, EventArgs e)  
    2.        {  
    3. //sp1是全局变量。 SerialPort>  

    四、发送信息因为这里涉及到字符的转换,难点在于,在发送16进制数据时,如何将文本框中的字符数据在内存中以同样的形式表现出来,例如我们输入16进制的“eb 90”显示到内存中,也就是如下形式:

            

    或输入我们想要的任何字节,如上面的“12 34 56 78 90”.内存中的数据时16进制显示的,而我们输入的数据时字符串,我们需要将字符串转换为对应的16进制数据,然后将这个16进制数据转换为字节数据,用到的主要方法是:

    [csharp] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1. Convert.ToInt32  (String, Int32);Convert.ToByte  (Int32);  


    这是我想到的,如果你有好的方法,希望你能告诉我。

    下面看代码:

    [csharp] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1. private void btnSend_Click(object sender, EventArgs e) {   
    2.         if (!sp1.IsOpen) //如果没打开   
    3.             { MessageBox.Show("请先打开串口!""Error");  
    4.             return;   
    5.             }   
    6.         String strSend = txtSend.Text;   
    7.         if (radio1.Checked == true//“16进制发送” 按钮   
    8.             {   
    9.             //处理数字转换,目的是将输入的字符按空格、“,”等分组,以便发送数据时的方便(此处转的比较麻烦,有高见者,请指点!)   
    10.             string sendBuf = strSend; string sendnoNull = sendBuf.Trim();   
    11.             string sendNOComma = sendnoNull.Replace(','' '); //去掉英文逗号 string sendNOComma1 = sendNOComma.Replace(',', ' '); //去掉中文逗号   
    12.             string strSendNoComma2 = sendNOComma1.Replace("0x"""); //去掉0x   
    13.             strSendNoComma2.Replace("0X"""); //去掉0X   
    14.             string[] strArray = strSendNoComma2.Split(' ');  //strArray数组中会出现“”空字符的情况,影响下面的赋值操作,故将byteBufferLength相应减小   
    15.             int byteBufferLength = strArray.Length;   
    16.             for (int i = 0; i <strArray.Length; i++ )   
    17.             {  
    18.                 if (strArray[i]=="")   
    19.                 {   
    20.                     byteBufferLength--;  
    21.                 }   
    22.                 } byte[] byteBuffer = new byte[byteBufferLength];   
    23.             }  
    24.         int ii = 0; //用于给byteBuffer赋值 >  
    25.     }  


    五、数据的接收

    亮点来了,看到这里,如果你还没吐(可能是我的代码比较拙劣!),那么下面的知识点对你也不成问题。这里需要用到 委托 的知识,我是搞C/C++出身,刚碰到这个知识点还真有点不适应。为了不偏离主题,关于委托,我仅给出两条比较好的链接,需要的网友可以去加深学习:C#委托订阅委托事件。       在窗体加载时就订阅上委托是比较好的,所以在Form1_Load中添加以下代码:

    1. <pre code_snippet_id="128560" snippet_file_name="blog_20131226_6_5274381" name="code" class="csharp">Control.CheckForIllegalCrossThreadCalls = false;    //意图见解释  
    2. sp1.DataReceived += new SerialDataReceivedEventHandler(sp1_DataReceived); //订阅委托  
    3.       注意,因为自.net 2.0以后加强了安全机制,,不允许在winform中直接跨线程(事件触发需要产生一个线程处理)访问控件的属性,第一条代码的意图是说在这个类中我们强制不检查跨线程的调用是否合法。处理这种问题的解决方案有很多,具体可参阅以下内容:解决方案。  
    4.       好了,订阅委托之后,我们就可以处理接收数据的事件了。  
    5.  void sp1_DataReceived(object sender, SerialDataReceivedEventArgs e)  
    6.         {  
    7.             if (sp1.IsOpen)     //此处可能没有必要判断是否打开串口,但为了严谨性,我还是加上了  
    8.             {  
    9.                 byte[] byteRead = new byte[sp1.BytesToRead];    //BytesToRead:sp1接收的字符个数  
    10.                 if (rdSendStr.Checked)                          //'发送字符串'单选按钮  
    11.                 {  
    12.                     txtReceive.Text += sp1.ReadLine() + "\r\n"//注意:回车换行必须这样写,单独使用"\r"和"\n"都不会有效果  
    13.                     sp1.DiscardInBuffer();                      //清空SerialPort控件的Buffer   
    14.                 }  
    15.                 else                                            //'发送16进制按钮'  
    16.                 {  
    17.                     try  
    18.                     {  
    19.                         Byte[] receivedData = new Byte[sp1.BytesToRead];        //创建接收字节数组  
    20.                         sp1.Read(receivedData, 0, receivedData.Length);         //读取数据                         
    21.                         sp1.DiscardInBuffer();                                  //清空SerialPort控件的Buffer  
    22.                         string strRcv = null;  
    23.   
    24.                         for (int i = 0; i < receivedData.Length; i++) //窗体显示  
    25.                         {  
    26.                             
    27.                             strRcv += receivedData[i].ToString("X2");  //16进制显示  
    28.                         }  
    29.                         txtReceive.Text += strRcv + "\r\n";  
    30.                     }  
    31.                     catch (System.Exception ex)  
    32.                     {  
    33.                         MessageBox.Show(ex.Message, "出错提示");  
    34.                         txtSend.Text = "";  
    35.                     }  
    36.                 }  
    37.             }  
    38.             else  
    39.             {  
    40.                 MessageBox.Show("请打开某个串口""错误提示");  
    41.             }  
    42.         }  
    43. </pre><br><br>  

           为了友好和美观,我将当前时间也显示出来,又将显示字体的颜色做了修改:

    1. //输出当前时间  
    2.                 DateTime>  

    做到这里,大部分功能就已实现了,剩下的工作就是些简单的操作设置了,有保存设置、定时发送信息、控制文本框输入内容等。六、保存设置这部分相对简单,但当时我没接触过,也花了点时间,现在想想,也不过如此。保存用户设置用ini文件是个不错的选择,虽然大部分都用注册表实现,但ini文件保存还是有比较广泛的使用。.ini 文件是Initialization File的缩写,也就是初始化文件。为了不偏离正题,也不过多说明,可参考相关内容(网上资源都不错,因人而异,就不加链接了)。使用Inifile读写ini文件,这里我用到了两个主要方法:

    1. //读出ini文件  
    2. a:=inifile.Readstring('节点','关键字',缺省值);// string类型   
    3. b:=inifile.Readinteger('节点','关键字',缺省值);// integer类型   
    4. c:=inifile.Readbool('节点','关键字',缺省值);// boolean类型   
    5. 其中[缺省值]为该INI文件不存在该关键字时返回的缺省值。   
    6. //写入INI文件:   
    7. inifile.writestring('节点','关键字',变量或字符串值);   
    8. inifile.writeinteger('节点','关键字',变量或整型值);   
    9. inifile.writebool('节点','关键字',变量或True或False);   
    10.   
    11. 请看代码:  
    12. //using 省写了  
    13. namespace INIFILE  
    14. {  
    15.     class Profile  
    16.     {  
    17.         public static void LoadProfile()  
    18.         {  
    19.             string strPath = AppDomain.CurrentDomain.BaseDirectory;  
    20.             _file = new IniFile(strPath + "Cfg.ini");  
    21.             G_BAUDRATE = _file.ReadString("CONFIG""BaudRate""4800");    //读数据,下同  
    22.             G_DATABITS = _file.ReadString("CONFIG""DataBits""8");  
    23.             G_STOP = _file.ReadString("CONFIG""StopBits""1");  
    24.             G_PARITY = _file.ReadString("CONFIG""Parity""NONE");  
    25.            
    26.         }  
    27.   
    28.         public static void SaveProfile()  
    29.         {  
    30.             string strPath = AppDomain.CurrentDomain.BaseDirectory;  
    31.             _file = new IniFile(strPath + "Cfg.ini");  
    32.             _file.WriteString("CONFIG""BaudRate", G_BAUDRATE);            //写数据,下同  
    33.             _file.WriteString("CONFIG""DataBits", G_DATABITS);  
    34.             _file.WriteString("CONFIG""StopBits", G_STOP);  
    35.             _file.WriteString("CONFIG""G_PARITY", G_PARITY);  
    36.         }  
    37.   
    38.         private static IniFile _file;//内置了一个对象  
    39.   
    40.         public static string G_BAUDRATE = "1200";//给ini文件赋新值,并且影响界面下拉框的显示  
    41.         public static string G_DATABITS = "8";  
    42.         public static string G_STOP = "1";  
    43.         public static string G_PARITY = "NONE";      
    44.     }  
    45. }  


    _file声明成了内置对象,可以方便各函数的调用。
    下面是“保存设置”的部分代码:

    1. private void btnSave_Click(object sender, EventArgs e)  
    2.         {  
    3.               
    4.             //设置各“串口设置”  
    5.             string strBaudRate = cbBaudRate.Text;  
    6.             string strDateBits = cbDataBits.Text;  
    7.             string strStopBits = cbStop.Text;  
    8.             Int32 iBaudRate = Convert.ToInt32(strBaudRate);  
    9.             Int32 iDateBits = Convert.ToInt32(strDateBits);  
    10.   
    11.   
    12.             Profile.G_BAUDRATE = iBaudRate+"";       //波特率  
    13.             Profile.G_DATABITS = iDateBits+"";       //数据位  
    14.             switch (cbStop.Text)            //停止位  
    15.             {  
    16.                 case "1":  
    17.                     Profile.G_STOP = "1";  
    18.                     break;  
    19.                 case "1.5":  
    20.                     Profile.G_STOP = "1.5";  
    21.                     break;  
    22.                //防止过多刷屏,下面省写了  
    23.         ……  
    24.             }  
    25.             switch (cbParity.Text)             //校验位  
    26.             {  
    27.                 case "无":  
    28.                     Profile.G_PARITY = "NONE";  
    29.                     break;  
    30.                 …………  
    31.             }  
    32.         Profile.SaveProfile();  //保存设置  
    33. }  

        读取ini文件主要在加载窗体时执行:
    INIFILE.Profile.LoadProfile();//加载所有


    七、控制文本输入这里倒挺简单,只是注意一点。当我们控制输入非法字符时,可通过控制e.Handed的属性值实现,注意这里的Handed属性是“操作过”的含义,而非“执行此处操作”之意,Handled是过去式,看字面意思,"操作过的=是;",将这个操作的状态设为已处理过,自然就不会再处理了。具体参见MSDN:Handed

    1. private void txtSend_KeyPress(object sender, KeyPressEventArgs e)  
    2.         {  
    3.             if (radio1.Checked== true)  
    4.             {  
    5.                 //正则匹配  
    6.                 string patten = "[0-9a-fA-F]|\b|0x|0X| "//“\b”:退格键  
    7.                 Regex r = new Regex(patten);  
    8.                 Match m = r.Match(e.KeyChar.ToString());  
    9.   
    10.   
    11.                 if (m.Success )//&&(txtSend.Text.LastIndexOf(" ") != txtSend.Text.Length-1))  
    12.                 {  
    13.                     e.Handled = false;  
    14.                 }  
    15.                 else  
    16.                 {  
    17.                     e.Handled = true;  
    18.                 }  
    19.             }//end of radio1  

    八、定时发送信息
         这边看似很简单,但也有一点需要注意,当定时器生效时,我们要间隔访问“发送”按键的内容,怎么实现?还好MS给我们提供了必要的支持,使用Button的 PerformClick可以轻松做到,  PerformClick参见MSDN:PerformClick


    1. private void tmSend_Tick(object sender, EventArgs e)  
    2.         {  
    3.             //转换时间间隔  
    4.             string strSecond = txtSecond.Text;  
    5.             try  
    6.             {  
    7.                 int isecond = int.Parse(strSecond) * 1000;//Interval以微秒为单位  
    8.                 tmSend.Interval = isecond;  
    9.                 if (tmSend.Enabled == true)  
    10.                 {  
    11.                     btnSend.PerformClick(); //产生“发送”的click事件  
    12.                 }  
    13.             }  
    14.             catch (System.Exception ex)  
    15.             {  
    16.                 MessageBox.Show("错误的定时输入!""Error");  
    17.             }  
    18.               
    19.         }  

       千万注意在一些情况下不要忘了让定时器失效,如在取消“定时发送数据"和“关闭串口”时等。


    代码下载:

    有CSDN账号的童鞋:《C#串口通信工具》

    无CSDN账户的童鞋:《C#串口通信工具》





       
    38
    0

    我的同类文章

     
    猜你在找
    C#.NET_面向对象编程技术
    微信公众平台深度开发Java版 v2.0(第一季)完整版
    使用C#开发信息管理系统
    ASP.NET企业级开发基础
    数据结构和算法
    在C#中使用SerialPort类实现串口通信
    C#串口通信MSComm控件使用详解
    在C#中使用SerialPort类实现串口通信 遇到多线程问题
    C#serialport类实现串口通信的源代码
    C# 使用SerialPort控件用类及线程实现串口通信
    <iframe id="iframeu1607657_0" src="http://pos.baidu.com/fcym?sz=728x90&rdid=1607657&dc=2&di=u1607657&dri=0&dis=0&dai=2&ps=10491x443&coa=at%3D3%26rsi0%3D728%26rsi1%3D90%26pat%3D6%26tn%3DbaiduCustNativeAD%26rss1%3D%2523FFFFFF%26conBW%3D1%26adp%3D1%26ptt%3D0%26titFF%3D%2525E5%2525BE%2525AE%2525E8%2525BD%2525AF%2525E9%25259B%252585%2525E9%2525BB%252591%26titFS%3D%26rss2%3D%2523000000%26titSU%3D0%26ptbg%3D90%26piw%3D0%26pih%3D0%26ptp%3D0&dcb=BAIDU_SSP_define&dtm=BAIDU_DUP_SETJSONADSLOT&dvi=0.0&dci=-1&dpt=none&tsr=0&tpr=1463452724928&ti=%E7%94%A8C%23%E4%B8%80%E6%AD%A5%E6%AD%A5%E5%86%99%E4%B8%B2%E5%8F%A3%E9%80%9A%E4%BF%A1%20-%20%E7%8E%8B%E6%B0%91%E5%88%A9%20-%20%E5%8D%9A%E5%AE%A2%E9%A2%91%E9%81%93%20-%20CSDN.NET&ari=1&dbv=2&drs=3&pcs=1663x907&pss=1663x14197&cfv=18&cpl=30&chi=1&cce=true&cec=UTF-8&tlm=1463452724&ltu=http%3A%2F%2Fblog.csdn.net%2Fgeekwangminli%2Farticle%2Fdetails%2F7851673&ltr=http%3A%2F%2Fsoguge.com%2F&ecd=1&psr=1680x1050&par=1680x1010&pis=-1x-1&ccd=24&cja=true&cmi=59&col=zh-CN&cdo=-1&tcn=1463452725&qn=189ec59d7467eed9&tt=1463452724915.20.288.290" width="728" height="90" align="center,center" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="border-width: 0px; vertical-align: bottom; margin: 0px;"></iframe>
    查看评论
    23楼 xulichun9 2016-03-09 11:06发表 [回复]
    接收的代码我改了一下:
    [csharp] view plain copy
     print?
    1. if(rbRcvStr.Checked)  
    2.                 {  
    3.                     try  
    4.                     {  
    5.                         txtReceive.Text += sp1.ReadLine() + "\r\n"//注意:回车换行必须这样写,单独使用"\r"和"\n"都不会有效果  
    6.                         sp1.DiscardInBuffer();                      //清空SerialPort控件的Buffer   
    7.                     }  
    8.                     catch (Exception)  
    9.                     {  
    10.                         sp1.DiscardInBuffer();  
    11.                         MessageBox.Show("发送端可能不是以字符串发送,尝试16进制看");  
    12.                         rbRcv16.Checked = true;  
    13.                     }  
    14.                       
    15.                 }  
    22楼 仙人掌的球 2015-05-07 15:36发表 [回复]
    楼主讲的很细!
    当时就是学的他的教程。
    21楼 cyp1230 2014-05-13 22:11发表 [回复]
    数据接收有问题啊。。
    txtReceive.Text += sp1.ReadLine() + "\r\n" 这一句显示(由于线程退出或应用程序请求,已中止 I/O 操作)
    20楼 um哈士奇 2013-12-03 12:49发表 [回复]
    hi,你好 上面的memory1,左边显示 十六进制,右边显示字母的程序要怎么写呢?多谢
    19楼 longdreams 2013-11-20 09:44发表 [回复]
    lz,能告诉我什么COM口短接,即就是收发信息都在本地 ..
    Re: 王民利 2013-11-25 17:41发表 [回复]
    回复longdreams:你找跟金属线将2、3两镇短接就行,我直接用一段铁丝缠上的。
    18楼 ShadowfaxGHH 2013-11-13 16:46发表 [回复]
    赞一个 楼主写的很不错哦 
    很详细,适合初学者,可以再把RS232的那张图贴上
    17楼 sonyshang_s 2013-11-13 13:14发表 [回复]
    鼎楼主
    16楼 mznxbc123456 2013-08-07 11:33发表 [回复]
    LZ,我在2010下编译代码第143行会报错,就是你打开com9那行代码,我注释以后运行的话,是不是我没有串口在打开?我是台式机没有串口...提示Error未将对象引用设置到对象的实例 意思是不是没有串口在打开啊
    Re: sonyshang_s 2013-12-09 17:05发表 [回复]
    回复mznxbc123456:在cbSerial的Item里边设置COM1,COM2==
    Re: 80donet 2013-09-09 16:52发表 [回复]
    回复mznxbc123456:选的端口号不对,不要注释,改写索引值就行了
    15楼 mznxbc123456 2013-08-07 11:18发表 [回复]
    很不错 在努力学习中
    14楼 kingie 2013-08-05 23:18发表 [回复]
    写得不错,开源精神更好,顶顶
    13楼 kingboyszxf 2013-07-29 12:04发表 [回复]
    刚下载了你写的这个程序,为什么我打开COM1之后,选好发送方:字符串,接收方:字符串,点了发送之后没反应呢?接收方的框内也不显示数据,不知道是啥原因?我用COM1 COM2都试了一样的问题.
    Re: 王民利 2013-08-05 21:47发表 [回复]
    回复kingboyszxf:挨个COM口测试一下看看呢?我这里测试用的是COM9口。
    12楼 kingboyszxf 2013-07-29 11:59发表 [回复]
    请教一下楼主:串口如果不接设备的话,可以自己发送一个信息,然后再接收回来吗?我下载了几个串口调试工具,发送出去之后都没有反应,不知道是什么原因..
    Re: 80donet 2013-09-09 16:49发表 [回复]
    回复kingboyszxf:不连单片机或者PC怎么会收到返回值呢?都要的,要不PC与PC通信,要不PC与单片机连
    11楼 HsuWin 2013-07-23 17:47发表 [回复]
    感謝分享~很詳盡^____^
    10楼 linch2012 2013-07-18 14:17发表 [回复]
    写的很详细
    9楼 lfc12345678 2013-06-27 20:43发表 [回复]
    写的不错,楼主真是好人
    8楼 你是最重要的决定 2013-06-19 10:55发表 [回复]
    楼主写得很好,很有用,可还是有不明白的地方,加了楼主的QQ,希望能加我,麻烦您啦
    7楼 xukai871105 2013-06-07 13:29发表 [回复]
    写的非常不错,也花了楼主不少时间吧,先向你学习了!
    6楼 Tigerdawn 2013-05-09 22:03发表 [回复]
    楼主好人,一生平安呀!!!
    5楼 lelliao 2013-05-03 10:21发表 [回复]
    楼主好人啊, 现在正在写发卡器程序, 参考了, 谢谢!
    4楼 lajalvam 2013-05-01 11:15发表 [回复]
    很好,谢谢楼主,我正在做串口。看起来简单,可是问题频出。
    3楼 暮色流年 2013-04-26 15:46发表 [回复]
    楼主,也给我发个源码zongruibiao@163.com.谢谢了
    Re: 王民利 2013-04-27 14:59发表 [回复]
    回复暮色流年:http://download.csdn.net/detail/rehongchen/4556190
    2楼 u010231733 2013-04-12 09:20发表 [回复]
    你好,为什么那个SerialPortConnection(串口调试)程序收发信息模块无法实现呢?
    1楼 marlin3018 2012-09-05 11:08发表 [回复]
    博主讲的很详细,最近也要做一个串口通信的界面,正在学习中。希望博主能上传源码。谢谢。
    Re: 黑色的彩 2013-04-01 15:43发表 [回复]
    回复marlin3018:您好,我是大一的学生,最近我们实验室让我们自己在网上找资料进行自学用C#编程写串口通信,可是我都只找到一些零碎的东西,看不明白,请问您有什么好的建议吗???希望您能帮帮我!!!谢谢!!我的邮箱是c1264483422@163.com
    Re: 王民利 2012-09-05 19:43发表 [回复]
    回复marlin3018:不好意思,我原本想完善之后立即上传的,时间长了也放下了。现在源代码在公司那台计算机上,我几天会会公司传给你吧。
    Re: marlin3018 2012-09-05 21:10发表 [回复]
    回复王民利:嗯,谢谢。最近刚开始看串口通信,一直没能入门。正纠结着。我的邮箱是243211816@qq.com!
    Re: 王民利 2012-09-05 21:21发表 [回复]
    回复marlin3018:好的,周六我去公司,我上传网络,你直接(0积分)下载就可以了。
    Re: marlin3018 2012-09-06 20:52发表 [回复]
    回复王民利:可以加你QQ好友吗,有些串口方面的疑惑想请教你。
    Re: 王民利 2012-09-08 10:06发表 [回复]
    回复marlin3018:代码我已上传,你可以去下载:http://download.csdn.net/detail/rehongchen/4556190
    Re: 王民利 2012-09-06 21:58发表 [回复]
    回复marlin3018:我也是临时抱佛脚学的串口通信,比较水的,只能说互相学习吧,QQ:974484821
    发表评论
    • 用 户 名:
    • kasama1953
    • 评论内容:
    • 插入代码
    展开全文
  • C++怎么调用C#函数

    2018-02-10 17:27:30
    C#编译DLL或者Axtive控件,再由C调用 !比如使用C++调用C#的DLLSwfDotNet是C#编写的,作者的C#水平,真是令我佩服。这是个特别好的读写Swf文件的库。但是,我要用在C++项目中,怎么让C++调用C#的DLL呢。今天一...
  • 今天介绍一下 C# 用 picturebox 画坐标系 编译环境:VS2010 1.首先建立一个 Windows窗体应用程序 如下图1所示 建好后,就会出现一个窗体设计界面,可以在属性栏更改其名字,图标,Text……如下图2所示: 2.添加...
  • C#的十大遗憾

    2016-07-12 20:42:31
    C# 设计组的一员,而且是 《Essential C# 6.0》 第5版的作者之一。 -------- 分割线 ----------- 我以前在 C# 设计组的时候,每年都有几场见面会活动,回答 C# 爱好者的问题。最常见的问题可能
  • C#中的类型转换

    2007-03-24 16:51:00
    C# 的装箱/拆箱/别名、数值类型间相互转换、字符的 ASCII 码和 Unicode 码、数值字符串和数值之间的转换、字符串和字符数组/字节数组之间的转换、各种数值类型和字节数组之间的转换、十六进制数输出以及日期型数据的...
  • c++ 与C#比较

    2011-10-06 04:32:13
    vc比c#难很多(入门学习) 实现同样的功能,vc的代码量要比c#多很多 对于高手来讲 他要考虑是项目的需求 是要程序运行速度快的那种,还是开发速度快的那种   C#不需要用到麻烦的mfc类,不需要头疼的*.h...
  •  昨天晚上做了一回雷锋,帮朋友写了个程序,把他2天都未整理好的Excel数据,一个小时...无奈,我用C#给他写了个excel工作表及cell读取的程序,因为只是他用,读取工作表的那些cell都写死了,没写可配置。<b
  • C#操作RTF文档

    2007-10-22 18:07:00
    来源: 博客园 笔者正在用C#开发一个名为XWriter的文本编辑器,其中需要提供对RTF文档的支持,以前从没有过RTF文档,因此临时突击研究了一下,经过几天的学习研究和实践,对C#操作RTF文档有所了解,因此才可以写出...
  • C#写数据库大作业

    2015-09-24 20:42:18
    其实一开始我是写word文档的,复制过来图片很难,可以直接下载来看 http://pan.baidu.com/s/1jGzYqma 这个攻略主要面向编程能力弱的同学,很多同学都认为编程很难,其实编程很简单,特别是找到类似我...
  • 参加工作快一年了,期间用C#开发过几个项目,如果从自己接触C#开始算起来的话,已经有三年了,期间东西学了 C++,ASP,JAVASCRIPT,HTML,DIV+CSS,QT,ASP.NET,PHP等,一直没有潜下心来好好学学C#,个人认为C#...
  • 1.右击你的工程,“属性”——“应用程序”——“输出类型”——“下拉菜单中选择类库”——“保存”——右击项目---“生成”——完毕。 2.右击你的工程——“在windows资源管理器中打开文件夹”——在bin文件夹...
  • C#与.NET之间的关系

    2019-07-25 08:29:25
    前些日子,在网上加了一个关于C#的学术群,里面的验证消息就是请说明C#与.NET之间的关系。于是便在网上找了一些资料,详细说一说C#和.NET。 C# C#是微软公司发布的一种面向对象的、运行于.NET Framework之上的高级...
  • C# Tif格式转Jpg

    2017-09-26 17:24:35
    C# Tif格式转jpg
  • C#高效绘图(转)

    2016-06-11 18:59:54
    2013-12-26 16:01 10938人阅读 评论(1) 收藏 举报  分类:   C#(235)  双缓冲技术 双缓冲是将图片在显示到DC前,现在要内存建一个DC,也就是用于存储这张图片的内存区,然后在将这部分...
  • 把最近学的一些知识总结了下。 然后,写了这个么一个小程序。 这个小程序的作用是,...所以用C#,自己手写算法的实现,锻炼一下。呵呵。   本程序的下载地址:http://download.csdn.net/detail/stevenkylelee/4852518
1 2 3 4 5 ... 20
收藏数 35,253
精华内容 14,101
热门标签