Nov 20, 2019
Jun 9, 2014
Hàm đổi số (đọc số) ra chữ bằng VB6, Java và C#.NET
Đợt rồi, có ông bạn nhờ, tiện thể mình làm tip này chia sẽ luôn.
Bao gồm code cho Java, C# và VB6 :))
Code này có thể đọc luôn cả trường hợp có dấu thập phân, và có thể chỉnh giới hạn độ dài số tùy ý, miễn là nằm trong phạm vi double
Ý tưởng xử lý cơ bản như sau: Xé nhỏ thành từng bộ 3 số và đọc cụm 3 số này sau đó ghép lại.
Có thắc mắc hay vướng mắc gì xin để lại comment, thanks
Link download (bấm vào link chờ 5 giây sau đó chọn "Skip Add" để download)
Code VB6:
Code C#: http://p.pw/babk9B
Code Java: http://p.pw/babk99
Jun 27, 2010
All of unlocking HDD Password
Unlocking a password protected harddisk
During development of the Rockbox firmware, on several occations the harddisk has become locked, i.e. password protected. This results in the Archos displaying:
Part. Error
Pls Chck HD
We are still not 100% sure why it happened. Theories range from low-power conditions to accidental chip select failure. It has also happened for normal users, using the standard Archos-supplied firmware, although it was more frequent for us developers.
Note: None of us developers have experienced this problem since march 2002.
We do however know how to unlock the disk:
Windows/DOS unlock
Note: This requires taking the Archos apart, which will void your warranty!
- Grab atapwd (written by Alex Mina)
- Create a bootable DOS floppy disk, and put atapwd.exe on it
- Remove the harddisk from your Archos and plug it into a laptop (or a standard PC, using a 3.5" => 2.5" IDE adapter)
- Boot from the floppy and run atapwd.exe
- Select the locked harddrive and press enter for the menu
- For Fujitsu disks: Choose "unlock with user password", then "disable with user password". The password is empty, so just press enter at the prompt.
- For Toshiba and Hitachi disks, if the above doesn't work: Choose "unlock with master password", then "disable with master password". The password is all spaces.
- Your disk is now unlocked. Shut down the computer and remove the disk.
Big thanks to Magnus Andersson for discovering the Fujitsu (lack of) user password!
There is also a program for win32, ArchosUnlock.exe, that creates a linux boot disk with the below mentioned patched isd200 driver.
Linux unlock
For those of us using Linux, we have written an isd200 driver patch for unlocking the disk. This modified driver will automatically unlock the disk when you connect your Archos via USB, so you don't have to do anything special. Apply the patch to a 2.4.18 linux kernel tree.
Still locked?
If the above suggestions don't work, here's some background info about the disk lock feature:
The disk lock is a built-in security feature in the disk. It is part of the ATA specification, and thus not specific to any brand or device.
A disk always has two passwords: A User password and a Master password. Most disks support a Master Password Revision Code, which can tell you if the Master password has been changed, or it it still the factory default. The revision code is word 92 in the IDENTIFY response. A value of 0xFFFE means the Master password is unchanged.
A disk can be locked in two modes: High security mode or Maximum security mode. Bit 8 in word 128 of the IDENTIFY response tell you which mode your disk is in: 0 = High, 1 = Maximum.
In High security mode, you can unlock the disk with either the user or master password, using the "SECURITY UNLOCK DEVICE" ATA command. There is an attempt limit, normally set to 5, after which you must power cycle or hard-reset the disk before you can attempt again.
In Maximum security mode, you cannot unlock the disk! The only way to get the disk back to a usable state is to issue the SECURITY ERASE PREPARE command, immediately followed by SECURITY ERASE UNIT. The SECURITY ERASE UNIT command requires the Master password and will completely erase all data on the disk. The operation is rather slow, expect half an hour or more for big disks. (Word 89 in the IDENTIFY response indicates how long the operation will take.)
Apr 10, 2010
Dotnet framework 3.5 SP1 as prerequisite
i have vs.net 2008 and i have installed sp1 ( for vs and the .net framework3.5) but when i try to add the .net framework 3.5 sp1 as a prerequisite i go the following errors when i build the setup program
there are 57 serrors : here are some sample
Error1The install location for prerequisites has not been set to 'component vendor's web site' and the file 'DotNetFX35SP1\dotNetFX20\aspnet.msp' in item '.NET Framework 3.5 SP1' can not be located on disk. See Help for more information.C:\Documents and Settings\Sami\My Documents\Visual Studio 2008\Projects\WindowsApplication1\Setup1\Setup1.vdprojSetup1
A:
The reason is that we shipped the .NET Frameworks 3.5 SP1 package as Homesite Only, i.e. only supports "Install Prerequisites from Component Vendor's Website". We did this to keep the size of VS 2008 SP1 from being too large.
We did put info in the SP1 readme on how to get the files put on your machine to enable the samesite scenario. Look for "Enable Samesite".
http://download.microsoft.com/download/A/2/8/A2807F78-C861-4B66-9B31-9205C3F22252/VS2008SP1Readme.htm
Enable Samesite for the .NET Framework 3.5 SP1 bootstrapper package
If the .NET Framework 3.5 SP1 bootstrapper package is selected in the Prerequisite dialog box for a Setup project or in ClickOnce publishing, and also the "Download prerequisites from the same location as my application" option is selected, the following build error is shown:
The install location for prerequisites has not been set to 'component vendor's web site' and the file 'dotNetFx35setup.exe' in item 'Microsoft.Net.Framework.3.5.SP1' cannot be located on disk.
To resolve this issue:
Update the Package Data
1. Open the [Program Files]\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\DotNetFx35SP1 folder or %ProgramFiles(x86)%\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\DotNetFx35SP1 on x64 operating systems
2. Edit the Product.xml file in Notepad.
3. Paste the following into the < PackageFiles > element:
4. Find the element for < PackageFile Name="dotNetFX30\XPSEPSC-x86-en-US.exe" and change the PublicKey value to: 3082010A0282010100A2DB0A8DCFC2C1499BCDAA3A34AD23596BDB6CBE2122B794C8EAAEBFC6D526C232118BBCDA5D2CFB36561E152BAE8F0DDD14A36E284C7F163F41AC8D40B146880DD98194AD9706D05744765CEAF1FC0EE27F74A333CB74E5EFE361A17E03B745FFD53E12D5B0CA5E0DD07BF2B7130DFC606A2885758CB7ADBC85E817B490BEF516B6625DED11DF3AEE215B8BAF8073C345E3958977609BE7AD77C1378D33142F13DB62C9AE1AA94F9867ADD420393071E08D6746E2C61CF40D5074412FE805246A216B49B092C4B239C742A56D5C184AAB8FD78E833E780A47D8A4B28423C3E2F27B66B14A74BD26414B9C6114604E30C882F3D00B707CEE554D77D2085576810203010001
5. Find the element for < PackageFile Name="dotNetFX30\XPSEPSC-amd64-en-US.exe" and change the PublicKey value to the same as in step 4 above
6. Save the product.xml file
Download and Extract the Core Installation Files
1. Navigate to the following URL: http://go.microsoft.com/fwlink?LinkID=118080
2. Download the dotNetFx35.exe file to your local disk.
3. Open a Command Prompt window and change to the directory to which you downloaded dotNetFx35.exe.
4. At the command prompt, type:
dotNetFx35.exe /x:.
This will extract the Framework files to a folder named “WCU” in the current directory.
5. Copy the contents of the WCU\dotNetFramework folder and paste them in the %Program Files%\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\DotNetFx35SP1 folder (%ProgramFiles(x86)%\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\DotNetFx35SP1 on x64 operating systems). Note: Do not copy the WCU\dotNetFramework folder itself. There should be 5 folders under the WCU folder, and each of these should now appear in the DotNetFx35SP1 folder. The folder structure should resemble the following:
o DotNetFx35SP1 (folder)
* dotNetFX20 (folder
* dotNetFX30 (folder)
* dotNetFX35 (folder)
* dotNetMSP (folder)
* TOOLS folder)
* en (or some other localized folder)
* dotNetFx35setup.exe (file)
You may now delete the files and folders you downloaded and extracted in steps 2 and 4.
Apr 6, 2010
Sending a bit image to an Epson TM-T88III receipt printer using C# and ESC/POS
At Skiviez/WFS, our “low-volume fulfillment requests” (which usually simply means “retail orders”) get a simple receipt printed out via an Epson TM-T88III receipt printer.
You’ve probably seen the TM-T88III before, though the current model is the TM-T88IV–they’re ubiquitous and especially common in restaurants. We chose it because the receipt prints quickly (which reduces the probability that a stack of order receipts gets missorted such that an employee puts the wrong receipt in the wrong parcel), is thermal so there is no ink to replace (which reduces the cost of consumables), uses standard thermal receipt paper (which is cheap, and we can always run out to Staples if we run out), and, perhaps most importantly, we found one in a box in the back of the warehouse.
At the top of the retail receipt, I print out the Skiviez logo. This is stored in a non-volatile area of memory in the printer (called NV RAM in EPSON parlance), and I dole out a command that essentially says “print out the image stored in slot #1,” having previously uploaded the bitmap via the printer driver’s flash utility. This worked great for as long as we only shipped Skiviez orders and only had one TM-T88III printer. But with the advent of WFS, however, Skiviez now ships orders (we call them “fulfillment requests”) for multiple e-commerce stores, each requiring a different logo to appear at the top of the receipt. So I now had two options:
- I could manually upload the logos for all of the merchants that we ship for into all of the receipt printers that we use, making sure to upload each merchant image into the same NV memory slot for each printer.
- I could store the merchant’s logo in our database, figure out how to use the “select bit image mode” receipt printer command, and just send the image data just-in-time whenever I generated a receipt.
The second option has obvious maintenance benefits, so that’s the path I started hacking my way down.
A brief primer on ESC/POS and data
Like the Zebra LP2844 and its brethren, the TM-T88III comes with a Windows printer driver that allows Windows and applications to see it as any other GDI-based printer. You can print a Word document to the TM-T88III and it will print, albeit slowly, and it’ll look like crap. That’s because, as with any other GDI printer, the Word document gets rendered as a bitmap and the entire big-ass bitmap gets sent to the poor little receipt printer which then prints it 24 dots at a time. A better method is to use the printer for its intended purpose and use the native printer language to generate the receipt. This allows us to select printer fonts that are specifically designed for the printer’s resolution and speed characteristics and use other advanced features of the printer such as the paper cutter.
That printer language is called ESC/POS, which is entirely unpronounceable and a stupid name. The “POS” stands for “Point of Sale” since this printer belongs to a class of devices commonly known as point of sale devices–cash drawers, VFDs, barcode scanners, and the like. The “ESC” stands for “escape” because the printer treats any data that is sent to it as text–passed straight through–unless it is escaped with a special character to indicate that a command instruction follows in the input stream. The most common escape character used is the ASCII ESC
character, and so we have “ESC/POS” as our language name.
If you’ve just picked up one of these printers on eBay, you may find that getting documentation on this language is hard to come by because you need to get a copy of the “ESC/POS Application Programming Guide” from your “EPSON Authorized Dealer,” whoever the hell that is. (Again, we found ours in a box in the back of the warehouse, so my “authorized dealer” is long gone.) Still, some Googling will give you more or less complete and current copies of these guides, which I’ve mirrored here. Go ahead and download them:
(I particularly like how the programming guide has “proprietary” and “confidential” stamped all over it and then proceeds to describe how great EPSON is for “taking initiative” for “expandability” and “universal applicability” on page 7 of the same document. You can’t make this crap up–it’s full of oxymorons.)
Having just come from writing in the EPL2 printer language for our Zebra LP2844 and Zebra ZP 500 Plus thermal label printers, I found the ESC/POS language to be confusing. My confusion mostly stemmed partly from the way the documentation was written and partly because I was used to the way EPL2 worked. Let’s take a look at the documentation for the bit image command that I’m trying to use:
If this were EPL2, I would actually send the document as a string to the printer, so if a command needed the integer 33 as a parameter, I would send the string “33″ (two bytes of data). In ESC/POS, each parameter is a single byte, so if I want to send 33 as a parameter for m
in the bit image command above, then I need to send 33 in a single byte: that is, 0b100001
, which is 25 + 20 = 32 + 1 = 33.
The reason that this is super confusing is that other parameters in the documentation are specified to be ASCII characters, such as the ‘*’ parameter in the bit image command above. This is because low-level programmers, such as those who designed the ESC/POS language, tend to blur the lines between data types: it’s all bytes at the end of the day. As a result, if you’re using C#, you might be tempted to use a StringBuilder
to build up your document to send to the printer. Don’t do it! You’ll inevitably get confused by its overloads. Let’s take that m = 33
parameter as an example:
var sb = new StringBuilder();
// ASCII escape
sb.Append((char)27);
// ASCII '*' for the bit image command
sb.Append('*');
// oops! this appends the string "33" in two bytes, doesn't work
sb.Append(33);
// this is what I want, but semantically weird
sb.Append((char)33);
A much better and less confusing in the long run solution is to eschew any notion that we are dealing with text. Instead, let’s use the semantics that we are sending bytes of raw binary data directly to the printer:
using (var ms = new MemoryStream())
using (var bw = new BinaryWriter(bw))
{
bw.Write(AsciiControlChars.Escape);
bw.Write('*'); // bit-image mode
bw.Write((byte)33); // 24-dot double-density
}
But remember that cast to byte
! Otherwise, you’ll get a C# int
, which is four bytes, written to the stream, when we only wanted one. Yes, that means that any parameter that an ESC/POS command takes has a maximum value of 255.
You could get by on the StringBuilder
method for a while and not have it burn you until you try the bit image command because all the characters that you’re appending happen to be less than 128–greater than that and you start getting into bytes that don’t map to a Unicode character for quirky historical reasons, such as the range 128 to 159, and you’ll be pulling your hair out as to why some of your data is getting “lost.” Just use the BinaryWriter
method. You can thank me later.
Why is the bit image command designed this way?
The m
parameter in the bit image command as defined in the previous section has 4 values, and each value changes the way we construct the image data bytes and the way the printer interprets them. The first three values are of dubious value, so let’s throw them out for now. We’ll focus on m = 33
, which means “24-dot double density mode.” So keep this in mind and set this aside.
When we think of drawing a bitmap in high level software, we usually think of drawing it pixel by pixel, left to right, top to bottom. So the order that the dots get printed as indicated by the programming guide seems strange:
That is, the dots are rendered from top to bottom, then left to right.
Why in the blue hell do I need to rearrange my nice and neat array of bitmap data into this Connect Four scheme of dots? It all comes down to the size of the print head.
For our mental model, the thermal print head in the receipt printer is physically 1 dot wide by 24 dots high where there are 203 dots per inch (square pixels). It moves left to right across the receipt paper, burning up to 24 dots in the vertical (y) direction for each dot in the horizontal (x) direction.
Since we’re talking about a thermal printer here, there’s no concept of gray scale tones–we’ve either burned a dot into the paper (black) or we haven’t. So if we’re sending image data to the printer in bytes, it would make sense to say that a bit set to 1 means “burn a dot” and a bit set to “0″ means do nothing.
But a byte is only 8 bits wide, and we have 24 dots to burn in each “slice” of the image. 24 just happens to be divisible by 8, so we can send 3 bytes of data for each slice to represent our 24 dots. (If the bitmap I want to draw is taller than 24 dots/pixels, then I need to send multiple bit image commands, effectively doing multiple stripes of the image that are 24 dots high; more on this later.)
So, now, the diagram in the programming guide makes sense: we’re not burning 1 dot at time, we’re burning a vertical stripe that is 1 dot wide and 24 dots high at a time, and then moving to the right. So our printer reads our first 3 bytes of image data, burns the dots specified by those 24 bits, then reads the next 3 bytes, and so on.
Aside: Converting the bitmap to monochrome
Right. So a merchant has given me a *.bmp file, and I need to convert that into array of bits that I can send to the receipt printer. It’s a good bet that the bitmap that the merchant sent me for the logo is not monochrome (that is, every pixel is either 100% black or 100% white).
So what we can do is look at each pixel in the bitmap, determine its luma, and if that’s below a certain threshold, count that pixel as black. How did I write the code to do this? I looked the formula up via the intertubes and modified it to fit my needs:
private static BitmapData GetBitmapData(string bmpFileName)
{
using (var bitmap = (Bitmap)Bitmap.FromFile(bmpFileName))
{
var threshold = 127;
var index = 0;
var dimensions = bitmap.Width * bitmap.Height;
var dots = new BitArray(dimensions);
for (var y = 0; y < bitmap.Height; y++)
{
for (var x = 0; x < bitmap.Width; x++)
{
var color = bitmap.GetPixel(x, y);
var luminance = (int)(color.R * 0.3 + color.G * 0.59 + color.B * 0.11);
dots[index] = (luminance < threshold);
index++;
}
}
return new BitmapData()
{
Dots = dots,
Height = bitmap.Height,
Width = bitmap.Width
};
}
}
BitmapData
is just a little struct that contains the three properties that you see here. After all, once I have my BitArray
with 1
indicating black dots and 0
indicating white dots, I don’t need the original Bitmap
instance anymore.
Just to clarify, this means that we’re holding onto a BitArray
of monochrome data in the Dots
property that conceptually looks something like this:
Marshalling the monochrome data
Now for the trickiest part of all. We need to take our BitArray
of monochrome data, divide it up into 8-dot chunks, represent those 8-dot chunks as bytes, and send them to the printer in the order required by the bit image command, discussed above. That is, I’ll need to send the data as bytes in this order:
So the printer will draw them one vertical stripe of three bytes each at a time. (If you don’t get it, print out that image, cut up the bytes, and then arrange them in the order shown in the diagram from the EPSON documentation. It’ll instantly make sense once you see it.) The X’s in the diagram correspond to bits that aren’t in our original image that we have to send anyway as padding. Remember, since we selected the 24-dot double density mode, the printer is going to draw a 1 x 24 dot slice as it moves the print head. My example bitmap is only 4 pixels tall, so I have to send 20 zero bits as padding.
And thus we would end up with our pretty image.
Unfortunately, there is some math involved in translating the bits in our BitArray
into these bytes that we need to send. Assuming bw
is our reference to a BinaryWriter
, here’s the code that does just that:
// So we have our bitmap data sitting in a bit array called "dots."
// This is one long array of 1s (black) and 0s (white) pixels arranged
// as if we had scanned the bitmap from top to bottom, left to right.
// The printer wants to see these arranged in bytes stacked three high.
// So, essentially, we need to read 24 bits for x = 0, generate those
// bytes, and send them to the printer, then keep increasing x. If our
// image is more than 24 dots high, we have to send a second bit image
// command to draw the next slice of 24 dots in the image.
// Set the line spacing to 24 dots, the height of each "stripe" of the
// image that we're drawing. If we don't do this, and we need to
// draw the bitmap in multiple passes, then we'll end up with some
// whitespace between slices of the image since the default line
// height--how much the printer moves on a newline--is 30 dots.
bw.Write(AsciiControlChars.Escape);
bw.Write('3'); // '3' just means 'change line height command'
bw.Write((byte)24);
// OK. So, starting from x = 0, read 24 bits down and send that data
// to the printer. The offset variable keeps track of our global 'y'
// position in the image. For example, if we were drawing a bitmap
// that is 48 pixels high, then this while loop will execute twice,
// once for each pass of 24 dots. On the first pass, the offset is
// 0, and on the second pass, the offset is 24. We keep making
// these 24-dot stripes until we've run past the height of the
// bitmap.
int offset = 0;
while (offset < data.Height)
{
// The third and fourth parameters to the bit image command are
// 'nL' and 'nH'. The 'L' and the 'H' refer to 'low' and 'high', respectively.
// All 'n' really is is the width of the image that we're about to draw.
// Since the width can be greater than 255 dots, the parameter has to
// be split across two bytes, which is why the documentation says the
// width is 'nL' + ('nH' * 256).
bw.Write(AsciiControlChars.Escape);
bw.Write('*'); // bit-image mode
bw.Write((byte)33); // 24-dot double-density
bw.Write(width[0]); // width low byte
bw.Write(width[1]); // width high byte
for (int x = 0; x < data.Width; ++x)
{
// Remember, 24 dots = 24 bits = 3 bytes.
// The 'k' variable keeps track of which of those
// three bytes that we're currently scribbling into.
for (int k = 0; k < 3; ++k)
{
byte slice = 0;
// A byte is 8 bits. The 'b' variable keeps track
// of which bit in the byte we're recording.
for (int b = 0; b < 8; ++b)
{
// Calculate the y position that we're currently
// trying to draw. We take our offset, divide it
// by 8 so we're talking about the y offset in
// terms of bytes, add our current 'k' byte
// offset to that, multiple by 8 to get it in terms
// of bits again, and add our bit offset to it.
int y = (((offset / 8) + k) * 8) + b;
// Calculate the location of the pixel we want in the bit array.
// It'll be at (y * width) + x.
int i = (y * data.Width) + x;
// If the image (or this stripe of the image)
// is shorter than 24 dots, pad with zero.
bool v = false;
if (i < dots.Length)
{
v = dots[i];
}
// Finally, store our bit in the byte that we're currently
// scribbling to. Our current 'b' is actually the exact
// opposite of where we want it to be in the byte, so
// subtract it from 7, shift our bit into place in a temp
// byte, and OR it with the target byte to get it into there.
slice |= (byte)((v ? 1 : 0) << (7 - b));
}
// Phew! Write the damn byte to the buffer
bw.Write(slice);
}
}
// We're done with this 24-dot high pass. Render a newline
// to bump the print head down to the next line
// and keep on trucking.
offset += 24;
bw.Write(AsciiControlChars.Newline);
}
// Restore the line spacing to the default of 30 dots.
bw.Write(AsciiControlChars.Escape);
bw.Write('3');
bw.Write((byte)30);
This code looks confusing, and is, but I’ve tried to document it with explanatory comments in line.
Sending the document to the printer
Finally, there is the task of sending this array of bytes directly to the printer. I’ve talked about this before and the code is uninteresting, requiring some P/Invokes into native APIs. Essentially, you pass in the name of the printer as it appears in the Control Panel, the array of bytes from our BinaryWriter
, and you’re all set:
private static void Print(string printerName, byte[] document)
{
NativeMethods.DOC_INFO_1 documentInfo;
IntPtr printerHandle;
documentInfo = new NativeMethods.DOC_INFO_1();
documentInfo.pDataType = "RAW";
documentInfo.pDocName = "Bit Image Test";
printerHandle = new IntPtr(0);
if (NativeMethods.OpenPrinter(printerName.Normalize(), out printerHandle, IntPtr.Zero))
{
if (NativeMethods.StartDocPrinter(printerHandle, 1, documentInfo))
{
int bytesWritten;
byte[] managedData;
IntPtr unmanagedData;
managedData = document;
unmanagedData = Marshal.AllocCoTaskMem(managedData.Length);
Marshal.Copy(managedData, 0, unmanagedData, managedData.Length);
if (NativeMethods.StartPagePrinter(printerHandle))
{
NativeMethods.WritePrinter(
printerHandle,
unmanagedData,
managedData.Length,
out bytesWritten);
NativeMethods.EndPagePrinter(printerHandle);
}
else
{
throw new Win32Exception();
}
Marshal.FreeCoTaskMem(unmanagedData);
NativeMethods.EndDocPrinter(printerHandle);
}
else
{
throw new Win32Exception();
}
NativeMethods.ClosePrinter(printerHandle);
}
else
{
throw new Win32Exception();
}
}
The P/Invoke declarations can be found in the sample code at the end of the article, and the documentation is available on MSDN. But, again, there’s nothing sexy going on here.
Conclusions and delusions
And that’s a wrap. I always find it interesting when I encounter a problem at work–which is a small business by any definition of the phrase–and the Google indicates that no one has ever encountered or documented this problem before. And it’s these types of integration problems that I find fascinating; after years of playing in castles in the sky, dealing with databases and exceptions and HTML forms, it’s nice to know that I can dust off the corners of my brain and my college degree and deal with bits and bytes when I need to. Writing software that makes hardware do stuff is always fun, too.
And after hours of watching the receipt printer spew out lines and lines of gibberish, there’s nothing like the feeling of seeing the bitmap print out and knowing that, finally, things have simply started to work.
Good luck!