Introduction
What is an Active Document? It�s a COM (Component Object Model) component that shows some data (charts, sheets, text documents, bitmaps) which is given by Active Document Server. An Active Document has to be put into an app (Container). For example, when you put a
Paintbrush bitmap into the Wordpad, the Paintbrush is an Active Document Server and the Wordpad is a Container. If you want to put an object into a Container, choose the "Insert/Object..." item from its menu. Despite being COM component, creating your own simple MFC Active Document Server doesn't need any COM knowledge.
There are two types of servers:
- Mini-server - It is compiled into exe file, but you can't execute it. You can only put it into a Container.
- Full-server - You can use it both as an Active Document Server and as an ordinary SDI or MDI application.
Of course your app can be both Full-server and Container.
Remember! Your Active Document Server has to be registrated in the Windows registry. Without it, you won't find it on "Insert object" list. Your app can register itself! You just have to execute it. If it's a Mini-server it will register itself before showing message "This server can only be run from a container application."
Creating a simple MFC Active Document Mini-Server
It isn't difficult to create your own MFC Active Document Server. AppWizard will create basic code for you! Select "New..." from "File" menu. Select "MFC AppWizard (exe)". Give a name to your project and click OK button.
Step 1: Select SDI or MDI app. I think for Mini-server SDI will be better.
Step 2: Any database support? It isn't necessary.
Step 3: It's the most important step. You can select compound document support. Container, server, active document? Anything you want! Choose "Mini-server". Select "Active document server". Think about "ActiveX Controls". Do you need it?
Step 4: Click "Advanced" button. In "Advanced Options" type the file extension. Even if your mini-server won't use files, the AppWizard wants it and won't continue without it.
Step 5: Nothing interesting!
Step 6: Select base class. It's important!
As you can see, AppWizard has created some code. It provides basic funcionallity. It will a�so register your app in the Registry (see Introduction). You can see two more classes: CInPlaceFrame and CAppnameSrvItem ("Appname" is of course a name of your application).
Server item and metafile device context
It is very important to understand how an Active Document works. When it isn't activated, what you can see in a Container is just a drawing. You can just resize it, copy it (of course with all stored data), but it isn't interactive. To edit it, you have to double-click on it.
CAppnameSrvItem is responsible for drawing it. It hasn't got any advanced funtions. Its most important function is OnDraw(). It draws what you can see in a Container. It receives a pointer to the metafile device context, on which unactivated Active Document is drawn. You don't have to worry about resizeing, creating, getting windows handles... You just have to draw what you want on device context given by the Container. It's very comfortable. For example, you don't have to write separate function for printing. A Container will just print the matafile DC. Remember that CAppnameSrvItem object is created by the document class.
Take a look at the following code:
BOOL CAppnameSrvrItem::OnDraw(CDC* pDC, CSize& rSize)
{
UNREFERENCED_PARAMETER(rSize);
CFaceDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowOrg(0,0);
pDC->SetWindowExt(3000, 3000);
pDC->Ellipse(500,1000,2500,2500);
return TRUE;
}
It was created by the AppWizard (except ellipse drawing line). pDC is of course the pointer to the metafile device context. What is important, you don't draw in pixel units! You draw in a rectangle which size is 3000x3000 HIMETRIC units. If you put something (line, rectange, text, bitmap) at x=3000 it will be always at the right edge of your Acctive Document.
Take a look at this code (put it into CAppnameSrvrItem::OnDraw function just before return TRUE; line). In a Container it will be represented like this (normal and after resizeing):
pDC->Rectangle(500,500,2500,2000);
pDC->LineTo(1500, 1500);

Remember to create a large font, when usuing text functions (like TextOut()).
In-place activation
And what's a View class for? I've already written that you have to double click on an Active Document to edit it. After double click the Container changes. Look at this two pictures:

The menu and the toolbar has changed. It's called In-place activation. Our app is run "inside" a Container. It has our toolbar and menu (combined with the Container's one!). The AppWizard has created the
IDR_SRVR_INPLACE toolbar and menu (take a look at Resources in your project). The Container's menu (of course not all) is added between two separators. The rectangle in the center of the screen isn't a Server item. It's the View! You can use it just like an ordinary View class (its derived from CView).
MFC AppWizard has created one more class: CInPlaceFrame. What's this? Look at the second picture in this paragraph. It's responsible for creating the frame around view. To deactivete an Active Document (after In-place activation), you just have to click outside that frame.
If you use some Document's data while drawing in Server Item, after deactivating an Active Document, nothing will change in a Container. For example, if you put a chart into a Container and change some data in it (In-place, after double-click), after returning to the Container, you won't see any changes in your chart unless you will refresh the Server Item. To refresh your data, just put the following code to the Document class (for example in function that changes some data):
UpdateAllItems(NULL);
But there are some situations, when your Mini-server will be shown as an usual app. When you put the Active Document as an icon (you can choose that in "Insert Object" dialog), and you double click on it, it won't activate in-place! It will be shown in separate Window. It will have other menu and toolbar
(IDR_MAINFRAME). Remember about it!
Storing data
And how about storing data? When you put an Active Document to an other document, you would like the Active Document data to be saved with the document data, when user choose "Save" from the Container's menu. All you have to do is to serialize data, which is stored in the Document class. The App Wizard has created the Serialize() function.
Take a look at this example:
void CSDCDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar << m_someData;
ar << m_otherData;
}
else
{
ar >> m_someData;
ar >> m_otherData;
}
}
And that's all! Take a look at the examples, it will help you to understand MFC Active Document Servers. If you want to wirte to me, my email address is hepico@cz.onet.pl. Thank you!
| You must Sign In to use this message board. |
|
|
 |
|
 |
I am writing an embeded object for MS-WORD. When writing OnDraw() for COleServerItem-derived class, I find that the DC is Windows standard metafile(MFC may not support enhanced metafile?). Now I will change it to enhanced metafile, do you know how to do that? Thank you in advance.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
we can got that a object could be resize or not: REOBJECT reobj = {0}; m_lpRichEditOle->GetObject(0,&reobj,0) but there isn't a method such as "SetObject"
  Help me!!!
off
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
hi!
The IRichEditOle interface doesn't provide any method that would act like "SetObject".
If an object is selected you can easily use m_lpRichEditOle->InsertObject method to change its flags (when an object is selected, it is automatically replaced by a new one, eg. the same one but with different flags).
That gave me an idea that we can get REOBJECT struct from an object delete that object, change the struct and using the struct recreate it. I know it's not a good solution, but the only one that came to my mind so far.
It would look sth like this:
CRichEditDoc* pDoc = GetDocument(); CRichEditCntrItem* pItem = NULL;
CReObject reo; HRESULT hr = m_lpRichEditOle->GetObject(0, &reo, REO_GETOBJ_ALL_INTERFACES);
if (GetScode(hr) == S_OK) { pItem = pDoc->LookupItem(reo.poleobj); } pDoc->RemoveItem((CDocItem*)pItem);
CReObject reo2=reo; reo2.dwFlags = reo.dwFlags & (!REO_RESIZABLE); m_lpRichEditOle->InsertObject(&reo2);
At the first sight it looks ok to me, but notice that this code is not tested so it may be completely out of sense
Ñ There is only one MP Ð
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
thank you very much.
by your message, I use the codes below, it work; but when I close the program. there is a NULL pointer access. is there sth wrong with the code.
or maybe we should allocate storage,and create a new object to replace the old one. but we just want to set a flag, ...
GetRichEditCtrl().SetSel(0,0);
REOBJECT reobj = {0}; reobj.cbStruct = sizeof(REOBJECT); HRESULT hr = m_lpRichEditOle->GetObject(i,&reobj,REO_GETOBJ_ALL_INTERFACES);
CRichEditCntrItem* pItem = pDoc->LookupItem(reobj.poleobj); if(pItem) pDoc->RemoveItem((CDocItem*)pItem);
reobj.dwFlags &= ~REO_RESIZABLE;
m_lpRichEditOle->InsertObject(&reobj);
if(reobj.pstg) reobj.pstg->Release(); if(reobj.poleobj) reobj.poleobj->Release(); if(reobj.polesite) reobj.polesite->Release();
if we use this way(by replacing). dose pRichEditOle->GetObject(index,...) work next time. is the index changed? how to deal with all objects in a loop?
I just want to set the richedit readonly after loading a document. can you give me a good solution.
best reguard. thx.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
off wrote: it work; but when I close the program. there is a NULL pointer access.
As I said, my code is only a general idea, so it is full of bugs and memory leaks for sure.
off wrote: is the index changed? how to deal with all objects in a loop?
I guess that the index will change. To deal with all objects, use sth like:
for(int i=0 ; i < m_lpRichEditOle->GetObjectCount(); i++) { m_lpRichEditOle->GetObject(i, ... }
Ñ There is only one MP Ð
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Dear,
It is a greate article teaching us how to add Active Document Support.
However, I have a question. As you can see in Excel WorkSheet, we can add a Chart Object to the Sheet and the Sheet can interact with the data in the sheet directly (i.e. the bar in bar chart will be raised if the data in the cell is increased).
Is this kind of technology Active Document as well ? Can you help me for that ?
Best regards, Hing
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hmm.... Active Documents are not actually so 'active'. They can proccess their own code (like redrawing a bar), only if asked by a container. The container must trace changes in the data and if it is changed, redraw the active document.
I'll try to create an example and publish it here.
Ñ There is only one MP Ð
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Dear,
Thx for your reply.
I found out that this kind of "active" object is a kind of ActiveX Control. However, it exposes some extra interface for interacting with excel worksheet (like watching some cells, make changes syncronously wih cell ... etc).
In the Internet, they call that "Excel Ole" or "Ole Excel". However, using MFC to build such a control is not an easy way. I am still searching for more information for that. (You can google the "Excel ole" or "ole excel")
As an example here, you can open Excel and show the toolbar "Control ToolBox". You will find that all the control in the toolbox are the so-called "Ole Excel" examples. However, to make it become interactive with excel sheet, you have to learn VBA also (the most direct way).
So, the task is to learn how to make a excel ole control using MFC + learn VBA, seem it takes time for beginner.
I am looking forward your new article for that.
Best regards, Hing
PS: I am busying at another project recently, if I have time, I really want to contribute more on that. Anyway, I will be back to this topic soon.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
No.. ActiveX is only a part of OLE technology (the first version of OLE was released in 1991 and the ActiveX didn't turn up until 1996). Search the Internet for more information on OLE.
Ñ There is only one MP Ð
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
IIRC Microsoft Marketing relabeled everything OLE to ActiveSomething with the OC96 specification. I wouldn't argue about that, however, since, basically, I don't casre 
(I *did* read tons of atuff about OLE and ActiveX, and how it came into live - and I'm not doing this again unless I really need to...)
we are here to help each other get through this thing, whatever it is Vonnegut jr.
sighist || Agile Programming | doxygen
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Can you give pointers as to how to support other verbs beside 'open' and 'edit' (The commands Edit, Open show up when you right click on the embedded document in the container, and when the user chooses one of these, the control comes to the DoVerb implementation in the server code) - I would like to add this ability without providing full-blown in-place editing, changing the container's menu/toolbar etc..
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
 |
I am developing Active Document server and also container applications. Each Active Document Server objects are seperate application just like word, excel etc. I need to list only the server objects created by me on the container OleInsert Dialog ListBox. But all the server objects are registered under "Document Objects", so the container is fetching all the Insertable Document Objects like word, excel and all document server objects.
What i really need is to load/list the server objects created by me, Is it possible to register my server objects under any Component categories? Otherwise is it possible to load all the server objects manually into the container without registering the server objects.
Can anyone pls help me out?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
vimala wrote: Is it possible to register my server objects under any Component categories?
As far as I know it isn't possible, but there are other ways to deal with your task.
My idea is to add some information to the system Registry in the same key, where other information about your Active Doc Server is stored. While opening the Insert Object window, all keys from HKEY_CLASSES_ROOT\CLSID will be checked for subkeys containing that information. Insert Object dialog do some similar operations to check which object is an Active Document Server (that's why it takes some time to open the dialog).
I'll give you an example:
In your Active Document server in CXxxApp in InitInstance() function add (after line m_server.UpdateRegistry(OAT_DOC_OBJECT_SERVER);):
CString s = "CLSID\\" + AfxStringFromCLSID(clsid); HKEY hkKey; HKEY hkCreated; if(RegOpenKeyEx(HKEY_CLASSES_ROOT, (LPCTSTR)s, 0, KEY_READ, &hkKey) == ERROR_SUCCESS) { if(RegCreateKeyEx(hkKey, "AddMyList", NULL, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkCreated, NULL) == ERROR_SUCCESS) RegCloseKey(hkCreated); RegCloseKey(hkKey); }
In your container derive a class from COleInsertDialog class:
class COleCustomInsertDialog : public COleInsertDialog { public: INT_PTR DoModal(DWORD dwFlags) { LPCLSID lpNewExcludeList = NULL; int nNewExcludeCount = 0; int nNewExcludeLen = 0;
if (dwFlags == ControlsOnly) m_io.dwFlags |= IOF_SELECTCREATECONTROL | IOF_SHOWINSERTCONTROL; else if (dwFlags == DocObjectsOnly) { m_io.dwFlags |= IOF_DISABLEDISPLAYASICON | IOF_DISABLELINK;
HKEY hkClassesRoot; HKEY hkCLSID; HKEY hkItem; HKEY hkDocObject; HKEY hkAddMyList; DWORD dwIndex = 0; TCHAR szName[MAX_PATH+1];
if (RegOpenKeyEx(HKEY_CLASSES_ROOT, NULL, 0, KEY_READ, &hkClassesRoot) == ERROR_SUCCESS) { if(RegOpenKeyEx(hkClassesRoot, _T("CLSID"), 0, KEY_ENUMERATE_SUB_KEYS, &hkCLSID) == ERROR_SUCCESS) { while(RegEnumKey(hkCLSID, dwIndex++, szName, sizeof(szName)) == ERROR_SUCCESS) { if (RegOpenKeyEx(hkCLSID, szName, 0, KEY_READ, &hkItem) == ERROR_SUCCESS) { if ((RegOpenKeyEx(hkItem, _T("Insertable"), 0, KEY_READ, &hkDocObject) == ERROR_SUCCESS) || (RegOpenKeyEx(hkItem, _T("Ole1Class"),0, KEY_READ, &hkDocObject) == ERROR_SUCCESS)) { RegCloseKey(hkDocObject);
bool deleteFromList = false; if(RegOpenKeyEx(hkItem, _T("AddMyList"), 0,KEY_READ, &hkAddMyList) == ERROR_SUCCESS) { RegCloseKey(hkAddMyList); } else { deleteFromList = true; }
if (RegOpenKeyEx(hkItem, _T("DocObject"), 0, KEY_READ, &hkDocObject) != ERROR_SUCCESS || deleteFromList ) { USES_CONVERSION;
CLSID clsid; CLSIDFromString(T2OLE(szName), &clsid); AddClassIDToList(lpNewExcludeList, nNewExcludeCount, nNewExcludeLen, &clsid); RegCloseKey(hkItem);
} RegCloseKey(hkDocObject); } } } RegCloseKey(hkCLSID); } } RegCloseKey(hkClassesRoot); }
UINT cOldClsidExclude = m_io.cClsidExclude; LPCLSID lpOldClsidExclude = m_io.lpClsidExclude;
if (lpNewExcludeList != NULL) { m_io.lpClsidExclude = lpNewExcludeList; m_io.cClsidExclude = nNewExcludeCount; }
QueryAgain: INT_PTR nDisposition = COleInsertDialog::DoModal();
if (nDisposition == IDOK && GetSelectionType() == insertFromFile && dwFlags == DocObjectsOnly) { USES_CONVERSION;
CLSID clsidFile; BOOL bIsDocObject = FALSE; if (GetClassFile(T2COLE(m_io.lpszFile), &clsidFile) == S_OK) { CString strKey; CString strCLSID = AfxStringFromCLSID(clsidFile); strKey.Format(_T("CLSID\\%s\\DocObject"), strCLSID);
HKEY hKey; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, strKey, 0, KEY_QUERY_VALUE, &hKey)) { RegCloseKey(hKey); bIsDocObject = TRUE; } }
if (!bIsDocObject) { nDisposition = AfxMessageBox(AFX_IDS_NOT_DOCOBJECT, MB_RETRYCANCEL | MB_ICONHAND); if (nDisposition == IDRETRY) goto QueryAgain; else nDisposition = IDCANCEL; } }
delete [] lpNewExcludeList;
m_io.cClsidExclude = cOldClsidExclude; m_io.lpClsidExclude = lpOldClsidExclude;
return nDisposition; }; };
Most of this code came from the MFC source files (oledlgs1.cpp). I've added only some stuff about deleteFromList variable (Why delete? Because you don't choose which element you want to add to the list, but which one shouldn't be added).
You can change "AddMyList" key name to some more unique identifier (like your company or the container name).
In the container in the ChXxxView::OnInsertObject() function replace
COleInsertDialog dlg; with
COleCustomInsertDialog dlg;
To see it working, run first the server to register it, and then the container.
The whole code was tested with MFC 7.0 (that from VS.NET)
Oh! One more thing. My compiler didn't want to copile this code, because it coudn't find an implementation of AfxStringFromCLSID() function. So here it is:
CString AFXAPI AfxStringFromCLSID(REFCLSID rclsid) { TCHAR szCLSID[256]; wsprintf(szCLSID, _T("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"), rclsid.Data1, rclsid.Data2, rclsid.Data3, rclsid.Data4[0], rclsid.Data4[1], rclsid.Data4[2], rclsid.Data4[3], rclsid.Data4[4], rclsid.Data4[5], rclsid.Data4[6], rclsid.Data4[7]); return szCLSID; }
I can send an example to your e-mail box if you want.
Bye!
Ñ There is only one MP Ð
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
In CAppnameApp class there is m_server member. Use its Unregister() function.
Little example:
BOOL CAppnameApp::InitInstance() { m_server.UpdateRegistry(OAT_DOC_OBJECT_SERVER); m_server.Unregister(); )
You can also delete it manually from the Registry.
MP Maciej Pirog
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Do you know if it is possible to mix GDI+ calls using the CDC* passed the OnDraw function ?
Jonathan de Halleux, Belgium.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I'm afraid that this metafile DC doesn't cooperate with GDI+. Whenever I do any GDI+ function (even creating a pen) the DC become blank  But I can be wrong. I've got WinXP for a short time and I have little experience with GDI+.
MP Maciej Pirog
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Looks like only CDC::LineTo and CDC::MoveTo are accepted...
Anybody knows how to blit a bitmap on such DC ?
Jonathan de Halleux, Belgium.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi, i have a question., pDC->TextOut(100,50,"suresh",6); Like this i have written in OnDraw function in view class. Its displayed on the screen after the execution..
I want to save this with a file name.. and i want open it .. How is it ..Plz let me know as early as possible..
thanks and regards suresh rsuresh777@gmail.com
Suresh
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
 | Legacy  Nemanja Trifunovic | 5:54 1 Feb '02 |
|
 |
Microsoft is going to phase out Active Documents. You cannot reuse them in .NET environments. Therefore, it would be not wise to start developing them.
I vote pro drink 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Wow !
Some months ago I tried to make an Active Document Server with ATL and that was a nightmare .... and now...
What are they going to use then ( I mean If I want to have control pasted in word with my own toolbar what should I do then ?).
Thanks, Bye ! Braulio
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Well, it seems I didn't read the article carefully . Microsoft is going to drop ActiveX Documents, and not Active Document Servers (well, not yet). Sorry for your nightmare
I vote pro drink 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|