vrijdag 28 november 2008

VB.NET: File.Copy error: The specified network name is no longer available.

Today I had a strange problem with a VB.NET program I made some time ago. It simply copies some file from a Windows Server 2003 share to a Windows XP client. Suddenly it didn't work any more, giving the following error: System error 64 has occurred. The specified network name is no longer available. This seemed to happen on a large file.

After some searches I found out that this problem is not really in VB.NET, but actually is an error in Windows Server. It can be fixed with a small registry change on the server. Please see http://support.microsoft.com/kb/892100/en-us for details.

vrijdag 14 november 2008

VB.NET: Sign PDF in Visual Basic .NET

There are lots of expensive components out there to sign PDF files from your program, but what if you could do it for free? Below you will find my VB.NET code to sign a PDF file with your own PKCS 12 certificate. If you added the classes below to your project, signing a PDF file will become this simple:


Dim signer As New PdfSigner("[your certificate.pfx]", "[your certificate password]", meta)
signer.SignPdf("[your pdf file]", sPdfFile)


It couldn't be much easier right? Please also check the PdfSigner parameters for additional options. The following classes make use of the completely free iTextSharp library, that is available here. And now finally the classes:




Imports iTextSharp.text
Imports iTextSharp.text.pdf
Imports System.IO

'
' This class signs pdf files
'

Public Class PdfSigner
Public Enum enumCertification
NOT_CERTIFIED = 0
CERTIFIED_NO_CHANGES_ALLOWED = 1
CERTIFIED_FORM_FILLING = 2
CERTIFIED_FORM_FILLING_AND_ANNOTATIONS = 3
End Enum

Public Structure structSigMetaData
Dim sReason As String
Dim sContact As String
Dim sLocation As String
End Structure


Protected pdfMetaData As PdfMetaData
Protected certificate As Certificate

Public sLastError As String = ""
Public bMultipleSignatures As Boolean = False
Public bSignatureStamp As Boolean = False
Public Certification As enumCertification = enumCertification.CERTIFIED_NO_CHANGES_ALLOWED
Public SignatureMetaData As structSigMetaData
Public sSigImageFile As String = ""

#Region "Contructors"
Public Sub New(ByVal certificate As Certificate, Optional ByVal sPassword As String = "", Optional ByVal PdfMetaData As PdfMetaData = Nothing)
If IsNothing(PdfMetaData) Then PdfMetaData = New PdfMetaData

Me.pdfMetaData = PdfMetaData
Me.certificate = certificate

Me.SignatureMetaData.sReason = ""
Me.SignatureMetaData.sContact = ""
Me.SignatureMetaData.sLocation = ""
End Sub

Public Sub New(ByVal sCertificateFile As String, Optional ByVal sPassword As String = "", Optional ByVal PdfMetaData As PdfMetaData = Nothing)
If IsNothing(PdfMetaData) Then PdfMetaData = New PdfMetaData

Me.pdfMetaData = PdfMetaData
Me.certificate = New Certificate
Me.certificate.LoadCertificate(sCertificateFile, sPassword)

Me.SignatureMetaData.sReason = ""
Me.SignatureMetaData.sContact = ""
Me.SignatureMetaData.sLocation = ""
End Sub
#End Region

#Region "Functions"
'
' Sign the sInFile and write the signed file to sOutFile
'

Public Function SignPdf(ByVal sInFile As String, ByVal sOutFile As String) As Boolean
Dim st As PdfStamper
Dim fs As FileStream
Dim iWidth As Integer = 150
Dim iHeight As Integer = 150

Try
'Check if the input file exist
If Not File.Exists(sInFile) Then
Me.sLastError = "The input file " & sInFile & " does not exist"
Return False
End If

Dim reader As PdfReader = New PdfReader(sInFile)
fs = New FileStream(sOutFile, FileMode.Create, FileAccess.Write)
If Me.bMultipleSignatures Then
st = PdfStamper.CreateSignature(reader, fs, Chr(0), Nothing, True)
Else
st = PdfStamper.CreateSignature(reader, fs, Chr(0))
End If

st.MoreInfo = Me.pdfMetaData.MetaData
st.XmpMetadata = Me.pdfMetaData.GetMetaDataXmp


Dim sap As PdfSignatureAppearance = st.SignatureAppearance
sap.CertificationLevel = Me.Certification
sap.SetCrypto(Me.certificate.akp, Me.certificate.chain, Nothing, PdfSignatureAppearance.WINCER_SIGNED)

sap.Reason = Me.SignatureMetaData.sReason
sap.Contact = Me.SignatureMetaData.sContact
sap.Location = Me.SignatureMetaData.sLocation

If Me.sSigImageFile <> "" Then
If Not File.Exists(Me.sSigImageFile) Then
Me.sLastError = "The image file " & Me.sSigImageFile & " does not exist"
Return False
End If

Dim img As System.Drawing.Image = System.Drawing.Image.FromFile(Me.sSigImageFile)
sap.Image = Image.GetInstance(img, img.RawFormat)
iWidth = img.Width
iHeight = img.Height
End If

If Me.bSignatureStamp Then
sap.SetVisibleSignature(New iTextSharp.text.Rectangle(100, 100, 100 + iWidth, 100 + iHeight), 1, Nothing)
End If

Return True
Catch ex As Exception
Me.sLastError = ex.Message
Return False
Finally
Try
If Not IsNothing(st) Then st.Close()
Catch
End Try
End Try
End Function
#End Region

End Class




Imports System.IO

Imports Org.BouncyCastle.X509
Imports Org.BouncyCastle.Crypto
Imports Org.BouncyCastle.Pkcs

'
' This class hold the certificate and extract private key needed for e-signature
'

Public Class Certificate
Protected sPath As String = ""
Protected sPassword As String = ""

Public akp As AsymmetricKeyParameter
Public chain As X509Certificate()
Public sLastError As String = ""


'
' This method will load the given pkcs certificate in sFile
'

Public Function LoadCertificate(ByVal sPath As String, ByVal sPassword As String) As Boolean
Dim sAlias As String = Nothing
Dim pk12 As Pkcs12Store

Try
Me.sPath = sPath
Me.sPassword = sPassword

'Check if file exists
If Not File.Exists(sPath) Then
Me.sLastError = "The file " & sPath & " does not exist"
Return False
End If

'Read the certificate file
pk12 = New Pkcs12Store(New FileStream(sPath, FileMode.Open, FileAccess.Read), sPassword.ToCharArray())

'Iterate throught the certificate entries to find the private key entry
Dim i As IEnumerator = pk12.Aliases.GetEnumerator()
While (i.MoveNext())
sAlias = i.Current.ToString
If pk12.IsKeyEntry(sAlias) Then Exit While
End While

Me.akp = pk12.GetKey(sAlias).Key
Dim ce As X509CertificateEntry() = pk12.GetCertificateChain(sAlias)
ReDim Me.chain(0 To ce.Length - 1)
For k As Integer = 0 To ce.Length - 1
chain(k) = ce(k).Certificate
Next

Return True
Catch ex As Exception
Me.sLastError = ex.Message
Return False
End Try
End Function

End Class




Imports System.IO

Imports iTextSharp.text.xml.xmp

'
' This class wraps the PDF metadata
'

Public Class PdfMetaData
Protected info As Hashtable = New Hashtable()

#Region "Properties"
Public Property Author() As String
Get
Return info("Author")
End Get
Set(ByVal value As String)
If info.ContainsKey("Author") Then
info("Author") = value
Else
info.Add("Author", value)
End If
End Set
End Property

Public Property Title() As String
Get
Return info("Title")
End Get
Set(ByVal value As String)
If info.ContainsKey("Title") Then
info("Title") = value
Else
info.Add("Title", value)
End If
End Set
End Property

Public Property Subject() As String
Get
Return info("Subject")
End Get
Set(ByVal value As String)
If info.ContainsKey("Subject") Then
info("Subject") = value
Else
info.Add("Subject", value)
End If
End Set
End Property

Public Property Keywords() As String
Get
Return info("Keywords")
End Get
Set(ByVal value As String)
If info.ContainsKey("Keywords") Then
info("Keywords") = value
Else
info.Add("Keywords", value)
End If
End Set
End Property

Public Property Producer() As String
Get
Return info("Producer")
End Get
Set(ByVal value As String)
If info.ContainsKey("Producer") Then
info("Producer") = value
Else
info.Add("Producer", value)
End If
End Set
End Property

Public Property Creator() As String
Get
Return info("Creator")
End Get
Set(ByVal value As String)
If info.ContainsKey("Creator") Then
info("Creator") = value
Else
info.Add("Creator", value)
End If
End Set
End Property

Public ReadOnly Property MetaData() As Hashtable
Get
Return Me.info
End Get
End Property

#End Region

#Region "Initialization"
Public Sub New()
Me.Author = ""
Me.Title = ""
Me.Subject = ""
Me.Keywords = ""
Me.Producer = ""
Me.Creator = ""
End Sub
#End Region

#Region "Functions"
'
' Retruns the PDF metadata as an array of bytes
'

Public Function GetMetaDataXmp() As Byte()
Dim os As MemoryStream = New System.IO.MemoryStream()
Dim xmp As XmpWriter = New XmpWriter(os, Me.info)
xmp.Close()
Return os.ToArray()
End Function
#End Region

End Class



MS SQL Server: Generate SQL insert script from table data

Unfortunately Microsoft SQL Server (including the management studio) does not provide a good way to generate a SQL insert script from the data in your tables. I don't know why they did not include this feature, like in MySQL, but it means we have to build it our selfs. Below you will find a stored procedure that will generate a complete insert script from the data in the table given as parameter.


CREATE PROCEDURE SP_GenerateInserts
@TableName as varchar(40)
AS
BEGIN
SET NOCOUNT ON;

create table #tmp (
SQLText varchar(8000) )

create table #tmp2 (
Id int identity,
SQLText varchar(8000) )

delete #tmp
delete #tmp2

declare @vsSQL varchar(8000),
@vsCols varchar(8000),
@vsTableName varchar(40)

declare csrTables cursor for
select name
from sysobjects
where type in ('u')
and name = @TableName
order by name

open csrTables
fetch next from csrTables into @vsTableName

while (@@fetch_status = 0)
begin

select @vsSQL = '',
@vsCols = ''
select @vsSQL = @vsSQL +
CASE when sc.type in (39,47,61,111) then
'''''''''+' + 'isnull(rtrim(replace(['+ sc.name + '],'''''''','''''''''''')),'''')' + '+'''''',''+'
when sc.type = 35 then
'''''''''+' + 'isnull(rtrim(replace(substring(['+ sc.name + '],1,1000),'''''''','''''''''''')),'''')' + '+'''''',''+'
else
'isnull(convert(varchar,[' + sc.name + ']),''null'')+'',''+'
end
from syscolumns sc
where sc.id = object_id(@vsTableName)
order by ColID

select @vsCols = @vsCols + quotename(sc.name,'[') + ','
from syscolumns sc
where sc.id = object_id(@vsTableName)
order by ColID

select @vsSQL = substring(@vsSQL,1,datalength(@vsSQL)-1)

select @vsCols = substring(@vsCols,1,datalength(@vsCols)-1)

insert #tmp
exec ('select ' + @vsSQL + ' from ' + @vsTableName)

update #tmp
set sqltext = 'insert ' + @vsTableName + '(' + @vsCols + ') values(' + substring(sqltext,1,datalength(sqltext)-1) + ')'

insert #tmp2
select 'DELETE from ' + @vsTableName

insert #tmp2 values ('GO')

if (select count(id) from syscolumns where id = object_id(@vsTableName) and ((status & 128) = 128) ) = 1
begin
insert #tmp2
select 'set identity_insert ' + @vsTableName + ' on'
end

insert #tmp2
select * from #tmp

if (select count(id) from syscolumns where id = object_id(@vsTableName) and ((status & 128) = 128) ) = 1
begin
insert #tmp2
select 'set identity_insert ' + @vsTableName + ' off'
end

insert #tmp2 values ('GO')

insert #tmp2
select 'update statistics ' + @vsTableName

insert #tmp2 values ('GO')

delete #tmp

fetch next from csrTables into @vsTableName

end

close csrTables
deallocate csrTables

update #tmp2
set sqltext = substring(sqltext,1,charindex(',)',sqltext)-1) + ',NULL)'
where not(charindex(',)',sqltext) = 0)

update #tmp2
set sqltext = replace(sqltext, ',''''',',null')
where not (charindex(',''''',sqltext) = 0)

update #tmp2
set sqltext = replace(sqltext, '(''''',',null')
where not (charindex('(''''',sqltext) = 0)

select sqltext from #tmp2 order by id

drop table #tmp
drop table #tmp2

END
GO

vrijdag 17 oktober 2008

Installing OpenOffice.org 3.0: Files in use by Explorer

Today I installed OpenOffice 3.0. I encountered some problems while installing though. According to OpenOffice some files were in use by other programs. So I closed all other programs, just to be sure. Most setup programs would just ask for a reboot, so the files that are in use can be replaced at boot time, but not OpenOffice. Well, it doesn't seem to be a big problem to close all programs right? Unfortunatily, OpenOffice was still not satisfied when I closed all programs. Some of the file were in use by Explorer.exe, which is the main process of Windows Vista.

How to overcome this problem and install OpenOffice anyways:
  • Close all applications
  • Start the OpenOffice.org 3.0 setup program
  • Continue until it tells you that you need to close Explorer.exe
  • Open the task manager (control + shift + escape)
  • Go to the Processes tab in the task manager
  • Click the button or checkbox that says `Show processes from all users`
  • If Windows asks for administrator rights, grand it
  • Find the Image name ´explorer.exe´, click it, and after that click ´End Process´
  • Now the Windows menu/application bar will disappear
  • Switch back to the OpenOffice.org installer and click the try again button
  • Now OpenOffice will install without problems
  • After the installation is complete, you may want to get your menu/application bar back. You can do this by choosing File -> New Task in the task manager menu. Enter 'explorer' in the text field and press enter.

maandag 29 september 2008

Vista boot disc / recovery disc

When you need to start Windows Vista from a CD/DVD because the version on the harddisk does not work properly anymore, the question rises how to make a boot disc? I had this problem recently on a laptop where there where disc errors on the C partition. Normally you could just schedule a disc check, unfortunately this did not work (chkdisc did not start). The truth is that I couldn't find out how to make a boot disc, maybe it is not even possible...

I found a solution though, you can download a recovery disc from this website. With this disc I was able to boot from CD and run chkdisc from the CD. The size of the download is approximatly 120 MB and best of all completly free.

vrijdag 26 september 2008

Show temporary file in an external application VB.NET

In this example I will show you how to start an external application and how to generate a temporary file in the temp directory configured in Windows. The source code below create a text file in the temp directory and shows this file Notepad. The filename for the file is automatically generated.

Dim sw As IO.StreamWriter
Dim sTmpFile As String

'Write the temp file
sTmpFile = System.IO.Path.GetTempFileName()
sw = New IO.StreamWriter(sTmpFile, False)
sw.Write(Me.BookingRow.sCRSRecord)
sw.Close()

'Launch the external application
Dim myProcess As System.Diagnostics.Process = New System.Diagnostics.Process()
myProcess.StartInfo.FileName = "notepad.exe"
myProcess.StartInfo.Arguments = sTmpFile
myProcess.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal myProcess.Start()

Encryption in VB.NET

Encryption in Visual Basic .NET has become quite simple, as the .NET Framework already includes some popular encryption providers. The following class makes it easier to use encryption in your application.

Public Class CryptTools
Private Shared HashKey As Byte() = {12, 45, 7, 123, 6, 34, 56, 12, 245, 34}

Public Shared Function GetHexHash(ByVal ba As Byte()) As String
Dim hb As String
Dim ret As String = ""

For i As Integer = 0 To ba.Length - 1
hb = Hex(ba(i))
If hb.Length = 0 Then
hb = "00"
ElseIf hb.Length = 1 Then
hb = "0" & hb
End If

ret &= hb
Next

Return ret
End Function

Public Shared Function Encrypt(ByVal data As String, ByVal key As String) As Byte()
Return Encrypt(System.Text.ASCIIEncoding.ASCII.GetBytes(data), key)
End Function

Public Shared Function Encrypt(ByRef data As Byte(), ByVal key As String) As Byte()
Dim RMCrypto As New System.Security.Cryptography.RijndaelManaged
Dim mStream As System.IO.MemoryStream
Dim CryptStream As System.Security.Cryptography.CryptoStream
Dim bytIV() As Byte = {115, 21, 106, 15, 12, 78, 19, 30, 200, 90, 40, 75, 13, 212, 21, 67}
Dim bytKEY() As Byte
Dim sha As New System.Security.Cryptography.HMACSHA256(HashKey)

'Create key from the hash of the string
bytKEY = sha.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(key))

'Encrypt the data
mStream = New IO.MemoryStream()
RMCrypto.KeySize = 256
CryptStream = New System.Security.Cryptography.CryptoStream(mStream, RMCrypto.CreateEncryptor(bytKEY, bytIV), System.Security.Cryptography.CryptoStreamMode.Write)
CryptStream.Write(data, 0, data.Length)
CryptStream.FlushFinalBlock()

'Cean up
CryptStream.Close()
mStream.Close()

Return mStream.ToArray()
End Function

Public Shared Function Decrypt(ByVal data As String, ByVal key As String) As Byte()
Return Decrypt(System.Text.ASCIIEncoding.ASCII.GetBytes(data), key)
End Function

Public Shared Function Decrypt(ByRef data As Byte(), ByVal key As String) As Byte()
Dim RMCrypto As New System.Security.Cryptography.RijndaelManaged
Dim mStream As System.IO.MemoryStream
Dim CryptStream As System.Security.Cryptography.CryptoStream
Dim cryptBuffer As Byte()
Dim bytIV() As Byte = {115, 21, 106, 15, 12, 78, 19, 30, 200, 90, 40, 75, 13, 212, 21, 67}
Dim bytKEY() As Byte
Dim sha As New System.Security.Cryptography.HMACSHA256(HashKey)

'Create key from the hash of the string
bytKEY = sha.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(key))

'Encrypt the data
mStream = New IO.MemoryStream(data)
RMCrypto.KeySize = 256
CryptStream = New System.Security.Cryptography.CryptoStream(mStream, RMCrypto.CreateDecryptor(bytKEY, bytIV), System.Security.Cryptography.CryptoStreamMode.Read)
ReDim cryptBuffer(data.Length - 1)
CryptStream.Read(cryptBuffer, 0, data.Length)

'Cean up
CryptStream.Close()
mStream.Close()

Return cryptBuffer
End Function

End Class