private void btnFirstPrototype_Click(object sender, EventArgs e) { int pagecount; if (!int.TryParse(txtNumberPages.Text, out pagecount)) { MessageBox.Show("Number of Pages is required", "Number of Pages is required", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (pagecount < 1 || pagecount > 99) { MessageBox.Show("Number of Pages must be between 1-99", "Invalid Number of Pages", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // todo: take this out of the UI thread, put it in a class and run on a worker thread // and also rename this button - it's been a long time since this was my original prototype string previousCaption = this.Text; btnFirstPrototype.Enabled = false; this.Text = "GENERATING..."; this.UseWaitCursor = true; // todo: move all the grunt work in to a nice proper Threaded situation // meanwhile, DoEvents is called here to allow the wait cursor, form caption and other "I'm busy..." indicators to be shown to the user before we get into the hard stuff that takes a few moments Application.DoEvents(); string outputFile = txtOutputFile.Text; string logFilePath = string.Format("{0} - LOG {1}.txt", outputFile, DateTime.Now.ToString("yyyy-MM-dd HHmmss")); StreamWriter logFile = new StreamWriter(logFilePath); CoinDef thiscoin = new CoinDef(bundle.template.CoinName, bundle.template.CoinAddressType, bundle.template.CoinIsWIFstupid); logFile.WriteLine(string.Format("Paper Wallet Printer version {0}", Application.ProductVersion)); logFile.WriteLine(string.Format("Generating wallets for coin {0}", thiscoin.ToString())); logFile.WriteLine("Using template: " + bundle.template.TemplateDescription.Replace("\n", " ")); // removes any line breaks from template description as it writes it to the log file bundle.template.LayoutDebugging = layoutDebuggingToolStripMenuItem.Checked; if (bundle.template.LayoutDebugging) { logFile.WriteLine("Layout Debugging is enabled - printed wallets are not intended for live use"); } // make a pdfSharp document PdfDocument doc = new PdfDocument(); int batchNumber = 1; bool crashingOut = false; XImage imgArtwork = XImage.FromGdiPlusImage(bundle.getArtworkImage()); // loop for each page for (int pageNumber = 1; pageNumber <= pagecount; pageNumber++) { // and put a page on it PdfPage page = doc.AddPage(); // note: if you change anything that gets written to logFile from this point until the end-marker is written, this will probably cause // compatibility issues with the Loader tool, which expects to be able to parse the log between these points logFile.WriteLine(string.Format("Generating Page {0} of {1}", pageNumber, pagecount)); logFile.WriteLine("Generated Addresses:"); logFile.WriteLine(); page.Size = bundle.template.pagePrintPaperSize; using (XGraphics gfx = XGraphics.FromPdfPage(page, XPageDirection.Downwards)) { // LOOP Down for (int y = 0; y < bundle.template.pagePrintRows; y++) { // LOOP Across for (int x = 0; x < bundle.template.pagePrintCols; x++) { try { // get PrivKey and Address for this new Wallet KeyPair kp = KeyPair.CreateX(ExtraEntropy.GetEntropy(), false, bundle.template.CoinAddressType, bundle.template.CoinIsWIFstupid); KeyCollectionItem item = new KeyCollectionItem(kp); string address = item.GetAddressBase58(); logFile.WriteLine(address); // get an XForm of our Wallet using (XForm walletForm = getSingleWallet(doc, bundle, imgArtwork, address, item.PrivateKey, batchNumber, bundle.template.LayoutDebugging)) { // decide where on the page to draw this wallet XForm object XUnit walletLeft = XUnit.FromMillimeter(bundle.template.pagePrintLeftMarginMM + ((bundle.template.widthMM + bundle.template.pagePrintColGap) * x)); XUnit walletTop = XUnit.FromMillimeter(bundle.template.pagePrintTopMarginMM + ((bundle.template.heightMM + bundle.template.pagePrintRowGap) * y)); XPoint whereToPutTheForm = new XPoint(walletLeft.Point, walletTop.Point); gfx.DrawImage(walletForm, whereToPutTheForm); } batchNumber++; } catch (Exception ex) { // well, damn... logFile.WriteLine("--- CRASH! ---"); logFile.WriteLine("Encountered Unhandled Exception inside primary Y-X Print Loop"); logFile.WriteLine("Exception:"); logFile.WriteLine(ex.Message); logFile.WriteLine(ex.Source); logFile.WriteLine(ex.StackTrace); crashingOut = true; break; } } if (crashingOut) { break; } } } if (crashingOut) { break; } logFile.WriteLine(); logFile.WriteLine("end"); logFile.WriteLine(); } if (!crashingOut) { logFile.WriteLine("Internal PDF Generation completed OK"); if (File.Exists(outputFile)) { File.Delete(outputFile); } try { doc.Save(outputFile); } catch (Exception ex) { logFile.WriteLine("--- CRASH! ---"); logFile.WriteLine("Encountered Unhandled Exception when Saving PDF from Object to File"); logFile.WriteLine("Exception:"); logFile.WriteLine(ex.Message); logFile.WriteLine(ex.Source); logFile.WriteLine(ex.StackTrace); crashingOut = true; } logFile.WriteLine("PDF saved to File OK - all done"); } logFile.Close(); // if we have failed in generating the PDF in to memory, or failed when saving it to file - show an error to the user, directing them to to the log file if (crashingOut) { this.Text = previousCaption; btnFirstPrototype.Enabled = true; this.UseWaitCursor = false; string errmsg = string.Format("Sorry, Wallet Printing has failed. See the log file for details.\n\nLog file:\n{0}", logFilePath); MessageBox.Show(errmsg, "Printing Failed, Internal Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (chkOpenAfterGenerating.Checked) { // note the current datetime, for comparison if we're trying to wait for PDF Viewer DateTime timeOfStartingViewer = DateTime.Now; System.Diagnostics.Process procViewer = System.Diagnostics.Process.Start(outputFile); if (chkWipeOutputAfterViewing.Checked) { btnFirstPrototype.Enabled = false; this.Text = "WAITING FOR PDF VIEWER TO CLOSE"; procViewer.WaitForExit(); // ok, we *think* that maybe their PDF viewer has closed. But that might not be true. // check to see if we were delayed by a Suspiciously Short length of time TimeSpan timeWaitedForViewer = DateTime.Now - timeOfStartingViewer; if (timeWaitedForViewer.TotalSeconds < 15) { MessageBox.Show("I think your PDF viewer closed really quickly - or I cannot detect if you are done with the generated PDF.\n\nPlease click OK when you are ready for me to delete the generated PDF.", "Ready to delete PDF file?", MessageBoxButtons.OK, MessageBoxIcon.Question); } // overwrite contents of PDF with junk, then delete. This won't be secure against serious forensic attack in all situations, but it's better than just-delete FileInfo info = new FileInfo(outputFile); byte[] junkbytes = new byte[info.Length]; MemSet(junkbytes, 255); File.WriteAllBytes(outputFile, junkbytes); File.Delete(outputFile); } } this.Text = previousCaption; btnFirstPrototype.Enabled = true; this.UseWaitCursor = false; }