|
|
Yes but I'm beginner of C# and I use Windows forms.
I would like to add page nr to this cede:
private void Table_From_DB_Click(object sender, EventArgs e)
{
iTextSharp.text.Font fontTitle = FontFactory.GetFont("Arial", 18, iTextSharp.text.Font.BOLD, BaseColor.RED);
iTextSharp.text.Font fontTable = FontFactory.GetFont("Arial", 5, iTextSharp.text.Font.NORMAL, BaseColor.RED);
string FileName = textBox1.Text;
Document doc = new Document(iTextSharp.text.PageSize.A4, 10, 10, 42, 35); // tak
PdfWriter wri = PdfWriter.GetInstance(doc, new FileStream(FileName + ".pdf", FileMode.Create));
string Fn;
Fn = Path.GetFileName(FileName + ".pdf");
MessageBox.Show("Plik zapisano jako: " + Fn);
doc.Open();
PdfPTable table = new PdfPTable(4);
//actual width of table in points
table.TotalWidth = 530f;
//fix the absolute width of the table
table.LockedWidth = true;
//relative col widths in proportions - 1/3 and 2/3
float[] widths = new float[] { 1f, 1f, 1f, 1f };
table.SetWidths(widths);
table.HorizontalAlignment = 0;
//leave a gap before and after the table
table.SpacingBefore = 20f;
table.SpacingAfter = 30f;
PdfPCell cell = new PdfPCell(new Phrase("Products"));
cell.Colspan = 4;
cell.Border = 0;
cell.HorizontalAlignment = 1;
table.AddCell(cell);
// SqlDataReader reader;
string connect = @"Data Source=RZW-L-PB72GX3\SQLEXPRESS;Initial Catalog=StepSample;Integrated Security=True";
using (SqlConnection conn = new SqlConnection(connect))
{
string query = "SELECT [ID],[FullName],[Admitted],[Capital] FROM [StepSample].[dbo].[StateRegion] WHERE [ID]<90";
SqlCommand cmd = new SqlCommand(query, conn);
try
{
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
table.AddCell(rdr[0].ToString());
table.AddCell(rdr[1].ToString());
table.AddCell(rdr[2].ToString());
table.AddCell(rdr[3].ToString());
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
doc.Add(table);
doc.Close();
}
}
|
|
|
|
|
If you follow some of the links in that Google search you will find out how to do it. And maybe learn other useful things at the same time.
|
|
|
|
|
I modified and updated my previous question by clarifying it more and adding some details, I would appreciate help on it as I am stuck with it.
I have a serial port class which looks something like below.
Please don't pay much attention at the WriteAll and ReadAll function contents- I don't think it matters much as far as thread safety is concerned. Please see below for more details.
public static class HASPClass
{
static readonly object m_locker = new object();
private static SerialPort m_port;
private static string m_portName;
private static bool m_isOpen = false;
private static int m_baudRate;
private static Parity m_parity;
private static int m_dataBits;
private static StopBits m_stopBits;
private static bool m_xonxoff;
private static bool m_dtr;
private static bool m_rts;
public static uint m_WR_ERROR = 0;
public static uint m_IO_ERROR = 0;
public static uint m_IO_ERROR1 = 0;
public static uint m_IO_ERROR2 = 0;
public static uint m_IO_ERROR3 = 0;
public static void OpenPort(string portName,
int baudRate,
Parity parity,
int dataBits,
StopBits stopBits,
bool dtr,
bool rts,
bool xonxoff = false)
{
lock(m_locker){
if(m_isOpen) return;
m_port = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
m_portName = portName;
m_baudRate = baudRate;
m_parity = parity;
m_dataBits = dataBits;
m_stopBits = stopBits;
m_xonxoff = xonxoff;
m_dtr = dtr;
m_rts = rts;
if (xonxoff)
m_port.Handshake = Handshake.XOnXOff;
m_port.DtrEnable = dtr;
m_port.RtsEnable = rts;
m_port.ReadTimeout = 500;
m_port.WriteTimeout = 500;
m_port.Open();
m_isOpen = true;
}
}
public static void Close()
{
lock(m_locker){
if(m_isOpen) m_port.Close();
}
}
private static void ReadAll(byte[] data, uint length, int retryCount)
{
if (length > data.Length)
{
throw new Exception("Wrong data length in ReadAll");
}
int offset = 0;
int i = 0;
int remaining = checked((int)length);
while (remaining > 0)
{
if (i >= retryCount)
{
throw new Exception("Exceeded retry count parameter during reading in ReadAll");
}
int read = m_port.Read(data, offset, remaining);
if (read <= 0)
{
throw new EndOfStreamException(String.Format("ReadAll(OldHaspCommunication) - End of data reached with {0} bytes left to read", remaining));
}
remaining -= read;
offset += read;
i++;
}
}
private static void WriteAll(byte[] data, uint length)
{
int len = checked((int)length);
if (len > data.Length)
{
throw new Exception("Wrong data length in WriteAll");
}
m_port.Write(data, 0, len);
}
public void Query(..)
{
lock(m_locker){
WriteAll(..);
ReadAll(..);
WriteAll(..);
ReadAll(..);
}
}
}
As you can see ReadAlland WriteAllcalls are private. Query is public and it calls ReadAll and WriteAll methods hence I put lock only inside Query.
Now imagine I have different class which wants to use Query method (after it has done OpenPort):
class Tester
{
static readonly object m_otherlockA= new object();
public static void function1()
{
lock(m_otherlockA)
{
HASPCLass.Query(..);
HASPCLass.Query(..);
}
}
public static void start()
{
HASPCLass.OpenPort(...)
}
public static void End()
{
HASPCLass.Close(...)
}
public static void function2()
{
lock(m_otherlockA)
{
HASPCLass.Query(..);
HASPCLass.Query(..);
}
}
}
My question is: Is the way I use HASPClass from class Tester thread safe? In other words HASP class will be used via Tester class only - is it thread safe?
I would really appreciate some help as I am stuck on it.
modified 8-Feb-16 6:16am.
|
|
|
|
|
You have serialized calls to HASPClass.Query, and serialized calls to Tester.function1 and Tester.function2. If Tester is the only class using HASPClass, this should be thread-safe as far as calls to HASPClass.Query() are concerned.
My problem with your code is that function1 and function2 each use TWO calls to HASPClass.Query to perform their work. If you ever have a second class calling HASPClass.Query, it is entirely possiblethat this code will no longer be thread-safe:
class Class2
{
static readonly object m_lock = new object();
public static function3()
{
lock(m_lock);
HASPClass.Query();
HASPClass.Query()
}
}
In this case, it is quite possible that a call to Tester.function1 will intervene, corrupting the data read by Class2.function3.
You should redesign HASPClass so that the double call to Query() is itself an atomic operation.
If you have an important point to make, don't try to be subtle or clever. Use a pile driver. Hit the point once. Then come back and hit it again. Then hit it a third time - a tremendous whack.
--Winston Churchill
|
|
|
|
|
Thanks a lot but currently only function1 and function2 consume HASP class.
No other classes call HASP class functions.
What you say in this case?
Thank you.
|
|
|
|
|
Assuming that you use HASPClass only as described, your code is thread-safe.
If you have an important point to make, don't try to be subtle or clever. Use a pile driver. Hit the point once. Then come back and hit it again. Then hit it a third time - a tremendous whack.
--Winston Churchill
|
|
|
|
|
|
Dear Daniel the only question remaining I have is that other class may call OpenPort method (or similar ClosePort - which has similar lock as OpenPort inside with same lock object). Please see the updated question.
Does this change anything?
modified 8-Feb-16 6:19am.
|
|
|
|
|
For a given port, OpenPort and ClosePort should be called only once. Calling OpenPort twice (with no intervening ClosePort) is an error.
One way to handle this would be to create a separate class which calls OpenPort in its constructor and ClosePort in its destructor. This would guarantee that each function is called only once. It is still your responsibility to ensure that only one instance of the class is created at any one time.
Search for the Singleton pattern for more details on how to do this.
If you have an important point to make, don't try to be subtle or clever. Use a pile driver. Hit the point once. Then come back and hit it again. Then hit it a third time - a tremendous whack.
--Winston Churchill
|
|
|
|
|
As you can see from my HASP class calling OpenPort twice is not possible because of m_isOpen flag. (please see updated question).
|
|
|
|
|
Not the answer to your question but some comments:
Because a serial port can be opened only once, there is no need to lock inside your OpenPort function. If the port is already opened, SerialPort::Open() will throw an exception.
There is also no need to have the m_isOpen member variable. Just use SerialPort::IsOpen() instead.
Regarding the locking question:
If you want to ensure that a series of transfers is not interrupted by another thread, you must lock like shown in your Tester class.
But then there is no need to lock also inside the Query() function. So you can use the locking object of your serial class instead by providing public access.
|
|
|
|
|
Thanks a lot foe your feedback but I didn't get your last sentence
But then there is no need to lock also inside the Query() function. So you can use the locking object of your serial class instead by providing public access.
You meant locking object of my Tester class?
|
|
|
|
|
Yes. You can provide a function to return that object and pass it to lock() .
|
|
|
|
|
didn't get you again sorry
|
|
|
|
|
In your Tester class lock using the object of your serial class (HASPClass::m_locker ) and remove the locking inside your Query() function.
|
|
|
|
|
Thanks for suggested improvement. (PS in your case I'd need to make HASPClass lock public?)
Otherwise my case is also correct right?
|
|
|
|
|
It must be public then.
Your case is correct if Tester is the only class using the serial class (as answered by Daniel).
But if you have another class that uses the serial communication from within another thread too, it would be not safe. The general rule is: All locking should use the same object.
|
|
|
|
|
Other Class may ONLY call OpenPort (or Similar ClosePort method - which has similar lock inside as OpenPort) method.
Otherwise indeed only tester uses that.
What you say now?
|
|
|
|
|
Member 12061600 wrote: What you say now? Nothing else than I had before: Then it would be safe.
But:
Using it that way (a locking object in the Tester class) is bad style. It would not work anymore if you (or even worse anyone else) decide later to use it from within another class.
Again:
There is no need to lock upon opening (and closing). The exclusive behaviour of serial ports makes it unnecessary. If you want to open the port from different code parts, you must handle the execptions (they should be handled anyway). Using locking there does not prevent the exception. It may just delay it.
Conclusion:
Your current code is actually thread safe. But there are unnecessary operations, missing error checks upon opening, and it is prone to be not thread safe when changed.
|
|
|
|
|
"Using locking there does not prevent the exception. It may just delay it."
It prevents because if port is already opened I don't reopen it.
PS did you see the updated version of question? (with open and close calls). Thanks a lot!
PPS I understood your conclusion too, thanks.
|
|
|
|
|
Member 12061600 wrote: It prevents because if port is already opened I don't reopen it. OK. You are right because you are checking the open state first. But it just returns without any error code or message.
But there may be other reasons that opening fails which should be handled (not existing, already opened by another application or another instance of your application).
So when handling the exception, there is no need for locking.
|
|
|
|
|
So we agree that although my approach maybe redundant at places
at least in its current form it should work (including the Open and Close methods being called like in updated question)
Thanks a lot for help
|
|
|
|
|
I have slightly updated my question with how open port and close port methods could be also called from other methods. Please give feedback on that if it is Okay.
|
|
|
|
|
See my above post. No need to lock when closing and (as noted in my initial answer) use SerialPort::IsOpen() instead of your own member variable.
|
|
|
|