Showing posts with label productivity. Show all posts
Showing posts with label productivity. Show all posts

Thursday, May 01, 2008

Ruby and Win32 API

If you are using Ruby as an automation process rather than a development tool, you sometimes might want to use some of Windows' GUI controls, such as a message box or folder browser box. In those cases, you might want to try the DL library.

Some of you have read this post about OLE Automation and DL. In a way it is quite simple but it can take a while to get right, especially if you're a beginner like me. However, after some time I have managed to get my folder browser to work and I haven't found Ruby code on the web that does this yet.
However, I feel like it is rather simple to do and to find out how to make your own version, all you need to do is to see how people who wrote VB code do it.
Why? Because it is that straight forward, in fact, Ruby reduces the amount of lines of code to do it.

I must warn and say that doing this kind of programming requires excessive use of the Win32API docs (one form or another) and some skilled programming, especially in the C-language area, because you are interfacing to the actual C-functions from Ruby.

Many VB code snippets that does this, have referenced these 2 c-functions (from the shell32 DLL):
  • SHGetPathFromIDListA

  • SHBrowseForFolderA

They also require a structure to send data to the BrowseForFolder function.
Here is a site to get you started.

Why don't I just paste the code? Well, actually the method I wrote to bring up the dialog box, requires a whole Module and I would like to write my own Ruby module before I publish anything.
Yeah, I might sound like a bugger but hey, I have posted lots of lines of code on this site before so... take it like a man! ;)
The hardest part is creating a Ruby C-like structure from the VB structure, so I think I'll help you out on that one:

ptr = DL.malloc(DL.sizeof('LLSSLLLL'))
ptr.struct!('LLSSLLLL', :br_hOwner, :br_pidRoot, :br_displayName, :br_title,
:br_flags, :br_fn, :br_lparam, :br_iImage)

Now don't say I didn't give you anything! :D

For those who still don't know what all those L's and S's are for, it is basically saying "This is a Long integer type" or "This is a string type". Its to determine the size of the structure, but you know that there are other data-types so you need to dig in on your own regarding that.

Now there is one thing I'm having trouble with and it is the Open/Save File Dialog box. Aparently if you take the exact same data-types as the prescribed docs say, Ruby fails to bring it up. After hours of struggling, I decided to skip the VB code and look on the web if someone already did this, and to my "surprise", nobody has posted any code on this. Bummer! So I decided to have a look on the MSDN site, and BAM! I found my answer. The reason why Ruby didn't bring up my dialog box was due to the fact that Ruby created a structure that had a different size than what the function was expecting! So now I am trying to find out where, how and why.

I might post my code and findings at a later stage, for this is a necessary thing for Windows Ruby Automation Programmers, like me! ;)

Wednesday, April 30, 2008

AutoIT Script for EasyWorship

EasyWorship (EW) is a wonderful app for church services and beats Powerpoint in that it can display Powerpoint slides on a different monitor (than your primary for the projectors) and you can view all the slides in advance of what it beholds. This is great unless you want to import some old songs from a Word document or Powerpoint slides into its Song database...

Well why not just import the Powerpoint slides and use that? Well in my case (as I've been doing this at my church for over a year now) this can take a long time in preparation and is very error prone. What we usually did was create a Powerpoint slide and create our whole church service presentation in it, which meant opening up the Word or Powerpoint Doc with the original songs inside, search for it and then copy-paste. Sometimes we accidentally took the wrong songs (like Psalm 256 instead of 265) and found out in the Sunday morning service! :D
With the EW Song DB, you can type in the song title and you just drag it into your schedule. Thats it! Save me about ... hmm... 3-8 minutes per song!
Also this means that if we imported the wrong song and found out the Sunday morning, we can correct it in a matter of seconds!

We already have all our Psalms and Songs on Word Doc and Powerpoint and its not 1 song per doc, its a hundred at least! EW can only import 1 thing at a time and they haven't released a tool that can do convertions and imports for you like we needed so I searched the web to find out that everyone else who has the same problem as I did, import it manually! Man, I can't imagine importing over a 1000 songs manually by copy & paste! 8-0
So I stumbled on AutoIT. This is a wonderful util to automate just about any task. By writing a BASIC script (pun intended) you can tell the mouse pointer to move to a certain window's control and wait for a while and then issue a click action. Now that's what I mean by GUI automation ;-). You can also do basic scripting automation like a BATCH or BASH script can.

Now back to my scenario... I played around and found how easy it actually was to write a script that can import all my hundreds of songs in minutes (it literally took just over 20 mins to import over 800 songs!) since there wasn't a way for me to to it programatically in EW. This script is tested on EW 2006, so if you want to do the same kind of thing like I did, maybe your fortunate to have Google'd onto this page :D


#cs
A little script to import songs into Easy Worship 2006.
Written by Dandre Jansen van Vuuren
#ce

;Select the files to import
$var = FileProcessSelectionDialog()

;Cancelled
If $var == -1 Then
MsgBox(0, "Error", "No files selected, quitting...")
Exit (1)
EndIf

$selectedSourceFolder = False
$sourceFolder = $var[1]

StartEasyWorship()

;Iterate through all the selected files to import
$total = Number($var[0])

For $i = 2 To $total
$curFile = $var[$i]
SelectNewSong()
OpenFileOpenDialog()
$ext = MapFileTypeToTypeName(GetExtensionFromFile($curFile))
SelectAFileFromOpenDialog($curFile, $ext)
WinWaitActive("New Song")
Send("!o")
WinWaitActive("EasyWorship")
Next

MsgBox(0, "Progress", "Finished.")

Func FileProcessSelectionDialog()
$filter = "HTML Files (*.html;*.htm)|Word 97-2002 (*.doc)|Text Files (*.txt)"
$importFiles = FileOpenDialog("Select the files to import into EasyWorship", "", $filter, 1 + 4)

If @error Then
return -1
Else
If StringInStr($importFiles, "|") Then
return StringSplit($importFiles, "|")
Else
;A single selected file isn't in the same form as a multiple
;selection of files.
$pos = StringLastIndexOf($importFiles, "\")
$folder = StringLeft($importFiles, $pos)
$file = StringMid($importFiles, $pos + 1)
$newString = $folder & "|" & $file
return StringSplit($newString, "|")
EndIf
EndIf
EndFunc

Func StartEasyWorship()
Run("C:\Program Files\Softouch\EasyWorship\EasyWorship.exe")
WinWaitActive("EasyWorship")
EndFunc

Func SelectNewSong()
Send("!s")
Send("n")
WinWaitActive("New Song")
EndFunc

Func OpenFileOpenDialog()
;The open button doesn't have a shortcut
;find out where the window is positioned And
;relatively position the mouse onto the open button
$winPos = WinGetPos("New Song")
MouseMove($winPos[0] + 305, $winPos[1] + 45)
MouseClick("left")
WinWaitActive("Open")
EndFunc

Func SelectAFileFromOpenDialog($file, $type)
Send("!t")
ControlCommand("Open", "", "ComboBox3", "SelectString" , $type)
sleep(10)
Send("!n")
Sleep(10)

;We only need to specify the folder where all the files
;lie, once. From there on we can import the files one
;at a time.
If $selectedSourceFolder == False Then
Send($sourceFolder)
Send("{Enter}")
Sleep(10)
$selectedSourceFolder = True
EndIf

Send($file)
Send("!o")
EndFunc

Func GetExtensionFromFile($filename)
Return StringMid($filename, StringLastIndexOf($filename, ".") + 1)
EndFunc

Func StringLastIndexOf($string, $char_target)
$strlen = StringLen($string)
For $i = $strlen to 1 Step -1
$char = StringMid($string, $i, 1)
If $char == $char_target Then
return $i
EndIf
Next
EndFunc

Func MapFileTypeToTypeName($fileType)
Switch StringLower($fileType)
Case "html"
return "HTML Files"
Case "htm"
return "HTML Files"
Case "doc"
return "Word 97-2002"
Case "txt"
return "Recover Text from Any File"
Default
return "Unknown"
EndSwitch
EndFunc


Here is how the script works (you obviously need AutoIT and EW):

  • Run the script

  • Choose the files you would like to import, EW only has a limited supported file-base, but includes HTML and Word Docs. I reformatted all my songs to HTML (I'll write a new article on how I automated that process).

  • After selecting the files and clicking on Open, leave everything! You'll see your mouse pointer and menu-popping in action. :-)

  • Once it is done, it will pop up with a "Done" message box. In the mean time don't let anything take control of your keyboard, mouse, etc (including you ;-) ). This script doesn't cater for problematic scenarios!



I don't mean to be full of myself (really I don't!) but I am glad that God made me a programmer so that I wouldn't have to do these kind of tasks manually! :D

Monday, March 31, 2008

Ruby, ruby, ruby ...

Yeah it's been a while since the last posting. I was lacking some inspiration on what to blog on or I was just too lazy or too busy to actually log in and blog on something that I wanted to, that is until now...

You might ask, whats up with the title? Well I remember the song written by a band called Keizer Chiefs (spelling) named: Ruby. Now that reminds me of the Ruby scripting language which lots of people on the web is going on about. A dynamic typed language with multiple ways of saying one thing.

I was among those who agreed that Ruby was stupid, until I wanted to do a few things in Windows which Python nor other scripting languages could offer (or easily accomplish), except Ruby. Some of us would mock the guy who presented a brief crash course in Ruby, saying that it is too dynamic for our taste and it would make debugging a nightmare, etc. but doing some code delving and with the latest Netbeans language addition, which is Ruby and Ruby on Rails of course, I started to see how cool Ruby actually is.
Usually I despised dynamic typed languages until I saw how it can increase productivity and coding speed for smallish scripts, which is what I wanted for automation.

"What kind of automation?" you may ask. Well ordinary and OLE automation. Sure AutoIT is great for GUI and other windows automation but the scripting language is very BASIC (pun intended) lacking structure and (as in, I need structures as in C or C++'s structures or atleast...) object orientation. With Ruby we have good OO support and with the 'win32ole' lib, you can kick VBA and do MS Office automation via Ruby instead. A good starting site is Ruby on Windows which covers Ruby automation in MS Office. I managed to convert Powerpoint slides to somewhat formatted Word docs via Ruby, just to give you an idea.

I think I'll post a couple of script snippets later on, on what I worked on with Ruby and AutoIT for others to see and for future reference.

Just to go back to Python, some of you might say that Python does have OLE automation... I had a look at it and it looked very nasty! I wouldn't touch it, unless I looked at the wrong files, and you need to download a library for Python to have OLE automation ability. With Ruby you use the OLE objects exactly as in VBA but within your Ruby context.

Currently I am trying to implement a few operations in Ruby to have Browse-For-Folder ability, Open-Dialog, and so on functionality by doing Windows API calls via the DL lib in Ruby. You can catch a quick course on DL here.

I hope this will help you as much as it helped me.
God Bless!

Friday, February 08, 2008

Getting Things Done

Heard of the GTD term? Not so new anyway but David Allen gave a good seminar on it at Google a while ago...

I read an article on another blog that dealt with GTD and had a embedded video of a 45 minute lecture.

I personally found it very interresting but there are a few things I want to bring out:
  • Some of the things he taught sounded like Biblical principles being brought up, especially the part where he talked about "letting things control you", people manipulating you through the smallest things (stop trying to control everything they will end up controlling you, thats the bottom line), and being like a "Master and Commander" where you have your eyes on the prize but humble enough to clean a toilet.
  • I like his view on the "Mind like water" part, it reminded me of Philipeans 4:6-7 where we shouldn't be anxious but always bringing our concerns to God (right now, "in every situation") to be able to receive His Peace.
  • The only thing I would tell you to bear in mind is to not apply this in your life like another law. The Bible clearly warns us about it.
  • To apply distributive cognition on all the tasks you still have to do, REALLY DOES lighten the weight your mind has to carry.
  • We need to exercise our ability to control by "having dominion" (Genesis) over creation and not people (like we used to do, which is like playing God, BTW).
It was also entertaining to watch as there were some clips of humor here and there and David really has a good talent for doing what he does (I think, IMHO).

My favorite part was:
"People think that information overload the is problem... actually if information overload was the problem, you'd walk into a library and die!", "It's actually potential overload..."

Let me know what you thought about it, I would be interrested to know what you thought about and learn't from it.

http://linuxhelp.blogspot.com/2008/01/getting-things-done-two-aspects-of-self.html

[Edit:] Here is also a Wikipedia entry regarding GTD: http://en.wikipedia.org/wiki/Getting_Things_Done

Wednesday, January 02, 2008

Hard Drive imaging

Formatting again maybe? Don't be so hard on yourself any longer. Start imaging!

What is that? It is to duplicate every bit on your hard-drive/partition in an image file for backup purposes. Why? Let me give you an example and maybe you'll discover what I mean:
Usually you format your hard drive, reinstall Windows and/or Linux with their software and settings the way you like it. All fine until something goes wrong and you have to go through it again! It can really take a very long time!
Now imagine just formatting once more, installing everything, backup your PC's hard drive (HD) with an image and later if something goes wrong, you just reload that image on your HD and there you go. Everything back to the way you wanted it. Saves you a lot of time and frustration!

How can one do that? Here is a tutorial containing a link to a downloadable boot CD to get you started: http://www.howtoforge.org/back_up_restore_harddrives_partitions_with_ghost4linux

Ghost4Linux (G4L) makes this imaging process possible. When you want to begin, make sure you have an extra hard drive or a local FTP Server on a different machine than the one you are imaging. The FTP server has to be on your network otherwise you are going to wait like forever! :(
Just because it contains the word 'Linux' doesn't mean that you have to have a Linux distro on your machine or anything, all it means is that it uses a compact Linux version to run its Ghost application to backup your HD.

It is really simple to use and make sure when compressing, you use something like GZip and not the other compress options, they compress more but take longer (Bz2 take MUCH, MUCH longer and doesn't necessarily make the image size MUCH smaller than GZip). It is important though that you do compress your image. I imaged my 160GB hard drive to my local FTP server and ended up taking 4 GB of space (because of the compression).

Now don't think that you'll get the same results, though! I had to do a very good Low Level Format (LLF or filling my HD with 0's). This makes the compression more effective! If you start to image your HD after using it for years, you definitely won't have the same result as I had. Even if you delete files, the data remains on the HD, it is just marked/removed from the File Table of the File System. So when you image your HD, even if you are just using 20% of it, your image file size will be close to your HD's capacity. So do a LLF first. It takes long to do that however (like Seagate's Disk Wizard took 6 hours to fill my 160GB SATA drive with 0's [because it writes 0's on a part of the HD multiple times to make sure the data isn't recoverable]), other software just writes a 0 once and it ends up taking a fraction of the time.
If your HD or FTP server has a lot of space, then it probably won't matter to you.

I remember having to image multiple machines at university 2 years ago. It sure saved a lot of time doing that because UDP has this broadcasting capability. The server just sends 1 packet and the network medium duplicates that packed and sends them to all PCs. We used something that came with DELL PCs which helped do the job well. We could redo like 30 PCs in 30 minutes. Imagine having to format, reinstall Windows and all its applications on each PC at a time, on top of that, you have to carry them all, set them up, configure them for the network, get them on the Active Directory Domain, etc.

I always wanted to be able to image my PC and I am glad that I came across that tutorial to do that. Let me know how you found it.

UPDATE (2008-05-01):
Today I re-imaged my PC because Windows was getting slow (don't we all know) and a bit buggy I reckon. However, after the 30-min re-image session I am back to my backed-up state and my PC a whirling again! Now it takes me about 2 hours to get my PC back the way it was again instead of the whole freekin day! Yay to Imaging! Yay to G4L!

Tuesday, June 20, 2006

A Cool Makefile

Recently I wanted to make a makefile that could detect what OS you are
using and weither it should compile for debug or release mode. After
hours of playing around and scouting the net for answers, I actually
managed to code one very close to what I wanted.
For those who doesn't know what a makefile is, well... ever heard of MingW C/C++ or GNU C/C++ ?

They are both the same except MingW is for Windows while GNU's one is
for Linux. Its a script file that prepares and calls the compiler to
compile your code.
Why would you need one? For various reasons. Mostly for automated
tasks, for instance, lets say you want all your compiled .o files in a
"output" directory in the current path, then you can instruct 'make'
(the interpreter that reads your Makefile) to create that directory and
to tell the compiler to place all those object files into that
directory.
You might wonder why you don't use BATCH scripting for windows or BASH
scripting for Linux instead of Makefiles, well... Makefiles are more
programming oriented, it has functionality specifically for compiling
purposes that BASH or BATCH scripting doesn't have. Well, you might
still accompilsh the same with BASH but not entirely, I believe.
How would you invoke a Makefile? Easy, make sure that you have a file
called Makefile in your
source directory and type : make
You can add parameters to 'make' according to what the Makefile
expects, so its a good idea to either read the Makefile itself or any
READMEs found with the source.
I'm not going through all the basics about Makefiles, but I will
explain how I managed to complete one mentioned above.


#
# NOTES:
# Please use GNU's make application for this makefile.
# Also, this is made for Windows & Linux compilations only!
# This creates an output folder where all the object
# files are going to be stored. It'll compile all .c files
# found in the current directory where the makefile lies.
# The makefile will detect if this is being compiled in
# an Windows NT environment, else it'll assume its
# a Linux environment.
# Also, by default it will compile in debug mode.
# To build in release mode, use the following:
# make dist
#
# This makefile is compatible with CodeBlocks (RC2).
#

# Set up OS specific stuff
# Since this could be compiled on a different
# windows version, we rather check if the
# OSTYPE var exists and that it is 'linux'.

# If so then we're compiling for linux,
# else we assume for some windows version.
ifeq ($(OSTYPE),linux)
EXT :=
CFLAGS := -DLINUX
OS := Linux
else
EXT :=.exe
CFLAGS := -DWIN32
OS := Windows
endif

# Ordinary vars needed
APP := lacp
OUTPUT := output
BIN := $(APP)$(EXT)
CC := gcc
OBJS := $(patsubst %.c,$(OUTPUT)/%.o,$(wildcard *.c))

# Not our actual targets!
.PHONY: all dist default clean clean_default debugmode releasemode

# Where to start building with what targets
all: default
default: debugmode
dist: releasemode

# The following targets (debug & release) has multiple
# lables because one cannot define variables after the
# first target line, ie. all.
# So, make allows you to change vars but only 1 per target
# line and that change is only valid within that target scope.
# Thats why I have $(BIN) in the debugmode and releasemode targets
# instead of the targets above, sothat the variable changes can be
# carried over to any $(BIN) compilations.
# The double colons are to tell make to execute the var changes
# and commands in order given below.

# DEBUG mode
debugmode:: MODE := DEBUG
debugmode:: CFLAGS += -g -D__DEBUG__
debugmode::
@echo Compiling for $(OS)...
@echo Compiling in $(MODE)...
ifeq ($(OS),Windows)
-@if not exist "./"$(OUTPUT)
mkdir $(OUTPUT)
else
mkdir -p $(OUTPUT)
endif
debugmode:: $(BIN)

# RELEASE mode
releasemode:: MODE := RELEASE
releasemode:: CFLAGS += -O3
releasemode:: LDFLAGS += -s
releasemode::
@echo Compiling for $(OS)...
@echo Compiling in $(MODE)...
ifeq ($(OS),Windows)
-@if not exist "./"$(OUTPUT)
mkdir $(OUTPUT)
else
mkdir -p $(OUTPUT)
endif
releasemode:: $(BIN)

# Clean up
distclean_default: clean
clean_default: clean
clean: @echo Cleaning up...
@$(RM) -f $(BIN)
@$(RM) -r -f $(OUTPUT)

# Actual compilation
$(BIN): $(OBJS)
$(CC) $(OBJS) -o $(BIN)

$(OUTPUT)/%.o: %.c
$(CC) $(CFLAGS) -c $^ -o $@



Those of you who know Makefiles, can remember that it is very important
to have actual TAB characters below every target label to indicate that
those statements belong to that target, almost like in Python.
I apologise if my code doesn't appear to be indented since the blog
site apparently clears out "unnecessary" spaces.
How this Makefile works is, it first checks if OSTYPE is equal to
linux. In Linux if you type 'export' in the console, you'll find a
variable calles OSTYPE. Since my Makefile is only for Windows and Linux
and nothing else, if OSTYPE doesn't exist, then the Makefile assumes
that you are in Windows.
Then if you have just performed a 'make' or 'make default' command,
then the Makefile assumes that you are compiling for debug mode. If you
have specified 'make dist' or make distribution, then it assumes
Release mode.
It creates an output directory where all the object files will be
placed, and it scouts the current directory only for C files and
compiles them into the output directory. So if you create a C file,
etc. the Makefile will automatically include it. If you need subdirs
with files in them, just tell the Makefile to also scout in those
specific directories to compile them into the output directory.
As you can see, now you don't have to update the compiling process for
smallish projects which are designed for Windows and Linux anymore. The
Makefile takes care of it for you. If you have made any cool
modifications to it, please drop me a mail with your change, I would
like to see what else can be done with this.

Check and enjoy!