|
In an ATL ClassWizard generated Control that is used in VB, how do you create a hidden window that can fire events for your worker thread? I've been trying to do this using a GlobalInterfaceTable, and firing events work, but not when you call WaitForSingleObject() to wait for a request to InternetReadFileEx() to finish (cuz it's in VB and that locks up the program until it's done) How is that possible with an already made class? I tried sticking CWinImpl in the class declaration, but it contradicts CComControl so I cant use it. Is there a class that I can call dynamically or a class that doesnt have to have a Type specifier aka:"CWinImpl<cmyobject>" <-- which gives me the error cuz it has CComControl in the dec. any Pointers, Tips, or Hints?
~Timothy T. Rymer
www.digipen.edu
tim.xpertz.com
|
|
|
|
|
Tim, see my aswers bellow , the anonymous and the other
Joao Vaz
|
|
|
|
|
Hey I've been working on an ATL Object that uses Asynchronous WinInet API's, which end up getting used up to about 20 at a time, but i'm only really testing at 2 right now, which has the same problem:
The program that uses more than one of these objects at a time locks up when waiting for data from the callback function. It uses little or no processor power, but sits there and stays locked until it gets the data, then finally moves on. (the OS is fine and switching windows and hitting the Start buttons all work fine, so it's just the program)
*NOTE: I noticed how MSDN did it
(ARTICLE:Calling Win32 Internet Functions Asynchronously),
and it seemed like a great idea to create the worker thread inside of the callback function, but for some reason I couldnt get my ATL object to work (i think it may have to do with not having experience first time around and not being smart and firing events from that callback thread) either way, it's not the way i'm doing it and i'm not changing it. unless you're "for sure" that it can work easily.
HERE'S HOW I DID IT:
1. I fire events from the worker thread by using IGlobalInterfaceTable and cookies. (the App is in VB - no changing that either)
2. More importantly, in my worker thread, I call WaitForSingleObject() and I wait for the event that i had created and a bool member variable that is changed (because it seems like sometimes it will pick up the other ATL objects' SetEvent() calls so i just make sure that it is that object's Event that was set. Now in MSDN's version, you dont have to do call WaitForSingleObject() because it starts the thread when it has the Url's HINTERNET handle. Right now that's not a big deal at all, cuz when it's waiting to connect, i can move the program's window around and it hasnt locked up.
OK HERE'S THE BAD PART:
When I Call InternetReadFileEx() I check for ERROR_IO_PENDING of course, and when i get that, I call WaitForSingleObject() and wait for the INTERNET_STATUS_REQUEST_COMPLETE status. IT LOCKS UP THE PROGRAM NOW. This is all in the worker thread! What is the deal? No CPU Usage And Yet, NO CONTROL!
And if that ever gets figured out, i have yet to find out how to properly kill the internet APIs and after that, do another address with the same object.
And in the callback function, what is dwInternetStatus = 320, 321???? I havent found a thing on what that might actually be! it's not in MSDN, and i dont have a clue what it is, but it's actually a good value. but i sure dont know though.
I just really need some good implementation advise. Any advise will be great, and being this is my first real job, i do not want to disappoint, so please help.
~Timothy T. Rymer
www.digipen.edu
tim.xpertz.com
|
|
|
|
|
Hum, you could try this link
http://www.codeproject.com/useritems/asyncwininet.asp#xx84731xx
Also you could check chapter 9 source code of the
Essential Wininet book, a good book that i used to program wininet and com winnet applications, and the chapter 10 that deals with http wininet com components, this book was a life savier for me in the past
,
Ah , and because your app is in VB and since Vb doesn't support multithreading, in this case i think they recommend 2 ways to solve this problem, and one, the best one(the one that i used )is to create a message pump in the main thread that catches the message of the worker thread and then dispatches the event in the callback that is on the main thread to the vb app, check for "events and atl and vb" in msdn for a more correct explanation.
Hope this helps ,
Joao Vaz
|
|
|
|
|
Here's the thing with the message pump, I dont really know how to do that coming from an ATL object... do you see where i'm coming from? I've heard that a bunch but i've always stayed away from it because i didnt know how to make one (that's what i used IGlobalInterfaceTable which worked with Firing Events, but obviously is not working with WaitForSingleObject() now if you have an example.... then that might be a different story... for me i wouldnt know where to start.
~Timothy T. Rymer
www.digipen.edu
tim.xpertz.com
|
|
|
|
|
|
Ok, I guess i'm just going to have a class that is derived from CWnd, do I HAVE to use CWnd? I've seen all those articles before, of course they dont make much sense when you havent actually done whatever they are saying to do, but I guess i'm just expecting to see an example of a normal CreateWindow function and then the message loop function that would fire the event after each PostMessage() not just a ctor to CWnd... but i'll try my best, thanks for the help, I just wish I had done this once before cause it always makes sense the 2nd time around...
~Timothy T. Rymer
www.digipen.edu
tim.xpertz.com
|
|
|
|
|
Tim, i thought that your object is in ATL ,
if so you must add the public CWindowImpl<cmyclass> to your inheritance chain, CWnd is for MFC controls, not ATL !
Do a step by step procedure of the kb: Q196026 http://support.microsoft.com/default.aspx?scid=kb;PT;q196026
Happy coding, and also don't forget to check Essential Wininet sourcecode for proper asynch behavoir.
Regarsds,
Joao Vaz
|
|
|
|
|
I didnt know that... its tough trying to pick out the important parts, especially when you havent done it before. I'll put CWindowImpl in my obj and try to use it. Thanks!
~Timothy T. Rymer
www.digipen.edu
tim.xpertz.com
|
|
|
|
|
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
|
|
|
|
|