Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

Thursday, May 3, 2012

Create Shortcuts using late binding

Simply create a new shortcut using some late binding, instead of having to reference the Windows Scripting Host : IWshRuntimeLibrary

Usage:
Shortcut.Create_Shortcut(PathtoExecutable, PathToShortcutFolder, ShortCutName,ExecutableFolderPath, Description, "", 0, 0)

 
Imports System.Runtime.CompilerServices
Imports Microsoft.VisualBasic.CompilerServices
Public Class Shortcut
Public Shared Function Create_Shortcut(ByVal Target_Path As String, ByVal Shortcut_Path As String, ByVal Shortcut_Name As String, _
ByVal Working_Directory As String, ByVal Description As String, ByVal Arguments As String, ByVal Window_Style As Integer, ByVal Icon_Num As Integer) As Boolean
Try
Dim objectValue As Object = RuntimeHelpers.GetObjectValue(Interaction.CreateObject("WScript.Shell", ""))
Dim objectValue2 As Object = RuntimeHelpers.GetObjectValue(NewLateBinding.LateGet(objectValue, Nothing, _
"CreateShortcut", New Object() {Shortcut_Path & "\" & Shortcut_Name & ".lnk"}, Nothing, Nothing, Nothing))
NewLateBinding.LateSet(objectValue2, Nothing, "TargetPath", New Object() {Target_Path}, Nothing, Nothing)
NewLateBinding.LateSet(objectValue2, Nothing, "WorkingDirectory", New Object() {Working_Directory}, Nothing, Nothing)
NewLateBinding.LateSet(objectValue2, Nothing, "Arguments", New Object() {Arguments}, Nothing, Nothing)
NewLateBinding.LateSet(objectValue2, Nothing, "WindowStyle", New Object() {Window_Style}, Nothing, Nothing)
NewLateBinding.LateSet(objectValue2, Nothing, "Description", New Object() {Description}, Nothing, Nothing)
NewLateBinding.LateSet(objectValue2, Nothing, "IconLocation", New Object() {Target_Path & "," _
& Conversions.ToString(Icon_Num)}, Nothing, Nothing)
NewLateBinding.LateCall(objectValue2, Nothing, "Save", New Object() {}, Nothing, Nothing, Nothing, True)
Return True
Catch ex As Exception
Return False
End Try
End Function
End Class

A special thanks to Tirthraaj Gobin for comming up with this solution...
http://social.msdn.microsoft.com/Forums/en-US/vbide/thread/126c09a2-6f38-4747-9ddd-55ff977fcba0?prof=required

Wednesday, May 2, 2012

PDF Layer Merger

 

Recently I’ve created a simple .NET tool, to merge 2 PDF files, one as a layer on top of the other.

The reason behind this, was to be able to digitally remark/comment/mark-up on a PDF file using AutoCAD.  People tend to print out a PDF file, use some highlighters and draw some clouds and comments on this PDF, only to re-scan this afterwards.
This is the way we used to work, but today we live in a digital era…

The tool has been built in VB.NET targeting the NET 2.0 framework, with the use of the excellent PDF library iTextSharp (based on iText).

image


Snippet of the actual transformation function:

  
'''
''' Combines two source pdf's to one single pdf.
'''

''' The file-location incl. the name of the original pdf.
''' The file-location incl. the name of the mark-up pdf.
''' The file-location incl. the name of the generated combined pdf.
''' The name which the added mark-up layer will have in the new pdf.
''' The transformation which is executed on the original pdf: rotation, scaleX, scaleY, offsetX, offsetY
''' Return boolean indication whether the combination of the pdf's was successful.
'''
Public Function PDFCombineLayer(ByVal sourceOriginal As String, ByVal sourceMarkup As String, ByVal destination As String, Optional ByVal NewLayer As String = "SOURCE-DRAWING", Optional ByVal Transform As String = "0;1;1;0;0") As Boolean
Dim oPdfOriginal As iTextSharp.text.pdf.PdfReader = Nothing
Dim oPdfMarkup As iTextSharp.text.pdf.PdfReader = Nothing
Dim oPdfStamper As iTextSharp.text.pdf.PdfStamper = Nothing
Dim PDFDirectContent As iTextSharp.text.pdf.PdfContentByte
Dim iNumberOfPages As Integer = Nothing
Dim iPage As Integer = 0
Dim matrix1 As Drawing2D.Matrix = Nothing
Dim matrix2 As Drawing2D.Matrix = Nothing

Try
'Read the source pdf-files and prepare new file.
oPdfOriginal = New iTextSharp.text.pdf.PdfReader(sourceOriginal)
oPdfMarkup = New iTextSharp.text.pdf.PdfReader(sourceMarkup)
oPdfStamper = New iTextSharp.text.pdf.PdfStamper(oPdfMarkup, New IO.FileStream(destination, IO.FileMode.Create, IO.FileAccess.Write))

'Count pages in original pdf and create new pdf wih same amount of pages.
iNumberOfPages = oPdfOriginal.NumberOfPages
oPdfStamper.GetPdfLayers()

'Make transformation matrix
If Transform = "" Then Transform = "0;1;1;0;0" 'Note transform: rotation(degrees), scaleX, scaleY, offsetX, offsetY
Dim ar() As String = Split(Transform, ";")
If Not ar.Length = 5 Then ar = New String() {0, 1, 1, 0, 0}
If ar(1) <= 0 Or ar(2) <= 0 Then Throw New ArgumentOutOfRangeException("transform", "Make sure that scaling factors of the transformation are larger than zero.")
matrix1 = New Drawing2D.Matrix
matrix1.Rotate(-CSng(ar(0)))
matrix1.Scale(CSng(ar(1)), CSng(ar(2)))
matrix1.Translate(CSng(ar(3)), CSng(ar(4)))
matrix2 = New Drawing2D.Matrix

'Add the new layer to each of the pdf-pages.
If NewLayer = "" Then NewLayer = "SOURCE-DRAWING"
Dim PDFNewLayer As New iTextSharp.text.pdf.PdfLayer(NewLayer, oPdfStamper.Writer)
Dim PDFLayerMem As New iTextSharp.text.pdf.PdfLayerMembership(oPdfStamper.Writer)
PDFLayerMem.AddMember(PDFNewLayer)
Do While (iPage < iNumberOfPages)
'Get new page
iPage += 1
Dim iPageOriginal As iTextSharp.text.pdf.PdfImportedPage = oPdfStamper.Writer.GetImportedPage(oPdfOriginal, iPage)
PDFDirectContent = oPdfStamper.GetUnderContent(iPage) 'add page under existing pdf in new layer
matrix2 = matrix1.Clone 'Cloning is necessary, otherwise all adaptations to matrix2 are also done in matrix1.

'Compensate for the orientation of the original pdf-file.
Dim iRotation As Single = oPdfOriginal.GetPageRotation(iPage)
Select Case iRotation
Case Is = 90
matrix2.Rotate(-iRotation)
matrix2.Translate(-oPdfOriginal.GetPageSize(iPage).Width, 0)
Case Is = 180 'Not tested yet.
matrix2.Rotate(-iRotation)
matrix2.Translate(-oPdfOriginal.GetPageSize(iPage).Width, -oPdfOriginal.GetPageSize(iPage).Height)
Case Is = 270 'Not tested yet.
matrix2.Rotate(-iRotation)
matrix2.Translate(0, oPdfOriginal.GetPageSize(iPage).Height)
Case Else
End Select

'Add the new layer ot this page.
PDFDirectContent.BeginLayer(PDFNewLayer)
PDFDirectContent.AddTemplate(iPageOriginal, matrix2)
PDFDirectContent.EndLayer()
Loop
oPdfStamper.SetFullCompression()
Return True
Catch ex As Exception
Return False
Finally
oPdfMarkup.Close()
oPdfOriginal.Close()
oPdfStamper.Close()
matrix1.Dispose()
matrix2.Dispose()
End Try
End Function

Code has been posted on Google Code http://code.google.com/p/pdflayermerger/


Many thanks to iText for the excellent library and to Lukasz Swiatkowski for the glass button component (http://www.codeproject.com/Articles/17695/Creating-a-Glass-Button-using-GDI).

Wednesday, March 9, 2011

Enable Compiler Extensions in .NET 2.0

If you get an error like shown below; this is probably due to the downgrade of a project from .NET 3.5 or higher to .NET 2.0

Cannot define a new extension method because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll?

Extension methods are a new feature in .NET 3.5 (C#3/VB9) that let you appear to "spot weld" new methods on to existing classes.
If you think that the "string" object needs a new method, you can just add it and call it on instance variables.

But how do we accomplish this in .NET 2.0?

VB.NET

' you need this once (only), and it must be in this namespace
Namespace System.Runtime.CompilerServices
    <AttributeUsage(AttributeTargets.Assembly Or AttributeTargets.[Class] Or AttributeTargets.Method)> _
    Public NotInheritable Class ExtensionAttribute
        Inherits Attribute
    End Class
End Namespace
' you can have as many of these as you like, in any namespaces
Public NotInheritable Class MyExtensionMethods
    Private Sub New()
    End Sub
    <System.Runtime.CompilerServices.Extension> _
    Public Shared Function MeasureDisplayStringWidth(graphics As Graphics, text As String) As Integer
        ' ... 
 
    End Function
End Class

C#



// you need this once (only), and it must be in this namespace
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class
         | AttributeTargets.Method)]
    public sealed class ExtensionAttribute : Attribute {}
}
// you can have as many of these as you like, in any namespaces
public static class MyExtensionMethods {
    public static int MeasureDisplayStringWidth (
            this Graphics graphics, string text )
    {
           /* ... */
    }
}

Tuesday, February 15, 2011

FileDB - A C# database to store files

When browsing through the codeplex website, I found an interesting project.
A simple easy to use storage container, in the past I used QVFS
http://www.codeproject.com/KB/vb/QVFS.aspx .
But this project seems to be abandoned. This seems to be very promising, the only thing I'm missing, that is available in QVFS is the ability
to create a folder structure inside the virtual storage file. On the other hand QVFS uses 2 files, 1 info file and a data file as this FileDB uses only 1 file.

Ripped of CodePlex website : http://filedb.codeplex.com/
Credits go to mbdavid http://www.codeplex.com/site/users/view/mbdavid

 

FileDB - Project Description




FileDB is a free, fast, lightweight C# (v3.5) DLL project to store, retrieve and delete files using a single archive file as a container on disk.
It's ideal for storing files (all kind, all sizes) without databases and keeping them organized on a single disk file.



  • First stable version was launched
  • Improvements on concurrent write access
  • New method: Export - Export all files to a directory

using Numeria.IO;

var pathDB = @"C:\Temp\MyDB.fdb";

// Creating an empty FileDB archive
FileDB.CreateEmptyFile(pathDB);

// Store file from input stream
var info = FileDB.Store(pathDB, "MyFileName.jpg", inputStream);
// -or- store directly from the file itself
var info = FileDB.Store(pathDB, @"C:\Temp\MyPhoto.jpg");

// The 'info' variable returned contains information about your file (generated GUID, filename, file-length, mime-type)
var fileGuid = info.ID;

// Reading file inside FileDB and writing it on an output stream (also available to write it directly to a file)
var info = FileDB.Read(pathDB, fileGuid, outputStream);

// Deleting a file
var ok = FileDB.Delete(pathDB, fileGuid);


FileDB Methods



  • CreateEmptyFile - Create an empty data file archive
  • Store - Store from file/stream to the data file
  • Read - Search a fileID and restore it to output file/stream
  • Delete - Delete a file
  • ListFiles - List all files inside the archive
  • Shrink - Reorganize archive removing unused disk space
  • Export - Export files inside archive to a directory

All operations have a static method helper or can be used from a FileDB instance.


ASP.NET MVC Example

Below is a basic example to store/retrieve/delete information in a ASP.NET MVC Controller



private string pathDB = @"C:\Temp\MvcDemo.fdb";

// Uploading a file
[HttpPost]
public ActionResult Upload()
{
HttpPostedFileBase file = Request.Files[0] as HttpPostedFileBase;

if (file.ContentLength > 0)
FileDB.Store(pathDB, file.FileName, file.InputStream);

return RedirectToAction("Index");
}

// Download
[HttpGet]
public ActionResult Download(string id)
{
// Using a classic way to download a file, instead of FileContentResult mode.
// Optimizing for big files. Your webserver will not consume CPU/Memory to download this file (even very large files)

using (var db = new FileDB(pathDB, FileAccess.Read))
{
var info = db.Search(Guid.Parse(id));

Response.Buffer = false;
Response.BufferOutput = false;
Response.ContentType = info.MimeType;
Response.AppendHeader("Content-Length", info.FileLength.ToString());
Response.AppendHeader("content-disposition", "attachment; filename=" + info.FileName);

db.Read(info.ID, Response.OutputStream);

return new EmptyResult();
}
}


Why?


Well, all web developers already had this problem: "I need to store same user files (photos, documents, ...) and I need to save them on my web server. But where? File system or database?"
(See some discussion in http://stackoverflow.com/questions/3748/storing-images-in-db-yea-or-nay#3756)
The problem is: the database was not designed to store files. ADO.NET doesn't have a good method to work with streams (only byte[]).
For small files (less then 100K) it works very nice. But what happens with a 10Mb file? 100Mb? It's terrible! The solution: work on file system!
The file system has a problem too: what if my user wants to upload 300 files? And what if I have 2000 users?
You will have thousands of files to backup and manage, and this will be a pain.
My idea: a single archive (or a single archive per user) to store only user uploaded files.
I use the SQL Server database to store the ID file reference (GUID) and FileDB does the rest of the job, storing and managing the files and bytes.


How FileDB works?


FileDB was designed to be fast, very simple, file-based and works with all file sizes.
FileDB doesn't consume lots of memory because it works only with streams (no byte[]).
The data structure is built with two kinds of pages: IndexPage and DataPage.
- The IndexPage stores information about the file descriptor and it's organized in a binary tree structure.
- The DataPage stores the file bytes.

Both pages have 4096 bytes (plus 100 bytes to file header). Each page has its own header to store information about the data inside the page.
You can delete a file inside the archive and the empty data pages will be used on next insert. Or you can shrink database to get your non-used bytes.
FileDB caches (in memory) only index pages, to be faster on a second search.


Limitations

I've made many tests to check performance and limitations with all file sizes.
To protect against many clients changing data on same archive, FileDB uses read share mode.
This way many users can search/read simultaneously but only one user can write (store/delete) at a time.
FileDB class also implements IDisposable so you can use it inside a using code.


using(var db = new FileDB(pathDB, FileAccess.ReadWrite))
{
db.Store(@"C:\Temp\MyPhoto.jpg");
}



The data size limitations are based on .NET MaxValue constants. FileDB works with UInt32 (4 bytes unsigned), which limits each file to 4GB and the database to 16TB (4096 Pages * UInt32.MaxValue).