|
The convention for an EXE is that passing the parameter /regserver will register the server, while passing /unregserver will unregister it.
The Wizard-generated MFC code will have produced an override of CWinApp::InitInstance which creates a CCommandLineInfo which is then passed to CWinApp::ParseCommandLine . When this returns it then calls CWinApp::ProcessShellCommand .
You can hook into this by deriving your own class from CCommandLineInfo , overriding ParseParam , and performing the necessary registration steps in response to the appropriate command line. You should exit (by returning FALSE from InitInstance ) after processing a registration flag.
You can use AfxOleRegisterTypeLib to register your type library and COleObjectFactoryEx::UpdateRegistryAll to register all the known classes.
Stability. What an interesting concept. -- Chris Maunder
|
|
|
|
|
Thanks Mike,
I've found the simpliest solution.
enum eRegistration{eNothing, eRegisterServer, eUNRegisterServer};
eRegistration process_program_arguments(LPSTR m_lpCmdLine);
...
App::InitInstance()
{
...
if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated)
return TRUE;
if (cmdInfo.m_nShellCommand == CCommandLineInfo::AppUnregister)
{
m_server.UpdateRegistry(OAT_DISPATCH_OBJECT);
COleObjectFactory::UpdateRegistryAll(FALSE);
}
else
{
m_server.UpdateRegistry(OAT_DISPATCH_OBJECT);
COleObjectFactory::UpdateRegistryAll();
}
if (!ProcessShellCommand(cmdInfo)) // Dispatch commands specified on the command line
return FALSE;
if(process_program_arguments(m_lpCmdLine))
return FALSE;
...
...
}
eRegistration process_program_arguments(LPSTR lpCmdLine)
{
char* lpProgArg = _strdup( lpCmdLine );
char seps[] = " -/"; // program arguments separators
char Register[] = "regserver";
char UNRegister[] = "unregserver";
eRegistration ret = eNothing;
char *token = strtok( lpProgArg, seps );
while( token != NULL )
{
if ( !strcmp(token,Register) )
{
ret = eRegisterServer;
}
if ( !strcmp(token,UNRegister) )
{
ret = eUNRegisterServer;
}
token = strtok( NULL, seps );
}
free( lpProgArg );
return(ret);
}
In StdAfx.cpp
#include <afxctl.h>
That's it.
Thanks for hint.
Oleg
|
|
|
|
|
hello!
I am learning the MS COM/DCOM architecture.
I ve developed a few simple client/server programs and I ve obtained always the same result: I think client cannot get the object.
I ve used C++ with Microsift Visual C++ 6.0.
To make the server I ve followed the next steps:
1- create an ATL COM AppWizard Project.
2- EXE Server.
3- New ATL object (simple object)
4- Add methods to Interface
5- Code the methods.
6- Build the project
it shows me:
Compiling resources...
Compiling...
Linking...
Performing registration
Server registration done!
..and client is something like this:
const IID IID_IHelloObj = {0x389A6DD0,0xC789,0x463F,{0x87,0xD5,0x8C,0x3A,0x51,0xDC,0xB4,0xDF}};
const CLSID CLSID_HelloObj = {0x1635DA5A,0xD02E,0x4049,{0xB9,0x21,0x07,0xE1,0x81,0x55,0xB2,0x0E}};
void main()
{
HRESULT hr;
IHelloObj *IHello;
int num; // to test the service
hr = CoInitialize(0);
if(SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_HelloObj,
NULL, CLSCTX_INPROC_SERVER,
IID_IHelloObj,
(void**)&IHello);
if(SUCCEEDED(hr))
{
hr=IHello->GetNumber(&num);
hr=IHello->Release();
}
else
cout << "error";
}
CoUninitialize();
}
This is all what i ve done.
Must i do more things to run the service?
What can be the cause of the error?
Thanks in advance!!!
|
|
|
|
|
0. What value is in hr, and when? This will tell you more about what's going wrong.
1. Are you sure you're using the correct interface IDs and class IDs?
2. Have you tried using CLSIDFromProgID to get your clsid instead?
Steve S
|
|
|
|
|
hello,
0.
com_error error(hr) (first, construct a _com_server)
HRESULT_FACILITY(hr) returns 4
HRESULT_SEVERITY(hr) returns 1
error.ErrorMessage() returns "Class not registered"
1.
I think so ... I get the IID and the CLSID from the _i.c file in the External Dependencies of the server project.
2. No, i haven´t. I dont know how to get the ProgID (the first parameter).
I search it in some documentation and i find something like:
hr = CLSIDFromProgID(OLESTR("Project_name.Object_name"),&pClsid);
but it does not run (error.ErrorMessage() returns "class String is not valid")
3. I ve tried to initialize as MTA instead with
CoInitializeEx(NULL, COINIT_MULTITHREADED) but i dont know what header files must i include. Is CoInitializeEx supported in Visual C++ 6.0 or is a .NET function???
A lot of thanks!!
|
|
|
|
|
You initialized client as single threaded apartment. It won't work if you don't have message queue.
Try to initialize as MTA instead:
CoInitializeEx(NULL, COINIT_MULTITHREADED)
Edward
|
|
|
|
|
Hello,
Finally, i ve used the CLSIDFromProgID function (CLSIDFromProgID(OLESTR("HelloServer.HelloObj.1"),&pClsid)) but the result is the same:
"Class not registered"
I ve sawn the windows register and i think there are all the data related with the registration:
LocalServer32 - path of the EXE server (HelloServer.exe)
ProgID - project_name.object_name.method (HelloServer.HelloObj.1)
Typelib - a String {FF638 ....)
VersionIndepentProgID - HelloServer.HelloObj
Apart from this, I ve tried to initialize as MTA instead with
CoInitializeEx(NULL, COINIT_MULTITHREADED) but the builder does not find this function and i dont know what header files must i include.
thank you very much.
|
|
|
|
|
Because you have made the EXE server, you cannot use the CLSCTX_INPROC_SERVER constant in calling of the CoCreateInstance. You should use the CLSCTX_LOCAL_SERVER or better CLSCTX_SERVER instead.
With best wishes,
Vita
|
|
|
|
|
Ohh, thanks for your help!
...but now i have got another problem :
HRESULT says "interface not compatible"
i ve tried it with CoCreateInstanceEx instead CoCreateInstance but the result is the same. Could be the MIDL definition the cause of the problem?
Code is something like this:
IHelloObj *iHello; // point to teh interface remoto
// other variables ans COM inicialitation
//mutli_qi strcuture
MULTI_QI qi = {&IID_IHelloObj, NULL, S_OK};
//server info
COSERVERINFO csi;
csi.pwszName = _bstr_t("localhost");
...
hr = CoCreateInstanceEx(CLSID_HelloObj,NULL,CLSCTX_SERVER,&csi,
1,&qi);
// here hr and qi.hr are not succeeded
if(SUCCEEDED(hr) && SUCCEEDED(qi.hr))
{
// get the interface
// call to remote methods
...
A lot of thanks!!
|
|
|
|
|
Hi All,
I am new to office automation and I need to perform a simple task. Basically I have to duplicate an Ms word application.
What I do is instantiate ms word through a dialog based application using
_Application app
if(!app.CreateDispatch("word.Application"))
{
AfxMessageBox("Couldn't start Word and get Application object.");
return;
}
Now I create another instance of word
_Application newapp
and copy the old instance to the new one.
newapp = app;
But how do I make the new instance refect the changes? Like show the old word's documents and stuff.
Kind of basic question but I have been stuck for hours.
Thanks for help
Regards,
Zahid
|
|
|
|
|
You don't. At least, not by doing what you're doing. That's like saying that I've overwritten your email address in my address-book with someone else's address, why aren't you them?
You can use automation to enumerate the open documents, and then open them in the second copy, but that's about it. You can force the first instance to save all changes, and then get the second one to reload, but why?
What's so special about the first instance of Word you have that means you want another one 'just like it'...?
Steve S
|
|
|
|
|
Thanks for suggestion
So I can't do it? Thats depressing since I not only needed the documents but also what buttons like BOLD, ITALIC were pressed or was the word in Print Preview mode or normal mode etc.
Actually my actual goal is that I want to transfer the word to another machine with its state intact. I figured that if I could do it on one machine then doing it on multiple would be easy.
So there is no other method available other than saving the document in a file and reloading it??? We can't copy the object's attributes and paste them to another object somehow?
Thanks.
Regards,
Zahid
|
|
|
|
|
No you can't.
That's a qualified "no". If you're doing it for disaster recovery purposes, then it might well be worth spending the time writing a clone routine that for a given document replicates it's content, cursor position etc to another instance on a separate machine. You could query every property and set them on another instance, but it would take a very long time in two senses.
Firstly, this would introduce a noticeable delay to the user (for noticeable, read unacceptable). Secondly, it's going to take a long time to cover all the properties.
Steve S
|
|
|
|
|
i am having trouble making a shape(be it rectangle or a circle) that is clickable and draggable. im using visual c++ 6.0 help me pls. thanks
|
|
|
|
|
Hi,
I am using COM to access Excel and read from an existing Excel file.
I tried to use a smart pointer but had a problem with that so I am using GetIdsOfNames for calling the function. However, I don't seem to be able to correctly call the function Open (of WorkBooks).
In VB it is possible to send only the file name, but from what I have seen in C++ I have to give 12 parameters (!). It seems that I don't know what they should be...
Can someone please send me a code snippet of a successfull call to this open function?
thanks!
|
|
|
|
|
If you don't know what they are, then chances are they are optional parameters.
You can handle this by passing a variant that has a particular makeup,
namely
v.vt = VT_ERROR;
v.scode = DISP_E_PARAMNOTFOUND;
and using the variant for any 'optional' arguments.
Steve S
|
|
|
|
|
This is exatly what I was looking for!
problem is solved.
thanks.
|
|
|
|
|
Currently I am developing an application, which uses SafeArray to pass UDT. When I create SafeArray at server side and destoy it at client side, there is no issue. But when I create SafeArray at client side, and tries to destoy it at server side, the method returns with HRESULT values (error 0x800703E6)-"Invalid access to memory location" .
The procedure at both ends as below -
SafeArray consists of array of SafeArrays. So first create & access root SafeArray, fill it. Then access SafeArray at level 2, fill it, unaccess it , unaccess root level SafeArray, destroy root level SafeArray.
It works in client side, ut fails at server side.
|
|
|
|
|
What arguments are you passing to the SafeArrayCreate function to create the outer and inner ones
Steve S
|
|
|
|
|
1. The client code for creation is - SafeArrayCreate( VT_VARIANT, 1, &saBounds) where saBounds[] = (0, 20}, It consists of other SafeArrays and normal data types like INT, BSTR.
2. Passed to server as - SetData(SAFEARRAY **psaData)
3. At server side - hr = SafeArrayDestroy( *psaProjConfig )
4. The hr is S_OK at step 3.
5. But when method returns, client throws error as "Invalid access to memory location"
Thanks for the interest shown.
Regards,
Mandar
|
|
|
|
|
1-4 No problem, that's exactly right.
5. Is the parameter [in,out] by any chance?
If so, that's your problem. You need to do
4. hr = SafeArrayDestroy(*psaProjConfig);
*psaProjConfig = NULL;
That tells OLEAUT32 (which contains the automation marshalling code) that there is nothing to marshall back. Without that step, the marshaller at the client end will be trying to marshall freed memory.
Hope that helps
Steve S
|
|
|
|
|
Thanks Steve. Prevoiusly SafeArray was [in] parameter. As pointed out by you, I make it [in,out] parameter as we are changing its value. But till I am getting error after returning the method.
|
|
|
|
|
But are you really changing the value?
In my example, after destroying the SafeArray on the server side, I set the value to be NULL. Since it's an in/out, you pass not 'SAFEARRAY*' but 'SAFEARRAY**', of course.
I know 'Mr.Prakash' says it's not safe to destroy the array on the server side, but that isn't true.
I think it's still failing because the automation marshaller is trying to send back the deleted safearray.
Steve S
|
|
|
|
|
Hi,
I solved the problem. I write the code to destoying the SafeArray at Client side only, after returnung method from Server. My method has SafeArray as a [in] parameter. Now it is working properly.
Thank for the help.
|
|
|
|
|
mandarpb wrote:
But when I create SafeArray at client side, and tries to destoy it at server side
I guess it should not be done, pointers should be destroyed by the owner of the pointer i.e client in this case.
This space is empty.
|
|
|
|
|