I want to store three certificates.
I've got two related questions:
how to write method Get_Offset_Read(), Get_Offset_Write() and calculate offset in buffer for write and read in buffer?
I use an ObjectArray to store certificate data. Is that a correct way of storing such data?
Here's the code:
public class writeApplet extends Applet {
private Object[] FileArray;
private byte FileCount;
private writeApplet() {
FileArray=new Object[3];
FileCount=0;
}
public static void install(byte bArray[], short bOffset, byte bLength)
throws ISOException {
new writeApplet().register();
}
public void process(APDU arg0) throws ISOException {
//….
}
private void ReadBinaryData(APDU apdu)
{
//How to write Get_Offset_Read();
short offset = Get_Offset_Read();
byte[] buf= apdu.getBuffer();
// p1value is certificate index in FileArray
byte p1value=buf[ISO7816.OFFSET_P1];
byte[] FileObj=(byte[]) FileArray[p1value];
short le = apdu.setOutgoing();
boolean eof = false;
if((short)(FileObj.length - offset) < le) {
le = (short)(FileObj.length - offset);
eof = true;
}
apdu.setOutgoingLength(le);
apdu.sendBytesLong(FileObj, offset, le);
if(eof)
{
ISOException.throwIt(SW_END_OF_FILE);
}
}
private void WriteBinaryData(APDU apdu)
{
if(FileCount==3)
{
ISOException.throwIt(SW_END_OF_ThreeFILES);
}
byte[] buf = apdu.getBuffer();
short offset =Get_Offset_Write();
byte lc=buf[ISO7816.OFFSET_LC];
if((short)(offset + lc) >((byte[])FileArray[FileCount]).length)
{
ISOException.throwIt(SW_WRONG_LENGTH);
}
apdu.setIncomingAndReceive();
Util.arrayCopyNonAtomic(buf, ISO7816.OFFSET_CDATA,(byte[]);
FileArray[FileCount],offset,lc);
FileCount++;
}
}
The idea to store persistent arrays in an object array is certainly not wrong. So the rest of this answer will focus on the offset issue.
Generally file operations are is implemented by following the ISO 7816-4 specifications.
In that case you've got a minimum of three - not two - methods to create:
CREATE FILE - creates a file (of a certain size)
UPDATE BINARY - writes (part of) a file using the offset in P1/P2
READ BINARY - reads (part of) a file using the offset in P1/P2
The offset indicated is of course the offset in the file rather than the offset in the buffer.
I'll skip the UPDATE BINARY and READ BINARY with Odd INS as they are generally only needed for files over 32KiB.
If you want to keep to your current design then you could store the offset in the command data (CDATA) section. Alternatively you could write the file in blocks of a certain size and indicate the block number in P2.
In my opinion an addition CREATE FILE command should be preferred. Currently the methods for reading and creation/writing of the file are not symmetric.
Related
I'm trying to figure out how to resolve a crash happening on my code when the number of cue points is high. My test reproduce it every time with a number of 530 cue items. Each cue has a dataSize of 1.
You can see my code below. Essentially native_update_cue_points is called from the java layer. This triggers multiple calls to the java layer from jni layer. Somehow this is causing a table overflow. I'm thinking somehow each call to the static java function is not releasing the allocated string values causing the stack overflow?
Sample code
static void native_update_cue_points(JNIEnv *env, jclass clazz)
{
if (smpMediaPlayer)
{
std::list<Cue> cuePoints;
smpMediaPlayer->getCuePoints(&cuePoints);
for (std::list<Cue>::iterator it = cuePoints.begin(); it != cuePoints.end(); it++)
{
Cue cue = *it;
java_update_cue_point(env, clazz, smFields.global_ref_thiz, 0, &cue);
}
}
}
static void java_update_cue_point (JNIEnv *env, jclass clazz, jobject thiz, int reqType, Cue *pCue)
{
if (!pCue) {
return;
}
jlong id = pCue->id;
jint type = pCue->type;
jint extra = pCue->extra;
jlong pos = pCue->pos;
jlong duration = pCue->duration;
jobjectArray jkeys = NULL;
jobjectArray jvalues = NULL;
int dataSize = pCue->data.size();
if (0 < dataSize)
{
jkeys = env->NewObjectArray(dataSize, env->FindClass("java/lang/String"), NULL);
jvalues = env->NewObjectArray(dataSize, env->FindClass("[B"), NULL);
int position = 0;
for (std::map<std::string, std::string>::iterator it = pCue->data.begin(); it != pCue->data.end(); it++)
{
// set key
env->SetObjectArrayElement(jkeys, position, env->NewStringUTF(it->first.c_str()));
// set value
jbyteArray byteArray = env->NewByteArray(it->second.length());
env->SetByteArrayRegion(byteArray, 0, it->second.length(), (const signed char *)it->second.c_str());
env->SetObjectArrayElement(jvalues, position, byteArray);
env->DeleteLocalRef(byteArray);
position++;
}
}
// report update
env->CallStaticVoidMethod(
clazz,
smFields.native_callback_add_cue_point,
thiz,
(jint)reqType, id, type, extra, pos, duration, jkeys, jvalues);
}
Stacktrace
JNI ERROR (app bug): local reference table overflow (max=512)
local reference table dump:
Last 10 entries (of 512):
511: 0x13054090 java.lang.String[] (1 elements)
510: 0x70732980 java.lang.Class<java.lang.String>
509: 0x130517c0 java.lang.String "song_artist"
508: 0x13054080 byte[][] (1 elements)
507: 0x7079af18 java.lang.Class<byte[]>
506: 0x13054070 java.lang.String[] (1 elements)
505: 0x70732980 java.lang.Class<java.lang.String>
504: 0x13051700 java.lang.String "song_artist"
503: 0x12d17ff0 byte[][] (1 elements)
502: 0x7079af18 java.lang.Class<byte[]>
Summary:
205 of java.lang.Class (2 unique instances)
103 of java.lang.String[] (1 elements) (103 unique instances)
102 of java.lang.String (102 unique instances)
102 of byte[][] (1 elements) (102 unique instances)
UPDATE 1:
I did a bit more digging and was able to narrow it down to the call FindClass. This leaves me even more confused! :/ Here is my simplified native_update_cue_point method. Do I need to release somehow the FindClass call!?
Code
static void java_update_cue_point (JNIEnv *env, jclass clazz, jobject thiz, int reqType, Cue *pCue)
{
jclass jstringClass = env->FindClass("java/lang/String");
}
Stacktrace
JNI ERROR (app bug): local reference table overflow (max=512)
local reference table dump:
Last 10 entries (of 512):
511: 0x70732980 java.lang.Class<java.lang.String>
510: 0x70732980 java.lang.Class<java.lang.String>
509: 0x70732980 java.lang.Class<java.lang.String>
508: 0x70732980 java.lang.Class<java.lang.String>
507: 0x70732980 java.lang.Class<java.lang.String>
506: 0x70732980 java.lang.Class<java.lang.String>
505: 0x70732980 java.lang.Class<java.lang.String>
504: 0x70732980 java.lang.Class<java.lang.String>
503: 0x70732980 java.lang.Class<java.lang.String>
502: 0x70732980 java.lang.Class<java.lang.String>
Summary:
512 of java.lang.Class (1 unique instances)
UPDATE 2:
I was able to fix the table overflow by doing a DeleteLocalRef on every local object regardless if the java VM would take care of it. My thoughts are that the VM is accumulating items on the stack which will release after executing. I need to remove each item explicitly from the stack to avoid unwanted accumulations for later removal while on a large loop.
Still I'm left feeling like I haven't understood completely why this is happening and why my solution solves it.
Fixed code
static void java_update_cue_point (JNIEnv *env, jclass clazz, jobject thiz, int reqType, Cue *pCue)
{
if (!pCue) {
return;
}
jclass jstringClass = env->FindClass("java/lang/String");
jclass jbyteArrayClass = env->FindClass("[B");
jlong id = pCue->id;
jint type = pCue->type;
jint extra = pCue->extra;
jlong pos = pCue->pos;
jlong duration = pCue->duration;
jobjectArray jkeys = NULL;
jobjectArray jvalues = NULL;
int dataSize = pCue->data.size();
if (0 < dataSize)
{
jkeys = env->NewObjectArray(dataSize, jstringClass, NULL);
jvalues = env->NewObjectArray(dataSize, jbyteArrayClass, NULL);
int position = 0;
for (std::map<std::string, std::string>::iterator it = pCue->data.begin(); it != pCue->data.end(); it++)
{
// set key
jstring jkey = env->NewStringUTF(it->first.c_str());
env->SetObjectArrayElement(jkeys, position, jkey);
env->DeleteLocalRef(jkey); // FIX
// set value
jbyteArray byteArray = env->NewByteArray(it->second.length());
env->SetByteArrayRegion(byteArray, 0, it->second.length(), (const signed char *)it->second.c_str());
env->SetObjectArrayElement(jvalues, position, byteArray);
env->DeleteLocalRef(byteArray);
position++;
}
}
// report update
env->CallStaticVoidMethod(
clazz,
smFields.native_callback_add_cue_point,
thiz,
(jint)reqType, id, type, extra, pos, duration, jkeys, jvalues);
if (jkeys) {
env->DeleteLocalRef(jkeys); // FIX
}
if (jvalues) {
env->DeleteLocalRef(jvalues); // FIX
}
if (jstringClass) {
env->DeleteLocalRef(jstringClass); // FIX
}
if (jbyteArrayClass) {
env->DeleteLocalRef(jbyteArrayClass); // FIX
}
}
You're creating too many local references (two for classes, two for object arrays and some more for strings and byte arrays per run of java_update_cue_point). The VM can only handle a limited amount of local references. See "Referencing Java Objects" for some documentation. The "Global and Local References" section in the JNI function documentation also has some details. The VM will free local references automatically when a native method returns, but in a loop like yours you will have to delete references you don't need anymore with DeleteLocalRef.
To optimize a little, my recommendation would be to do the two FindClass calls outside of the loop in native_update_cue_points and pass them as parameters to java_update_cue_point. This way you'll only have two references for the class objects that the VM will free for you when native_update_cue_points returns, and you save processing time by not finding the same classes over and over again.
I suspect you need to call DeleteLocalRef() on the Java String object created in this line of code:
env->SetObjectArrayElement(jkeys, position, env->NewStringUTF(it->first.c_str()));
Your call to NewStringUTF() creates a Java object with a local reference, which you do not delete.
I have a Form that create a UDP object, in the UDP class a UDPClient is created and the received data is done in the BeginReceive Method using EndReceive.
When I print the string of the reveived data, after converting the byte[], to the console from within the beginreceive method, with text appended, only the received data prints not the appended text.
So it looks like the received data is incomplete.
When the string prints, the NewLine and appended "done" is not shown.
Any help would be great!!
Thanks
class Udp
{
public EventHandler _dataReceived;
public Udp()
{
int receiverPort = 1248;
UdpClient receiver = new UdpClient(receiverPort);
string discovery = "<?xml version=\"1.0\"?><ServiceQuery></ServiceQuery>";
receiver.BeginReceive(new AsyncCallback( DataReceived), receiver);
IPEndPoint end = new IPEndPoint(IPAddress.Broadcast, 1248);
receiver.Send(Encoding.ASCII.GetBytes(discovery + "\0"), discovery.Length + 1, end);
}
private void DataReceived(IAsyncResult ar)
{
UdpClient c = (UdpClient)ar.AsyncState;
IPEndPoint receivedIpEndPoint = new IPEndPoint(IPAddress.Any, 1248);
Byte[] receivedBytes = c.EndReceive(ar, ref receivedIpEndPoint);
string receivedText = ASCIIEncoding.ASCII.GetString(receivedBytes);
Console.WriteLine("\n");
if(_dataReceived != null)
{
Console.Write(receivedIpEndPoint + ": " + receivedText + Environment.NewLine + "done");
_dataReceived(receivedText, new EventArgs());
}
c.BeginReceive(new AsyncCallback(DataReceived), c);
}
}
The simplest repro I can think of for this problem is this code:
private void button1_Click(object sender, EventArgs e) {
Byte[] receivedBytes = new byte[] { 0x48, 0x65, 0x6c, 0x00, 0x6c, 0x6f };
string receivedText = Encoding.ASCII.GetString(receivedBytes);
Console.Write(receivedText + ", you won't see this");
}
Output after clicking the button several times:
HelHelHelHel
Surely you now recognize the poison-pill in the receivedBytes array, it is the presence of the 0x00 byte that causes the output string to get cut short. Nothing beyond that byte makes it into the Visual Studio Output window.
Explaining this behavior requires a pretty deep dive in how Console.Write() in a Winforms app works and how it is capable of generating output, even though your program has no console. It is a long-winded story that isn't that likely to entertain anybody so I'll punt for the short version. With the Visual Studio Hosting Process debugger option enabled, Console.Write() is equivalent to Debug.Write(). Debug output is intercepted by the DefaultTraceListener class, it pinvokes OutputDebugString() to get the text to appear in the debugger trace window. These winapi functions takes C strings, a C string is zero-terminated to indicate the end of the string.
There are several ways to fix this, the programmer's way is to convert the byte[] array content to hex:
Byte[] receivedBytes = new byte[] { 0x48, 0x65, 0x6c, 0x00, 0x6c, 0x6f };
string receivedText = BitConverter.ToString(receivedBytes);
Console.WriteLine(receivedText + ", you see this");
Output:
48-65-6C-00-6C-6F, you see this
48-65-6C-00-6C-6F, you see this
48-65-6C-00-6C-6F, you see this
Or you might want to take a better look at the data you transmit, ensuring it is actually printable text that can be properly converted with Encoding.ASCII
In the applications I'm developing I need to store data for Customer,Products and their Prices.
In order to persist that data I use RMS, but knowing that RMS doesn't support object serializing directly and since that data I read already comes in json format, I store every JSONObject as its string version, like this:
rs = RecordStore.openRecordStore(mRecordStoreName, true);
JSONArray jsArray = new JSONArray(data);
for (int i = 0; i < jsArray.length(); i++) {
JSONObject jsObj = jsArray.getJSONObject(i);
stringJSON = jsObj.toString();
addRecord(stringJSON, rs);
}
The addRecord Method
public int addRecord(String stringJSON, RecordStore rs) throws JSONException,RecordStoreException {
int id = -1;
byte[] raw = stringJSON.getBytes();
id= rs.addRecord(raw, 0, raw.length);
return id;
}
So I have three RecordStores (Customer,Products and their Prices) and for each of them I do the save as shown above to save their corresponding data.
I know this might be a possible to solution, but I'm sure there's gotta be a better implementation. Even more,considering that over those three "tables" I'm going to perform searching, sorting,etc.
In those cases, having to deserialize before proceeding to search or sort doesn't seem a very good idea.
That's why I want to ask you guys. In your experience, how do store custom objects in RMS in way that is easy to work with them later??
I really appreciate all your comments and suggestions.
EDIT
It seems that it's easier to work with records when you define a fixed max length for each field. So here's what I tried:
1) First all, this is the class I use to retrieve the values from the record store:
public class Customer {
public int idCust;
public String name;
public String IDNumber;
public String address;
}
2) This is the code I use to save every jsonObject to the record store:
RecordStore rs = null;
try {
rs = RecordStore.openRecordStore(mRecordStoreName, true);
JSONArray js = new JSONArray(data);
for (int i = 0; i < js.length(); i++) {
JSONObject jsObj = js.getJSONObject(i);
byte[] record = packRecord(jsObj);
rs.addRecord(record, 0, record.length);
}
} finally {
if (rs != null) {
rs.closeRecordStore();
}
}
The packRecord method :
private byte[] packRecord(JSONObject jsonObj) throws IOException, JSONException {
ByteArrayOutputStream raw = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(raw);
out.writeInt(jsonObj.getInt("idCust"));
out.writeUTF(jsonObj.getString("name"));
out.writeUTF(jsonObj.getString("IDNumber"));
out.writeUTF(jsonObj.getString("address"));
return raw.toByteArray();
}
3) This is how I pull all the records from the record store :
RecordStore rs = null;
RecordEnumeration re = null;
try {
rs = RecordStore.openRecordStore(mRecordStoreName, true);
re = rs.enumerateRecords(null, null, false);
while (re.hasNextElement()) {
Customer c;
int idRecord = re.nextRecordId();
byte[] record = rs.getRecord(idRecord);
c = parseRecord(record);
//Do something with the parsed object (Customer)
}
} finally {
if (re != null) {
re.destroy();
}
if (rs != null) {
rs.closeRecordStore();
}
}
The parseRecord Method :
private Customer parseRecord(byte[] record) throws IOException {
Customer cust = new Customer();
ByteArrayInputStream raw = new ByteArrayInputStream(record);
DataInputStream in = new DataInputStream(raw);
cust.idCust = in.readInt();
cust.name = in.readUTF();
cust.IDNumber = in.readUTF();
cust.address = in.readUTF();
return cust;
}
This is how I implemented what Mister Smith suggested(hope it's what he had in mind). However, I'm still not very sure about how to implement the searchs.
I almost forget to mention that before I made theses changes to my code, the size of my RecordStore was 229048 bytes, now it is only 158872 bytes :)
RMS is nothing of the sort of a database. You have to think of it as a record set, where each record is a byte array.
Because of this, it is easier to work with it when you define a fixed max length for each field in the record. For instance, a record could be some info about a player in a game (max level reached, score, player name, etc). You could define the level field as 4 bytes long (int), then a score field of 8 bytes (a long), then the name as a 100 bytes field (string). This is tricky because strings usually will be of variable length, but you would probably like to have a fixed max length for this field, and if some string is shorter than that, you'd use a string terminator char to delimite it. (This example is actually bad because the string is the last field, so it would have been easier to keep it variable length. Just imagine you have several consecutive fields of type string.)
To help you with serialization/deserialization, you can use DataOutputstream and DataInputStream. With these classes you can read/write strings in UTF and they will insert the string delimiters for you. But this means that when you need a field, as you don't know exactly where it is located, you'll have to read the array up to that position first.
The advantage of fixed lengths is that you could later use a RecordFilter and if you wanted to retrieve recors of players that have reached a score greater than 10000, you can look at the "points" field in exactly the same position (an offset of 4 bytes from the start of the byte array).
So it's a tradeoff. Fixed lengths means faster access to fields (faster searches), but potential waste of space. Variable lengths means minimum storage space but slower searches. What is best for your case will depend on the number of records and the kind of searches you need.
You have a good collection of tutorials in the net. Just to name a few:
http://developer.samsung.com/java/technical-docs/Java-ME-Record-Management-System
http://developer.nokia.com/community/wiki/Persistent_Data_in_Java_ME
I have a mission to make a "stamp creator" for Powder Toy. The "stamp" looks like this: http://i.imgur.com/3Cac3.jpg . I'd like to use the bytes' values for my creator. For that I need to read the unencoded content of the file. Any help? Also, I'll need to create such content as well. (Maybe I'm better off using a byte[] array?(but how to enter the special control characters?))
You can easily use File.ReadAllBytes to read the file, and File.WriteAllBytes to write it back.
If you need more control instead of reading an entire file into memory, you can use File.OpenRead and File.OpenWrite to read and write using a Stream. If you do that make sure you dispose of it - using the using keyword is encouraged.
Here are a couple of examples
public void CopyFileByReadingItAllToMemory() {
byte[] data = File.ReadAllBytes(#"C:\Temp\input");
File.WriteAllBytes(#"C:\temp\output", data);
}
public void CopyFileWithoutReadingItAllToMemory() {
using (Stream input = File.OpenRead(#"C:\Temp\input"))
using (Stream output = File.OpenWrite(#"C:\Temp\output"))
{
while (true) {
int value = input.ReadByte();
if (value == -1) {
break;
}
output.WriteByte((byte)value);
}
}
}
Using C# 4 in a Windows console application that continually reports progress how can I make the "redraw" of the screen more fluid?
I'd like to do one of the following:
- Have it only "redraw" the part of the screen that's changing (the progress portion) and leave the rest as is.
- "Redraw" the whole screen but not have it flicker.
Currently I re-write all the text (application name, etc.). Like this:
Console.Clear();
WriteTitle();
Console.WriteLine();
Console.WriteLine("Deleting:\t{0} of {1} ({2})".FormatString(count.ToString("N0"), total.ToString("N0"), (count / (decimal)total).ToString("P2")));
Which causes a lot of flickering.
Try Console.SetCursorPosition. More details here: How can I update the current line in a C# Windows Console App?
static void Main(string[] args)
{
Console.SetCursorPosition(0, 0);
Console.Write("################################");
for (int row = 1; row < 10; row++)
{
Console.SetCursorPosition(0, row);
Console.Write("# #");
}
Console.SetCursorPosition(0, 10);
Console.Write("################################");
int data = 1;
System.Diagnostics.Stopwatch clock = new System.Diagnostics.Stopwatch();
clock.Start();
while (true)
{
data++;
Console.SetCursorPosition(1, 2);
Console.Write("Current Value: " + data.ToString());
Console.SetCursorPosition(1, 3);
Console.Write("Running Time: " + clock.Elapsed.TotalSeconds.ToString());
Thread.Sleep(1000);
}
Console.ReadKey();
}
I know this question is a bit old but I found if you set Console.CursorVisible = false then the flickering stops as well.
Here's a simple working demo that shows multi-line usage without flickering. It shows the current time and a random string every second.
private static void StatusUpdate()
{
var whiteSpace = new StringBuilder();
whiteSpace.Append(' ', 10);
var random = new Random();
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var randomWord = new string(Enumerable.Repeat(chars, random.Next(10)).Select(s => s[random.Next(s.Length)]).ToArray());
while (true)
{
Console.SetCursorPosition(0, 0);
var sb = new StringBuilder();
sb.AppendLine($"Program Status:{whiteSpace}");
sb.AppendLine("-------------------------------");
sb.AppendLine($"Last Updated: {DateTime.Now}{whiteSpace}");
sb.AppendLine($"Random Word: {randomWord}{whiteSpace}");
sb.AppendLine("-------------------------------");
Console.Write(sb);
Thread.Sleep(1000);
}
}
The above example assumes your console window is blank to start. If not, make sure to use Console.Clear() first.
Technical Note:
SetCursorPosition(0,0) places the cursor back to the top (0,0) so the next call to Console.Write will start from line 0, char 0. Note, it doesn't delete the previous content before writing. As an example, if you write "asdf" over a previous line such as "0123456", you'll end up with something like "asdf456" on that line. For that reason, we use a whiteSpace variable to ensure any lingering characters from the previous line are overwritten with blank spaces. Adjust the length of the whiteSpace variable to meet your needs. You only need the whiteSpace variable for lines that change.
Personal Note:
For my purposes, I wanted to show the applications current status (once a second) along with a bunch of other status information and I wanted to avoid any annoying flickering that can happen when you use Console.Clear(). In my application, I run my status updates behind a separate thread so it constantly provides updates even though I have numerous other threads and long running tasks going at the same time.
Credits:
Thanks to previous posters and dtb for the random string generator used in the demo.
How can I generate random alphanumeric strings in C#?
You could try to hack something together using the core libraries.
Rather than waste your time for sub-standard results, I would check out this C# port of the ncurses library (which is a library used for formatting console output):
Curses Sharp
I think you can use \r in Windows console to return the beginning of a line.
You could also use SetCursorPosition.
I would recommend the following extension methods. They allow you to use a StringBuilder to refresh the console view without any flicker, and also tidies up any residual characters on each line
The Problem: The following demo demonstrates using a standard StringBuilder, where updating lines that are shorter than the previously written line get jumbled up. It does this by writing a short string, then a long string on a loop:
public static void Main(string[] args)
{
var switchTextLength = false;
while(true)
{
var sb = new StringBuilder();
if (switchTextLength)
sb.AppendLine("Short msg");
else
sb.AppendLine("Longer message");
sb.UpdateConsole();
switchTextLength = !switchTextLength;
Thread.Sleep(500);
}
}
Result:
The Solution: By using the extension method provided below, the issue is resolved
public static void Main(string[] args)
{
var switchTextLength = false;
while(true)
{
var sb = new StringBuilder();
if (switchTextLength)
sb.AppendLineEx("Short msg");
else
sb.AppendLineEx("Longer message");
sb.UpdateConsole();
switchTextLength = !switchTextLength;
Thread.Sleep(500);
}
}
Result:
Extension Methods:
public static class StringBuilderExtensions
{
/// <summary>
/// Allows StrinbBuilder callers to append a line and blank out the remaining characters for the length of the console buffer width
/// </summary>
public static void AppendLineEx(this StringBuilder c, string msg)
{
// Append the actual line
c.Append(msg);
// Add blanking chars for the rest of the buffer
c.Append(' ', Console.BufferWidth - msg.Length - 1);
// Finish the line
c.Append(Environment.NewLine);
}
/// <summary>
/// Combines two StringBuilders using AppendLineEx
/// </summary>
public static void AppendEx(this StringBuilder c, StringBuilder toAdd)
{
foreach (var line in toAdd.ReadLines())
{
c.AppendLineEx(line);
}
}
/// <summary>
/// Hides the console cursor, resets its position and writes out the string builder
/// </summary>
public static void UpdateConsole(this StringBuilder c)
{
// Ensure the cursor is hidden
if (Console.CursorVisible) Console.CursorVisible = false;
// Reset the cursor position to the top of the console and write out the string builder
Console.SetCursorPosition(0, 0);
Console.WriteLine(c);
}
}
I actually had this issue so I made a quick simple method to try and eliminate this.
static void Clear(string text, int x, int y)
{
char[] textChars = text.ToCharArray();
string newText = "";
//Converts the string you just wrote into a blank string
foreach(char c in textChars)
{
text = text.Replace(c, ' ');
}
newText = text;
//Sets the cursor position
Console.SetCursorPosition(x, y);
//Writes the blank string over the old string
Console.WriteLine(newText);
//Resets cursor position
Console.SetCursorPosition(0, 0);
}
It actually worked surprisingly well and I hope it may work for you!
Naive approach but for simple applications is working:
protected string clearBuffer = null; // Clear this if window size changes
protected void ClearConsole()
{
if (clearBuffer == null)
{
var line = "".PadLeft(Console.WindowWidth, ' ');
var lines = new StringBuilder();
for (var i = 0; i < Console.WindowHeight; i++)
{
lines.AppendLine(line);
}
clearBuffer = lines.ToString();
}
Console.SetCursorPosition(0, 0);
Console.Write(clearBuffer);
Console.SetCursorPosition(0, 0);
}
Console.SetCursorPosition(0, 0); //Instead of Console.Clear();
WriteTitle();
Console.WriteLine();
Console.WriteLine("Deleting:\t{0} of {1} ({2})".FormatString(count.ToString("N0")