RichEdit Friendly Name Hyperlinks

This post is a companion to Automatic RichEdit Hyperlinks. As stated in that post, RichEdit has two kinds of hyperlinks, automatic hyperlinks (autoURLs) and friendly name hyperlinks. A friendly name hyperlink has a name, which is displayed, and a hidden instruction part that contains the actual URL. Such hyperlinks are commonly used when an author wants to display an informative name for a link rather than the URL itself. The present post describes RichEdit’s implementation of friendly name hyperlinks. It’s aimed at programmers, so if you’re not so inclined, you may want to skip it.

A friendly name hyperlink is essentially a field with two parts: an instruction part containing the URL and a result part containing the name. In fact that’s the way it appears in RTF, which has the syntax {\field{\*\fldinst {HYPERLINK “…”}}{\fldrslt{…}}}.

In RichEdit, the hyperlink field entity is represented by character formatting effects, as contrasted to delimiters which are used to structure math objects. As such, these hyperlinks cannot be nested, although in RichEdit 5.0 and later they can be adjacent to one another. The whole hyperlink has the character formatting effects of CFE_LINK and CFE_LINKPROTECTED, while autoURLs only have the CFE_LINK attribute. The CFE_LINKPROTECTED is included for the former so that the autoURL scanner skips over friendly name links. The instruction part, i.e., the URL, has the CFE_HIDDEN attribute as well, since it’s not supposed to be displayed. The URL itself is enclosed in ASCII double quotes and preceded by the string “HYPERLINK “. Since CFE_HIDDEN plays an integral role in friendly name hyperlinks, it cannot be used in the name.

For example, in WordPad, which uses RichEdit, a hyperlink with the name MSN would have the plain text

                HYPERLINK “”MSN

The whole link would have CFE_LINK and CFE_LINKPROTECTED character formatting attributes and all but the MSN would have the CFE_HIDDEN attribute.

In RichEdit 4.1, the only way to insert a friendly name hyperlink is to read in the corresponding RTF. For the example above, this RTF could be as simple as

{\rtf1{\field{\*\fldinst{ HYPERLINK ””}}{\fldrslt{ MSN} }}}.

Reading in the RTF can work well, but it’s convenient to have a more programmatic approach. Accordingly RichEdit 6.0 added the ITextRange2::SetURL(BSTR bstr) method, which uses the bstr as the URL for the range of text selected by the ITextRange2. The text in the bstr needs to start and end with ASCII double quotes. The word HYPERLINK is inserted in front of the URL by the SetURL() method. RichEdit 7.0 allows you to remove the link status from a friendly name hyperlink by calling SetURL() with a NULL bstr or one that has only the start and end quotes, signifying an empty string.

RichEdit doesn’t allow the CFE_LINKPROTECTED attribute to be set directly by programs. Maybe it will allow it someday after enough testing is completed to ensure that things cannot go awry. Programmatically you can use ITextRange::Expand(tomLink, pDelta) to select the whole friendly name link: hidden URL together with the friendly name. You can move from link to link using ITextRange::Move(tomLink, Count, pDelta).

As for autoURLs, the RichEdit client enables hyperlink notifications (EN_LINK) by sending RichEdit the ENM_LINK flag in the mask included with theEM_SETEVENTMASK message. A notification is sent if the user hits Enter, moves the mouse over it (not doing drag&drop), left clicks (or double clicks) on it. It is also sent if the message EM_HANDLELINKNOTIFICATION is received (OneNote uses this). The ENLINK notification structure contains a CHARRANGE with the start and end character positions of the actual URL (IRI, file path name, email address, etc.) that typically appears in a browser URL window. This doesn’t include the “HYPERLINK ” string nor the quotes in the hidden part. For the MSN link above, it identifies only the characters in the backing store.

In RichEdit 5.0 and later, the client can enable tooltips displaying the URLs by sending the EM_SETEDITSTYLE message with the SES_HYPERLINKTOOLTIPS (8) flag.

Comments (20)

  1. Alex says:

    Hello, Murray. This is great to have such functionality, but using friendly named hyperlinks leads to strange bug for me.

    When I try to select the text after the hyperlink, it occurs shifted by the length of the hyperlink address to the beginning of the text.

    For example, if I have the following text:

    "To get more info go [here] and find out all the stuff"

    In square brackets I marked the hyperlink. Now if I try to select the word after, it will be shifted. The shift is varying depending on the hyperlink address length (I mentioned that above).

    Is there any workaround to that issue? Or maybe I do something wrong?

  2. Daniel says:

    Murray, your post is such a tease! 😉

    Searching the eb for ITextRange2 yields no helpful links, nor does CFE_LINKPROTECTED.

    And I've had only limited success using CFE_LINK, CFE_HIDDEN and CFE_PROTECTED.

    Any further information you could add would be gratefully received.

  3. MurrayS3 says:

    Sorry about that. Hopefully the documentation will improve soon. CFE_LINKPROTECTED is defined as 0x00800000. ITextRange2 is still undocumented, but you can use EM_GETCHARFORMAT and EM_SETCHARFORMAT with the flag SCF_ONLYCFEFFECTS (0x0200) to get/set the flag.

  4. Aleks says:

    String {rtf1{field{*fldinst{ HYPERLINK ""}}{fldresult{ MSN} }}} did't work for me. I've spent a day trying to make it work until I finally found that 'fldresult' should be 'fldrslt'. So double check the manuals 🙂

  5. MurrayS3 says:

    Many thanks for pointing out the typo. I've fixed it

  6. Daniel Firka says:

    Hi! is there any way to see the pInvoke declaration of ENLINK? it is working for me Ok in 32 bits, but fails in 64 bits. My c# declaration is:


           private struct ENLINK


               public NMHDR hdr;

               public int msg;

               public IntPtr wp;

               public IntPtr lp;

               public CHARRANGE range;



           private struct CHARRANGE


               public int cpmin;

               public int cpmax;



           private struct NMHDR


               public IntPtr hWnd;

               public IntPtr id;

               public int code;


    (the cpMax element of CHARRANGE returns zero in 64 bits, an cpMin is equal to what it should be cpMax, which I suspect is a problem of alignment)


  7. MurrayS3 says:

    I think the problem on 64 bit builds is that int is treated as a 64-bit integer. But CHARRANGE is defined to be two 32-bit integers by LONG. The code stores them as 32-bit integers even on 64-bit builds.

  8. HB says:


    I'm having the same problem as @Alex, creating a link with a friendly name different than the URL is giving a wrong selection index. I'm using a RichEdit 4.1 control because of the improved table support. Is there a workaround for this bug or some flag that needs to be set? Googling around I found a few more people with this same issue, but no solution.

  9. MurrayS3 says:

    I don't have any problem selecting the text following a friendly-name link inserted using RTF in my current RichEdit build. But that build is much more recent than RichEdit 4.1. Maybe we fixed something in between Windows 7 and 8. The RichEdit in Windows 8.1 is much more powerful than RichEdit 4.1 and the one in Windows 10 is still more powerful. Hopefully you can upgrade to Windows 10.

  10. HB says:

    Thanks for your reply MurrayS3. Note that I'm using .NET, I've also tried using richedit 6.0 in my application, but the result is the same, so maybe I need to set some option in richedit? I'm also having a problem when later capturing EN_LINK notifications, The CHARRANGE received points to the corresponding location of the hidden text, but EM_GETTEXTRANGE doesn't seem to be able to get it.

  11. HB says:

    Forgot to mention, I tried EM_EXGETSEL as well, but it seems the result is the same.

  12. MurrayS3 says:

    Automatic selection updates are applied to APIs that use the UI selection, e.g., EM_EXGETSEL. If you want to select hidden text, try using an ITextRange. That's available in RichEdit 4.1 as well as later (and earlier) versions. You can then get the raw text for the range using ITextRange::GetText(). ITextRange2 has more power, but for this purpose ITextRange works well

  13. Mick P. _ says:

    Hello MurrayS3! I'm surprised these comments are active.

    In Windows 8 and later the friendly hyperlinks are blue and underlined. How can this be disabled? We use the friendly links like rich buttons, not URLs. For instance a bold B or emboldening text, and an underlined ul for underlining! So it's ridiculous to underline B as well.

    Furthermore the underlines and blue are clutter elsewhere in breadcrum like headings, and they interfere with Chinese characters and the underlines stick out the sides of fullwidth characters. It's a nightmare!

  14. MurrayS3 says:

    The answer is in the last section of the post…/richedit-colors.aspx. By default, friendly-name hyperlink text is displayed in blue with a blue underline unless the name text is formatted with an explicit color. Explicit formatting takes precedence. The client can also suppress the automatic blue coloring for friendly-name links by sending EM_SETEDITSTYLEEX with wparam = 0 and lparam = SES_EX_HANDLEFRIENDLYURL.

  15. Mick P. _ says:

    ^Thank you Murray. SendMessage(ed,WM_USER+275,0,0×00000100); sounds perfect, except for the underline remains. Your post doesn't suggest otherwise, but the documentation for the flag mentions underlining.

    Just for the record on Windows 7 the friendly name links are not styled, so it seems like Microsoft has visually broken applications that use this feature. Maybe you can send out a memo or something?

    Any fix for the underlining? Adding ul0 to the front of the name doesn't work. Must it be a stylesheet like tactic? If so how to single out friendly name links?

  16. MurrayS3 says:

    Sending EM_SETEDITSTYLEEX with wparam = 0 and lparam = SES_EX_HANDLEFRIENDLYURL disables the underlining as well in my handy test app reitp2.exe. It's true that having the feature on by default can change the appearance of existing friendly name links. It was done to mimic what Word does.

  17. Mick P. _ says:


    The underlines still remain. Is it possible that post-7 Windows is more prone to force the richedit control into "advanced typography" mode, which forces underlines to appear, and happens I know when adding bidirectional text in Windows 7.

    I tried setting the "advanced typography" mode to 0 on all bits to remove the underlines without success. Unfortunately I had to downgrade from Windows 10, so I can't easily check the results for myself anymore. I think the underlines are very unattractive and make reading difficult for non-English friendly names. It would be nice if someone at MS would try to make underlines conformant across versions of Windows. Forcing them on in the advanced typography mode seems like just an oversight that could serve no purpose.

    Thanks for being the only Rich Edit information resource available online. Too much of Windows is black magic.

  18. MurrayS3 says:

    You're right! When LineServices is active, all links get underlined, both auto and friendly-name. The fix to take the SES_EX_HANDLEFRIENDLYURL setting into account is easy, but I don't see a workaround to use in the interim until the fix is shipped. It's surprising no one reported this earlier. Thanks for telling us.

  19. Mick P. _ says:

    If you have experience that Windows 8/10 has LineServices always on, it's probably safe to assume it's the culprit–underlines wise. I hope there is a fix that includes the MSFTEDIT.DLL/RICHEDIT50W module for regular applications going forward.

    [I was looking forward to having spellchecking on Windows 10. I followed your other guide to enable it, but it wasn't working, and I didn't have time to investigate while trying Windows 10. Random black screens cut my visit short. I think RichEdit with OS level spellchecking services will have a long shelf life (and deserves better documentation.)

    Application programmers can't possibly provide spell checkers equally for everyone in the world. Built-in spellchecker should be Windows 8+'s headline selling point :)]

  20. Mick P. _ says:

    Windows 10's Wordpad is using RICHEDIT50W. Does it still use MSFTEDIT.DLL? And is there any chance there will ever be an update/hotfix for the friendly name underline issue discussed above? Anytime soon?

    In the meantime, is there a way to disable "Line Services"?