【问题标题】:Some hyperlinks don't trigger the LinkClicked event when loading a file into RichTextBox将文件加载到 RichTextBox 时,某些超链接不会触发 LinkClicked 事件
【发布时间】:2019-07-08 14:09:58
【问题描述】:

在 .net 4.7 中的一个简单的 Windows 窗体应用程序中,我的窗体上只有一个 RichTextBox。我正在从本地加载一个在 MS Word 2016 中创建的 *.rtf 文件。超链接已在 Word 中设置。问题是在应用程序中单击超链接时,并非所有链接都会触发 LinkClicked 事件。

行为如下: 如果超链接后面有足够的字符(会有所不同),则会由 LinkClicked 事件触发。如果我删除超链接后面的字符,它不会触发事件。

更新 经过一番测试,最后一个 URL 后需要插入的字符数等于正在加载的 *.rtf 文件中所有 URL 的总字符数。

我不能发图片,括号里的文字是超链接

不起作用: [点击这里]了解更多信息。

{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang4105{\fonttbl{\f0\fnil\fcharset0 Calibri;}}
{\*\generator Riched20 10.0.17134}\viewkind4\uc1 
{\field{\*\fldinst { HYPERLINK "http://www.google.com" }}{\fldrslt {Click here}}}
\pard\sa200\sl276\slmult1\f0\fs22\lang9  for more information.\par
}

作品: [点击这里查看更多信息。 Lorem ipsum

{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang4105{\fonttbl{\f0\fnil\fcharset0 Calibri;}}
{\*\generator Riched20 10.0.17134}\viewkind4\uc1 
{\field{\*\fldinst { HYPERLINK "http://www.google.com" }}{\fldrslt {Click here}}}
\pard\sa200\sl276\slmult1\f0\fs22\lang9  for more information. Lorem ipsum\par
}

链接工作所需的字符数在大约 20 到大约 100 个字符之间变化。

我创建了一个小项目,以确保问题不是源于主项目中的其他任何地方。该项目仅包含一个 RichTextBox。我已将 DetectUrls 设置为 True,这没有任何区别。我还尝试在 Google Docs 中创建 *.rtf 文件,以检查 Word 的版本是否可能是问题所在。我还使用写字板进行了测试,包括在 Notepad++ 中手动输入的 URL。 .Net Framework 4.6 中不会出现此问题,但我需要使用 .Net 4.7。如果我动态添加链接,问题也不会发生,但我不能按照我的要求这样做。

Public Sub Form1_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load

        Dim LoadFileName As Object

        LoadFileName = "C:\Users\anononym\source\repos\WindowsApp1\Test.rtf"

        RichTextBox1.LoadFile(LoadFileName, RichTextBoxStreamType.RichText)

End Sub

Private Sub RichTextBox_LinkClicked(sender As Object, e As LinkClickedEventArgs) Handles RichTextBox1.LinkClicked
        System.Diagnostics.Process.Start(e.LinkText)
End Sub

预期结果是超链接在所有情况下都重定向到Word中设置的网站,我使用www.google.com进行测试。

【问题讨论】:

    标签: c# vb.net winforms richtextbox rtf


    【解决方案1】:

    从 .Net 4.7 开始,RichTextBox 使用 RichEdit50 控件;以前的版本使用 RichEdit20 控件。我不知道控件版本之间的超链接处理差异的原因,但显然存在一些差异。

    一种解决方法是配置您的 .Net 4.7 应用程序以使用旧控件。这是通过将以下内容添加到您的 App.config 文件来完成的。

    <runtime>
      <AppContextSwitchOverrides value="Switch.System.Windows.Forms.DoNotLoadLatestRichEditControl=true" />
    </runtime>
    

    编辑:

    问题的根源似乎是原始RichTextBox.CharRangeToString Method 中的黑客攻击。

            //Windows bug: 64-bit windows returns a bad range for us.  VSWhidbey 504502.  
            //Putting in a hack to avoid an unhandled exception.
            if (c.cpMax > Text.Length || c.cpMax-c.cpMin <= 0) {
                return string.Empty;
            }
    

    使用 RichEdit50 控件中可用的Friendly Name Hyperlinks 时,RichTextBox.Text.Length 属性可以小于c.cpMax 值,因为链接不包含在返回的属性值中。这会导致方法将 String.Empty 返回到调用 RichTextBox.EnLinkMsgHandler Method,如果返回 Empty.String,则不会引发 LickClicked 事件。

                case NativeMethods.WM_LBUTTONDOWN:
                    string linktext = CharRangeToString(enlink.charrange);
                    if (!string.IsNullOrEmpty(linktext))
                    {
                        OnLinkClicked(new LinkClickedEventArgs(linktext));
                    }
                    m.Result = (IntPtr)1;
                    return;
    

    为了处理这个bug,下面定义了一个自定义的RichTextBox类来修改CharRangeToString方法的逻辑。在WndProc 过程中调用此修改后的逻辑以绕过默认逻辑。

    Imports System.Runtime.InteropServices
    Imports WindowsApp2.NativeMthods ' *** change WindowsApp2 to match your project
    
    Public Class RichTextBoxFixedForFriendlyLinks : Inherits RichTextBox
    
      Friend Function ConvertFromENLINK64(es64 As ENLINK64) As ENLINK
        ' Note: the RichTextBox.ConvertFromENLINK64 method is written using C# unsafe code
        ' this is version uses a GCHandle to pin the byte array so that 
        ' the same Marshal.Read_Xyz methods can be used
    
        Dim es As New ENLINK()
        Dim hndl As GCHandle
        Try
          hndl = GCHandle.Alloc(es64.contents, GCHandleType.Pinned)
    
          Dim es64p As IntPtr = hndl.AddrOfPinnedObject
          es.nmhdr = New NMHDR()
          es.charrange = New CHARRANGE()
    
          es.nmhdr.hwndFrom = Marshal.ReadIntPtr(es64p)
          es.nmhdr.idFrom = Marshal.ReadIntPtr(es64p + 8)
          es.nmhdr.code = Marshal.ReadInt32(es64p + 16)
          es.msg = Marshal.ReadInt32(es64p + 24)
          es.wParam = Marshal.ReadIntPtr(es64p + 28)
          es.lParam = Marshal.ReadIntPtr(es64p + 36)
          es.charrange.cpMin = Marshal.ReadInt32(es64p + 44)
          es.charrange.cpMax = Marshal.ReadInt32(es64p + 48)
        Finally
          hndl.Free()
        End Try
    
        Return es
      End Function
    
      Protected Overrides Sub WndProc(ByRef m As Message)
        If m.Msg = WM_ReflectNotify Then
          Dim hdr As NMHDR = CType(m.GetLParam(GetType(NMHDR)), NMHDR)
          If hdr.code = EN_Link Then
    
            Dim lnk As ENLINK
    
            If IntPtr.Size = 4 Then
              lnk = CType(m.GetLParam(GetType(ENLINK)), ENLINK)
            Else
              lnk = ConvertFromENLINK64(CType(m.GetLParam(GetType(ENLINK64)), ENLINK64))
            End If
    
            If lnk.msg = WM_LBUTTONDOWN Then
              Dim linkUrl As String = CharRangeToString(lnk.charrange)
              ' Still check if linkUrl is not empty
              If Not String.IsNullOrEmpty(linkUrl) Then
                OnLinkClicked(New LinkClickedEventArgs(linkUrl))
              End If
              m.Result = New IntPtr(1)
              Exit Sub
            End If
    
          End If
        End If
    
        MyBase.WndProc(m)
      End Sub
    
      Private Function CharRangeToString(ByVal c As CHARRANGE) As String
        Dim ret As String = String.Empty
        Dim txrg As New TEXTRANGE With {.chrg = c}
    
        ''Windows bug: 64-bit windows returns a bad range for us.  VSWhidbey 504502.  
        ''Putting in a hack to avoid an unhandled exception.
        'If c.cpMax > Text.Length OrElse c.cpMax - c.cpMin <= 0 Then
        '  Return String.Empty
        'End If
    
        ' *********
        ' c.cpMax can be greater than Text.Length if using friendly links
        ' with RichEdit50. so that check is not valid.  
    
        ' instead of the hack above, first check that the number of characters is positive 
        ' and then use the result of sending EM_GETTEXTRANGE  to handle the 
        ' possibilty of Text.Length < c.cpMax
        ' *********
    
        Dim numCharacters As Int32 = (c.cpMax - c.cpMin) + 1 ' +1 for null termination
        If numCharacters > 0 Then
          Dim charBuffer As CharBuffer
    
          charBuffer = CharBuffer.CreateBuffer(numCharacters)
          Dim unmanagedBuffer As IntPtr
    
          Try
            unmanagedBuffer = charBuffer.AllocCoTaskMem()
            If unmanagedBuffer = IntPtr.Zero Then
              Throw New OutOfMemoryException()
            End If
    
            txrg.lpstrText = unmanagedBuffer
            Dim len As Int32 = CInt(SendMessage(New HandleRef(Me, Handle), EM_GETTEXTRANGE, 0, txrg))
    
            If len > 0 Then
              charBuffer.PutCoTaskMem(unmanagedBuffer)
              ret = charBuffer.GetString()
            End If
          Finally
            If txrg.lpstrText <> IntPtr.Zero Then
              Marshal.FreeCoTaskMem(unmanagedBuffer)
            End If
          End Try
        End If
    
        Return ret
      End Function
    End Class
    

    虽然上面的代码不是那么重要,但它需要一些来自基本实现的方法/结构,这些方法/结构是不可公开访问的。下面介绍了这些方法的 VB 版本。大多数是从原始 C# 源代码直接转换而来的。

    Imports System.Runtime.InteropServices
    Imports System.Text
    
    Public Class NativeMthods
    
      Friend Const EN_Link As Int32 = &H70B
      Friend Const WM_NOTIFY As Int32 = &H4E
      Friend Const WM_User As Int32 = &H400
      Friend Const WM_REFLECT As Int32 = WM_User + &H1C00
      Friend Const WM_ReflectNotify As Int32 = WM_REFLECT Or WM_NOTIFY
      Friend Const WM_LBUTTONDOWN As Int32 = &H201
      Friend Const EM_GETTEXTRANGE As Int32 = WM_User + 75
    
      Public Structure NMHDR
        Public hwndFrom As IntPtr
        Public idFrom As IntPtr 'This is declared as UINT_PTR in winuser.h
        Public code As Int32
      End Structure
    
      <StructLayout(LayoutKind.Sequential)>
      Public Class ENLINK
        Public nmhdr As NMHDR
        Public msg As Int32 = 0
        Public wParam As IntPtr = IntPtr.Zero
        Public lParam As IntPtr = IntPtr.Zero
        Public charrange As CHARRANGE = Nothing
      End Class
    
      <StructLayout(LayoutKind.Sequential)>
      Public Class ENLINK64
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=56)>
        Public contents(0 To 55) As Byte
      End Class
    
      <StructLayout(LayoutKind.Sequential)>
      Public Class CHARRANGE
        Public cpMin As Int32
        Public cpMax As Int32
      End Class
    
      <StructLayout(LayoutKind.Sequential)>
      Public Class TEXTRANGE
        Public chrg As CHARRANGE
        Public lpstrText As IntPtr ' allocated by caller, zero terminated by RichEdit
      End Class
    
      Public MustInherit Class CharBuffer
        Public Shared Function CreateBuffer(ByVal size As Int32) As CharBuffer
          If Marshal.SystemDefaultCharSize = 1 Then
            Return New AnsiCharBuffer(size)
          End If
          Return New UnicodeCharBuffer(size)
        End Function
    
        Public MustOverride Function AllocCoTaskMem() As IntPtr
        Public MustOverride Function GetString() As String
        Public MustOverride Sub PutCoTaskMem(ByVal ptr As IntPtr)
        Public MustOverride Sub PutString(ByVal s As String)
      End Class
    
      Public Class AnsiCharBuffer : Inherits CharBuffer
        Friend buffer() As Byte
        Friend offset As Int32
    
        Public Sub New(ByVal size As Int32)
          buffer = New Byte(0 To size - 1) {}
        End Sub
    
        Public Overrides Function AllocCoTaskMem() As IntPtr
          Dim result As IntPtr = Marshal.AllocCoTaskMem(buffer.Length)
          Marshal.Copy(buffer, 0, result, buffer.Length)
          Return result
        End Function
    
        Public Overrides Function GetString() As String
          Dim i As Int32 = offset
          Do While i < buffer.Length AndAlso buffer(i) <> 0
            i += 1
          Loop
          Dim result As String = Encoding.Default.GetString(buffer, offset, i - offset)
          If i < buffer.Length Then
            i += 1
          End If
          offset = i
          Return result
        End Function
    
        Public Overrides Sub PutCoTaskMem(ByVal ptr As IntPtr)
          Marshal.Copy(ptr, buffer, 0, buffer.Length)
          offset = 0
        End Sub
    
        Public Overrides Sub PutString(ByVal s As String)
          Dim bytes() As Byte = Encoding.Default.GetBytes(s)
          Dim count As Int32 = Math.Min(bytes.Length, buffer.Length - offset)
          Array.Copy(bytes, 0, buffer, offset, count)
          offset += count
          If offset < buffer.Length Then
            buffer(offset) = 0
            offset += 1
          End If
        End Sub
      End Class
    
      Public Class UnicodeCharBuffer : Inherits CharBuffer
        Friend buffer() As Char
        Friend offset As Int32
    
        Public Sub New(ByVal size As Int32)
          buffer = New Char(size - 1) {}
        End Sub
    
        Public Overrides Function AllocCoTaskMem() As IntPtr
          Dim result As IntPtr = Marshal.AllocCoTaskMem(buffer.Length * 2)
          Marshal.Copy(buffer, 0, result, buffer.Length)
          Return result
        End Function
    
        Public Overrides Function GetString() As String
          Dim i As Int32 = offset
          Do While i < buffer.Length AndAlso AscW(buffer(i)) <> 0
            i += 1
          Loop
          Dim result As New String(buffer, offset, i - offset)
          If i < buffer.Length Then
            i += 1
          End If
          offset = i
          Return result
        End Function
    
        Public Overrides Sub PutCoTaskMem(ByVal ptr As IntPtr)
          Marshal.Copy(ptr, buffer, 0, buffer.Length)
          offset = 0
        End Sub
    
        Public Overrides Sub PutString(ByVal s As String)
          Dim count As Int32 = Math.Min(s.Length, buffer.Length - offset)
          s.CopyTo(0, buffer, offset, count)
          offset += count
          If offset < buffer.Length Then
            buffer(offset) = ChrW(0)
            offset += 1
          End If
        End Sub
      End Class
    
      <DllImport("user32.dll", CharSet:=CharSet.Auto)>
      Public Shared Function SendMessage(ByVal hWnd As HandleRef, ByVal msg As Int32, ByVal wParam As Int32, ByVal lParam As TEXTRANGE) As IntPtr
      End Function
    
    End Class
    

    将这些类添加到我们的项目并执行构建。 RichTextBoxFixedForFriendlyLinks 应该在工具箱中可用。您可以在通常使用RichTextBox 控件的地方使用它。


    编辑2:

    此问题已在 MS 开发者社区发布为:WinForm RichTextBox LinkClicked event fails to fire when control loaded with RTF containing a friendly name hyperlink

    【讨论】:

    • 它确实解决了 URL 不触发事件的问题,但它也删除了链接的格式。 “[点击这里查看更多信息。”变为“单击此处google.com> 了解更多信息”。到目前为止,唯一可行的方法是在 URL 后添加 21 个空格。
    • @stephbrouil,我添加了自定义 RTB 的代码来处理问题。
    • 感谢您的帮助,这解决了我的问题。
    • @TnTinMn 谢谢!我发布了您的代码的 C# 转换。
    【解决方案2】:

    这是对我有用的@TnTinMn 的 VB 实现的 C# 转换。感谢 https://github.com/icsharpcode/CodeConverter 完成大部分转换。

    显然,根据需要更改下面的命名空间。再次感谢@TnTinMn。

    RichTextBoxFixedForFriendlyLinks.cs

    using System;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    using static TestRtf.NativeMthods;
    
    namespace TestRtf
    {
        public partial class RichTextBoxFixedForFriendlyLinks : RichTextBox
        {
            internal ENLINK ConvertFromENLINK64(ENLINK64 es64)
            {
                // Note: the RichTextBox.ConvertFromENLINK64 method is written using C# unsafe code
                // this is version uses a GCHandle to pin the byte array so that 
                // the same Marshal.Read_Xyz methods can be used
    
                var es = new ENLINK();
                GCHandle? hndl = null;
                try
                {
                    hndl = GCHandle.Alloc(es64.contents, GCHandleType.Pinned);
                    var es64p = hndl.Value.AddrOfPinnedObject();
                    es.nmhdr = new NMHDR();
                    es.charrange = new CHARRANGE();
                    es.nmhdr.hwndFrom = Marshal.ReadIntPtr(es64p);
                    es.nmhdr.idFrom = Marshal.ReadIntPtr(es64p + 8);
                    es.nmhdr.code = Marshal.ReadInt32(es64p + 16);
                    es.msg = Marshal.ReadInt32(es64p + 24);
                    es.wParam = Marshal.ReadIntPtr(es64p + 28);
                    es.lParam = Marshal.ReadIntPtr(es64p + 36);
                    es.charrange.cpMin = Marshal.ReadInt32(es64p + 44);
                    es.charrange.cpMax = Marshal.ReadInt32(es64p + 48);
                }
                finally
                {
                    if (hndl.HasValue)
                        hndl.Value.Free();
                }
    
                return es;
            }
    
            protected override void WndProc(ref Message m)
            {
                if (m.Msg == WM_ReflectNotify)
                {
                    NMHDR hdr = (NMHDR)m.GetLParam(typeof(NMHDR));
                    if (hdr.code == EN_Link)
                    {
                        ENLINK lnk;
                        if (IntPtr.Size == 4)
                        {
                            lnk = (ENLINK)m.GetLParam(typeof(ENLINK));
                        }
                        else
                        {
                            lnk = ConvertFromENLINK64((ENLINK64)m.GetLParam(typeof(ENLINK64)));
                        }
    
                        if (lnk.msg == WM_LBUTTONDOWN)
                        {
                            string linkUrl = CharRangeToString(lnk.charrange);
                            // Still check if linkUrl is not empty
                            if (!string.IsNullOrEmpty(linkUrl))
                            {
                                OnLinkClicked(new LinkClickedEventArgs(linkUrl));
                            }
    
                            m.Result = new IntPtr(1);
                            return;
                        }
                    }
                }
    
                base.WndProc(ref m);
            }
    
            private string CharRangeToString(CHARRANGE c)
            {
                string ret = string.Empty;
                var txrg = new TEXTRANGE() { chrg = c };
    
                // 'Windows bug: 64-bit windows returns a bad range for us.  VSWhidbey 504502.  
                // 'Putting in a hack to avoid an unhandled exception.
                // If c.cpMax > Text.Length OrElse c.cpMax - c.cpMin <= 0 Then
                // Return String.Empty
                // End If
    
                // *********
                // c.cpMax can be greater than Text.Length if using friendly links
                // with RichEdit50. so that check is not valid.  
    
                // instead of the hack above, first check that the number of characters is positive 
                // and then use the result of sending EM_GETTEXTRANGE  to handle the 
                // possibilty of Text.Length < c.cpMax
                // *********
    
                int numCharacters = c.cpMax - c.cpMin + 1; // +1 for null termination
                if (numCharacters > 0)
                {
                    var charBuffer = default(CharBuffer);
                    charBuffer = CharBuffer.CreateBuffer(numCharacters);
                    IntPtr unmanagedBuffer = IntPtr.Zero;
                    try
                    {
                        unmanagedBuffer = charBuffer.AllocCoTaskMem();
                        if (unmanagedBuffer == IntPtr.Zero)
                        {
                            throw new OutOfMemoryException();
                        }
    
                        txrg.lpstrText = unmanagedBuffer;
                        IntPtr len = SendMessage(new HandleRef(this, Handle), EM_GETTEXTRANGE, 0, txrg);
                        if (len != IntPtr.Zero)
                        {
                            charBuffer.PutCoTaskMem(unmanagedBuffer);
                            ret = charBuffer.GetString();
                        }
                    }
                    finally
                    {
                        if (txrg.lpstrText != IntPtr.Zero)
                        {
                            Marshal.FreeCoTaskMem(unmanagedBuffer);
                        }
                    }
                }
    
                return ret;
            }
        }
    }
    

    NativeMthods.cs:

    using System;
    using System.Runtime.InteropServices;
    using System.Text;
    
    namespace TestRtf
    {
        public partial class NativeMthods
        {
            internal const int EN_Link = 0x70B;
            internal const int WM_NOTIFY = 0x4E;
            internal const int WM_User = 0x400;
            internal const int WM_REFLECT = WM_User + 0x1C00;
            internal const int WM_ReflectNotify = WM_REFLECT | WM_NOTIFY;
            internal const int WM_LBUTTONDOWN = 0x201;
            internal const int EM_GETTEXTRANGE = WM_User + 75;
    
            public partial struct NMHDR
            {
                public IntPtr hwndFrom;
                public IntPtr idFrom; // This is declared as UINT_PTR in winuser.h
                public int code;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public partial class ENLINK
            {
                public NMHDR nmhdr;
                public int msg = 0;
                public IntPtr wParam = IntPtr.Zero;
                public IntPtr lParam = IntPtr.Zero;
                public CHARRANGE charrange = null;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public partial class ENLINK64
            {
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 56)]
                public byte[] contents = new byte[56];
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public partial class CHARRANGE
            {
                public int cpMin;
                public int cpMax;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public partial class TEXTRANGE
            {
                public CHARRANGE chrg;
                public IntPtr lpstrText; // allocated by caller, zero terminated by RichEdit
            }
    
            public abstract partial class CharBuffer
            {
                public static CharBuffer CreateBuffer(int size)
                {
                    if (Marshal.SystemDefaultCharSize == 1)
                    {
                        return new AnsiCharBuffer(size);
                    }
    
                    return new UnicodeCharBuffer(size);
                }
    
                public abstract IntPtr AllocCoTaskMem();
                public abstract string GetString();
                public abstract void PutCoTaskMem(IntPtr ptr);
                public abstract void PutString(string s);
            }
    
            public partial class AnsiCharBuffer : CharBuffer
            {
                internal byte[] buffer;
                internal int offset;
    
                public AnsiCharBuffer(int size)
                {
                    buffer = new byte[size];
                }
    
                public override IntPtr AllocCoTaskMem()
                {
                    var result = Marshal.AllocCoTaskMem(buffer.Length);
                    Marshal.Copy(buffer, 0, result, buffer.Length);
                    return result;
                }
    
                public override string GetString()
                {
                    int i = offset;
                    while (i < buffer.Length && buffer[i] != 0)
                        i += 1;
                    string result = Encoding.Default.GetString(buffer, offset, i - offset);
                    if (i < buffer.Length)
                    {
                        i += 1;
                    }
    
                    offset = i;
                    return result;
                }
    
                public override void PutCoTaskMem(IntPtr ptr)
                {
                    Marshal.Copy(ptr, buffer, 0, buffer.Length);
                    offset = 0;
                }
    
                public override void PutString(string s)
                {
                    var bytes = Encoding.Default.GetBytes(s);
                    int count = Math.Min(bytes.Length, buffer.Length - offset);
                    Array.Copy(bytes, 0, buffer, offset, count);
                    offset += count;
                    if (offset < buffer.Length)
                    {
                        buffer[offset] = 0;
                        offset += 1;
                    }
                }
            }
    
            public partial class UnicodeCharBuffer : CharBuffer
            {
                internal char[] buffer;
                internal int offset;
    
                public UnicodeCharBuffer(int size)
                {
                    buffer = new char[size];
                }
    
                public override IntPtr AllocCoTaskMem()
                {
                    var result = Marshal.AllocCoTaskMem(buffer.Length * 2);
                    Marshal.Copy(buffer, 0, result, buffer.Length);
                    return result;
                }
    
                public override string GetString()
                {
                    int i = offset;
                    while (i < buffer.Length && buffer[i] != 0)
                        i += 1;
                    string result = new string(buffer, offset, i - offset);
                    if (i < buffer.Length)
                    {
                        i += 1;
                    }
    
                    offset = i;
                    return result;
                }
    
                public override void PutCoTaskMem(IntPtr ptr)
                {
                    Marshal.Copy(ptr, buffer, 0, buffer.Length);
                    offset = 0;
                }
    
                public override void PutString(string s)
                {
                    int count = Math.Min(s.Length, buffer.Length - offset);
                    s.CopyTo(0, buffer, offset, count);
                    offset += count;
                    if (offset < buffer.Length)
                    {
                        buffer[offset] = '\0';
                        offset += 1;
                    }
                }
            }
    
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, TEXTRANGE lParam);
        }
    }
    

    【讨论】:

    • 您可以消除 Microsoft.VisualBasic 引用。将while (i &lt; buffer.Length &amp;&amp; Strings.AscW(buffer[i]) != 0) 更改为while (i &lt; buffer.Length &amp;&amp; buffer[i] != 0)AscW 所做的只是将 char 转换为 int
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-01
    相关资源
    最近更新 更多