I have a question related to managing OLE objects in a RichTextBox.
What I've found so far is lots of info, but not exactly what I need, so I will make a quick introduction first (I also hope someone might find this helpful).
1. What I know so far
First of all, I am using OLE to insert images (or any ActiveX) into the RichTextBox. This is supposed to be "the right way" to do it, since there is no clipboard involved, and you can insert any ActiveX control you want. There is an article on CodeProject (MyExtRichTextBox) which explains how to do it (with complete source code), but to make it short:
Using P/Invoke, the OleCreateFromFile
function is imported from ole32.dll to create an OLE object from the image file.
int hresult = OleCreateFromFile(...);
Function returns an IOleObject
instance, which then has to be referenced by a REOBJECT
struct:
REOBJECT reoObject = new REOBJECT();
reoObject.cp = 0; // charated index for insertion
reoObject.clsid = guid; // iOleObject class guid
reoObject.poleobj = Marshal.GetIUnknownForObject(pOleObject); // actual object
// etc. (set other fields
// Then we set the flags. We can, for example, make the image resizable
// by adding a flag. I found this question to be asked frequently
// (how to enable or disable image drag handles).
reoObject.dwFlags = (uint)
(REOOBJECTFLAGS.REO_BELOWBASELINE | REOOBJECTFLAGS.REO_RESIZABLE);
// and I use the `dwUser` property to set the object's unique id
// (it's a 32-bit word, and it will be sufficient to identify it)
reoObject.dwUser = id;
And finally the structure is passed to RichTextBox using IRichEditOle.InsertObject
. IRichEditOle
is a COM interface, imported using P/Invoke also.
The "id" for the object enables me to iterate through the list of inserted objects, and "do stuff". Using IRichEditOle.GetObject
I can get each inserted object and check the dwUser
field to see if the id matched.
2. Problems
Now come the questions:
a) First problem is updating an inserted image. I want to be able to "refresh" certain images on demand (or change them). The way I am doing it right now is something like this:
if (reoObject.dwUser == id)
{
// get the char index for the "old" image
oldImageIndex = reoObject.cp;
// insert the new image (I added this overload for testing,
// it does the thing described above)
InsertImageFromFile(oldImageIndex, id, filename);
// and now I select the old image (which has now moved by one "character"
// position to the right), and delete it by setting the selection to ""
_richEdit.SelectionStart = oldImageIndex + 1;
_richEdit.SelectionLength = 1;
_richEdit.SelectedText = "";
}
Since I am updating from the Gui thread, I believe I shouldn't be worried about user changing the selection during this method, because OLE insertion blocks the thread, and the app is running in STA.
But I somehow feel that there might be a better/safer way to do it? This method looks like I should mark it with a [DirtyHack]
attribute.
b) The other problem is that, in the moment of insertion (IRichEditOle.InsertObject
), I get an unhandled exception (Paint Shop Pro has stopped working). It seems that inserting the OLE object somehow starts this app, although no file associations exist for Open or Edit shell commands.
Does anybody know what might be causing this and how to prevent it?
[Edit]
I just got a different idea - I could create my custom ActiveX control which would take care of updating the image. In that case I would only need to invalidate that part of the RichTextBox (similar to what the author of the CodeProject article does). But this would make deploying a bit more complicated (I need to expose a .Net class to COM, and then register it before embedding).