|
Problem:
I have a COM+ business logic object which seems to lose state for no aparent reason. But when I debug it in VB it works fine. ?!?!?!
Baskground:
My BL object has a method which returns a reference to itself. (Why? Short-story: I was returning an ADO Recordset, but then (for reasons beyond this article) I didn't want EOF to return True when the Recordset was at EOF.) So my BL object implements some of the ADO interface and passes calls to the underlying recordset which is now stored in a private variable; except the EOF property which does it's own thing.
Gotcha:
After calling the method from ASP/VBScript I get an object back alright, but the object has lost state. Here is sample code of the object being used:
<%
...
Dim oOrders, adorsOrders
Set oOrders = Server.CreateObject("BusinessLogicLayer.OrderHistory")
Set adorsOrders = oOrders.ListByPage(sCustomerID, lPageNo, lPageSize)
Do Until adorsOrders.EOF
...
Loop
...
%>
The line "Do Until..." biffs an error saying that the object was not set. At this point adorsOrders and oOrders are actually the same object because ListByPage returns Me. Furthur examination reveals that all of the object's internal variables have been initialized right after the method completes; even though the object shouldn't get garbaged till the bottom of the page.
Now the oddest thing is that if I set a public string variable on object creation and then call a different method I can still access that variable and it contains "HelloWorld" as expected. But if I access it after calling this method which returns it's own reference then the string variable has now been reinitialised.
Oh, did I mention that the object works fine if I debug it in VB?
Any help leading to a fast solution out of this mess will earn you naming rights on my first born.
|
|
|
|
|
show IDL and implementation of this metohd:
oOrders.ListByPage(sCustomerID, lPageNo, lPageSize)
soptest
|
|
|
|
|
HRESULT ListByPage(
[in] BSTR sCustomerID,
[in] long lPageNo,
[in] long lPageSize,
[out, retval] IDispatch** );
Public Function ListByPage(ByVal sCustomerID As String, _
ByVal lPageNo As Long, _
ByVal lPageSize As Long) As Object
'==================================================================================================
' PURPOSE: Returns a page of orders. The page is specified by lPageNo and
' lPageSize. lTotalRecordCount returns the total number of records on all pages.
'
' Criteria field(s):
' sCustomerID
' lPageNo
' lPageSize
' lTotalRecordCount
'
' RETURNS: Disconnected recordset.
'==================================================================================================
On Error GoTo ErrorHandler
Dim oOrderHistory As Object
Dim vOrders As Variant
Dim lTotalRecordCount As Long
Set oOrderHistory = CtxCreateObject(m_RemoteOrderHistoryDALProgID, m_RemoteDALHost)
vOrders = oOrderHistory.ListByPage(sCustomerID, lPageNo, lPageSize, lTotalRecordCount)
Set oOrderHistory = Nothing
Set m_adorsOrders = Variant2RecordSet(vOrders(1), vOrders(0))
m_lTotalRecordCount = lTotalRecordCount
m_lLastRecordInCurrentRecordSet = (lPageNo - 1) * lPageSize + m_adorsOrders.RecordCount
Set ListByPage = Me
CtxSetComplete
Exit Function
ErrorHandler:
Set oOrderHistory = Nothing
CtxRaiseError m_strModuleName, "ListByPage"
End Function
|
|
|
|
|
You call "obj.EOF". Is "EOF" defined in your object.
soptest
|
|
|
|
|
Public Property Get EOF() As Boolean
On Error GoTo ErrorHandler
If m_lLastRecordInCurrentRecordSet >= m_lTotalRecordCount Then
' At this next line I get the error "Object variable or With block variable not set"
EOF = m_adorsOrders.EOF
Else
EOF = False
End If
Exit Property
ErrorHandler:
CtxRaiseError m_strModuleName, "EOF"
End Property
|
|
|
|
|
JBoy wrote:
CtxSetComplete
Exit Function
Heya,
since you call CtxSetComplete (which I think makes a call to the SetComplete method of IObjectContext) you tell COM to make this object available for use by someone else as soon as the function ends, and COM then deactivates it and clears all class global parameters.
Cheers
/WW
|
|
|
|
|
Duh!
Thank you thank you thank you... I drove myself nuts on that one. How did I miss that one myself?
|
|
|
|
|
Hehe,
no probs...been there too (several times)!
/WW
|
|
|
|
|
Hi,
I made a COM DLL using ATL COM Appwizard. Then I inserted a dialog (derived from CDialog) into the COM. But I got the following compiling error:
"INDOWS.H already included. MFC apps must not #include <windows.h>"
Also, I tried to add ballon help and some button features into it. So I included the following MFC header files:
#include <afxwin.h> // MFC core and standard components
#include <afxpriv.h>
#include <afx.h>
#include <afxtempl.h>
#include <afxmt.h>
#include <afxres.h>
etc...
Then I got the same compiling error. Can't I include MFC header files in the COM? If someone has experience in this area, please give me some help.
Thanks!
|
|
|
|
|
You want to have dialog in ATL?
Inset ATL Object. In this wizard one of items are Dialog and you can insert it into your project. I don't have visual studio now to give you details. Do you need more?
Mazy
"The more I search, the more my need
For you,
The more I bless, the more I bleed
For you."The Outlaw Torn-Metallica
|
|
|
|
|
If you use Visula C6 you can't use MFC by default.I think when you can add MFC when you create your project.
For using Control in Dialog in ATL,it is too much to discribe it here.Its different from MFC,you have to handle most things yourself.
Maybe this article help you.
Mazy
"The more I search, the more my need
For you,
The more I bless, the more I bleed
For you."The Outlaw Torn-Metallica
|
|
|
|
|
Thank you, Mazy!
I added MFC support when I created the project. Now it works.
|
|
|
|
|
Hi
What data types to use to pass Date/Time.
Thanks
|
|
|
|
|
If you want your COM Object to be accessible form Visual Basic use DATE data type (which is typedefed to double). Use CComDate available at http://www.sellsbrothers.com.
|
|
|
|
|
Non-MFC Date Routines in ATL
Mazy
"The more I search, the more my need
For you,
The more I bless, the more I bleed
For you."The Outlaw Torn-Metallica
|
|
|
|
|
Hi all,
At home I want to connect atleast two to three PC's to test DCOM objects.
Right now I have a PC with winXP and other win95.
The computer with winXP is connected with 100Mbits internet connection.
Now my question is what hardware and software do I need so that I can build my own LAN.
Further I have network cards on both computers.
I would appreciate if someone can help me in this regards.
thanks in advance
regards
/rsasalm
|
|
|
|
|
Hey there!
On the hardware side you need:
1 switch or router (preferrably with 4-8 ports or so) probably a router to get the best performance.
1 network card for each computer, see that you get enough performance here so that you can take advantage of your internet connection (100Mbps)
Off course you need to have som network cable to connect the computers to the router.
Then you just connect your internet connection to one port on the router and use one port for each pc. If you don't connect to the internet through the router then you probably can't use all your pcs on the internet
Mike
|
|
|
|
|
Hi Mike,
Thanks for reply.
I have one more question.
I am getting confused with the terms router, switch and hub.
Can someone explain the difference and which one is better and why, particularly in my case?
thanks
regards
/rsasalm
|
|
|
|
|
I have a MFC ActiveX component which processes a large inputfile. The component is contained in a VB form. I want to have a Cancel button on the VB form to tell the component to exit the loop.
Inside the component's main loop I'm calling PeekMessage then TranslateMessage and DispatchMessage to allow the user to interact with the VB form (otherwise it would just freeze up while the Component executes).
I imagine something like this would be the way to go:
bool CFileControl::Process()
{
while (!file.eof)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)//maybe used a userdefined message instead
return TRUE;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//read next line of large file and process data
}
return...
}
Is it straightforward to pass a userdefined message from VB or does this break some COM rules. Otherwise has anybody any ideas? If I maybe set a flag which the loop condition could test, must I use a separate thread in order to set this flag.
Thanks for reading this far,
John
|
|
|
|
|
I think having a flag to exit the loop is better than having to send a message.
Yes, in that case you may need to have a separate thread to do (since you have a
large file...). If your control display a UI and has a button to stop the process,
you may still want to use a technic similar to the one with PeekMessage but this is
just a matter a choice. The best method in my opinion is to have a flag and use
a second thread but you will have to work a bit harder to make sure that your control
is thread safe.
Good luck!
One good thing about getting older, you don't lose the ages you've been!
|
|
|
|
|
Hi,
I'm an extreme newby to COM. What I want to do is be able to create a system where there are different components that manipulate an array of data. For example one might do a moving average, one might do a standard deviation etc. However I want other people to be able to develop components as well.
Could I develop a base interface and then VC++ and VB programmers could develop their components based on this interface and register somehow with my program. Then I could just have a Calculate( .. ) function and a GetDescription( .. ) function to create menus from.
Would I be better off just using dlls and using GetProcAddress?
Thanks
|
|
|
|
|
Since there are many functions in picture I think you should go for COM. Using COM will turn out to be simpler.
|
|
|
|
|
I have an out-proc server i'm currently writing. In this server I have a number of interfaces. I was wondering if I could do the following. All the interfaces and implementations of the interfaces are in the same project.
interface IUser {....}; // IUser definition
interface IUsers {....}; // IUsers definitions
class CUser : public IUser {....}; // The implementation of IUser
class CUsers : public IUsers {...}; // The implementation of IUsers
HRESULT CUsers::SomeFunc() {
CComPtr<IUser> pUser;
pUser.CreateInstance(__uuidof(IUser));
((CUser*)(pUser.p))->m_SomeData; // Can I do this cast from IUser to CUser?
...
}
That was my question, is the cast possible?
Justin Turney
|
|
|
|
|
Yes, but it is not safe.
Since you are in your CUser class when you create the new object, it would be safer to call new, then cast the object to the interface if the interface needed to be exported from the function.
HRESULT CUsers::SomeFunc()
{
CUser pUser = new CUser;
pUser->m_SomeData;
...
CComPtr piUser
piUser = static_cast<IUser*>(pUser);
...
}
Good Luck
Build a man a fire, and he will be warm for a day Light a man on fire, and he will be warm for the rest of his life!
|
|
|
|
|
Justin Turney wrote:
That was my question, is the cast possible?
Why do you want to do that?
You should never do anything like that. If a proxy or stub gets involved in between then the code will not be safe.
|
|
|
|