I am trying to solve an issue with one of my Smart Device projects (.NET CF 3.5 on Windows Mobile 6.5 Device).
The code tries to make webservice calls continuously to get some data and use it in the form. During the usage, for a particular case is an ObjectDisposedException thrown and the application crashes. The stacktrace is
System.ObjectDisposedException was unhandled
Message="ObjectDisposedException"
ObjectName=""
StackTrace:
at System.Threading.Timer.throwIfDisposed()
at System.Threading.Timer.Change(UInt32 dueTime, UInt32 period)
at System.Threading.Timer.Change(Int32 dueTime, Int32 period)
at System.Net.HttpWebRequest.startReadWriteTimer()
at System.Net.HttpWebRequest.ConnectionClient.Read(Byte[] data, Int32 offset, Int32 length)
at System.Net.HttpReadStream.NetworkRead(Byte[] data, Int32 offset, Int32 length)
at System.Net.ChunkedReadStream.fillBuffer()
at System.Net.ChunkedReadStream.getLine()
at System.Net.ChunkedReadStream.doRead(Byte[] data, Int32 offset, Int32 length)
at System.Net.HttpReadStream.ReadToDrain(Byte[] buffer, Int32 offset, Int32 length)
at System.Net.HttpReadStream.doClose()
at System.Net.HttpReadStream.Finalize()
I have read many blogs and forums, including this, and the solution suggested seems to be to close the request stream and the request, before getting the response.
requestStream = webRequest.GetRequestStream();
requestStream.Close(); // WE NEED THIS LINE in order to avoid the ObjectDisposedException.
But this does not help my situation. If the requestStream is closed before writing to the data to the stream then it does not do anything. If I close after getting the response, then it throws InvalidOperationException.
Following is my code:
Reference.cs
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="ProductResolveServiceSOAP11Binding", Namespace="urn:ProductResolveService")]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Exception))]
public partial class ProductResolveService : System.Web.Services.Protocols.SoapHttpClientProtocol {
/// <remarks/>
public ProductResolveService() {
this.Url = "http://172.26.37.115:8080/axis/services/ProductResolveService";
}
/// <remarks/>
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("urn:getResolvedEpcs", RequestNamespace="http://services.axis.oatsystems.com", ResponseNamespace="http://services.axis.oatsystems.com", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("return", IsNullable=true)]
public ResolvedProductList getResolvedEpcs([System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] EpcToResolve message) {
object[] results = this.Invoke("getResolvedEpcs", new object[] {
message});
return ((ResolvedProductList)(results[0]));
}
/// <remarks/>
public System.IAsyncResult BegingetResolvedEpcs(EpcToResolve message, System.AsyncCallback callback, object asyncState) {
return this.BeginInvoke("getResolvedEpcs", new object[] {
message}, callback, asyncState);
}
/// <remarks/>
public ResolvedProductList EndgetResolvedEpcs(System.IAsyncResult asyncResult) {
object[] results = this.EndInvoke(asyncResult);
return ((ResolvedProductList)(results[0]));
}
}
Form1.cs
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Web.Services.Protocols;
using System.Windows.Forms;
using NFEHandlingProject.StatusService;
using System.IO;
using MVProductResolveService;
namespace NFEHandlingProject
{
public partial class Form1 : Form
{
private Thread resolveThread;
int counter = 0;
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
if (resolveThread == null)
{
this.BeginInvoke((Action)delegate { lstbxStatusMsgs.Items.Add("Resolve Product: Creating Thread"); lstbxStatusMsgs.SelectedIndex = lstbxStatusMsgs.Items.Count - 1; });
resolveThread = new Thread(new ThreadStart(GetEpcProductMapping));
resolveThread.IsBackground = true;
resolveThread.Priority = ThreadPriority.BelowNormal;
resolveThread.Start();
}
}
object syncRoot2 = new object();
bool resolving = false;
private void GetEpcProductMapping()
{
lock (syncRoot2)
{
if (resolving)
{
return;
}
resolving = true;
}
while (resolving)
{
using (ProductResolveService2 productResolveService = new ProductResolveService2())
{
EpcToResolve epcToResolve = null;
try
{
this.BeginInvoke((Action)delegate { lstbxStatusMsgs.Items.Add("Resolve Product: Resolving..."); lstbxStatusMsgs.SelectedIndex = lstbxStatusMsgs.Items.Count - 1; });
productResolveService.Url = "http://172.26.37.115:8080/axis/services/ProductResolveService?wsdl";
productResolveService.Timeout = 60000;
// The input object that is sent to xpress
epcToResolve = new EpcToResolve();
string epcBase = "3410402AEA0000000000";
int baseDec = Convert.ToInt32("1000", 16);
// Creating the input of epc's baed on the ResolveBatchSize and number epcs's that needs to be resolved at xpress
string[] epcs = new string[1];
for (int i = 0; i < 1; i++)
{
int epcDec = baseDec + i;
epcs[i] = epcBase + epcDec.ToString("X");
}
// setting the epc list which is the input that is sent to xpress
epcToResolve.epcList = epcs;
//pass the flag to check if say whether the productInformation or just the product_id is resolved
epcToResolve.returnOnlyProductId = false;
//return productResolveService.getResolvedEpcs(epcToResolve);
productResolveService.getResolvedEpcs(epcToResolve);
this.BeginInvoke((Action)delegate { lstbxStatusMsgs.Items.Add("Resolved"); lstbxStatusMsgs.SelectedIndex = lstbxStatusMsgs.Items.Count - 1; });
}
catch (SoapHeaderException)
{
// do nothing
}
catch (SoapException se)
{
this.BeginInvoke((Action)delegate { lstbxStatusMsgs.Items.Add("Problem resolving products at xpress"); lstbxStatusMsgs.SelectedIndex = lstbxStatusMsgs.Items.Count - 1; });
}
catch (WebException we)
{
// get the reason for the exception
WebExceptionStatus status = we.Status;
String description = we.Message;
WebResponse response = we.Response;
if (response != null)
{
Stream respStream = response.GetResponseStream();
if (respStream != null)
{
respStream.Close();
respStream.Dispose();
respStream = null;
}
// close the response
response.Close();
response = null;
}
// Case when there is no connectivity. Just display an error message to the user to indicate that there is no connectivity.
this.BeginInvoke((Action)delegate { lstbxStatusMsgs.Items.Add("Resolve Product: There is no connectivity to xpress"); lstbxStatusMsgs.SelectedIndex = lstbxStatusMsgs.Items.Count - 1; });
}
catch (ThreadAbortException)
{
// Do nothing. Do not log
}
catch (System.Exception e)
{
this.BeginInvoke((Action)delegate { lstbxStatusMsgs.Items.Add("An exception occured when fetching data from xpress"); lstbxStatusMsgs.SelectedIndex = lstbxStatusMsgs.Items.Count - 1; });
}
try
{
Thread.Sleep(200);
}
catch
{
}
}
}
resolving = false;
}
private void btnStop_Click(object sender, EventArgs e)
{
if (resolveThread != null && resolving)
{
resolveThread.Abort();
resolveThread.Join();
resolveThread = null;
resolving = false;
this.BeginInvoke((Action)delegate { lstbxStatusMsgs.Items.Add("Resolve Product: Stopped Thread"); lstbxStatusMsgs.SelectedIndex = lstbxStatusMsgs.Items.Count - 1; });
}
}
}
}
On clicking on the Start Button in the form, the thread is created and keeping calling the webservice, when the stop is called, the thread is stopped. Repeated start and stop causes the ObjectDisposedException (that is how I reproduced this exception).
Any help on this regard will be highly appreciated, as I have been trying to solve this issue for a few days now.
Thanks
Senthil
This is a pretty old post. However, I wanted to record my answer here for any body who is still looking for an answer.
Two options:
Move to WCF Clients which is much easier and cleaner.
Use the below solution.
public class ExtendedDataImport : DataImport.DataImport
{
private WebRequest webRequest;
private WebResponse webResponse;
/// <summary>
/// This method overrides the generated method and sets parameters so that HTTP 1.0
/// is used (without chunking). If left with default parameters it
/// sometimes fails.
/// </summary>
protected override WebRequest GetWebRequest(Uri uri)
{
webRequest = base.GetWebRequest(uri);
((HttpWebRequest)webRequest).KeepAlive = false;
((HttpWebRequest)webRequest).ProtocolVersion = HttpVersion.Version10;
return webRequest;
}
protected override WebResponse GetWebResponse(WebRequest request)
{
webResponse = base.GetWebResponse(request);
return webResponse;
}
public void Close()
{
if (webResponse != null)
{
Stream responseStream = webResponse.GetResponseStream();
responseStream.Close();
responseStream.Dispose();
responseStream = null;
webResponse.Close();
webResponse = null;
}
if (webRequest != null)
{
// Aborting the WebRequest, cleans up the webrequest and
// stops the timer which causes the ObjectDisposedException
try
{
webRequest.Abort();
webRequest = null;
}
catch (ObjectDisposedException ex)
{
// Ignoring the object disposed exception as mentioned in the follwoing link
//http://social.msdn.microsoft.com/Forums/en/netfxcompact/thread/8f21514c-9b7c-40d3-96c9-794c0dc167fe
}
}
}
protected override void Dispose(bool disposing)
{
Close();
base.Dispose(disposing);
}
}
Related
I have a Magellan scanner/scale. It is connected to my pc through rs232. When i send the command "S11" on ComTestSerial programm, i receive the weight. However, with my vb.net code i cannot receive a response. As a result i get a TimeoutException.
The file that sends the command:
Dim yy() As Byte = System.Text.Encoding.ASCII.GetBytes("S11" & vbLf)
Me.Port.Send(yy)
Dim input = Me.Port.Receive
Return Me.ExtractMeasurement(input)
The file that writes and reads from serialport:
public void Send(byte b)
{
byte[] bytes = new byte[1] { b };
this.Send(bytes);
}
public void Send(byte[] bytes)
{
this.Send(bytes, 0, bytes.Length);
}
public void Send(byte[] bytes, int offset, int count)
{
this._port.Write(bytes, offset, count);
}
public byte[] Receive()
{
int attempts = 1;
Boolean dataReceived;
try
{
while (!this.DataReceived && this._port.BytesToRead == 0 && attempts < 15)
{
System.Threading.Thread.Sleep(100);
attempts++;
}
}
finally
{
dataReceived = this.DataReceived;
this.DataReceived = false;
}
if (!dataReceived && this._port.BytesToRead == 0) throw new TimeoutException();
byte[] bytes = new byte[this._port.BytesToRead];
this._port.Read(bytes, 0, bytes.Length);
return bytes;
}
I can't understand why BytesToRead and BytesToWrite stays 0 after this._port.Write(bytes, offset, count);
Here is the serialportconfig.xml file
<PortName>COM3:</PortName>
<BaudRate>Baud_9600</BaudRate>
<DataBits>Eight</DataBits>
<Parity>None</Parity>
<StopBits>One</StopBits>
<FlowCtrl>CtsRts</FlowCtrl>
Update: i figure out that if i send vbCr instead of vbLf i sometimes get the right response back. But the problem is SOMETIMES. I sometimes get a TimeoutException and sometimes get the response. I am using an adaptor from RS232 to usb. Could this be the problem?
Here is all the code related to the serial:
public class SerialPortAdapter
{
#region Private Members
private System.IO.Ports.SerialPort _port;
private Object _dataReceivedLock = new Object();
private Boolean _dataReceived;
#endregion
#region Constructor/Destructor
public SerialPortAdapter(SerialCnfg config)
{
if (string.IsNullOrEmpty(config.PortName))
{
this._port = new System.IO.Ports.SerialPort();
}
else
{
string portName = config.PortName.TrimEnd(':');
this._port = new System.IO.Ports.SerialPort(portName);
}
this._port.WriteTimeout = 2000;
this._port.ReadTimeout = 2000;
this._port.SetBaudRate(config.BaudRate);
this._port.SetDataBits(config.DataBits);
this._port.SetStopBits(config.StopBits);
this._port.SetHandshake(config.FlowCtrl);
this._port.SetParity(config.Parity);
}
~SerialPortAdapter()
{
this.Close();
this._port = null;
}
#endregion
#region Public Properties
public Boolean IsOpen
{
get { return this._port.IsOpen; }
}
public System.Text.Encoding Encoding
{
get { return this._port.Encoding; }
set { this._port.Encoding = value; }
}
#endregion
#region Public Methods
public void Open()
{
if (this.IsOpen) return;
this.DataReceived = false;
this.AttachPortHandlers();
this._port.Open();
}
public void Close()
{
if (!this.IsOpen) return;
this._port.Close();
this.DetachPortHandlers();
this.DataReceived = false;
}
public void Send(byte b)
{
byte[] bytes = new byte[1] { b };
this.Send(bytes);
}
public void Send(byte[] bytes)
{
this.Send(bytes, 0, bytes.Length);
}
public void Send(byte[] bytes, int offset, int count)
{
this._port.Write(bytes, offset, count);
}
public byte[] Receive()
{
int attempts = 1;
Boolean dataReceived;
try
{
while (!this.DataReceived && this._port.BytesToRead == 0 && attempts < 15)
{
System.Threading.Thread.Sleep(100);
attempts++;
}
}
finally
{
dataReceived = this.DataReceived;
this.DataReceived = false;
}
if (!dataReceived && this._port.BytesToRead == 0) throw new TimeoutException();
byte[] bytes = new byte[this._port.BytesToRead];
this._port.Read(bytes, 0, bytes.Length);
return bytes;
}
#endregion
#region Private Properties
private Boolean DataReceived
{
get
{
lock (this._dataReceivedLock)
{
return this._dataReceived;
}
}
set
{
lock (this._dataReceivedLock)
{
this._dataReceived = value;
}
}
}
#endregion
#region Initialization/Finalization
private void AttachPortHandlers()
{
this._port.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(this.OnDataReceived);
}
private void DetachPortHandlers()
{
this._port.DataReceived -= new System.IO.Ports.SerialDataReceivedEventHandler(this.OnDataReceived);
}
#endregion
#region Event Handlers
private void OnDataReceived(Object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
this.DataReceived = true;
}
#endregion
}
Based on the code you posted, you are attempting to handle your own timeout exception. SerialPort class has its own built in timeout (ie, ReadTimeout, WriteTimeout) which you set in the constructor. Therefore you do not need the other methods to handle the timeout as it would be redundant. Moreover, stay away from the System.Threading.Sleep method as it can be a huge waste of resources Why is Thread.Sleep so harmful.
I suggest that you refactor your code a bit to get rid of the self imposed Throw TimeoutException.
Here is just a suggestion:
public byte[] Receive()
{
try
{
byte[] bytes = new byte[]{};
while(_port.BytesToRead > 0)
{
bytes = new byte[this._port.BytesToRead];
this._port.Read(bytes, 0, bytes.Length);
}
}
catch (TimeoutException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
this.DataReceived = false;
}
return bytes;
}
It appears that Magellan can work if you resend the command to request the weight(S11). So the solution for me was whenever i have _port.bytesToRead=0 after this._port.Write(bytes, offset, count) , then i resend the command S11. Eventually, it will response the right result.
i have problem with PushStreamContent in asp.net core.
It display video on the website but my problem is that it will buffer whole file and then play it when my goal is to buffer small part of it and play on the website. Code i have:
My endpoint for playing video in browser
public IActionResult Play(string file)
{
var fileName = "C:\\repo\\trailer1.mp4";
var video = new VideoStream(fileName);
var response = new PushStreamContent(video.WriteToStream, new MediaTypeHeaderValue("video/mp4"))
{
};
var objectResult = new ObjectResult(response);
objectResult.ContentTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("video/mp4"));
return objectResult;
}
Ive got VideoStreamClass to help with displaying video
public class VideoStream
{
private readonly string _filename;
public VideoStream(string filename)
{
_filename = #"C:\\repo\\trailer1.mp4";
}
public async Task WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
{
try
{
var buffer = new byte[65536];
using (var video = File.Open(_filename, FileMode.Open, FileAccess.Read))
{
var length = (int)video.Length;
var bytesRead = 1;
while (length > 0 && bytesRead > 0)
{
bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length));
await outputStream.WriteAsync(buffer, 0, bytesRead);
await outputStream.FlushAsync();
length -= bytesRead;
}
}
}
catch (Exception)
{ return; }
finally
{
outputStream.Dispose();
}
}
}
And here is my VideoOutputFormatter added to bootstraper
public class VideoOutputFormatter : IOutputFormatter
{
public bool CanWriteResult(OutputFormatterCanWriteContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (context.Object is PushStreamContent)
return true;
return false;
}
public async Task WriteAsync(OutputFormatterWriteContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
using (var stream = ((PushStreamContent)context.Object))
{
var response = context.HttpContext.Response;
if (context.ContentType != null)
{
response.ContentType = context.ContentType.ToString();
}
await stream.CopyToAsync(response.Body);
}
}
}
I've tried to add atributes to controller "UseBufferedOutputStream" and "UseBufferedInputStream" setted to false but this still dosent work for me
ObjectResult is intended for holding an in-memory object as a response. If you want to return an existing file, then PhysicalFileResult is probably your best bet.
If you're interested in PushStreamContent, I believe the one you're using is for HttpClient, not ASP.NET. If you want a PushStreamContent equivalent, I have a FileCallbackResult that would work once it's updated for the latest .NET Core.
I am transforming an Image into pdf for test purposes.
To ensure that the Image is compatible with the printing process later on, I'm running a quick test print during the upload.
I'm creating a simple Test-PDF with a transformer. When I try to print an image with an incompatible format, the ImageManager of the transformer throws an ImageException, starting in the preloadImage() function:
public ImageInfo preloadImage(String uri, Source src)
throws ImageException, IOException {
Iterator iter = registry.getPreloaderIterator();
while (iter.hasNext()) {
ImagePreloader preloader = (ImagePreloader)iter.next();
ImageInfo info = preloader.preloadImage(uri, src, imageContext);
if (info != null) {
return info;
}
}
throw new ImageException("The file format is not supported. No ImagePreloader found for "
+ uri);
}
throwing it to:
public ImageInfo needImageInfo(String uri, ImageSessionContext session, ImageManager manager)
throws ImageException, IOException {
//Fetch unique version of the URI and use it for synchronization so we have some sort of
//"row-level" locking instead of "table-level" locking (to use a database analogy).
//The fine locking strategy is necessary since preloading an image is a potentially long
//operation.
if (isInvalidURI(uri)) {
throw new FileNotFoundException("Image not found: " + uri);
}
String lockURI = uri.intern();
synchronized (lockURI) {
ImageInfo info = getImageInfo(uri);
if (info == null) {
try {
Source src = session.needSource(uri);
if (src == null) {
registerInvalidURI(uri);
throw new FileNotFoundException("Image not found: " + uri);
}
info = manager.preloadImage(uri, src);
session.returnSource(uri, src);
} catch (IOException ioe) {
registerInvalidURI(uri);
throw ioe;
} catch (ImageException e) {
registerInvalidURI(uri);
throw e;
}
putImageInfo(info);
}
return info;
}
}
throwing it to :
public ImageInfo getImageInfo(String uri, ImageSessionContext session)
throws ImageException, IOException {
if (getCache() != null) {
return getCache().needImageInfo(uri, session, this);
} else {
return preloadImage(uri, session);
}
}
Finally it gets caught and logged in the ExternalGraphic.class:
/** {#inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
src = pList.get(PR_SRC).getString();
//Additional processing: obtain the image's intrinsic size and baseline information
url = URISpecification.getURL(src);
FOUserAgent userAgent = getUserAgent();
ImageManager manager = userAgent.getFactory().getImageManager();
ImageInfo info = null;
try {
info = manager.getImageInfo(url, userAgent.getImageSessionContext());
} catch (ImageException e) {
ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
getUserAgent().getEventBroadcaster());
eventProducer.imageError(this, url, e, getLocator());
} catch (FileNotFoundException fnfe) {
ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
getUserAgent().getEventBroadcaster());
eventProducer.imageNotFound(this, url, fnfe, getLocator());
} catch (IOException ioe) {
ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
getUserAgent().getEventBroadcaster());
eventProducer.imageIOError(this, url, ioe, getLocator());
}
if (info != null) {
this.intrinsicWidth = info.getSize().getWidthMpt();
this.intrinsicHeight = info.getSize().getHeightMpt();
int baseline = info.getSize().getBaselinePositionFromBottom();
if (baseline != 0) {
this.intrinsicAlignmentAdjust
= FixedLength.getInstance(-baseline);
}
}
}
That way it isn't accessible for me in my code that uses the transformer.
I tried to use a custom ErrorListener, but the transformer only registers fatalErrors to the ErrorListener.
Is there any way to access the Exception and handle it myself without changing the code of the library?
It was easier than I thought. Before I call the transformation I register a costum EventListener to the User Agent of the Fop I'm using. This Listener just stores the Information what kind of Event was triggered, so I can throw an Exception if it's an ImageError.
My Listener:
import org.apache.fop.events.Event;
import org.apache.fop.events.EventListener;
public class ImageErrorListener implements EventListener
{
private String eventKey = "";
private boolean imageError = false;
#Override
public void processEvent(Event event)
{
eventKey = event.getEventKey();
if(eventKey.equals("imageError")) {
imageError = true;
}
}
public String getEventKey()
{
return eventKey;
}
public void setEventKey(String eventKey)
{
this.eventKey = eventKey;
}
public boolean isImageError()
{
return imageError;
}
public void setImageError(boolean imageError)
{
this.imageError = imageError;
}
}
Use of the Listener:
// Start XSLT transformation and FOP processing
ImageErrorListener imageListener = new ImageErrorListener();
fop.getUserAgent().getEventBroadcaster().addEventListener(imageListener);
if (res != null)
{
transformer.transform(xmlDomStreamSource, res);
}
if(imageListener.isImageError()) {
throw new ImageException("");
}
fop is of the type Fop ,xmlDomStreamSource ist the xml-Source I want to transform and res is my SAXResult.
I'm wondering as to how I'd go aroung converting this Begin-End-Async approach to the newer TPL syntax:
public override IAsyncResult BeginRegisterClaimant(ClaimantSex claimantSex, AgeGroup ageGroup, AsyncCallback callback, object state)
{
AsyncResult<string> asyncResult = new AsyncResult<string>(callback, state);
InitManagementProxy();
m_management.RegisterClaimantCompleted += RegisterClaimantCallbackInternal;
m_management.RegisterClaimantAsync(m_configurationID, (PowWow.Service.ClaimantSex)claimantSex, (PowWow.Service.AgeGroup)ageGroup, new object[]
{ this, asyncResult
});
return asyncResult;
}
public override string EndRegisterClaimant(IAsyncResult asyncResult)
{
if (!(asyncResult is AsyncResult<string>))
{
throw new Exception("Invalid IAsyncResult object");
}
return ((AsyncResult<string>)asyncResult).Close();
}
private void InitManagementProxy()
{
WSHttpBinding binding;
if (m_management != null)
{
return;
}
binding = new WSHttpBinding(SecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
m_management = new PowWow.Service.VerifierContractClient(binding, new EndpointAddress(m_managementUri));
m_management.ClientCredentials.UserName.UserName = m_userName;
m_management.ClientCredentials.UserName.Password = m_password;
}
private static void RegisterClaimantCallbackInternal(object senderObject, PowWow.Service.RegisterClaimantCompletedEventArgs eventArgs)
{
PowWowVerifier powWowVerifier = (PowWowVerifier)((Object[])eventArgs.UserState)[0];
powWowVerifier.m_management.RegisterClaimantCompleted -= RegisterClaimantCallbackInternal;
powWowVerifier.m_management.Close();
powWowVerifier.m_management = null;
if (eventArgs.Error != null)
{
((AsyncResult<string>)((Object[])eventArgs.UserState)[1]).OnException(eventArgs.Error);
}
else
{
((AsyncResult<string>)((Object[])eventArgs.UserState)[1]).OnExecuted(eventArgs.Result);
}
}
Thanks!
EDIT
ATM, cancellation is not required. Progress reporting maybe required but not a priority.
The simplest way would be to wrap your existing code in a TaskCompletionSource and return it's Task - this will then fit in with the new async/await model coming in C# 5
I'm trying to print XPS documents from a windows service on the .net framework. Since Microsoft does not support printing by using System.Drawing.Printing nor by using System.Printing (WPF), I'm using the native XPSPrint API.
This is suggested to me by Aspose in http://www.aspose.com/documentation/.net-components/aspose.words-for-.net/howto-print-a-document-on-a-server-via-the-xpsprint-api.html.
When I try to print an XPS document from a windows service, the result contains strange characters instead of the text I want.
I tried with different printers (including virtual printers like for instance PDFCreator), different users and user-privileges for the service, different xps generators (aspose, word 2007, word 2010), different platforms (windows 7, windows 2008 R2) but all have the same result.
Does anybody knows how to solve this? Any help would be appreciated!
For those who want to try it, I shared some files via:
https://docs.google.com/leaf?id=0B4J93Ly5WzQKNWU2ZjM0MDYtMjFiMi00NzM0LTg4MTgtYjVlNDA5NWQyMTc3&hl=nl
document.xps: the XPS document to print
document_printed_to_pdfcreator.pdf: the printed document that demonstrates what is going wrong
XpsPrintTest.zip: a sample VS2010 solution with the sample code
The sample code for the managed windows service is:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
namespace PrintXpsService
{
public partial class XpsPrintService : ServiceBase
{
// Change name of printer here
private String f_printerName = "PDFCreator";
// path to some file where logging is done
private String f_logFile = #"C:\temp\testdoc\xps_printing_service_log.txt";
// path to xps file to print
private String f_xpsFile = #"C:\temp\testdoc\document.xps";
public XpsPrintService()
{
InitializeComponent();
}
private void Log(String fmt, params Object[] args)
{
try
{
DateTime now = DateTime.Now;
using (StreamWriter wrt = new StreamWriter(f_logFile, true))
{
wrt.Write("{0} {1} - ", now.ToShortDateString(), now.ToShortTimeString());
wrt.WriteLine(fmt, args);
}
}
catch (Exception ex)
{
}
}
protected override void OnStart(string[] args)
{
// uncomment to allow to connect debugger
//int i = 0;
//while (i == 0)
//{
// if (i == 0)
// {
// Thread.Sleep(1000);
// }
//}
Log("Starting Service");
try
{
Log("Printing xps file {0}", f_xpsFile);
using (Stream stream = new FileStream(f_xpsFile, FileMode.Open, FileAccess.Read))
{
Log("Starting to print on printer {0}", f_printerName);
String jobName = f_xpsFile;
this.Print(stream, jobName);
}
Log("Document printed");
}
catch (Exception ex)
{
Log("Exception during execution: {0}", ex.Message);
Log(" {0}", ex.StackTrace);
Exception inner = ex.InnerException;
while (inner != null)
{
Log("=== Inner Exception: {0}", inner.Message);
Log(" {0}", inner.StackTrace);
inner = inner.InnerException;
}
}
}
protected override void OnStop()
{
}
public void Print(Stream stream, String jobName)
{
String printerName = f_printerName;
IntPtr completionEvent = CreateEvent(IntPtr.Zero, true, false, null);
try
{
IXpsPrintJob job;
IXpsPrintJobStream jobStream;
StartJob(printerName, jobName, completionEvent, out job, out jobStream);
CopyJob(stream, job, jobStream);
WaitForJob(completionEvent, -1);
CheckJobStatus(job);
}
finally
{
if (completionEvent != IntPtr.Zero)
CloseHandle(completionEvent);
}
}
private void StartJob(String printerName,
String jobName, IntPtr completionEvent,
out IXpsPrintJob job,
out IXpsPrintJobStream jobStream)
{
int result = StartXpsPrintJob(printerName, jobName, null, IntPtr.Zero, completionEvent,
null, 0, out job, out jobStream, IntPtr.Zero);
if (result != 0)
throw new Win32Exception(result);
}
private void CopyJob(Stream stream, IXpsPrintJob job, IXpsPrintJobStream jobStream)
{
try
{
byte[] buff = new byte[4096];
while (true)
{
uint read = (uint)stream.Read(buff, 0, buff.Length);
if (read == 0)
break;
uint written;
jobStream.Write(buff, read, out written);
if (read != written)
throw new Exception("Failed to copy data to the print job stream.");
}
// Indicate that the entire document has been copied.
jobStream.Close();
}
catch (Exception)
{
// Cancel the job if we had any trouble submitting it.
job.Cancel();
throw;
}
}
private void WaitForJob(IntPtr completionEvent, int timeout)
{
if (timeout < 0)
timeout = -1;
switch (WaitForSingleObject(completionEvent, timeout))
{
case WAIT_RESULT.WAIT_OBJECT_0:
// Expected result, do nothing.
break;
case WAIT_RESULT.WAIT_TIMEOUT:
// timeout expired
throw new Exception("Timeout expired");
case WAIT_RESULT.WAIT_FAILED:
throw new Exception("Wait for the job to complete failed");
default:
throw new Exception("Unexpected result when waiting for the print job.");
}
}
private void CheckJobStatus(IXpsPrintJob job)
{
XPS_JOB_STATUS jobStatus;
job.GetJobStatus(out jobStatus);
switch (jobStatus.completion)
{
case XPS_JOB_COMPLETION.XPS_JOB_COMPLETED:
// Expected result, do nothing.
break;
case XPS_JOB_COMPLETION.XPS_JOB_IN_PROGRESS:
// expected, do nothing, can occur when printer is paused
break;
case XPS_JOB_COMPLETION.XPS_JOB_FAILED:
throw new Win32Exception(jobStatus.jobStatus);
default:
throw new Exception("Unexpected print job status.");
}
}
[DllImport("XpsPrint.dll", EntryPoint = "StartXpsPrintJob")]
private static extern int StartXpsPrintJob(
[MarshalAs(UnmanagedType.LPWStr)] String printerName,
[MarshalAs(UnmanagedType.LPWStr)] String jobName,
[MarshalAs(UnmanagedType.LPWStr)] String outputFileName,
IntPtr progressEvent, // HANDLE
IntPtr completionEvent, // HANDLE
[MarshalAs(UnmanagedType.LPArray)] byte[] printablePagesOn,
UInt32 printablePagesOnCount,
out IXpsPrintJob xpsPrintJob,
out IXpsPrintJobStream documentStream,
IntPtr printTicketStream); // This is actually "out IXpsPrintJobStream", but we don't use it and just want to pass null, hence IntPtr.
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
[DllImport("Kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern WAIT_RESULT WaitForSingleObject(IntPtr handle, Int32 milliseconds);
[DllImport("Kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
}
/// <summary>
/// This interface definition is HACKED.
///
/// It appears that the IID for IXpsPrintJobStream specified in XpsPrint.h as
/// MIDL_INTERFACE("7a77dc5f-45d6-4dff-9307-d8cb846347ca") is not correct and the RCW cannot return it.
/// But the returned object returns the parent ISequentialStream inteface successfully.
///
/// So the hack is that we obtain the ISequentialStream interface but work with it as
/// with the IXpsPrintJobStream interface.
/// </summary>
[Guid("0C733A30-2A1C-11CE-ADE5-00AA0044773D")] // This is IID of ISequenatialSteam.
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IXpsPrintJobStream
{
// ISequentualStream methods.
void Read([MarshalAs(UnmanagedType.LPArray)] byte[] pv, uint cb, out uint pcbRead);
void Write([MarshalAs(UnmanagedType.LPArray)] byte[] pv, uint cb, out uint pcbWritten);
// IXpsPrintJobStream methods.
void Close();
}
[Guid("5ab89b06-8194-425f-ab3b-d7a96e350161")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IXpsPrintJob
{
void Cancel();
void GetJobStatus(out XPS_JOB_STATUS jobStatus);
}
[StructLayout(LayoutKind.Sequential)]
struct XPS_JOB_STATUS
{
public UInt32 jobId;
public Int32 currentDocument;
public Int32 currentPage;
public Int32 currentPageTotal;
public XPS_JOB_COMPLETION completion;
public Int32 jobStatus; // UInt32
};
enum XPS_JOB_COMPLETION
{
XPS_JOB_IN_PROGRESS = 0,
XPS_JOB_COMPLETED = 1,
XPS_JOB_CANCELLED = 2,
XPS_JOB_FAILED = 3
}
enum WAIT_RESULT
{
WAIT_OBJECT_0 = 0,
WAIT_ABANDONED = 0x80,
WAIT_TIMEOUT = 0x102,
WAIT_FAILED = -1 // 0xFFFFFFFF
}
}
Note: some links for more information:
MS not supporting printing from managed code: http://support.microsoft.com/kb/324565 , http://msdn.microsoft.com/en-us/library/system.drawing.printing.aspx and http://msdn.microsoft.com/en-us/library/bb613549.aspx
XPSPrint API: http://msdn.microsoft.com/en-us/library/dd374565(VS.85).aspx
I talked with microsoft about this issue and we discovered the problem is related to incorrect font substitution in the printer-spooler. When the printer is set to not spool the documents, they are printed correctly, also from a windows service. Otherwise, all fonts, except arial (and maybe some others), are substituted by another font. In the sample I provided, calibri is substituted by wingdings.
So, they acknowledge this to be a bug but at the moment they will not resolve it. It will depend on how many people will suffer from this bug in order for them to decide whether are not they are willing to fix it...