Blog Moved

July 9, 2011

This blog have been continued here:
http://www.henrikfalk.se

I have a non technical blog here:
http://www.nerdyentertainment.com


Send Lotus Notes emails using VBS

February 1, 2011

Here’s a block of code to send a Lotus Notes email using VBS.


Set objNotes = CreateObject("Lotus.NotesSession")
Call objNotes.Initialize(strNotesPassword) 
Set Maildb = objNotes.GetDatabase("", strLotusNotesDatabase)
If Not Maildb.IsOpen = True Then
  Call Maildb.Open
End If
Set MailDoc = Maildb.CreateDocument
Call MailDoc.ReplaceItemValue("Form", "Memo")
Call MailDoc.ReplaceItemValue("SendTo", arrSendTo)
Call MailDoc.ReplaceItemValue("BlindCopyTo", arrBlindCopyTo)
Call MailDoc.ReplaceItemValue("Subject", strSubject)
Set Body = MailDoc.CreateRichTextItem("Body")
Call Body.AppendText(strBodyText)
Call Body.AddNewLine(2)
Call Body.EmbedObject(1454, "", c:\attachment.doc", "Attachment")
MailDoc.SaveMessageOnSend = True
Call MailDoc.ReplaceItemValue("PostedDate", Now())
Call MailDoc.Send(False)


Get the local IP using VBS

February 1, 2011

This is a handy function that returns the local IP settings.
I run it (like many people) at boot on servers to have the IP address
sent to me every time the server starts up, in case the IP have changed.

Function GetLocalIPConfig
  strResult = ""
  With CreateObject("Wscript.Shell").Exec("ipconfig").StdOut
    Do Until .AtEndOfStream
      strResult = strResult & .ReadLine
    Loop
  End With
  GetLocalIPConfig = strResult
End Function


VBS and OpenOffice

August 31, 2010

Here’s a quick piece of code that opens an OpenOffice Calc spreadsheet, writes text to a cell, and makes that text bold.

Set objServiceManager = CreateObject(“com.sun.star.ServiceManager”)
Set objDesktop = objServiceManager.createInstance(“com.sun.star.frame.Desktop”)
Set objCalcDoc = objDesktop.loadComponentFromURL(“file:///C:/file.ods”, “_blank”, 0, Array())
Set objSheet = objCalcDoc.getSheets().getByIndex(0)
objSheet.getCellByPosition(0, 0).SetString(“Henrik Falk”)
objSheet.getCellByPosition(0, 0).charWeight = 150

If you want to open a new spreadsheet, use this code instead:
Set objCalcDoc = objDesktop.loadComponentFromURL(“private:factory/scalc”, “_blank”, 0, Array())


UTF8ToUTF16

September 10, 2009

I wrote this script since I had to convert between UTF8 and UTF16 using vbscript. It turned out to be very non intuitive. A definition of UTF8 can be found here.

I have only used it to translate from 3 octets of bytes (the type starting with 1110), but the principle should stand.

Function UTF8ToUTF16(strUTF8)
  binUTF8 = HexToBin(strUTF8)
  if left(binUTF8, 1) = "0" then
    strResult = "0" & BinToHex(mid(binUTF8, 2, 7))
  elseif left(binUTF8, 3) = "110" then
    strResult = "0" & mid(binUTF8, 2, 7) & mid(binUTF8, 11, 6)
  elseif left(binUTF8, 4) = "1110" then
    strResult = mid(binUTF8, 5, 4) & mid(binUTF8, 11, 6) & mid(binUTF8, 19, 6)
  elseif left(binUTF8, 5) = "11110" then
    strResult = "000" & mid(binUTF8, 6, 3) & mid(binUTF8, 11, 6) & mid(binUTF8, 27, 6)
  end if
  UTF8ToUTF16 = BinToHex(strResult)
End Function

Here are the BinToHex and the HexToBin functions that it uses.

Function HexToBin(hexNumber)
redim arrHexNumber(len(hexNumber) - 1)
 for i=1 to len(hexNumber)
 arrHexNumber(i - 1) = mid(hexNumber, i, 1)
 next
 for each hexChar in arrHexNumber
 if hexChar = "0" then strResult = strResult & "0000"
 if hexChar = "1" then strResult = strResult & "0001"
 if hexChar = "2" then strResult = strResult & "0010"
 if hexChar = "3" then strResult = strResult & "0011"
 if hexChar = "4" then strResult = strResult & "0100"
 if hexChar = "5" then strResult = strResult & "0101"
 if hexChar = "6" then strResult = strResult & "0110"
 if hexChar = "7" then strResult = strResult & "0111"
 if hexChar = "8" then strResult = strResult & "1000"
 if hexChar = "9" then strResult = strResult & "1001"
 if hexChar = "A" then strResult = strResult & "1010"
 if hexChar = "B" then strResult = strResult & "1011"
 if hexChar = "C" then strResult = strResult & "1100"
 if hexChar = "D" then strResult = strResult & "1101"
 if hexChar = "E" then strResult = strResult & "1110"
 if hexChar = "F" then strResult = strResult & "1111"
 next
 HexToBin = strResult
End Function

Function BinToHex(binNumber)
redim arrBinNumber(len(binNumber) - 1)
 for i=1 to len(binNumber) step 4
 arrBinNumber(i - 1) = mid(binNumber, i, 4)
 next
 for each binChar in arrBinNumber
 if binChar = "0000" then strResult = strResult & "0"
 if binChar = "0001" then strResult = strResult & "1"
 if binChar = "0010" then strResult = strResult & "2"
 if binChar = "0011" then strResult = strResult & "3"
 if binChar = "0100" then strResult = strResult & "4"
 if binChar = "0101" then strResult = strResult & "5"
 if binChar = "0110" then strResult = strResult & "6"
 if binChar = "0111" then strResult = strResult & "7"
 if binChar = "1000" then strResult = strResult & "8"
 if binChar = "1001" then strResult = strResult & "9"
 if binChar = "1010" then strResult = strResult & "A"
 if binChar = "1011" then strResult = strResult & "B"
 if binChar = "1100" then strResult = strResult & "C"
 if binChar = "1101" then strResult = strResult & "D"
 if binChar = "1110" then strResult = strResult & "E"
 if binChar = "1111" then strResult = strResult & "F"
 next
 BinToHex = strResult
End Function

Hexadecimal

July 22, 2009

Not everyone sees the point with the hexadecimal numeral system, often called hex for short. It might be seen as just another weird system that isn’t decimal.

I like hexadecimal since it has a direct correlation to the binary numeral system.

Each number in the hexadecimal system represents four numbers in the binary system. This is because hexadecimal uses the radix 16, and binary the radix 2. And 2^4 = 16.

The usefulness of hex quickly becomes apparent when you are working with large binary numbers. If you want to translate 0101 to decimal, you have to find the value for each of the numbers individually and add them up.

In this case it isn’t very hard. 8*0 + 4*1 + 2*0 + 1*1 = 5. And in hexadecimal 5 is still 5.

Lets try a longer binary number, 1001 0101. Now it gets harder (more time consuming) to convert it to decimal.

128*1 + 64*0 + 32*0 + 16*1 + 8*0 + 4*1 + 2*0 + 1*1 = 149

This is when hexadecimal starts to shine. Remember that one hexadecimal number equals four binary numbers? That means that we can separate the binary numbers and translate them to hexadecimal in groups of four.

First we translate 0101, since we already know that’s 5 in decimal and hex.

Then we translate 1001 to decimal.
8*1 + 4*0 + 2*0 + 1*1 = 9. So in hexadecimal 1001 0101 is written 95.

Binary          1001 0101
Hexadecimal  9      5

This means that we never have to work with larger binary numbers then four bytes when translating to hex. Since the range of four bits in decimal is 0 to 15, this is something that comes naturally after a short time of working with them.

Does this mean that hex is easy to read? Of course not, but it’s easier to read then binary, and since there is a direct correlation between hex and binary, that’s what makes hex useful.


C# OpenProcess

July 21, 2009

Certain tasks simply can’t be done using only managed code. In order to call a Win32 API (or simply a DLL) using C#, DllImport is used. DllImport resides in the namespace System.Runtime.InteropServices.

If you want to implement the OpenProcess Function in C#. We can see on MSDN that the C++ definition for the function is:

HANDLE WINAPI OpenProcess(
  __in  DWORD dwDesiredAccess,
  __in  BOOL bInheritHandle,
  __in  DWORD dwProcessId
);

In C# thats written:

[DllImport("kernel32.dll")]
  public static extern IntPtr OpenProcess(
  int dwDesiredAccess,
  bool bInheritHandle,
  int dwProcessId
);

To not get ahead or myself, this is the code I used to get the handle to the process.  The class Process resides in the namespace System.Diagnostics.

Process[] hProcess = Process.GetProcessesByName("calc");

This line of code returns an array of all the instances of the calculator running on your machine.

IntPtr hReadProcess = OpenProcess(0x0010, true, handle[0].Id);

This line of code opens the first instance of the calculator found, and assigns the handle to hReadProcess. 0x0010 is the process access right specified to open the process with. 0x0010 means PROCESS_VM_READ, and is needed in order to use the ReadProcessMemory function on the process.

On MSDN we can see that if the OpenProcess function succeeds, it return's an open handle. That's of course to be expected.

But if the function fails, it will return null, still not a problem, but we are referred to call GetLastError to get a more specific error message. GetLasError unfortunately doesn't work in C#. Sure, it "works" but the CLR itself could very easily make it's own calls and all of a sudden the last error message isn't what you are after.

In order to fix this problem, all we need to do is add SetLastError=true to our DllImport statement. It should now look like this:

[DllImport("kernel32.dll",SetLastError=true)]
  public static extern IntPtr OpenProcess(
  int dwDesiredAccess,
  bool bInheritHandle,
  int dwProcessId
);

Now that this is done, you can call Marshal.GetLastWin32Error, that is found in the System.Runtime.InteropServices, which we need to use anyway because of DllImport.

Now simply use this line of code to get the last error message:

MessageBox.Show(Marshal.GetLastWin32Error().ToString());

Hiragana

May 20, 2009

While studying Japanese I created this HTA a while back.
Here’s the code:

<HEAD>
<TITLE>Hiragana - 2009-03-26</TITLE>
<HTA:APPLICATION
 APPLICATIONNAME="Hiragana"
 BORDER="thin"
 CAPTION="yes"
 MAXIMIZEBUTTON="yes"
 MINIMIZEBUTTON="yes"
 SCROLL="no"
 SHOWINTASKBAR="yes"
 SINGLEINSTANCE="yes"
 SYSMENU="yes"
 WINDOWSTATE="normal"
>
</HEAD>

<SCRIPT LANGUAGE="VBScript">
//Documentation goes here.
//Author: Henrik Falk

arrCharacterKeys = Array()

Set Characters = CreateObject("Scripting.Dictionary")

 //304x
 Characters.Add "a", "x3042"
 Characters.Add "i", "x3044"
 Characters.Add "u", "x3046"
 Characters.Add "e", "x3048"
 Characters.Add "o", "x304A"
 Characters.Add "ka", "x304B"
 Characters.Add "ga", "x304C"
 Characters.Add "ki", "x304D"
 Characters.Add "gi", "x304E"
 Characters.Add "ku", "x304F"

 //305x
 Characters.Add "gu", "x3050"
 Characters.Add "ke", "x3051"
 Characters.Add "ge", "x3052"
 Characters.Add "ko", "x3053"
 Characters.Add "go", "x3054"
 Characters.Add "sa", "x3055"
 Characters.Add "za", "x3056"
 Characters.Add "shi", "x3057"
 Characters.Add "ji", "x3058"
 Characters.Add "su", "x3059"
 Characters.Add "zu", "x305A"
 Characters.Add "se", "x305B"
 Characters.Add "ze", "x305C"
 Characters.Add "so", "x305D"
 Characters.Add "zo", "x305E"
 Characters.Add "ta", "x305F"

 //306x
 Characters.Add "da", "x3060"
 Characters.Add "chi", "x3061"
 Characters.Add "tsu", "x3064"
 Characters.Add "te", "x3066"
 Characters.Add "de", "x3067"
 Characters.Add "to", "x3068"
 Characters.Add "do", "x3069"
 Characters.Add "na", "x306A"
 Characters.Add "ni", "x306B"
 Characters.Add "nu", "x306C"
 Characters.Add "ne", "x306D"
 Characters.Add "no", "x306E"
 Characters.Add "ha", "x306F"

 //307x
 Characters.Add "ba", "x3070"
 Characters.Add "pa", "x3071"
 Characters.Add "hi", "x3072"
 Characters.Add "bi", "x3073"
 Characters.Add "pi", "x3074"
 Characters.Add "fu", "x3075"
 Characters.Add "bu", "x3076"
 Characters.Add "pu", "x3077"
 Characters.Add "he", "x3078"
 Characters.Add "be", "x3079"
 Characters.Add "pe", "x307A"
 Characters.Add "ho", "x307B"
 Characters.Add "bo", "x307C"
 Characters.Add "po", "x307D"
 Characters.Add "ma", "x307E"
 Characters.Add "mi", "x307F"

 //308x
 Characters.Add "mu", "x3080"
 Characters.Add "me", "x3081"
 Characters.Add "mo", "x3082"
 Characters.Add "ya", "x3084"
 Characters.Add "yu", "x3086"
 Characters.Add "yo", "x3088"
 Characters.Add "ra", "x3089"
 Characters.Add "ri", "x308A"
 Characters.Add "ru", "x308B"
 Characters.Add "re", "x308C"
 Characters.Add "ro", "x308D"
 Characters.Add "wa", "x308F"

 //309x
 Characters.Add "wi", "x3090"
 Characters.Add "we", "x3091"
 Characters.Add "wo", "x3092"
 Characters.Add "n", "x3093"
 //Characters.Add "vu", "x3094"

 arrCharacterKeys = Characters.keys

constNumberOfHiragana = Characters.count
correctAnswerButton = ""
correctCharacterRomaji = ""

Sub AskQuestion
 strAnswers = GetUniqueRandomNumber(4)
 arrAnswers = split(strAnswers, ",")
 Button1.value = arrCharacterKeys(arrAnswers(0) - 1)
 Button2.value = arrCharacterKeys(arrAnswers(1) - 1)
 Button3.value = arrCharacterKeys(arrAnswers(2) - 1)
 Button4.value = arrCharacterKeys(arrAnswers(3) - 1)
 correctAnswerButton = GetRandomNumber(1, 4)
 correctCharacterRomaji = arrCharacterKeys(arrAnswers(correctAnswerButton - 1) - 1)
 question.innerHTML = "&#" & GetHiragana(correctCharacterRomaji) & ";"
End Sub

Function CheckAnswer(number)
 if correctAnswerButton = number Then
 answer.innerHTML = "<FONT COLOR='green'>Correct!</FONT>"
 else
 answer.innerHTML = "<FONT COLOR='red'>Wrong!</FONT>"
 end if
 answer.innerHTML = answer.innerHTML & "<BR>" & "&#" & GetHiragana(correctCharacterRomaji) & ";" & " is pronounced " & correctCharacterRomaji
 AskQuestion
End Function

Function GetRandomNumber(intLow, intHigh)
 intLowNumber = intLow
 intHighNumber = intHigh
 Randomize
 GetRandomNumber = Int((intHighNumber - intLowNumber + 1) * Rnd + intLowNumber)
End Function

Function GetRomaji(hiragana)
 GetRomaji = Characters.item(hiragana)
End Function

Function GetHiragana(romaji)
 GetHiragana = Characters.item(romaji)
End Function

Function GetUniqueRandomNumber(number)
 On Error Resume Next
 Set Dict = CreateObject("Scripting.Dictionary")
 Do Until Counter = number
 num = GetRandomNumber(1, constNumberOfHiragana)
 Dict.Add num, num
 If Err = 0 Then
 Counter = Counter + 1
 Else
 Err.Clear
 End If
 Loop
 a = Dict.Items
 For i = 0 To Dict.Count -1
 temp = temp & a(i) & ", "
 Next
 GetUniqueRandomNumber = left(temp, len(temp) - 2)
End Function

Sub Window_onLoad
 window.resizeTo 180,210
 AskQuestion
End Sub
</SCRIPT>

<BODY>
<CENTER>
<DIV ID="question"></DIV>
<TABLE>
<TR><TD><INPUT TYPE="button" STYLE="width:25" TITLE="" VALUE="" NAME="button1" onClick="CheckAnswer(1)"></TD>
<TD><INPUT TYPE="button" STYLE="width:25" TITLE="" VALUE="" NAME="button2" onClick="CheckAnswer(2)"></TD></TR>
<TR><TD><INPUT TYPE="button" STYLE="width:25" TITLE="" VALUE="" NAME="button3" onClick="CheckAnswer(3)"></TD>
<TD><INPUT TYPE="button" STYLE="width:25" TITLE="" VALUE="" NAME="button4" onClick="CheckAnswer(4)"></TD></TR>
</TABLE>
<DIV ID="answer"></DIV>
</CENTER>
</BODY>

Simply copy this code and put it into a file with a name ending in .hta


ByRef and ByVal

May 20, 2009

Something I have recently been trying in my never ending quest to make my code look good is ByRef and ByVal. The standard in VB Script is ByVal, so lets look at a function:

Function AddOneByVal(intNumber)
  AddOneByVal = intNumber + 1
End Function

The way that the code really looks like is this:

Function AddOneByVal(ByVal intNumber)
  AddOneByVal = intNumber + 1
End Function

ByVal simply means that we are passing the function a value. If we call AddOneByVal(integer), this means that we are sending the value of the variable integer to the function AddOneByVal. If integer equals 5, the function will return 6. Pretty simple stuff.

But now lets look at ByRef:

Function AddOneByRef(ByRef intNumber)
  intNumber = intNumber + 1
End Function

Here we are sending the function a reference to a value. If we now call this function using AddOne(integer), with integer again equaling 5, we aren’t just sending the value 5 to the function, we are sending the actual variable integer, that currently equals 5. The function will still add one and return 6 as a result.

What’s good about that? Mainly (for me) it’s easier to handle. Instead of calling the function like this:

intResult = AddOneByVal(intTestByVal)

We can now call the function like this:

AddOneByRef intTestByRef

Another (and better) example would be to call a sorting function like this:

Sort arrList

Instead of:

strListSorted = Sort(arrList)

I have been looking around online for further differences with using ByVal and ByRef, but I haven’t really been able to find anything definitive. That said, I would guess that ByRef is slightly faster since you are using the actual variable, and not creating an extra copy of the variable. We also don’t need to assign the new value back anything.


Lotus Notes Scripting

May 20, 2009

I have recently been looking into automatisation of different kinds, and in the scope of that I looked into scripting for Lotus Notes. There is of course LotusScript and Lotus Domino Designer. But since I’m already using Visual Basic Script quite a lot, I was happy to find out that there is a way to script Lotus Notes using VB Script. I have been using VB Script to interface with Excel, and this works in much the same way.

These two rows are the really important ones:

Set objNotes = CreateObject("Lotus.NotesSession")
Call objNotes.Initialize

When the object is created it can be used in a lot of ways, for example:

alert(objNotes.CommonUserName)

If you don’t want to enter your notes password every time the application is run, use this addition:

Call objNotes.Initialize("password")

Here is a very good list of Lotus Script COM/OLE classes:
LotusScript Classes A-Z


Follow

Get every new post delivered to your Inbox.