I may be stating the obvious but so as you know I'm new to C++/CLR and
coming from VB I'm easily confused! I want to create an event handler to
'listen' for StateChange events raised by a SqlConnection object, so I
can update my class' IsConnected property.
But for the life of me I can't work it out, I've googled and I can't
find an example anywhere and what I have read only serves to confuse.
Can anyone help me out please.
--
Bubba
> I may be stating the obvious but so as you know I'm new to C++/CLR and
> coming from VB I'm easily confused!
Do you use C++/CLI as your primary language / development environment? Why
don't you use C# or VB.Net? Do you have a large unmanaged Codebase (C or
C++) which you need to use? The reason for me to ask, is that for pure (or
mostly) managed solutions, programming in C# (or maybe VB.Net) is much more
effective (lower development costs) as C++/CLR, which is really greate if
you have (much) existing C(++) code you need to use, or if you need to speak
to libraries which have no COM support.
If you use C++/CLI to emit managed code only, you won't see any performance
benefit, because the IL Code is executed by the CLR as in other managed
lanaguages.
> I want to create an event handler
> to 'listen' for StateChange events raised by a SqlConnection object,
> so I can update my class' IsConnected property.
What you should be aware of is that is event is only raised if someone calls
"Close" or "Open" on a Connection. It is not raised if the Connection is
closed by anything else (like "KILL <myspid>" from another SQL-Server
process). And currently only "Open" and "Closed" are supported. All other
states definied in the enum (like "Broken", "Executing", "Fetching", ...)
are "reserved for future usage".
So maybe you don't need to subscribe to this Event, because it will not be
raised from anything outside. If only your class can call this methods (e.g.
because the connection instance is private), you can manage internal state
without the Event.
> But for the life of me I can't work it out, ...
You meen how to subscribte to Events from C++/CLI?
You just have to create an instance of the Handler, and assign it to the
Event:
// signature of Handler-Method
void MyConnectionHandler(Object ^ sender, StateChangeEventArgs ^ e) {
// ...
}
// attach it
connection->StateChange += gcnew
StateChangeEventHandler(&MyConnectionHandler);
When you have to detach it manually, you have to remember the
Handler-Instance (e.g. a private field), and call the "-=" operator to
remove your handler from the Event-Handler List.
> ... I've googled and I can't
> find an example anywhere and what I have read only serves to confuse.
Most samples / tutorials are based on C#/VB.Net. Of cause you can translate
the syntax as you become more familiar with C++/CLI.
OK?
GP
No real reason, just to make things awkward for myself I guess.
> If you use C++/CLI to emit managed code only, you won't see any performance
> benefit, because the IL Code is executed by the CLR as in other managed
> lanaguages.
No, I know this.
>> I want to create an event handler
>> to 'listen' for StateChange events raised by a SqlConnection object,
>> so I can update my class' IsConnected property.
>
> What you should be aware of is that is event is only raised if someone calls
> "Close" or "Open" on a Connection. It is not raised if the Connection is
> closed by anything else (like "KILL <myspid>" from another SQL-Server
> process). And currently only "Open" and "Closed" are supported. All other
> states definied in the enum (like "Broken", "Executing", "Fetching", ...)
> are "reserved for future usage".
OK, thanks. Kind of defeats the purpose of the event I guess!
> So maybe you don't need to subscribe to this Event, because it will not be
> raised from anything outside. If only your class can call this methods (e.g.
> because the connection instance is private), you can manage internal state
> without the Event.
>
>> But for the life of me I can't work it out, ...
>
> You meen how to subscribte to Events from C++/CLI?
>
> You just have to create an instance of the Handler, and assign it to the
> Event:
>
>
> // signature of Handler-Method
> void MyConnectionHandler(Object ^ sender, StateChangeEventArgs ^ e) {
> // ...
> }
>
> // attach it
> connection->StateChange += gcnew
> StateChangeEventHandler(&MyConnectionHandler);
>
>
> When you have to detach it manually, you have to remember the
> Handler-Instance (e.g. a private field), and call the "-=" operator to
> remove your handler from the Event-Handler List.
OK this is working for me now that I really appreciate your efforts but
can you answer me this. Where do I get the Handler-Instance to store in
order to remove it later?
>
> OK?
> GP
Yes, thanks again.
--
Bubba
>> Do you use C++/CLI as your primary language / development
>> environment? Why don't you use C# or VB.Net? Do you have a large
>> unmanaged Codebase (C or C++) which you need to use?
>> ...
>
> No real reason, just to make things awkward for myself I guess.
If possible I would to re-think if this is really a good choice in your
scenario.
>> What you should be aware of is that is event is only raised if
>> someone calls "Close" or "Open" on a Connection. It is not raised if
>> the Connection is closed by anything else (like "KILL <myspid>" from
>> another SQL-Server process). And currently only "Open" and "Closed"
>> are supported. All other states definied in the enum (like "Broken",
>> "Executing", "Fetching", ...) are "reserved for future usage".
>
> OK, thanks. Kind of defeats the purpose of the event I guess!
In most cases actually. The event may be used if your class makes
SqlConnection instance available for public callers (so you don't know if
someone calls .Close()).
>> You just have to create an instance of the Handler, and assign it to
>> the Event:
>>
>>
>> // signature of Handler-Method
>> void MyConnectionHandler(Object ^ sender, StateChangeEventArgs ^
>> e) { // ...
>> }
>>
>> // attach it
>> connection->StateChange += gcnew
>> StateChangeEventHandler(&MyConnectionHandler);
>>
>>
>> When you have to detach it manually, you have to remember the
>> Handler-Instance (e.g. a private field), and call the "-=" operator
>> to remove your handler from the Event-Handler List.
>
> OK this is working for me now that I really appreciate your efforts
> but can you answer me this. Where do I get the Handler-Instance to
> store in order to remove it later?
e.g.
private:
StateChangeEventHandler stateHandlerDelegate;
MyClass() {
this -> stateHandlerDelegate = gcnew
StateChangeEventHandler(&MyConnectionHandler);
this -> sqlConnectoin->StateChange += this -> stateHandlerDelegate;
}
~ MyClass() {
this -> sqlConnection -> StateChange -= this ->
stateHandlerDelegtte;
}
If most cases you don't need to unsubscribe explicitly, because if the
Instance of "MyClass" is not referenced anymore (or better: has no
root-reference), the References to the Connection and the EventHandler are
no (indirect) roots anymore, and so will get collected.
EventHandlers are "memory only" instances, so you cannot explicitly free
anything here. In opposite to the SqlConnection, which encapsulates a
unmanaged (not only memory) - and very heavyweight - resource, which should
be closes as soon as possible (eigher by .Close or IMO better by the general
.Dispose Methode (Dispose - Pattern) which calls .Close internally).
As mentioned, normally you don't need to remove Handlers manually, but there
are scenarios where EventHandler that are not manually removed will cause
objects to get to collected by the GC. For example in Windows - Forms, the
.NET Implemenation of the Windows Message Queue keeps a Top-Level Reference
to the Handlers (which hold references to the Form or Control, which holds
references to the complete Control-Tree, ...). So if you create controls
with handlers dynamically (if you use the desiger it creates the code for
you), you sould manually removed them when not needed anymore (e.g. the Form
is closed), otherwise they will not get release an you have a memory-leak.
GP
Hi Gunther,
Thank you once again for all this information, but having put these in
place I can't access the controls on my form within the handler. I
assume I have to create some sort of delegate to do this.
I've created a FileSystemWatcher and attached the appropriate event
handlers. When the changed event fires I want to update my form's status
bar but the compiler give me this error:
error C2227: left of '->Text' must point to class/struct/union/generic type
for this
System::Void FormMain::FileChanged(System::Object ^sender,
System::IO::FileSystemEventArgs ^e)
{
FormMain::Status->Text = "Output Updated Press F5 to Reload" ;
}
Could you point me in the direction of a fix please. I'm going away for
a few days so I won't be able to reply until the weekend. And thank you
once again.
--
Bubba.
> Thank you once again for all this information,
You're welcome.
> ... but having put these in
> place I can't access the controls on my form within the handler. I
> assume I have to create some sort of delegate to do this.
>
> I've created a FileSystemWatcher and attached the appropriate event
> handlers. When the changed event fires I want to update my form's
> status bar but the compiler give me this error:
>
> error C2227: left of '->Text' must point to
> class/struct/union/generic type
> for this
>
> System::Void FormMain::FileChanged(System::Object ^sender,
> System::IO::FileSystemEventArgs ^e)
> {
> FormMain::Status->Text = "Output Updated Press F5 to Reload" ;
> }
Then "FromMain::Status" is wrong here.
GP
Yes, I worked this out for myself! It should in fact be -> sorry. Hey,
we're all entitled to our off day(s).
Thanks again.