Last week it has been brought to my attention that Blog Navigator can no longer post web articles. Alas, this was only a symptom of a much worse disease, but first things first...
This month in a noble effort of making the Windows platform more secure for us and our (perhaps future) kids Microsoft rolled out a new set of updates, among which is this one innocently called MS05-013 and located in Microsoft Knowledge Base under a mysterious KB891781.
You will find more about the update and the horror it caused among the programmers using the control affected by the update on usenet, posts as this and this are only small sample of the damage. It seems that MS broke quite a few programs while making us more safe.
The effect, saying it bluntly is: IDHTMLEdit has been made useless and Blog Navigator fallen prey to that change.
Since IDHTMLEdit is simply a wrapper around MSHTML editor, I've examined quite a few approaches and some existing libraries, to fix the issue as fast and with as little modifications to the existing code as possible. After a day of hunting around and testing the existing approaches I've decided that if I want to retain the control over my code and keep it at a reasonable size, I need to do it myself.
Below is my simplistic (and working) approach to migrating from DHTML Editing Component to MSHTML editor (for Delphi programmers).
- I use a TEmbeddedWB as the web browser control wrapper as it offers quite a bit additional functionality. I'm assuming you use that instead of the TWebbrowser as well. If you're not, it'a not a big problem to extract the functionalty you will need here.
- In the DhtmlEdit you operate on the DOM structure and that's the interface the control does not allow you to access any loger and the change thaat makes it useless. The TWebBrowser offers a Document IDispatch which you can cast on IHTMLDocument2. This is exactly the structure you were working on before.
- Now the browser control itself does not offer the ExecCommand(...) functionality. but not all is lost. when you get to the IHTMLDocument2 interface as described before, you can pretty much do anythng you were doing before by calling its execCommand.
- You can also pull ann the formatting data from it by querrying it with its: queryCommandEnabled, execCommand, queryCommandIndeterm, queryCommandState, queryCommandSupported, queryCommandText and queryCommandValue methods.
- Now the problem is that IDHTMLEdit used numeric values while the Document requires you to use strings. The simplest method is to use a translating table which is what I did:
type
TMSHtmlCommandTranslation = array [DECMD_BOLD .. DECMD_PROPERTIES] of WideString;
const
MSHtmlCommandTranslation : TMSHtmlCommandTranslation = (
'Bold', //DECMD_BOLD = $00001388;
'', //
'Copy', //DECMD_COPY = $0000138A;
'Cut', //DECMD_CUT = $0000138B;
'Delete', //DDECMD_DELETE = $0000138C;
'', //DDECMD_DELETECELLS = $0000138D;
'', //DDECMD_DELETECOLS = $0000138E;
'', //DDECMD_DELETEROWS = $0000138F;
'Find', //DDECMD_FINDTEXT = $00001390;
'FontName', //DDECMD_FONT = $00001391;
'', //DDECMD_GETBACKCOLOR = $00001392;
'', //DDECMD_GETBLOCKFMT = $00001393;
'', //DDECMD_GETBLOCKFMTNAMES = $00001394;
'', //DDECMD_GETFONTNAME = $00001395;
'FontSize', //DDECMD_GETFONTSIZE = $00001396;
'', //DDECMD_GETFORECOLOR = $00001397;
'CreateLink', //DDECMD_HYPERLINK = $00001398;
'InsertImage', //DDECMD_IMAGE = $00001399;
'Indent', //DDECMD_INDENT = $0000139A;
'', //DDECMD_INSERTCELL = $0000139B;
'', //DDECMD_INSERTCOL = $0000139C;
'', //DDECMD_INSERTROW = $0000139D;
'', //DDECMD_INSERTTABLE = $0000139E;
'Italic', //DDECMD_ITALIC = $0000139F;
'JustifyCenter', //DDECMD_JUSTIFYCENTER = $000013A0;
'JustifyLeft', //DDECMD_JUSTIFYLEFT = $000013A1;
'JustifyRight', //DDECMD_JUSTIFYRIGHT = $000013A2;
'', //DDECMD_LOCK_ELEMENT = $000013A3;
'', //DDECMD_MAKE_ABSOLUTE = $000013A4;
'', //DDECMD_MERGECELLS = $000013A5;
'InsertOrderedList', //DDECMD_ORDERLIST = $000013A6;
'Outdent', //DDECMD_OUTDENT = $000013A7;
'Paste', //DDECMD_PASTE = $000013A8;
'Redo', //DDECMD_REDO = $000013A9;
'', //DDECMD_REMOVEFORMAT = $000013AA;
'SelectAll', //DDECMD_SELECTALL = $000013AB;
'', //DDECMD_SEND_BACKWARD = $000013AC;
'', //DDECMD_BRING_FORWARD = $000013AD;
'', //DDECMD_SEND_BELOW_TEXT = $000013AE;
'', //DDECMD_BRING_ABOVE_TEXT = $000013AF;
'', //DDECMD_SEND_TO_BACK = $000013B0;
'', //DDECMD_BRING_TO_FRONT = $000013B1;
'', //DDECMD_SETBACKCOLOR = $000013B2;
'', //DDECMD_SETBLOCKFMT = $000013B3;
'FontName', //DDECMD_SETFONTNAME = $000013B4;
'FontSize', //DDECMD_SETFONTSIZE = $000013B5;
'ForeColor', //DDECMD_SETFORECOLOR = $000013B6;
'', //DDECMD_SPLITCELL = $000013B7;
'Underline', //DDECMD_UNDERLINE = $000013B8;
'Undo', //DDECMD_UNDO = $000013B9;
'Unlink', //DDECMD_UNLINK = $000013BA;
'InsertUnorderedList',//DDECMD_UNORDERLIST = $000013BB;
''); //DECMD_PROPERTIES = $000013BC;
I store the command values as the controls Tags so now instead of calling:
DHTMLEdit.ExecCommand(TControl(Sender).Tag,OLECMDEXECOPT_DODEFAULT);
I simply call:
FDocument.execCommand(MSHtmlCommandTranslation[TControl(Sender).Tag],True,variant)
where the FDocument is the IHTMLDocument2 DOM document I extracted from the TWebBrowser for the sake of not having to cast it each time I use it. and instead of:
DHTMLEdit.QueryStatus(TControl(Sender).Tag);
I can now use:
FDocument.queryCommandValue(MSHtmlCommandTranslation[TControl(Sender).Tag]);
FDocument.queryCommandEnabled(MSHtmlCommandTranslation[TControl(Sender).Tag]);
this allowed me to migrate away from DHTML Editing Component within one day after shunning all other options).
One thing that was pretty indispensible for DHTML Editing Component was that it called me back evary time a blok formatting was changed, it had this useful OnDisplayChanged event which was summoned every time the UI needed updating. That's where TEmbeddedWB comes handy it implements a
function UpdateUI: HRESULT; stdcall;
which than triggers the OnUpdateUI event, which for our needs does exactly the save what the OnDisplayChanged event did. so you can simply call your previous OnDisplayChanged event with the numerical querries remapped to strings and... with minimal effort you have your control fully migrated to the MSHTML editor.
So... this post is the first post made with the updated Blog Navigator with its poat editong brought back to normal functionality.