Problems hooking olesetclipboard - winapi

Goal:
While running inside another process (... Say a plugin inside an Office application like Word), intercept the calls to OleSetClipboard/OleGetClipboard and proxy the iDataObject interface by replacing the object originally set/get by the application with one we control, passing on calls to the original object as needed.
Essentially, I would like to control access to the clipboard for a given application.
Progress:
I have successfully hooked the OleSet/GetClipboard functions (as well as others) and can replace the objects with my "proxy" objects. My proxy objects use a .Net definition of IDataObject (System.Runtime.InteropServices.ComTypes.IDataObject).
Note, in this case, I'm using C#, but a similar breakdown happened when I reimplemented in c++.
Problem:
Surprise ... Surprise, the application doesn't function completely normally when I proxy the OleSetClipboard function. Its does just fine when I hook only the OleGetClipboard function side.
I can see my proxied objects on the getter and setter sides calling each other. The breakdown seems to be that when the proxy object on the olegetclipboard side needs to pass thru a getdata call, the olesetclipboard side proxy will throw an exception while trying to execute getdata on the original object that is being proxied on the setter side.
Sudo Example:
Note, this example is not specific to this format. Most/All non text formats fails this way.
I see that the set side calls GetData on the object it is proxying from the original OleSetClipboard call from Word:
Proxy Object (OLESETCLIPBOARD): GetData Called for 15
The structure seems correct:
FORMATETC Structure Info: cfFormat (15) dwAspect (DVASPECT_CONTENT) lindex (-1) ptd (0) tymed (TYMED_HGLOBAL)
But I get an exception:
System.Runtime.InteropServices.COMException:GetData: Message:Invalid
FORMATETC structure (Exception from HRESULT: 0x80040064
(DV_E_FORMATETC))
What may be wrong? What have I not considered? Other thoughts?
Thanks!
Update:
Example class used to proxy ...
public enum DataObjectSource { OLESETCLIPBOARD = 0, OLEGETCLIPBOARD = 1};
public class DataObjectProxy System.Runtime.InteropServices.ComTypes.IDataObject
{
private System.Runtime.InteropServices.ComTypes.IDataObject OriginalClipboardObject;
private DataObjectSource eSource;
public DataObjectProxy(object OriginalObjectToWrap, DataObjectSource eType)
{
this.OriginalClipboardObject = (System.Runtime.InteropServices.ComTypes.IDataObject)OriginalObjectToWrap;
this.eSource = eType;
}
#region Utility Methods
public string GetSingleLineLoggableOutput(FORMATETC FormatToPrint)
{
return $"FORMATETC Structure Info: cfFormat ({(ushort)FormatToPrint.cfFormat} - {GetClipboardFormatName((uint)FormatToPrint.cfFormat)}) dwAspect ({FormatToPrint.dwAspect}) lindex ({FormatToPrint.lindex}) ptd ({FormatToPrint.ptd}) tymed ({FormatToPrint.tymed})";
}
private String GetClipboardFormatName(uint ClipboardFormat)
{
StringBuilder sb = new StringBuilder(1000);
GetClipboardFormatName(ClipboardFormat, sb, sb.Capacity);
return sb.ToString();
}
#endregion Utility Methods
#region pInvokes
[DllImport("user32.dll")]
static extern int GetClipboardFormatName(uint format, [Out] StringBuilder lpszFormatName, int cchMaxCount);
#endregion pInvokes
#region System.Runtime.InteropServices.ComTypes.IDataObject Interface Implemenation
public int DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): DAdvise Called");
return OriginalClipboardObject.DAdvise(ref pFormatetc, advf, adviseSink, out connection);
}
public void DUnadvise(int connection)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): DUnadvise Called");
OriginalClipboardObject.DUnadvise(connection);
}
public int EnumDAdvise(out IEnumSTATDATA enumAdvise)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): EnumDAdvise Called");
return OriginalClipboardObject.EnumDAdvise(out enumAdvise);
}
public IEnumFORMATETC EnumFormatEtc(DATADIR direction)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): EnumFormatEtc Called");
return OriginalClipboardObject.EnumFormatEtc(direction);
}
public int GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): GetCanonicalFormatEtc Called");
return OriginalClipboardObject.GetCanonicalFormatEtc(ref formatIn, out formatOut);
}
public void GetData(ref FORMATETC format, out STGMEDIUM medium)
{
try
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): GetData Called for {(ushort)format.cfFormat}");
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, GetSingleLineLoggableOutput(format));
STGMEDIUM medTemp;
FORMATETC formatTemp = format;
OriginalClipboardObject.GetData(ref formatTemp, out medTemp);
medium = medTemp;
}
catch(Exception getException)
{
HookLogger.LogException(HookLogger.HOOK_CHANNEL, "GetData", getException);
throw;
}
}
public void GetDataHere(ref FORMATETC format, ref STGMEDIUM medium)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): GetDataHere Called");
OriginalClipboardObject.GetDataHere(ref format, ref medium);
}
public int QueryGetData(ref FORMATETC format)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): QueryGetData Called");
return OriginalClipboardObject.QueryGetData(ref format);
}
public void SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release)
{
HookLogger.LogDebug(HookLogger.HOOK_CHANNEL, $"Proxy Object ({eSource}): SetData Called");
OriginalClipboardObject.SetData(ref formatIn, ref medium, release);
}
#endregion System.Runtime.InteropServices.ComTypes.IDataObject Interface Implemenation
}

Related

Entity Framework 6 "DbContext has been disposed" exception

Something very strange is happening in production, and it only happens in production. I have a Web API running and in one of the APIs, there is a repository created in the constructor and used in the functions. This is how the flow of a request works:
HTTP request comes in
MVC API controller decides which "worker" class to instantiate and creates it using Activator.CreateInstance
API controller calls worker.OnExecute inside of a Task.Run() and returns the http response
Worker calls _engine.Execute
Each worker instantiates another "engine" class that has all of the logic.
The engine in case constructs 3 repositories created using a UnitOfWork that is created per engine instance, like so:
public class MyWorker : Worker
{
private readonly MyEngine _engine;
public MyWorker()
{
_engine = new MyEngine();
}
protected override WorkerResult OnExecute(JObject data, CancellationToken cta)
{
return new WorkerResult(HttpStatusCode.OK, _engine.Execute(data));
}
}
public class MyEngine : EngineBase
{
private BaseRepository<Order> OrderRepo { get; set; }
private BaseRepository<OrderItem> OrderItemRepo { get; set; }
public MyEngine()
{
OrderRepo = new BaseRepository<Order>(MyUnitOfWork);
OrderItemRepo = new BaseRepository<OrderItem>(MyUnitOfWork);
}
public string Execute(JObject data)
{
return IsOrderValid(data).ToString();
}
public bool IsOrderValid(JObject data)
{
var orderId = data.Value<int>("OrderId");
// Without this line it crashes. With this line it crashes
//OrderRepo = new BaseRepository<Order>(InternationalWork);
// This is where it crashes
Order order = OrderRepo.First(x => x.OrderID == orderId);
// more code
}
}
public class EngineBase : UnitOfWorker, IDisposable
{
private UnitOfWork _myUnitOfWork;
public EngineBase() { }
public UnitOfWork MyUnitOfWork
{
get
{
return _myUnitOfWork ?? (_myUnitOfWork = new UnitOfWork(new DbContextAdapter(new MyDbContext())));
}
}
}
This is the actual stack trace:
The operation cannot be completed because the DbContext has been disposed.
StackTrace1
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.LazyInternalContext.get_ObjectContext()
at System.Data.Entity.Internal.Linq.InternalSet`1.CreateObjectQuery(Boolean asNoTracking, Nullable`1 streaming, IDbExecutionStrategy executionStrategy)
at System.Data.Entity.Internal.Linq.InternalSet`1.InitializeUnderlyingTypes(EntitySetTypePair pair)
at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
The stack trace shows "FirstOrDefault" because OrderRepo.First internally calls DbSet.FirstOrDefault, like so:
public virtual T First(Expression<Func<T, bool>> query)
{
return _dbSet.FirstOrDefault(query);
}
I'm stumped because each worker is created per http request. Each DBContext is created per engine instance so I don't know how it could be disposed when it was just created in the constructor. And this only happens on the production web server where I presume it's being called more. Any tips would be greatly appreciated.

Read Application Object from GemFire using Spring Data GemFire. Data stored using SpringXD's gemfire-json-server

I'm using the gemfire-json-server module in SpringXD to populate a GemFire grid with json representation of “Order” objects. I understand the gemfire-json-server module saves data in Pdx form in GemFire. I’d like to read the contents of the GemFire grid into an “Order” object in my application. I get a ClassCastException that reads:
java.lang.ClassCastException: com.gemstone.gemfire.pdx.internal.PdxInstanceImpl cannot be cast to org.apache.geode.demo.cc.model.Order
I’m using the Spring Data GemFire libraries to read contents of the cluster. The code snippet to read the contents of the Grid follows:
public interface OrderRepository extends GemfireRepository<Order, String>{
Order findByTransactionId(String transactionId);
}
How can I use Spring Data GemFire to convert data read from the GemFire cluster into an Order object?
Note: The data was initially stored in GemFire using SpringXD's gemfire-json-server-module
Still waiting to hear back from the GemFire PDX engineering team, specifically on Region.get(key), but, interestingly enough if you annotate your application domain object with...
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#type")
public class Order ... {
...
}
This works!
Under-the-hood I knew the GemFire JSONFormatter class (see here) used Jackson's API to un/marshal (de/serialize) JSON data to and from PDX.
However, the orderRepository.findOne(ID) and ordersRegion.get(key) still do not function as I would expect. See updated test class below for more details.
Will report back again when I have more information.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = GemFireConfiguration.class)
#SuppressWarnings("unused")
public class JsonToPdxToObjectDataAccessIntegrationTest {
protected static final AtomicLong ID_SEQUENCE = new AtomicLong(0l);
private Order amazon;
private Order bestBuy;
private Order target;
private Order walmart;
#Autowired
private OrderRepository orderRepository;
#Resource(name = "Orders")
private com.gemstone.gemfire.cache.Region<Long, Object> orders;
protected Order createOrder(String name) {
return createOrder(ID_SEQUENCE.incrementAndGet(), name);
}
protected Order createOrder(Long id, String name) {
return new Order(id, name);
}
protected <T> T fromPdx(Object pdxInstance, Class<T> toType) {
try {
if (pdxInstance == null) {
return null;
}
else if (toType.isInstance(pdxInstance)) {
return toType.cast(pdxInstance);
}
else if (pdxInstance instanceof PdxInstance) {
return new ObjectMapper().readValue(JSONFormatter.toJSON(((PdxInstance) pdxInstance)), toType);
}
else {
throw new IllegalArgumentException(String.format("Expected object of type PdxInstance; but was (%1$s)",
pdxInstance.getClass().getName()));
}
}
catch (IOException e) {
throw new RuntimeException(String.format("Failed to convert PDX to object of type (%1$s)", toType), e);
}
}
protected void log(Object value) {
System.out.printf("Object of Type (%1$s) has Value (%2$s)", ObjectUtils.nullSafeClassName(value), value);
}
protected Order put(Order order) {
Object existingOrder = orders.putIfAbsent(order.getTransactionId(), toPdx(order));
return (existingOrder != null ? fromPdx(existingOrder, Order.class) : order);
}
protected PdxInstance toPdx(Object obj) {
try {
return JSONFormatter.fromJSON(new ObjectMapper().writeValueAsString(obj));
}
catch (JsonProcessingException e) {
throw new RuntimeException(String.format("Failed to convert object (%1$s) to JSON", obj), e);
}
}
#Before
public void setup() {
amazon = put(createOrder("Amazon Order"));
bestBuy = put(createOrder("BestBuy Order"));
target = put(createOrder("Target Order"));
walmart = put(createOrder("Wal-Mart Order"));
}
#Test
public void regionGet() {
assertThat((Order) orders.get(amazon.getTransactionId()), is(equalTo(amazon)));
}
#Test
public void repositoryFindOneMethod() {
log(orderRepository.findOne(target.getTransactionId()));
assertThat(orderRepository.findOne(target.getTransactionId()), is(equalTo(target)));
}
#Test
public void repositoryQueryMethod() {
assertThat(orderRepository.findByTransactionId(amazon.getTransactionId()), is(equalTo(amazon)));
assertThat(orderRepository.findByTransactionId(bestBuy.getTransactionId()), is(equalTo(bestBuy)));
assertThat(orderRepository.findByTransactionId(target.getTransactionId()), is(equalTo(target)));
assertThat(orderRepository.findByTransactionId(walmart.getTransactionId()), is(equalTo(walmart)));
}
#Region("Orders")
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#type")
public static class Order implements PdxSerializable {
protected static final OrderPdxSerializer pdxSerializer = new OrderPdxSerializer();
#Id
private Long transactionId;
private String name;
public Order() {
}
public Order(Long transactionId) {
this.transactionId = transactionId;
}
public Order(Long transactionId, String name) {
this.transactionId = transactionId;
this.name = name;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public Long getTransactionId() {
return transactionId;
}
public void setTransactionId(final Long transactionId) {
this.transactionId = transactionId;
}
#Override
public void fromData(PdxReader reader) {
Order order = (Order) pdxSerializer.fromData(Order.class, reader);
if (order != null) {
this.transactionId = order.getTransactionId();
this.name = order.getName();
}
}
#Override
public void toData(PdxWriter writer) {
pdxSerializer.toData(this, writer);
}
#Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Order)) {
return false;
}
Order that = (Order) obj;
return ObjectUtils.nullSafeEquals(this.getTransactionId(), that.getTransactionId());
}
#Override
public int hashCode() {
int hashValue = 17;
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getTransactionId());
return hashValue;
}
#Override
public String toString() {
return String.format("{ #type = %1$s, id = %2$d, name = %3$s }",
getClass().getName(), getTransactionId(), getName());
}
}
public static class OrderPdxSerializer implements PdxSerializer {
#Override
public Object fromData(Class<?> type, PdxReader in) {
if (Order.class.equals(type)) {
return new Order(in.readLong("transactionId"), in.readString("name"));
}
return null;
}
#Override
public boolean toData(Object obj, PdxWriter out) {
if (obj instanceof Order) {
Order order = (Order) obj;
out.writeLong("transactionId", order.getTransactionId());
out.writeString("name", order.getName());
return true;
}
return false;
}
}
public interface OrderRepository extends GemfireRepository<Order, Long> {
Order findByTransactionId(Long transactionId);
}
#Configuration
protected static class GemFireConfiguration {
#Bean
public Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", JsonToPdxToObjectDataAccessIntegrationTest.class.getSimpleName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", "warning");
return gemfireProperties;
}
#Bean
public CacheFactoryBean gemfireCache(Properties gemfireProperties) {
CacheFactoryBean cacheFactoryBean = new CacheFactoryBean();
cacheFactoryBean.setProperties(gemfireProperties);
//cacheFactoryBean.setPdxSerializer(new MappingPdxSerializer());
cacheFactoryBean.setPdxSerializer(new OrderPdxSerializer());
cacheFactoryBean.setPdxReadSerialized(false);
return cacheFactoryBean;
}
#Bean(name = "Orders")
public PartitionedRegionFactoryBean ordersRegion(Cache gemfireCache) {
PartitionedRegionFactoryBean regionFactoryBean = new PartitionedRegionFactoryBean();
regionFactoryBean.setCache(gemfireCache);
regionFactoryBean.setName("Orders");
regionFactoryBean.setPersistent(false);
return regionFactoryBean;
}
#Bean
public GemfireRepositoryFactoryBean orderRepository() {
GemfireRepositoryFactoryBean<OrderRepository, Order, Long> repositoryFactoryBean =
new GemfireRepositoryFactoryBean<>();
repositoryFactoryBean.setRepositoryInterface(OrderRepository.class);
return repositoryFactoryBean;
}
}
}
So, as you are aware, GemFire (and by extension, Apache Geode) stores JSON in PDX format (as a PdxInstance). This is so GemFire can interoperate with many different language-based clients (native C++/C#, web-oriented (JavaScript, Pyhton, Ruby, etc) using the Developer REST API, in addition to Java) and also to be able to use OQL to query the JSON data.
After a bit of experimentation, I am surprised GemFire is not behaving as I would expect. I created an example, self-contained test class (i.e. no Spring XD, of course) that simulates your use case... essentially storing JSON data in GemFire as PDX and then attempting to read the data back out as the Order application domain object type using the Repository abstraction, logical enough.
Given the use of the Repository abstraction and implementation from Spring Data GemFire, the infrastructure will attempt to access the application domain object based on the Repository generic type parameter (in this case "Order" from the "OrderRepository" definition).
However, the data is stored in PDX, so now what?
No matter, Spring Data GemFire provides the MappingPdxSerializer class to convert PDX instances back to application domain objects using the same "mapping meta-data" that the Repository infrastructure uses. Cool, so I plug that in...
#Bean
public CacheFactoryBean gemfireCache(Properties gemfireProperties) {
CacheFactoryBean cacheFactoryBean = new CacheFactoryBean();
cacheFactoryBean.setProperties(gemfireProperties);
cacheFactoryBean.setPdxSerializer(new MappingPdxSerializer());
cacheFactoryBean.setPdxReadSerialized(false);
return cacheFactoryBean;
}
You will also notice, I set the PDX 'read-serialized' property (cacheFactoryBean.setPdxReadSerialized(false);) to false in order to ensure data access operations return the domain object and not the PDX instance.
However, this had no affect on the query method. In fact, it had no affect on the following operations either...
orderRepository.findOne(amazonOrder.getTransactionId());
ordersRegion.get(amazonOrder.getTransactionId());
Both calls returned a PdxInstance. Note, the implementation of OrderRepository.findOne(..) is based on SimpleGemfireRepository.findOne(key), which uses GemfireTemplate.get(key), which just performs Region.get(key), and so is effectively the same as (ordersRegion.get(amazonOrder.getTransactionId();). The outcome should not be, especially with Region.get() and read-serialized set to false.
With the OQL query (SELECT * FROM /Orders WHERE transactionId = $1) generated from the findByTransactionId(String id), the Repository infrastructure has a bit less control over what the GemFire query engine will return based on what the caller (OrderRepository) expects (based on the generic type parameter), so running OQL statements could potentially behave differently than direct Region access using get.
Next, I went onto try modifying the Order type to implement PdxSerializable, to handle the conversion during data access operations (direct Region access with get, OQL, or otherwise). This had no affect.
So, I tried to implement a custom PdxSerializer for Order objects. This had no affect either.
The only thing I can conclude at this point is something is getting lost in translation between Order -> JSON -> PDX and then from PDX -> Order. Seemingly, GemFire needs additional type meta-data required by PDX (something like #JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#type") in the JSON data that PDXFormatter recognizes, though I am not certain it does.
Note, in my test class, I used Jackson's ObjectMapper to serialize the Order to JSON and then GemFire's JSONFormatter to serialize the JSON to PDX, which I suspect Spring XD is doing similarly under-the-hood. In fact, Spring XD uses Spring Data GemFire and is most likely using the JSON Region Auto Proxy support. That is exactly what SDG's JSONRegionAdvice object does (see here).
Anyway, I have an inquiry out to the rest of the GemFire engineering team. There are also things that could be done in Spring Data GemFire to ensure the PDX data is converted, such as making use of the MappingPdxSerializer directly to convert the data automatically on behalf of the caller if the data is indeed of type PdxInstance. Similar to how JSON Region Auto Proxying works, you could write AOP interceptor for the Orders Region to automagicaly convert PDX to an Order.
Though, I don't think any of this should be necessary as GemFire should be doing the right thing in this case. Sorry I don't have a better answer right now. Let's see what I find out.
Cheers and stay tuned!
See subsequent post for test code.

How can I add a Dependency that can be used as type parameter 'TImpl' for Castle Windsor?

I was getting this runtime exception with a particular URL:
"Missing dependency. Component NRBQ.Web.Controllers.DeliveryController has a dependency on SeaStore.Data.Legacy.Interfaces.INRBQDeliveryRepository, which could not be resolved. Make sure the dependency is correctly registered in the container as a service, or provided as inline argument."
ExceptionType: "Castle.MicroKernel.Resolvers.DependencyResolverException"
...so I added this code (based on existing code that works) to the IOC class:
_container.Register
(Component
.For<INRBQDeliveryRepository>()
.ImplementedBy<DeliveryController>()
.LifeStyle.Transient);
In some context:
private static Castle.Windsor.IWindsorContainer _container;
_container = new Castle.Windsor.WindsorContainer();
_container.AddFacility<Castle.Facilities.FactorySupport.FactorySupportFacility>();
. . .
_container.Register
(Component
.For<INRBQDeliveryRepository>()
.ImplementedBy<DeliveryController>()
.LifeStyle.Transient);
...but that won't even compile; I now get:
The type 'NRBQ.API.Controllers.DeliveryController' cannot be used as type parameter 'TImpl' in the generic type or method 'Castle.MicroKernel.Registration.ComponentRegistration.ImplementedBy()'. There is no implicit reference conversion from 'NRBQ.API.Controllers.DeliveryController' to 'SeaStore.Data.Legacy.Interfaces.INRBQDeliveryRepository'.
I know this is probably a bit myseterious (I'm probably omitting some important details in this question), but I'm not sure what additional clues I should add.
UPDATE
In response to whether DeliveryController implements INRBQDeliveryRepository: Actually, there are three controller-type things; a user-facing one in NRBQ.Web:
public class DeliveryController : ApiController
{
private readonly INRBQDeliveryRepository _deliveryRepository;
public DeliveryController(INRBQDeliveryRepository deliveryRepository)
{
if (deliveryRepository == null)
{
throw new ArgumentNullException("DeliveriesController");
}
_deliveryRepository = deliveryRepository;
}
[Route("api/Deliveries/Count")]
public int GetCountOfDeliveryRecords()
{
return _deliveryRepository.GetCount();
}
. . .
...then a middle one in NRBQ.Client:
namespace NRBQ.Client
{
public class RESTNRBQDelivery : INRBQDelivery
{
INRBQClientSettings NRBQClientSettings;
IRESTAPIClient RESTAPIClient;
public RESTNRBQDelivery(IRESTAPIClient RESTAPIClient, INRBQClientSettings NRBQClientSettings)
{
this.NRBQClientSettings = NRBQClientSettings;
this.RESTAPIClient = RESTAPIClient;
}
public RESTNRBQDelivery(IRESTAPIClient RESTAPIClient, INRBQClientSettings NRBQClientSettings, AuthenticationHeaderValue AuthHeader)
{
this.NRBQClientSettings = NRBQClientSettings;
this.RESTAPIClient = RESTAPIClient;
this.RESTAPIClient.AuthHeader = AuthHeader;
}
public int GetCount()
{
throw new NotImplementedException(); //TODO: Implement
}
....and finally the one that really does the behind-the-scenes heavy lifting, in NRBQ.API:
namespace NRBQ.API.Controllers
{
public class DeliveryController : ApiController
{
ILogger Logger;
INRBQService NRBQService;
public DeliveryController(ILogger Logger, INRBQService NRBQService)
{
this.NRBQService = NRBQService;
this.Logger = Logger;
}
[HttpGet]
[Route("api/Deliveries/Count")]
public int GetCountOfDeliveryRecords()
{
//return _deliveryRepository.GetCount();
return NRBQService.GetNRBQEntity();
}
That last call refers to here:
public int GetNRBQEntity()
{
return 17; // Bogus val for now
}
This all is my best attempt at copying existing test/sample code, but I admit that my head is swimming and I don't really grok what's happening (how and why).
UPDATE 2
Although I'm pretty sure it's still not quite a Nathan's hot dog (it seems to chase itself all over creation, calling first this abstraction, then that one, then another, then back to the first, then another, then back to the second, etc. etc. ad infinitum ad nauseum advillium), adding this:
container.Register
(Component
.For<INRBQDelivery>()
.ImplementedBy<RESTNRBQDelivery>()
.LifeStyle.Transient);
...in context:
public class NRBQClientInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register
(Component
.For<SeaStore.Common.HTTP.IRESTAPIClient>()
.ImplementedBy<SeaStore.Common.HTTP.WebAPIClient>()
.LifeStyle.Transient);
container.Register
(Component
.For<INRBQClient>()
.ImplementedBy<RESTNRBQClient>()
.LifeStyle.Transient);
container.Register
(Component
.For<INRBQDelivery>()
.ImplementedBy<RESTNRBQDelivery>()
.LifeStyle.Transient);
}
}
...got rid of the err msg and returned the fake result I was expecting (or, rather, hoping for, not really expecting).
In your IWindsorInstaller unit, register the interface and its implementer:
container.Register
(Component
.For<INRBQDelivery>()
.ImplementedBy<RESTNRBQDelivery>()
.LifeStyle.Transient);

How to invoke TraceListener.Write(object) through TraceSource

We have a custom TraceListener implementation which only logs when a specific object (LogMessage) is received. This all works well when using directly with the Trace.Write(object) method.
Due to Performance reason, I want to separate the Listener, so all non-relevant Trace messages are not passed to the listener. Therefore I created a specific TraceSource whith only this listener attached.
Now I struggle to pass my custom log object (LogMessage) to the listener using the TraceSource. The TraceSource.TraceData(TraceEventType, int, object) always invokes the TraceListener.Write(string) method, not the TraceListener.Write(object) method.
Is there any way I can pass the custom object to the Listener using the TraceSource?
Sample code:
using System.Diagnostics;
namespace Sample
{
public class LogMessage
{
public byte[] Data { get; set; }
//...
}
public class Sample
{
public void Foo()
{
var ts = new TraceSource("Test");
var lm = new LogMessage();
//lm.Data = ...;
//this works: calls the Write(object) method in listener
Trace.Write(lm);
//this doesn't work: calls the Write(string) method in listener
ts.TraceData(TraceEventType.Information, 0, lm);
}
}
public class MyListener : TraceListener
{
public override void Write(string message)
{
//not in use
}
public override void WriteLine(string message)
{
//not in use
}
public sealed override void Write(object o)
{
if (o is LogMessage)
{
//do someting with the LogMessage
}
}
}
}
Thanks
Thomas
maybe it's too late for an answer but anyway :
By using a tool like JustDecompile you can easily see that TraceSource.TraceData uses TraceListener.TraceData method which itself basically calls WriteLine with object.ToString() for message.
So you'll have to override the ToString method for your class LogMessage in order to do as you want.

Access full page source in WatiN

I am looking for a way to pass my webpages through a DTD validator as part of my WatiN tests, but I haven't found a clean way to access the raw HTML yet. Is there a built-in way to do it?
I think I could access the property IE.InternetExplorer and QueryInterface for the IPersistStreamInit interface and serialize the document to an IStream, but it seems like a lot of work for what I guess must be a fairly common task.
Am I missing something obvious in WatiN? Or can someone think of a better solution than the one I outlined above? That solution is very IE specific after all.
Here is how you access the source code:
browser.ActiveElement.Parent.OuterHtml
string html = browser.Body.Parent.OuterHtml;
There doesn't appear to be a better way. I filed a feature request and submitted a patch at WatiN's sourceforge tracker instead.
Thought to drop some lines to help anyone struggling out there to get the pristine HTML source of a web page via WatiN without, however, patching WatiN - just as a matter of taste.
So capitalizing on Johan Levin's patch I bolted together the following. Be safe and hope you find it useful.
private static TextVariant GetWebPageSource(IE browser)
{
IHTMLDocument2 htmlDocument = ((IEDocument)(browser.DomContainer.NativeDocument)).HtmlDocument;
Encoding encoding = Encoding.GetEncoding(htmlDocument.charset);
IPersistStreamInit persistStream = (IPersistStreamInit)htmlDocument;
MinimalIStream stream = new MinimalIStream();
persistStream.Save(stream, false);
return new TextVariant(encoding.GetString(stream.ToArray()));
}
[Guid("7FD52380-4E07-101B-AE2D-08002B2EC713")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPersistStreamInit
{
void GetClassID(out Guid pClassID);
int IsDirty();
void Load(IStream pStm);
void Save(IStream pStm, bool fClearDirty);
void GetSizeMax(out long pcbSize);
void InitNew();
}
// http://stackoverflow.com/questions/6601355/passing-an-fstream-or-equivalent-from-c-to-c-through-cli
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class MinimalIStream : MemoryStream, IStream
{
public MinimalIStream() { }
public MinimalIStream(byte[] data) : base(data) { }
#region IStream Members
public void Write(byte[] pv, int cb, IntPtr pcbWritten)
{
base.Write(pv, 0, cb);
if (pcbWritten != IntPtr.Zero)
Marshal.WriteInt64(pcbWritten, (long)cb);
}
public void Stat(out STATSTG pstatstg, int grfStatFlag)
{
pstatstg = new STATSTG();
pstatstg.cbSize = base.Length;
}
public void Read(byte[] pv, int cb, IntPtr pcbRead)
{
long bytes_read = base.Read(pv, 0, cb);
if (pcbRead != IntPtr.Zero) Marshal.WriteInt64(pcbRead, bytes_read);
}
public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
{
long pos = base.Seek(dlibMove, (SeekOrigin)dwOrigin);
if (plibNewPosition != IntPtr.Zero) Marshal.WriteInt64(plibNewPosition, pos);
}
public void Clone(out IStream ppstm)
{
ppstm = null;
}
public void Commit(int grfCommitFlags)
{
}
public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
{
}
public void LockRegion(long libOffset, long cb, int dwLockType)
{
}
public void SetSize(long libNewSize)
{
}
public void Revert()
{
}
public void UnlockRegion(long libOffset, long cb, int dwLockType)
{
}
#endregion
}
I found:
browser.ActiveElement.Parent.OuterHtml
will not always get everything, as is dependant on your 'ActiveElement', Therefore:
browser.Body.Parent.OuterHtml
seems to work better. (browser being your instance of IE)
Though I believe Johan Levin is correct in saying the DOM is serialised back to text format.
Therefore would it not be easier to just fetch the document by it's URL (without using WatiN) to validate it.

Resources