|
1. When I throw CWinImpl<myobject>, into the header, It gives me an error about already having CComControl in my thing as well. What should I do? Should I just make a variable of CWinImpl in my ATL Class?
and if that works, how do I make it windowless? do i just not set a STYLE when I create the window? or do i just make a DialogBox and call ShowWindow(WS_HIDE) or something?
I dont want anything showing up of course, nothing in the background, etc. Any Tips?
~Timothy T. Rymer
www.digipen.edu
tim.xpertz.com
|
|
|
|
|
From the article
Steps to Reproduce Behavior
The following example first creates an ATL project, then a Visual Basic 6.0 project. It then uses these to demonstrate the proper way to have a secondary thread request that the primary thread fire an event.
Steps to Create an ATL Project with Visual C++
Create a new ATL COM AppWizard Project named MyAtlDll and keep the default settings.
In the class view, right-click the project name and select New ATL Object. Select Simple Object in the displayed dialog box and click Next. Type in "MyAtl" as the C++ short name. Click the Attribute tab and select Support Connection Points. Click OK and a new ATL object is added.
In the class view, right-click the IMyAtl and choose Add Method. Type "InitTask" in the Method Name text box and "[in] long number" in the parameters text box. Click OK.
In the class view, right-click _IMyAtlEvents and select Add Method. Select "void" from return type drop-down list box. Type "TaskFinished" in the Method Name text box and "[in] long result" in the parameters text box. Click OK.
In the class view, right-click CMyAtl and choose Add member variable. Type "long" in the variable type text box and "m_number" in the variable name text box.
Build the project to generate the type library needed for step 10.
In the class view, expand and double-click CMyAtl -> IMyAtl -> InitTask. Edit the InitTask function to make it appear as follows:
STDMETHODIMP CMyAtl::InitTask(long number)
{
HANDLE hThrd;
DWORD tid;
m_number = number;
if((hThrd = CreateThread(
0,
0,
(LPTHREAD_START_ROUTINE)justDoIt,
(void *)this,
0,
&tid)) == NULL)
{
//error handling here
}
CloseHandle(hThrd);
return S_OK;
}
Add the following code to the MyAtl.cpp file:
DWORD WINAPI justDoIt(LPVOID lpParameter)
{
CMyAtl *myAtl = (CMyAtl*)lpParameter;
long result;
for (int i = 1; i <= myAtl->m_number; ++i)
result = i * 2;
myAtl->Fire_TaskFinished(result);
return 0;
}
Add the following code right above the line "#endif //__MYATL_H_" in MyAtl.h file:
DWORD WINAPI justDoIt(LPVOID lpParameter);
In the class view, right-click CMyAtl and select Implement Connection Point. Select _IMyAt-Events in the displayed dialog box. Click OK.
Build the ATL project and the control will be registered automatically.
Steps to Create the Visual Basic 6.0 Project
Create a new Standard EXE project. Form1 is created by default.
Choose References from the Project menu, select "MyAtlDll 1.0 Type library," and click OK.
Add a CommandButton to the form and keep the default name (command1).
Add the following code to the code window of form1:
Option Explicit
Private WithEvents vbATL As MYATLDLLLib.MyAtl
Private Sub Command1_Click()
vbATL.InitTask 11111
End Sub
Private Sub Form_Load()
Set vbATL = New MYATLDLLLib.MyAtl
End Sub
Private Sub vbATL_TaskFinished(ByVal result As Long)
MsgBox result
End Sub
Press the F5 key to run the project. Click Command1 and you will get 22222 in a message box.
Build the project to be an EXE and run the EXE outside the IDE. You will get an error message when you click the CommandButton.
Note that it may work sometimes, but it does not work consistently.
To fix the problem in this specific sample, you need to derive the CMyAtl from CWindowImpl, and add a message map. Make sure the window is hidden. Now, instead of firing an event from the secondary thread, you can post a message to the main thread, and fire the event in the message handler.
Step by Step Example
Using the ATL project created in the steps above, add the following line:
#include <atlwin.h>
after the line:
#define _MYATL_H
in the MyAtl.h file.
Add the line:
#define WM_TASK_FINISH (WM_USER + 101)
before the line:
class ATL_NO_VTABLE CMyAtl :
in the MyAtl.h file.
Add CWindowImpl to one of the parent classes of CMyAtl by adding the following line:
public CWindowImpl<CMyAtl>,
right after the line:
class ATL_NO_VTABLE CMyAtl :
in the file MyAtl.h.
Add the following code to the CMyAtl definition part of the MyAtl.h file:
public:
DECLARE_WND_CLASS("MyAtl")
BEGIN_MSG_MAP(CMyAtl)
MESSAGE_HANDLER(WM_TASK_FINISH, OnTaskFinished)
END_MSG_MAP()
public:
LRESULT OnTaskFinished(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
Fire_TaskFinished((long)wParam);
return 0;
}
HRESULT FinalConstruct()
{
RECT rect;
rect.left=0;
rect.right=100;
rect.top=0;
rect.bottom=100;
HWND hwnd = Create( NULL, rect, "MyAtlWindow", WS_POPUP);
if (hwnd)
return S_OK;
else
return HRESULT_FROM_WIN32(GetLastError());
}
void FinalRelease()
{
if (m_hWnd != NULL)
DestroyWindow();
}
Change the following line:
myAtl->Fire_TaskFinished(result);
in the function
DWORD WINAPI justDoIt(LPVOID lpParameter)
in the file MyAtl.cpp, to be:
myAtl->PostMessage(WM_TASK_FINISH,result,0);
Rebuild the ATL project.
Run the Visual Basic project from the IDE and as an EXE. Note that the event fires just fine either way.
|
|
|
|
|
The CComControl class derives by default from CWindowImpl, so the error...
About the windowless aspect, i think that by default CComControl window is windowless, so no reason to worry, i think
If isn't windowless, then put this in the private parte of your class:
unsigned m_bWndLess:1;
, this variable is a member of a union declared in
CComControlBase, a base class of CComControl, so it's necessary to declare it in the current class, but i think you wouldn't need this ...
Cheers;
Joao Vaz
|
|
|
|
|
Dear All,
ITypeInfo::GetFuncDesc() accepts function-index(between 0 to cFuncs-1). Why it's so is beyond me?
Can somebody let me know ASAP how I can determine this index using DISPID, given the fact that the method DISPIDs of the VB DLL I am using are not continuous ( I mean some methods might be deleted by DLL authors after creating them)
This fact prevents me from using this :
index = (func's DISPID) - (DISPID of 1st DLL method) - 7
Context : VC++/ATL 6.0, IDispatch
Thanx in advance.
Rohit Arora
rohita@dsrsolutions.com
|
|
|
|
|
I don't understand, for instante to do the opposite, the normal order of the operations is
- invoke ITypeInfo::GetTypeAttr to retrieve an TYPEATTR structure
- then TYPEATTR::cFuncs is the number of methods the interface supports, so passing an number(index) between 0 and cFuncs - 1 to ITypeInfo::GetFuncDesc retrieves the appropriate function description.
- and FUNCDESC::memid is the DISPID of the method.
What are you trying to achieve ?
Joao Vaz
|
|
|
|
|
Hi Joao,
Thanx for showing interest in my question.
I'm sorry if I haven't made my point clear and it confused you.
Anyway, here is the synopsis of a function I tried to write.
The function got a PROGID, and name of one its Methods. The said function has to invoke given method. It carries on this way :
-> Get DISPID of the method using IDispatch::GetIDsOfNames().
-> Somehow Get Method-index using its DISPID. (Q. How to do this?)
-> Get FUNCDESC using ITypeInfo::GetFuncDesc().
Presently I'm doing the "SECOND STEP" by passing all values between
0 to ITypeInfo::cFuncs-1 to ITypeInfo::GetFuncDesc(). Now check FUNCDESC::memid against method DISPID. This way get FUNCDESC of desired method. Works fine but what if PROGID has 500+ methods??
Can you suggest me a better way for this.
Hope to get the answer soon.
Rohit Arora
rohita@dsrsolutions.com
|
|
|
|
|
Rohit wrote:
The function got a PROGID, and name of one its Methods. The said function has to invoke given method. It carries on this way :
-> Get DISPID of the method using IDispatch::GetIDsOfNames().
What ?
After using IDispatch::GetIDsOfNames to retrieve the DISPID of the method, the normal thing is to Invoke the method passing the resulting dispid to the IDispatch::Invoke(...), to call the method, of course passing all the information required to method, like DISPPARAMS structure with the arguments needed by the method.
ITypeInfo::GetFuncDesc normally is used to for instance to produce a type library viewer, with generaly a tree view to load all the methods of a particular interface of a particular coclass, like the OLE/COM viewer util that ships with VC++.
Hope this helps,
Joao Vaz
|
|
|
|
|
I wonder how I can use an ActiveX control in IE. I develop an ActiveX control... like a clock for example and I want the user to install the control... it displays a Yes/No license agreement... it installs the control and then it executes the control.
Like it works for Quicktime plugins...
Any tips ?
Jean-Marc Molina
Email: jmmolina@ifrance.com
Web: http://goa.ifrance.com
|
|
|
|
|
Use OBJECT tag in your HTML
Mazy
"So,so you think you can tell,
Heaven from Hell,
Blue skies from pain,...
How I wish,how I wish you were here." Wish You Were Here-Pink Floyd-1975
|
|
|
|
|
what can i do to let all my clients to share the same data on the server.
every time i start a new client, a new server instance is created.
i 'm looking up to your answer.
|
|
|
|
|
Declare your server as a singleton can be the solution for you like this:
DECLARE_CLASSFACTORY_SINGLETON(CYourClassObject)
Declare it before your COM_MAP(...). I assume you are using ATL
For advanced concept, change the way you register your com object.
Change the multiple instance in: _Module.RegisterClassObjects call to single instance
Good luck
"Dirty hands lead to important discovery..." - Thomas Edisson
|
|
|
|
|
Hi newbe
A solution could be:
Register your com-server as a windows nt service.
Use a special account to start the com-server.
Take 'dcomcnfg.exe' to modify the rights.
... share the same data ... means, you have to make your code thread-safe.
PS: I have an german instruction for (D)COM newbies, I you want to, you can download it from: (job)
http://mypage.bluewin.ch/a-z/kurt.muellner/
cheers
Kurt Muellner
Fast Prototyping
|
|
|
|
|
I can use dll's resource file to store the typelib and registe it. But If I want put the typelib to a EXE module. What should I do?
BTW, I compile a typelib into a EXE and write a regster item. But VB cann't Load it.
Thanks
Rainfall
|
|
|
|
|
hello,
I've written vb dll and i want to call it from another vc app.
How do i do that?
Can someone pls explain me the steps involved and give me code example?
bless u all
tnx
|
|
|
|
|
|
Also try http://www.codeproject.com/dll/vbactivexwithvc.asp.
#define MOSTLY_LEAN_AND_MEAN
|
|
|
|
|
Hello everybody,
I have created a class to implment an Interface. But one i try to create an object by :
CComObject<client> * pclient;
CComObject<client>::CreateInstance(&pclient);
i get the error that the class CComObject is abstract and that i can't make an instance of it.
i 've used all the macros right:
BEGIN_COM_MAP(client)
COM_INTERFACE_ENTRY(iCLIENT)
END_COM_MAP()
i did also the object_map
so please help
|
|
|
|
|
You need to use templates.
CComObject<myobject>* pMyObj;
CComObject<myobject>::CreateInstance(&pMyObj);
|
|
|
|
|
hi,
Does COM use SendMessage(...) internally,atleast in Windows environment?If not what is the modus operandi?
Cheers.
|
|
|
|
|
Yes, when you have components that live in the Single Threaded apartment, STA, COM creates a message pump for each new thread in order to synchronize the data between other threads when they call the STA component.
|
|
|
|
|
|
Just off the top of my head, you should be able to use the get_parent, get_child and get_sibling methods to navigate around the DOM.
|
|
|
|
|
Hi,
thanks for your suggestion. Can I use the IHTMLElement::get_sourceIndex, which hands me a long index, to uniquely identify an element? I can then come back to the same element using the index after calling IHTMLDOcument2::get_all() and navigating the resultant IHTMLElementCollection? Is a source index guranteed to be unique? Then I can bypass any complex notation and this would make things very simple. Have you ever used this or something similar?
Thanks.
#define MOSTLY_LEAN_AND_MEAN
|
|
|
|
|
Hi,
I want to pass a metafile to an ATL server, so I encapsulated it on a PictureBox object, and then saved it on one IStream, then I try to recover it in the ATL object, and it gives me on error, the code that I use is:
MFC client:
if(IPict) {
IStorage *pIStg;
Res = ::StgCreateDocfile(NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &pIStg);
if(pIStg) {
IStream *TempStream = NULL;
Res = pIStg->CreateStream(OLESTR("MyPicture"), STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0,0, &TempStream);
HRESULT HRes = IPict->SaveAsFile(TempStream, TRUE, 0);
m_GDIGr.SetMetafile(TempStream);
}
}
ATL Server:
IPicture *Pict = NULL;
HRESULT Res = ::OleLoadPicture(pStream, 0, FALSE, IID_IPicture,(void **) &Pict);
What I had made wrong ?, Thanks in advance, Bye !
Braulio
|
|
|
|
|
Hi Braulio
you say an error, what kind of error? An exception, a wrong hres or what.
PS: You have to resolve ANY HRESULT as a MFC-Client.
cheers
Kurt Muellner
Fast Prototyping
|
|
|
|
|