Can we use QZ API in windows service (C# code)

345 views
Skip to first unread message

nirajo...@gmail.com

unread,
Feb 23, 2018, 12:25:37 AM2/23/18
to qz-print
Hello Community,

I want to use QZ API as a stand alone API, which I can consume from my windows service as per scheduling. Can I do that?

I have explored this and found that right now only with HTML Page (browser) I can use this, If somebody help me out then it would be great.

For this If I have to buy the paid version that is fine for me.

Tres Finocchiaro

unread,
Feb 23, 2018, 12:52:53 AM2/23/18
to nirajo...@gmail.com, qz-print
We have a few clients that use QZ Tray from a pure C# perspective (no browser) by simulating the JavaScript calls using C# WebSockets instead.

QZ Industries, LLC does't offer C# consulting as a service, so this would be at the effort of you or your company.  We also provide no guarantee of API compatibility when not using our browser-based API (qz-tray.js).

We do license to each of these clients.  They do still require a subscription for bug fixes, etc to the Java component as long as the issues can be replicated with the browser.

If you require help, it may make more sense to reach out to one of our clients for assistance as they have already written the code.  We can share their information with you privately.

--
You received this message because you are subscribed to the Google Groups "qz-print" group.
To unsubscribe from this group and stop receiving emails from it, send an email to qz-print+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

nirajo...@gmail.com

unread,
Feb 23, 2018, 3:48:23 AM2/23/18
to qz-print
> To unsubscribe from this group and stop receiving emails from it, send an email to qz-print+u...@googlegroups.com.
>
> For more options, visit https://groups.google.com/d/optout.

Hello Tres Finocchiaro

Thank you for your quick update on this. This will definitely help me out in my functionality.
Yes If you can share the details of client or you can confirm with him that they have used "node js" for this functionality. Because using "node js" I can do this, so just want to confirm with them.

Thank you
Niraj

Tres Finocchiaro

unread,
Feb 23, 2018, 12:23:40 PM2/23/18
to nirajo...@gmail.com, qz-print
I've reached out to Martin K.  He is preparing a reply and will be replying directly to this thread so that anyone can learn from his C# efforts.

jmk...@gmail.com

unread,
Feb 25, 2018, 5:53:47 AM2/25/18
to qz-print
On Friday, February 23, 2018 at 6:23:40 PM UTC+1, Tres Finocchiaro wrote:
> I've reached out to Martin K.  He is preparing a reply and will be replying directly to this thread so that anyone can learn from his C# efforts.

Hi Tres, Nirajo,
Indeed we have implemented QZ Tray based solutions in C#, Java and ElectronJS.

I think since you want a nodejs implementation you should be able to use pretty much the JavaScript approach in the official QZ Tray documentation.

Key pointers are; message serialization, message signing (this especially) and the chosen QZ Tray version(or protocol) (i would recommend 2.x).

Serialization : Since QZ Tray uses JSON, objects from nodejs do not need any extra attention from your side.

Message signing: The QZ Tray official documentation provide a signing example in JS but it is important to note that 1.9.x and 2.x differ when it comes to message signing. But this too is clear in the documentation.

Version: 2.x introduces a promise based messaging protocol to QZ Tray (this was not in 1.9.x). But what comes with the promises' approach is the assigning of unique message identifiers to each message you send to QZ Tray. The power of this is that you can implement the message exchange logic based only on these message uid's. This greatly simplified things for us.

Having said that, the key messages for printing documents with a custom protocol implementation should be; (1) One to retrieve available or installed printers on the QZ Tray host. (2) Another to tell QZ Tray which printer is to be used to perform the printing and (3) The one that provides document data to QZ Tray for printing.


QZ Tray connects over web-sockets, and the most basic web-socket connections works with no problem.

My response as you can see is generally descriptive but not specifically pointing to an implementation; this is because i do not know exactly how you would like to implement your solution.

In case, you would like more specific input from me, i am glad to provide it though as by my legal obligations to my current employer, i may not directly implement it for you, but it should be fine though to explain a design which you can yourself implement.

Hope this helps.

Martin

pablo.gabr...@gmail.com

unread,
Jun 30, 2020, 2:52:24 AM6/30/20
to qz-print
Hello, I was wondering about your C# implementation approach, in my company we are currently doing something similar, 
we have created a dotnet Core library that simulates the JavaScript calls using WebSockets, it is purely C#, We also have our dedicated service for Signing the requests, 
the signing works fine, as I have tested it with a simple page app(using the Jscript approach), but the problems come with the signature itself when sending it over C#, every time I try to send a signed request, it throws an error that states: 
qz.auth.Certificate:? Unable to verify signature java.io.IOException: Bad Base64 input character '34' in array position 0, this is only happening when I am making the requests from our C# library. 

Have you ever faced an issue like this with your C# approach?

If you need code examples, I can provide them without problems, I would really appreciate the help. 

Thank you before hand!

Tres Finocchiaro

unread,
Jun 30, 2020, 11:26:37 AM6/30/20
to pablo.gabr...@gmail.com, qz-print
Bad Base64 input character '34' in array position 0, this is only happening when I am making the requests from our C# library. 

I haven't authored a C# wrapper (and we don't officially support this), but I've had a lot of experience with the base64 signatures and have a YouTube tutorial which covers how we came up with our C# signing examples: https://www.youtube.com/watch?v=u3XcvAEWvEI

Anyway, the best way to tackle this is to just read the console logs from QZ Tray: https://qz.io/wiki/faq#console-logs

There's a good chance the base64 is getting serialized (e.g. JSON) or something.  Looking at the data should help understand why it can't be decoded.  Note, it's quite easy to unserialize it using JavaScript (e.g. $.ajax, fetch) but when you're talking directly to the Java websocket, you don't have the same luxuries and it will have to be a plain/text string.

For example:

Good:

"signature": "TWFuIGlzIGRpc..."

Bad:

"signature": { "response": "TWFuIGlzIGRpc..." }



--
You received this message because you are subscribed to the Google Groups "qz-print" group.
To unsubscribe from this group and stop receiving emails from it, send an email to qz-print+u...@googlegroups.com.

Joseph M Kavuma

unread,
Jun 30, 2020, 11:34:37 AM6/30/20
to Tres Finocchiaro, pablo.gabr...@gmail.com, qz-print
Hello Pablo, Tres,
Indeed we have a C# implementation similar to what you are doing.
I was hoping to share it with you today after work but my laptop has crashed today. I have arranged to have it fixed tomorrow, so the moment I have my environment restored, I will share with you the approach we took. 

But Tres is right, it is mostly likely related to serialization.

Kind regards
Martin

You received this message because you are subscribed to a topic in the Google Groups "qz-print" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/qz-print/MmFO6Xo_5aE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to qz-print+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/qz-print/CANQs7dBnE5BQDC9B%2BN1tjWt4HC2oXT0bgAcW-k8_2h%2B490gtNA%40mail.gmail.com.

pablo.gabr...@gmail.com

unread,
Jul 1, 2020, 1:36:47 PM7/1/20
to qz-print
Hi Joseph, thank you very much, I'd really appreciate the help, and, btw, Tres, I think you are right about the serialization thing, here's an example of what I'm doing. 
and the output I am getting. All of this is C# code

Create the hash:
 // request example = {"call":"printers.getDefault","promise":{},"params":null,"timestamp":1593623264176,"uid":null,"signAlgorithm":"SHA256","position":{"x":840,"y":505},"signature":null}

 public static string BuildHash(string request )
        {
            //Encode request to create the message we wil be signing
            using (SHA256 toHash = SHA256.Create())
            {
                byte[] bytes = toHash.ComputeHash(Encoding.UTF8.GetBytes(request)); 
                StringBuilder message = new StringBuilder();
                for (int i = 0; i < bytes.Length; i++)
                {
                    message.Append(bytes[i].ToString("x2"));
                }
                return message.ToString();
            }                
        }

Sign the message (based from QZ's youtube C# signing example):

public async Task<string> Signing(string msgTosign)
        {
            var values = new Dictionary<string, string>
            {
                { "message", msgTosign},
                { "apiKey", localApiKey } //apiKey for internal validation/security
            };
            HttpContent content = new StringContent(values.ToJson(), System.Text.Encoding.UTF8, "application/json");
            var response = await client.PostAsync(signingUrl, content);
            return await response.Content.ReadAsStringAsync();

//current output example: "hPMxT/veLXCMv6FXXtjBRyhNVzYKrjbz0KiVEHhT2xwqPjgOiE0wIGh0jRm3+UAibdMkAfU5TdyIfO5c7Qac8f6gHTk+4TbVkG9y7cOGtMMZ6N3dwvI5m/xgudaQehGJGW1A/6i0VwCq19NnPMp9LPfVjg/y+1b1KkKHkZudWA6taekdqRCvn9AIOCv66kfp48rN5W81XOf9HZkyup6QsgA9G6kkX6ht+PTmOVwJadIiwNh7JelYVQBFKMDaxJW3DHrxBWRdHdvPAWp9VJP5pFWpYaQ9XAa8hbfHQl/XsIHrCIrCPBzQsvqOH5gBR3Dk5AsYZUMByuicLfvKTM1ANA=="
 
}


//include signature into request object 
            var signature = await qzClient.Signing(hashed);            
            defaultPrinter.Signature = signature;
 
// send command to socket:
await ws.SendString(defaultPrinter.ToJson(), cancellationToken);
 

// Send command to socket task:
Task SendString(this ClientWebSocket ws, string defaultPrinter, CancellationToken cancellation)
        {
            var encoded = Encoding.UTF8.GetBytes(
defaultPrinter);
            var buffer = new ArraySegment<byte>(encoded, 0, encoded.Length);
            return ws.SendAsync(buffer, WebSocketMessageType.Text, true, cancellation);
        }

This returns the default printer correctly, but not without throwing the error qz.auth.Certificate:? Unable to verify signature java.io.IOException: Bad Base64 input character '34' in array position 0 at net.sourceforge.iharder.Base64.decode(Unknown Source):

this is how my request looks after serializing it to json and sending it to the socket:

{"call":"printers.getDefault","promise":{},"params":null,"timestamp":1593623264176,"uid":"dl38pf","signAlgorithm":"SHA256","position":{"x":840,"y":505},"signature":"\u0022hPMxT/veLXCMv6FXXtjBRyhNVzYKrjbz0KiVEHhT2xwqPjgOiE0wIGh0jRm3\u002BUAibdMkAfU5TdyIfO5c7Qac8f6gHTk\u002B4TbVkG9y7cOGtMMZ6N3dwvI5m/xgudaQehGJGW1A/6i0VwCq19NnPMp9LPfVjg/y\u002B1b1KkKHkZudWA6taekdqRCvn9AIOCv66kfp48rN5W81XOf9HZkyup6QsgA9G6kkX6ht\u002BPTmOVwJadIiwNh7JelYVQBFKMDaxJW3DHrxBWRdHdvPAWp9VJP5pFWpYaQ9XAa8hbfHQl/XsIHrCIrCPBzQsvqOH5gBR3Dk5AsYZUMByuicLfvKTM1ANA==\u0022"}

Sorry for the really long post, but I wanted to give you some context of what I'm doing.






El martes, 30 de junio de 2020, 10:34:37 (UTC-5), Joseph M Kavuma escribió:
Hello Pablo, Tres,
Indeed we have a C# implementation similar to what you are doing.
I was hoping to share it with you today after work but my laptop has crashed today. I have arranged to have it fixed tomorrow, so the moment I have my environment restored, I will share with you the approach we took. 

But Tres is right, it is mostly likely related to serialization.

Kind regards
Martin

On Tue, 30 Jun 2020, 17:26 Tres Finocchiaro <tres.fi...@gmail.com> wrote:
Bad Base64 input character '34' in array position 0, this is only happening when I am making the requests from our C# library. 

I haven't authored a C# wrapper (and we don't officially support this), but I've had a lot of experience with the base64 signatures and have a YouTube tutorial which covers how we came up with our C# signing examples: https://www.youtube.com/watch?v=u3XcvAEWvEI

Anyway, the best way to tackle this is to just read the console logs from QZ Tray: https://qz.io/wiki/faq#console-logs

There's a good chance the base64 is getting serialized (e.g. JSON) or something.  Looking at the data should help understand why it can't be decoded.  Note, it's quite easy to unserialize it using JavaScript (e.g. $.ajax, fetch) but when you're talking directly to the Java websocket, you don't have the same luxuries and it will have to be a plain/text string.

For example:

Good:

"signature": "TWFuIGlzIGRpc..."

Bad:

"signature": { "response": "TWFuIGlzIGRpc..." }


To unsubscribe from this group and stop receiving emails from it, send an email to qz-p...@googlegroups.com.

--
You received this message because you are subscribed to a topic in the Google Groups "qz-print" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/qz-print/MmFO6Xo_5aE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to qz-p...@googlegroups.com.

Tres Finocchiaro

unread,
Jul 1, 2020, 1:45:41 PM7/1/20
to pablo.gabr...@gmail.com, qz-print
\u0022 is a double-quote character, it looks like the base64 is being wrapped in double-quotes.


To unsubscribe from this group and stop receiving emails from it, send an email to qz-print+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/qz-print/8efc9660-85a7-43b4-9b01-2e5a8250f32co%40googlegroups.com.

Joseph M Kavuma

unread,
Jul 1, 2020, 3:06:01 PM7/1/20
to Tres Finocchiaro, pablo.gabr...@gmail.com, qz-print
Hi Pablo,
I have managed to retrieve the code I mentioned. Please take a look at the attached file.
It is the class specifically addressing signing.

To unsubscribe from this group and all its topics, send an email to qz-print+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/qz-print/CANQs7dAu95GQHC9_j09dLm_sUsfcRJAsuHvhsqe6Rmi8pc5UOg%40mail.gmail.com.
QzSigning.cs

pablo.gabr...@gmail.com

unread,
Jul 1, 2020, 6:35:11 PM7/1/20
to qz-print
Thank you very much, Joseph, this was really good, I noticed that our logic is much alike yours, 
I was wondering, would it be too much to ask you about your logic to send the data to the webSocket?

in the overall, what we do is the following:

         // create an object for the request
         var defaultPrinter = new Printers
            {
                Call = QzCallTypes.DefaultPrinter,
                PrinterParameters = null,
                timestamp: <dateTime>,
                Promise = { }                
            };

           var qzClient = new QzClient("wss://localhost:8181");
          
            // encoding test
            var hashed = QzSHAEncoding.BuildHash(defaultPrinter.ToJson());
          
           //signing test
            var signature = await qzClient.Signing(hashed);
            defaultPrinter.Signature = JsonSerializer.Deserialize<string>(signature);
         
           // send data to websocket
            var encoded = Encoding.UTF8.GetBytes(defaultPrinter.ToJson());
            var buffer = new ArraySegment<byte>(encoded, 0, encoded.Length);
            return ws.SendAsync(buffer, WebSocketMessageType.Text, true, cancellation);

Thank you for your time. 


El miércoles, 1 de julio de 2020, 14:06:01 (UTC-5), Joseph M Kavuma escribió:
Hi Pablo,
I have managed to retrieve the code I mentioned. Please take a look at the attached file.
It is the class specifically addressing signing.

On Wed, Jul 1, 2020 at 7:45 PM Tres Finocchiaro <tres.fi...@gmail.com> wrote:
\u0022 is a double-quote character, it looks like the base64 is being wrapped in double-quotes.

--
You received this message because you are subscribed to a topic in the Google Groups "qz-print" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/qz-print/MmFO6Xo_5aE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to qz-p...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/qz-print/CANQs7dAu95GQHC9_j09dLm_sUsfcRJAsuHvhsqe6Rmi8pc5UOg%40mail.gmail.com.

Joseph M Kavuma

unread,
Jul 2, 2020, 12:55:41 AM7/2/20
to pablo.gabr...@gmail.com, qz-print
Hi Pablo,
I think the issue could be due to the serialization of the message/signature.
Key points:
(1) The signature is the signed hashed string of your "Printers" (or any other) message C# object << this is critical to get right>>
(2) The message sent to QZ (over the websocket) is a string, not the object. It has to be serialized correctly.

One of the things i noticed when implementing the connection from C# was that serializing (as to the key points mentioned earlier) does not work well with the toString() generic method.
Putting this in mind, i think it could be that you need to serialize your C# objects with JsonConvert.SerializeObject()
for example: JsonConvert.SerializeObject(defaultPrinter)

Regarding; I was wondering, would it be too much to ask you about your logic to send the data to the webSocket? please find below the key elements of the logic i use to send to QZ Tray over the websocket.


using WebSocket4Net;

/// <summary>
/// Establishes a connection to the QZ tray server
/// </summary>
protected void ConnectToQZServer()
{
try
{
serverUrl = String.Format(serverUrl, ip, port);
wSocket = new WebSocket(serverUrl);
wSocket.Opened += new EventHandler(connectedToServer);
wSocket.MessageReceived += new EventHandler<MessageReceivedEventArgs>(handleMsg);
wSocket.Open();
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception(QZTrayUtilities.GetErrorMessage(string.Format(classPath, "ConnectToQZServer"), ex.Message)));
}            
}

/// <summary>
/// Sends a signed message to the QZ Tray server.
/// </summary>
/// <param name="_msgToQz">The message to be sent to QZ Tray</param>
protected void SendMsgToQz(string _msgToQz)
{
try
{
wSocket.Send(_msgToQz);
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception(QZTrayUtilities.GetErrorMessage(string.Format(classPath, "SendMsgToQz"), ex.Message)));
}            
}

I hope it helps.

To unsubscribe from this group and all its topics, send an email to qz-print+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/qz-print/7bb781b3-946e-45a7-a7c2-96604daa86a2o%40googlegroups.com.

pablo.gabr...@gmail.com

unread,
Jul 2, 2020, 6:30:51 PM7/2/20
to qz-print
Hi guys, just wanted to let you know that I have been able to successfully sign my messages. 

It turns out that I was hashing the request with more parameters that I was supposed to, I had to look inside of Qz's jscript library and
see the way it was creating one object to sign it and other object to make the request

Thank you so much, Jason, Tres for your help!

Tres Finocchiaro

unread,
Jul 2, 2020, 10:51:31 PM7/2/20
to pablo.gabr...@gmail.com, qz-print
Thanks for sharing!

--
You received this message because you are subscribed to the Google Groups "qz-print" group.
To unsubscribe from this group and stop receiving emails from it, send an email to qz-print+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/qz-print/fae727e9-53ab-44a2-b4c2-577903e15106o%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages