What am I doing wrong with my Workflow bookmark pick trigger? - c#

I have a NativeActivity derived activity that I wrote that is to use bookmarks as a trigger for a pick branch. Using something I found on MSDN I tried writing this to trigger the branch. The branch contains activities that fire service callbacks to remote clients via send activities. If I set a delay for the trigger, callbacks fire to the clients successfully. If I use my code activity, the pick branch activities don't fire.
public sealed class UpdateListener : NativeActivity<ClientUpdate>
{
[RequiredArgument]
public InArgument<string> BookmarkName { get; set; }
protected override void Execute(NativeActivityContext context)
{
context.CreateBookmark(BookmarkName.Get(context),
new BookmarkCallback(this.OnResumeBookmark));
}
protected override bool CanInduceIdle
{
get { return true; }
}
public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj )
{
Result.Set(context, (ClientUpdate)obj);
}
}
So it takes an arg to set the bookmark name for future bookmark references to execute the trigger. OnResumeBoookmark() takes in a ClientUpdate object that is passed by my application that is hosting the workflowapp. The activity is to return the object so the ClientUpdate can be passed to the workflow and have it sent to the remote clients via the send activity in the pick branch. In theory anyways.
For some reason it seems to be correct but feels wrong. I'm not sure if I should write the Activity in a different way to take care of what I need for my WF service.

I think your intentions would be a bit clearer if you created an extension (that implements IWorkflowInstanceExtension) to perform your action here.
For example:
public sealed class AsyncWorkExtension
: IWorkflowInstanceExtension
{
// only one extension per workflow
private WorkflowInstanceProxy _proxy;
private Bookmark _lastBookmark;
/// <summary>
/// Request the extension does some work for an activity
/// during which the activity will idle the workflow
/// </summary>
/// <param name="toResumeMe"></param>
public void DoWork(Bookmark toResumeMe)
{
_lastBookmark = toResumeMe;
// imagine I kick off some async op here
// when complete system calls WorkCompleted below
// NOTE: you CANNOT block here or you block the WF!
}
/// <summary>
/// Called by the system when long-running work is complete
/// </summary>
/// <param name="result"></param>
internal void WorkCompleted(object result)
{
//NOT good practice! example only
//this leaks resources search APM for details
_proxy.BeginResumeBookmark(_lastBookmark, result, null, null);
}
/// <summary>
/// When implemented, returns any additional extensions
/// the implementing class requires.
/// </summary>
/// <returns>
/// A collection of additional workflow extensions.
/// </returns>
IEnumerable<object> IWorkflowInstanceExtension
.GetAdditionalExtensions()
{
return new object[0];
}
/// <summary>
/// Sets the specified target
/// <see cref="WorkflowInstanceProxy"/>.
/// </summary>
/// <param name="instance">The target workflow instance to set.</param>
void IWorkflowInstanceExtension
.SetInstance(WorkflowInstanceProxy instance)
{
_proxy = instance;
}
}
Within the Activity, you'd use this thusly:
var ext = context.GetExtension<AsyncWorkExtension>();
var bookmark = context.CreateBookmark(BookmarkCallback);
ext.DoWork(bookmark);
return;
This way is much more explicit (instead of using the bookmark name to convey meaning to the "outside" world) and is much easier to extend if, say, you require to send out more information than a bookmark name.

Is there something actually resuming the bookmark here? If not the workflow will wait very patiently and nothing will happen.

Related

Trial experience of app is allowing unrestricted access - Windows Phone 8

I launched my first app yesterday to the store. The app has a trial version which simply restricts full access to the game.
I downloaded the trial and played for a while, no problems - good.
I then purchased the full version by from the store and started the app again only to find that it didnt release it's restrited areas - NOT good.
I have implemented the following code in the app to implement the restrictions:
/// <summary>
/// The TrialExperienceHelper class can serve as a convenient building-block in your app's trial experience implementation. It queries
/// the license as infrequently as possible, and caches the results, for maximum performance. When built in debug configuration, the class simulates the purchase
/// experience when the Buy method is called. To customize what happens in debug when Buy is called, initialize the simulatedLicMode and/or
/// simulatedLicModeOnPurchase fields to different values. A release build of this class will have access to a license only when the app is
/// published to the Windows Phone Store. For a release build not published to the Store, the value of the LicenseMode property will be
/// LicenseModes.MissingOrRevoked.
/// </summary>
public static class TrialExperienceHelper
{
#region enums
/// <summary>
/// The LicenseModes enumeration describes the mode of a license.
/// </summary>
public enum LicenseModes
{
Full,
MissingOrRevoked,
Trial
}
#endregion enums
#region fields
#if DEBUG
// Determines how a debug build behaves on launch. This field is set to LicenseModes.Full after simulating a purchase.
// Calling the Buy method (or navigating away from the app and back) will simulate a purchase.
internal static LicenseModes simulatedLicMode = LicenseModes.Trial;
#endif // DEBUG
private static bool isActiveCache;
private static bool isTrialCache;
#endregion fields
#region constructors
// The static constructor effectively initializes the cache of the state of the license when the app is launched. It also attaches
// a handler so that we can refresh the cache whenever the license has (potentially) changed.
static TrialExperienceHelper()
{
TrialExperienceHelper.RefreshCache();
PhoneApplicationService.Current.Activated += (object sender, ActivatedEventArgs e) => TrialExperienceHelper.
#if DEBUG
// In debug configuration, when the user returns to the application we will simulate a purchase.
OnSimulatedPurchase();
#else // DEBUG
// In release configuration, when the user returns to the application we will refresh the cache.
RefreshCache();
#endif // DEBUG
}
#endregion constructors
#region properties
/// <summary>
/// The LicenseMode property combines the active and trial states of the license into a single
/// enumerated value. In debug configuration, the simulated value is returned. In release configuration,
/// if the license is active then it is either trial or full. If the license is not active then
/// it is either missing or revoked.
/// </summary>
public static LicenseModes LicenseMode
{
get
{
#if DEBUG
return simulatedLicMode;
#else // DEBUG
if (TrialExperienceHelper.isActiveCache)
{
return TrialExperienceHelper.isTrialCache ? LicenseModes.Trial : LicenseModes.Full;
}
else // License is inactive.
{
return LicenseModes.MissingOrRevoked;
}
#endif // DEBUG
}
}
/// <summary>
/// The IsFull property provides a convenient way of checking whether the license is full or not.
/// </summary>
public static bool IsFull
{
get
{
return (TrialExperienceHelper.LicenseMode == LicenseModes.Full);
}
}
#endregion properties
#region methods
/// <summary>
/// The Buy method can be called when the license state is trial. the user is given the opportunity
/// to buy the app after which, in all configurations, the Activated event is raised, which we handle.
/// </summary>
public static void Buy()
{
MarketplaceDetailTask marketplaceDetailTask = new MarketplaceDetailTask();
marketplaceDetailTask.ContentType = MarketplaceContentType.Applications;
marketplaceDetailTask.Show();
}
/// <summary>
/// This method can be called at any time to refresh the values stored in the cache. We re-query the application object
/// for the current state of the license and cache the fresh values. We also raise the LicenseChanged event.
/// </summary>
public static void RefreshCache()
{
TrialExperienceHelper.isActiveCache = CurrentApp.LicenseInformation.IsActive;
TrialExperienceHelper.isTrialCache = CurrentApp.LicenseInformation.IsTrial;
TrialExperienceHelper.RaiseLicenseChanged();
}
private static void RaiseLicenseChanged()
{
if (TrialExperienceHelper.LicenseChanged != null)
{
TrialExperienceHelper.LicenseChanged();
}
}
#if DEBUG
private static void OnSimulatedPurchase()
{
TrialExperienceHelper.simulatedLicMode = LicenseModes.Full;
TrialExperienceHelper.RaiseLicenseChanged();
}
#endif // DEBUG
#endregion methods
#region events
/// <summary>
/// The static LicenseChanged event is raised whenever the value of the LicenseMode property has (potentially) changed.
/// </summary>
public static event LicenseChangedEventHandler LicenseChanged;
#endregion events
}
Then in my code I'm using if's to determine what to allow with this:
if ((TrialExperienceHelper.LicenseMode == TrialExperienceHelper.LicenseModes.Trial)||
(TrialExperienceHelper.LicenseMode == TrialExperienceHelper.LicenseModes.MissingOrRevoked))
{
//Then add ad' etc
}
This call must be returning TRUE...!
For the life of me I can't see the mistake, maybe someone here more experienced can help? What have I done wrong here?

wcf webservice timing methods?

I recently found this article:
http://coderjournal.com/2010/10/timing-the-execution-time-of-your-mvc-actions/
Which shows a nice little attribute that you can add to your Controller classes, which creates a stopwatch and sticks an X-Runtime header into the result.
I've been trying to add something similar to my WCF Rest service, but have been unable to figure it out so far. Any suggestions on how I could go about implementing something similar on my webservice?
This is a perfect place for aspects. Check out post sharp.
You can then create an aspect kind of like:
/// <summary>
/// Adds execution timing and automatic logging to the tagged method
/// </summary>
[Serializable]
public class MethodTimeAttribute : OnMethodBoundaryAspect
{
private String MethodName { get; set; }
private DateTime StartTime { get; set; }
/// <summary>
/// Method executed at build time. Initializes the aspect instance. After the execution
/// of <see cref="CompileTimeInitialize"/>, the aspect is serialized as a managed
/// resource inside the transformed assembly, and deserialized at runtime.
/// </summary>
/// <param name="method">Method to which the current aspect instance
/// has been applied.</param>
/// <param name="aspectInfo">Unused.</param>
public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
{
MethodName = method.DeclaringType.FullName "." method.Name;
}
public override void OnEntry(MethodExecutionArgs args)
{
StartTime = DateTime.Now;
}
public override void OnExit(MethodExecutionArgs args)
{
Log.Debug(this, "Method {0} took {1} to execute", MethodName, DateTime.Now - StartTime);
}
}
And all you have to do is tag your methods like this:
[MethodTime]
private void RunFor(TimeSpan runTime){
// do stuff
}
Advanced post sharp features require a license but simple ones like method boundary aspects are free.
While this doesn't test any network boundaries for your wcf service it will test your service implementation time.
If you really wanted to you could create some custom behaviors and inject that into your WCF chain to test timing, but the likelihood of bottlenecks at that phase is pretty unlikely. What's more likely is you are sending a lot of data so things are slow, or things in your implementation are slow, all of which can be tested server side or client side independently.

Return a string from a Service already running using Remoting

im trying to use .Net Remoting to get a value of a variable that i use in a thread of an windows service.
TcpChannel tcpChannel = new TcpChannel(9998);
ChannelServices.RegisterChannel(tcpChannel, false);
Type commonInterfaceType = typeof(MyNameSpace.Core.Engine);
RemotingConfiguration.RegisterWellKnownServiceType(commonInterfaceType,
"CopyFilePercentage",
WellKnownObjectMode.SingleCall);
myEngine = Engine.EngineInstance;
myEngine.Start();
But it seams that every time that i use the Client to get that value, a new thread is created returning an empty string.
Any idea why is this happening or am I doing something wrong?
Thanks in advance,
Miguel de Sousa
WellKnownObjectMode.SingleCall creates a new instance of your class for each call. try WellKnownObjectMode.Singleton
EDIT
Maybe you should read about client activated objects. Turn your singleton object to a class factory and return a new instance of a real worker class(ofcourse inheriting from MarshalByRefObject) which will be used by the client.
so your client will be something like this
var worker = client.GetWorkerClass();
worker.GetSomeData();
and you will have one server object per connection (this may not be the correct terminology).
well i just used a Global Variable Class not really what I wanted but does the job.
/// <summary>
/// Contains global variables for project.
/// </summary>
public static class GlobalVar
{
/// <summary>
/// Global variable that is constant.
/// </summary>
public const string GlobalString = "Important Text";
/// <summary>
/// Static value protected by access routine.
/// </summary>
static int _globalValue;
/// <summary>
/// Access routine for global variable.
/// </summary>
public static int GlobalValue
{
get
{
return _globalValue;
}
set
{
_globalValue = value;
}
}
/// <summary>
/// Global static field.
/// </summary>
public static bool GlobalBoolean;
}

Trace PRISM / CAL events (best practice?)

Ok,
this question is for people with either a deep knowledge of PRISM or some magic skills I just lack (yet). The Background is simple: Prism allows the declaration of events to which the user can subscribe or publish. In code this looks like this:
_eventAggregator.GetEvent<LayoutChangedEvent>().Subscribe(UpdateUi, true);
_eventAggregator.GetEvent<LayoutChangedEvent>().Publish("Some argument");
Now this is nice, especially because these events are strongly typed, and the declaration is a piece of cake:
public class LayoutChangedEvent : CompositePresentationEvent<string>
{
}
But now comes the hard part: I want to trace events in some way. I had the idea to subscribe using a lambda expression calling a simple log message. Worked perfectly in WPF, but in Silverlight there is some method access error (took me some time to figure out the reason).. If you want to see for yourself, try this in Silverlight:
eA.GetEvent<VideoStartedEvent>().Subscribe(obj => TraceEvent(obj, "vSe", log));
If this would be possible, I would be happy, because I could easily trace all events using a single line to subscribe. But it does not... The alternative approach is writing a different functions for each event, and assign this function to the events. Why different functions? Well, I need to know WHICH event was published. If I use the same function for two different events I only get the payload as argument. I have now way to figure out which event caused the tracing message.
I tried:
using Reflection to get the causing event (not working)
using a constructor in the event to enable each event to trace itself (not allowed)
Any other ideas?
Chris
PS: Writing this text took me most likely longer than writing 20 functions for my 20 events, but I refuse to give up :-) I just had the idea to use postsharp, that would most likely work (although I am not sure, perhaps I end up having only information about the base class).. Tricky and so unimportant topic...
Probably the easiest thing would be to subclass CompositePresentationEvent and override the behavior of the Publish event. Here's the source for CompositePresentationEvent:
http://compositewpf.codeplex.com/SourceControl/changeset/view/26112#496659
Here's the current Publish behavior:
public virtual void Publish(TPayload payload)
{
base.InternalPublish(payload);
}
So you could just add a little to this:
public virtual override void Publish(TPayload payload)
{
ILoggerFacade logger = ServiceLocator.Current.GetInstance<ILoggerFacade>();
logger.Log("Publishing " + payload.ToString(), Category.Debug, Priority.Low);
base.InternalPublish(payload);
}
Here I'm using the logger facility built into Prism, but feel free to substitute your own (or better, just implement ILoggerFacade!).
I was surprised that there were any default messages being published or places to plug in tracing in this system... as much as EventAggregator is abused by people, you'd think this would be a big request!
A little late but better late than never! I recently had the same problem and this is how I solved it.
First, I didn't like the Prism method of publishing/subscribing to events, so I used a method like this instead:
http://neverindoubtnet.blogspot.com/2009/07/simplify-prism-event-aggregator.html
This post above suggests using Extension methods on Event Aggregator to simplify the call to publish/subscribe. As a result your client code looks like this:
IEventAggregator ev;
ev.Publish<MyCustomMessage>();
//or
ev.Publish(new MyCustomMessage(someData));
//and similarly subscription
ev.Subscribe<MyCustomMessage(this.OnCustomMessageReceived);
// ...
private void OnCustomMessageReceived(MyCustomMessage message)
{
// ...
}
// With a BaseMessageEvent class as follows (see the blog post above for where this comes from)
/// <summary>
/// Base class for all messages (events)
/// </summary>
/// <typeparam name="TMessage">The message type (payload delivered to subscribers)</typeparam>
public class BaseEventMessage<TMessage> : CompositePresentationEvent<TMessage>
{
}
Ok this is great, but rather than hacky extension methods I implemented my own event service as follows:
/// <summary>
/// The EventService instance
/// </summary>
public class EventService : IEventService
{
private readonly IEventAggregator eventAggregator;
private readonly ILoggerFacade logger;
/// <summary>
/// Initializes a new instance of the <see cref="EventService"/> class.
/// </summary>
/// <param name="logger">The logger instance.</param>
/// <param name="eventAggregator">The event aggregator instance.</param>
public EventService(IEventAggregator eventAggregator, ILoggerFacade logger)
{
this.logger = logger;
this.eventAggregator = eventAggregator;
}
#region IEventService Members
/// <summary>
/// Publishes the event of type TMessageType to all subscribers
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
public void Publish<TMessageType>() where TMessageType : BaseEventMessage<TMessageType>, new()
{
TMessageType message = Activator.CreateInstance<TMessageType>();
this.Publish(message);
}
/// <summary>
/// Publishes the event of type TMessageType to all subscribers
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
/// <param name="message">The message to publish</param>
public void Publish<TMessageType>(TMessageType message) where TMessageType : BaseEventMessage<TMessageType>, new()
{
// Here we can log our message publications
if (this.logger != null)
{
// logger.log etc..
}
this.eventAggregator.GetEvent<TMessageType>().Publish(message);
}
/// <summary>
/// Subscribes to the event of type TMessage
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
/// <param name="action">The action to execute when the event is raised</param>
public void Subscribe<TMessageType>(Action<TMessageType> action) where TMessageType : BaseEventMessage<TMessageType>, new()
{
// Here we can log our message publications
if (this.logger != null)
{
// logger.log etc..
}
this.eventAggregator.GetEvent<TMessageType>().Subscribe(action);
}
#endregion
}
Then I register IEventService/EventService as a singleton in the bootstrapper and forget about using the IEventAggregator, just use this (however if someone uses the IEventAggregator, its the same instance as that used by the EventService so will still work).
Finally, another trick to add is to use the Stack Frame to tell me where publications and subscriptions are coming from. Note this is a slow process (unwinding the stack frame) so use it sparingly. If you are
raising an event regularly then perhaps put a flag in your BaseEventMessage and check that to see whether to log publications for certain event types.
// Inside Publish method ... Log the subscription
if (this.logger != null)
{
Type messageType = typeof(TMessageType);
Type callingType = GetCallingType();
string methodName = GetCallingMethod().Name;
// Log the publication of this event
this.logger.Log(
string.Format("Event {0} was published by {1}.{2}()",
messageType.Name,
callingType.Name,
methodName),
Category.Debug,
Priority.Low));
}
// Additional methods to add to EventService to get the calling type/class
//
/// <summary>
/// Gets the Type that called the method or property where GetCallingType is called
/// </summary>
/// <returns>The class type that called</returns>
[MethodImplAttribute(MethodImplOptions.NoInlining)]
public static Type GetCallingType()
{
int skip = 2;
MethodBase method = new StackFrame(skip, false).GetMethod();
return method.DeclaringType;
}
/// <summary>
/// Gets the Method that called the method or property where GetCallingMethod is called
/// </summary>
/// <returns>The method type that was called</returns>
public static MethodBase GetCallingMethod()
{
return new StackFrame(2, false).GetMethod();
}
Note the above won't work in Silverlight (the use of the StackFrame), but the rest does. I've found this invaluable when debugging the multitude of events flying around a Prism app!

raising a vb6 event using interop

I have a legacy VB6 component that I've imported into VS using tlbimp.exe to generate my interop assembly. The VB6 component defines an event that allows me to pass messages within VB6.
Public Event Message(ByVal iMsg As Variant, oCancel As Variant)
I would really like to be able to raise this even in my C# program, but its getting imported as an event, not a delegate or something else useful. So, I can only listen, but never fire. Does anyone know how to fire an event contained within VB6? The C# event looks like
[TypeLibType(16)]
[ComVisible(false)]
public interface __MyObj_Event
{
event __MyObj_MessageEventHandler Message;
}
I unfortunately cannot change the VB6 code. Thanks.
Actually, hope is not lost yet. It is possible to raise an event on a COM object from outside of the object's class. This functionality is actually provided by COM itself, although in an indirect manner.
In COM, events work on a publish/subscribe model. A COM object that has events (the "event source") publishes events, and one or more other COM objects subscribe to the event by attaching an event handler to the source object (the handlers are called "event sinks"). Normally, the source object raises an event by simply looping through all the event sinks and calling the appropriate handler method.
So how does this help you? It just so happens that COM lets you query an event source for a list of all the event sink objects currently subscribed to the source object's events. Once you have a list of event sink objects, you can simulate raising an event by invoking each of the sink object's event handlers.
Note: I'm over-simplifying the details and being liberal with some of the terminology, but that's the short (and somewhat politically incorrect) version of how events work in COM.
You can take advantage of this knowledge to raise events on a COM object from external code. In fact, it is possible to do all of this in C#, with the help of the COM interop support in the System.Runtime.Interop and System.Runtime.Interop.ComTypes namespaces.
EDIT
I wrote a utility class that will allow you to raise events on a COM object from .NET. It's pretty easy to use. Here is an example using the event interface from your question:
MyObj legacyComObject = new MyObj();
// The following code assumes other COM objects have already subscribed to the
// MyObj class's Message event at this point.
//
// NOTE: VB6 objects have two hidden interfaces for classes that raise events:
//
// _MyObj (with one underscore): The default interface.
// __MyObj (with two underscores): The event interface.
//
// We want the second interface, because it gives us a delegate
// that we can use to raise the event.
// The ComEventUtils.GetEventSinks<T> method is a convenience method
// that returns all the objects listening to events from the legacy COM object.
// set up the params for the event
string messageData = "Hello, world!";
bool cancel = false;
// raise the event by invoking the event delegate for each connected object...
foreach(__MyObj sink in ComEventUtils.GetEventSinks<__MyObj>(legacyComObject))
{
// raise the event via the event delegate
sink.Message(messageData, ref cancel);
if(cancel == true)
{
// do cancel processing (just an example)
break;
}
}
Below is the code for the ComEventUtils class (as well as helper class, SafeIntPtr, because I'm paranoid and wanted a nice way to deal with the IntPtrS needed by the COM-related code):
Disclaimer: I haven't thoroughly tested the code below. The code performs manual memory management in a few places, and therefore there is the possibility that it could introduce memory leaks into your code. Also, I didn't add error-handling to the code, because this is only an example. Use with care.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using COM = System.Runtime.InteropServices.ComTypes;
namespace YourNamespaceHere
{
/// <summary>
/// A utility class for dealing with COM events.
/// Needs error-handling and could potentially be refactored
/// into a regular class. Also, I haven't extensively tested this code;
/// there may be a memory leak somewhere due to the rather
/// low-level stuff going on in the class, but I think I covered everything.
/// </summary>
public static class ComEventUtils
{
/// <summary>
/// Get a list of all objects implementing an event sink interface T
/// that are listening for events on a specified COM object.
/// </summary>
/// <typeparam name="T">The event sink interface.</typeparam>
/// <param name="comObject">The COM object whose event sinks you want to retrieve.</param>
/// <returns>A List of objects that implement the given event sink interface and which
/// are actively listening for events from the specified COM object.</returns>
public static List<T> GetEventSinks<T>(object comObject)
{
List<T> sinks = new List<T>();
List<COM.IConnectionPoint> connectionPoints = GetConnectionPoints(comObject);
// Loop through the source object's connection points,
// find the objects that are listening for events at each connection point,
// and add the objects we are interested in to the list.
foreach(COM.IConnectionPoint connectionPoint in connectionPoints)
{
List<COM.CONNECTDATA> connections = GetConnectionData(connectionPoint);
foreach (COM.CONNECTDATA connection in connections)
{
object candidate = connection.pUnk;
// I tried to avoid relying on try/catch for this
// part, but candidate.GetType().GetInterfaces() kept
// returning an empty array.
try
{
sinks.Add((T)candidate);
}
catch { }
}
// Need to release the interface pointer in each CONNECTDATA instance
// because GetConnectionData implicitly AddRef's it.
foreach (COM.CONNECTDATA connection in connections)
{
Marshal.ReleaseComObject(connection.pUnk);
}
}
return sinks;
}
/// <summary>
/// Get all the event connection points for a given COM object.
/// </summary>
/// <param name="comObject">A COM object that raises events.</param>
/// <returns>A List of IConnectionPoint instances for the COM object.</returns>
private static List<COM.IConnectionPoint> GetConnectionPoints(object comObject)
{
COM.IConnectionPointContainer connectionPointContainer = (COM.IConnectionPointContainer)comObject;
COM.IEnumConnectionPoints enumConnectionPoints;
COM.IConnectionPoint[] oneConnectionPoint = new COM.IConnectionPoint[1];
List<COM.IConnectionPoint> connectionPoints = new List<COM.IConnectionPoint>();
connectionPointContainer.EnumConnectionPoints(out enumConnectionPoints);
enumConnectionPoints.Reset();
int fetchCount = 0;
SafeIntPtr pFetchCount = new SafeIntPtr();
do
{
if (0 != enumConnectionPoints.Next(1, oneConnectionPoint, pFetchCount.ToIntPtr()))
{
break;
}
fetchCount = pFetchCount.Value;
if (fetchCount > 0)
connectionPoints.Add(oneConnectionPoint[0]);
} while (fetchCount > 0);
pFetchCount.Dispose();
return connectionPoints;
}
/// <summary>
/// Returns a list of CONNECTDATA instances representing the current
/// event sink connections to the given IConnectionPoint.
/// </summary>
/// <param name="connectionPoint">The IConnectionPoint to return connection data for.</param>
/// <returns>A List of CONNECTDATA instances representing all the current event sink connections to the
/// given connection point.</returns>
private static List<COM.CONNECTDATA> GetConnectionData(COM.IConnectionPoint connectionPoint)
{
COM.IEnumConnections enumConnections;
COM.CONNECTDATA[] oneConnectData = new COM.CONNECTDATA[1];
List<COM.CONNECTDATA> connectDataObjects = new List<COM.CONNECTDATA>();
connectionPoint.EnumConnections(out enumConnections);
enumConnections.Reset();
int fetchCount = 0;
SafeIntPtr pFetchCount = new SafeIntPtr();
do
{
if (0 != enumConnections.Next(1, oneConnectData, pFetchCount.ToIntPtr()))
{
break;
}
fetchCount = pFetchCount.Value;
if (fetchCount > 0)
connectDataObjects.Add(oneConnectData[0]);
} while (fetchCount > 0);
pFetchCount.Dispose();
return connectDataObjects;
}
} //end class ComEventUtils
/// <summary>
/// A simple wrapper class around an IntPtr that
/// manages its own memory.
/// </summary>
public class SafeIntPtr : IDisposable
{
private bool _disposed = false;
private IntPtr _pInt = IntPtr.Zero;
/// <summary>
/// Allocates storage for an int and assigns it to this pointer.
/// The pointed-to value defaults to 0.
/// </summary>
public SafeIntPtr()
: this(0)
{
//
}
/// <summary>
/// Allocates storage for an int, assigns it to this pointer,
/// and initializes the pointed-to memory to known value.
/// <param name="value">The value this that this <tt>SafeIntPtr</tt> points to initially.</param>
/// </summary>
public SafeIntPtr(int value)
{
_pInt = Marshal.AllocHGlobal(sizeof(int));
this.Value = value;
}
/// <summary>
/// Gets or sets the value this pointer is pointing to.
/// </summary>
public int Value
{
get
{
if (_disposed)
throw new InvalidOperationException("This pointer has been disposed.");
return Marshal.ReadInt32(_pInt);
}
set
{
if (_disposed)
throw new InvalidOperationException("This pointer has been disposed.");
Marshal.WriteInt32(_pInt, Value);
}
}
/// <summary>
/// Returns an IntPtr representation of this SafeIntPtr.
/// </summary>
/// <returns></returns>
public IntPtr ToIntPtr()
{
return _pInt;
}
/// <summary>
/// Deallocates the memory for this pointer.
/// </summary>
public void Dispose()
{
if (!_disposed)
{
Marshal.FreeHGlobal(_pInt);
_disposed = true;
}
}
~SafeIntPtr()
{
if (!_disposed)
Dispose();
}
} //end class SafeIntPtr
} //end namespace YourNamespaceHere
In VB6 the event can only be raised from within the class (or Form as the case may be) declaring the Event. To force a event to be raised in VB6 you need to expose a method on the class to do this. If you don't have the source code the you are out of luck.
From the documentation
RaiseEvent eventname [(argumentlist)]
The required eventname is the name of
an event declared within the module
and follows Basic variable naming
conventions.
For example
Option Explicit
Private FText As String
Public Event OnChange(ByVal Text As String)
'This exposes the raising the event
Private Sub Change(ByVal Text As String)
RaiseEvent OnChange(Text)
End Sub
Public Property Get Text() As String
Text = FText
End Property
Public Property Let Text(ByVal Value As String)
FText = Value
Call Change(Value)
End Property
Sorry to be the bearer of bad news.

Categories