Thursday, August 27, 2009

Code Launcher

Requires: Windows (2000 and higher) and Ruby

When I first started playing with lots of languages two years ago, I built a simple extensible ruby script for myself that compiles/executes source code. I called it "Code Launcher." I was using Textpad mostly at that time, so I set up Textpad to run codeLauncher on the active file with the press of "ctrl-1". "Code Launcher" has a different effect depending on the extension of the currently opened file. For example, I have HTML files open in firefox, ruby code run through a batch file (so it can have a guaranteed pause), and java code compiled and then run through a batch file. It's a simple idea, but it was also a really useful idea that I still use for quick coding. Here's the code for it... the nicest thing about it is that you can add and take from it freely and quickly. Build in special cases for yourself and whatnot.

  1. # This program was created for use in TextPad, so that I could add this  
  2. # as a user tool which would do exactly what I wanted to the current open file.  
  3. # This way, I didn't need a different user tool (and different key shortcut)   
  4. # for every different kind of file.  I did this in Ruby because I had been learning   
  5. # it and liking it a lot at the time.  
  6. #  
  7. # The current problem with it is that there can not be more than one parameter.  
  8. #  
  9. # This program requires ruby 1.8 and AutoIt3 installed.  
  10. # 03/20/07  
  11.   
  12. require 'win32ole'  
  13.   
  14. class CodeLauncher  
  15.     def initialize( fileParam, promptForParams = false, browser = "C:\\Program Files\\Mozilla Firefox\\firefox.exe" )  
  16.         params = ""  
  17.         exit if( !fileParam )  
  18.   
  19.         #Get parameters if parameters are asked for  
  20.         if( promptForParams )   
  21.             require 'getParams.rb'  
  22.             app = FXApp.new("Get Parameters""FXRuby")  
  23.             gp = GetParams.new(app)  
  24.             app.create  
  25.             app.run  
  26.             params = gp.param  
  27.             params = "\" \""+params+"\""  
  28.         end  
  29.   
  30.         #parse out the file name  
  31.         if( fileParam =~ /(.*)\\(.*)\Z/ )  
  32.             fileDir = $1  
  33.             fileName = $2  
  34.         else  
  35.             exit  
  36.         end  
  37.         extension = $2 if( fileName =~ /(.*)\.(.*)\Z/ )  
  38.   
  39.         #Special Cases (if any, for quick parameter testing)  
  40.   
  41.         #Extension based general executions/compilations  
  42.   
  43.         # Ruby, simpy open using a batch file (for the pause effect)  
  44.         if( extension == "rb" || extension == "rbw")  
  45.             runInCmd( fileDir, fileName, params, true )  
  46.         end  
  47.   
  48.         # Java, compile the code an run in a batch file (for the pause effect)  
  49.         if( extension == "java" )  
  50.             runInCmd( fileDir, "javac\" \""+fileName, ""false )  
  51.             runInCmd( fileDir, "java\" \""+fileName.chomp(".java"), params, true )  
  52.         end  
  53.   
  54.         # HTML, open the page in the browser  
  55.         if( extension == "html" )  
  56.             openInBrowser( browser, fileDir+"\\"+fileName )  
  57.         end  
  58.   
  59.         # PHP, open the page in the browser  
  60.         if( extension == "php" )  
  61.             openInBrowser( browser, fileDir+"\\"+fileName )  
  62.         end  
  63.   
  64.         # JSP, open the page in the browser  
  65.         if( extension == "jsp" )  
  66.             openInBrowser( browser, fileDir+"\\"+fileName )  
  67.         end  
  68.   
  69.         # batch, simply open the batch file  
  70.         if( extension == "bat" )  
  71.             runInCmd( fileDir, fileName, params, false )  
  72.         end  
  73.     end  
  74.   
  75.     #Run the code  
  76.     def runAutoIT3( cmdString )  
  77.         au3 = WIN32OLE.new("AutoItX3.Control")  
  78.         au3.opt("WinTextMatchMode", 2)  
  79.         au3.RunWait( cmdString )  
  80.     end  
  81.   
  82.     def openInBrowser( browser, url )  
  83.         runAutoIT3( browser+" \""+url+"\"" )  
  84.     end  
  85.   
  86.     def runInCmd( fileDir, command, params, leaveOpen )  
  87.         cmdString = ""  
  88.         if( leaveOpen )  
  89.             cmdString = "cmd.exe /c \"codeLauncherPause.bat \"" + fileDir + "\" \""+ command + params + "\"\""  
  90.         else  
  91.             cmdString = "cmd.exe /c \"cd " + fileDir + " && \"" + command + params + "\"\""  
  92.         end  
  93.         runAutoIT3( cmdString )  
  94.     end  
  95. end  
  96.   
  97. if __FILE__ == $0  
  98.     #Get Parameters  
  99.       
  100.     filename = ARGV[0]  
  101.     exit if( !filename )  
  102.     browser = ARGV[1]  
  103.       
  104.     browser = "C:\\Program Files\\Mozilla Firefox\\firefox.exe" if( !browser )  
  105.       
  106.     #execute  
  107.     CodeLauncher.new( filename, false, browser )  
  108.     exit  
  109. end  
  1. cd %1  
  2. %2 %3  
  3. pause  
And setting CodeLauncher to be run through "ctrl-1" in Textpad is pretty easy. Go to configure, then select preferences, then select tools. Add a DOS Command, name it whatever you want. I called mine Code Launcher.

Click Apply, then expand Tools. For Parameters, type "codeLauncher.rb $File". For Initial Folder, enter the full path to the codeLauncher.rb script. Make sure "Close DOS window on exit" is checked. Then click OK. You're all set.



As a parting note, make sure whatever you're using to develop code with does code launching (maybe build/execute is a better term) and make sure you tailor fit it to your usage. Any decent development environment does... F5 is a common key to try. This is really basic funcitonality that makes a developer's life tons easier.

Monday, August 17, 2009

My Path to a Java/COM Bridge

Accurate as of July 2007.

A company my company was subcontracting to for a project told us that we could only use their hardware via COM objects. As a team, we didn't know very much about COM. Our application was entirely Java based. To us, their requirement implied that we needed to write JNI code, which frightened us because we've done -that- before.

Since we didn't know that much about COM, I was tasked to do a study on COM and a feasibility study for using COM objects from Java. I broke up the study into two parts. First, I figured out the basics of COM so that I could explain it to my team members. Second, I looked into and evaluated solutions to our problem. (SPOILER: We ended up going with J-Integra.)

The study was actually interesting and I think it's useful in general. Being able to use COM means that you have a lot of opportunity to interact with Windows applications. The ability to automate Microsoft Office applications is really convenient. So I'm going to share some of the results of my COM studies. First, I'll try to explain COM if you don't know much about it. Then I'll go into a comparison of Java/COM bridges.

I'm no expert on any of this, so don't let this be the end of your own studies. And I don't really go very deeply into either COM nor the Java-COM bridges. Also, I did quickly convert this from a private internal document to one for public use, so it might read funny in some places. So use this article more as a starting point.

If you want to learn, COM, listen

To teach COM, I'm going to reference the best of what I've read in an order I find very conducive to learning COM. I don't care too much about in-depthness to start off with: I build up to that when I feel I'm ready. This order will reflect that. So, let's start learning. Please note that I'm writing from the perspective of using Visual Studio 2005 and Windows XP Home:

1. http://www.codeproject.com/com/comintro.asp
This covers the COM interaction client side basics and is quite straightforward. This article (and the ones that will follow) reference ATL without describing what it is. The Active Template Library is simply a set of template-based C++ classes that simplify the programming of COM objects. ATL includes an object wizard that sets up the primary structure of COM Objects (including OLE Automation servers and ActiveX controls) very quickly with a minimum of hand coding. ATL allows creating smaller controls without support DLLs, so ATL is in a sense a lightweight alternative to Microsoft Foundation Classes (MFC) for the COM control environment.

2. http://www.codeproject.com/com/comintro2.asp
This covers making a COM server. Note that the client will not work unless you register the DLL you create/download. Do this by running "regsvr32 MsgBoxSvr.dll" in the command window.

3. http://www.microsoft.com/msj/1295/activex1295.aspx
COM distinguishes between three interface types: custom, dispatch, and dual interfaces. It's at least important to be familiar with the results of implementing the IDispatch interface. The IDispatch interface is the interface that exposes the OLE Automation protocol. The Automation (IDispatch) interface allows a client application to find out what properties and methods are supported by an object at run-time. It also provides the information necessary to invoke these properties and methods. Client applications do not need to be aware of the object members when they are compiled.

Those three are good enough on their own as a starter, but personally, I needed more reinforcement. And thus...

4. http://www.codeproject.com/com/rrcombasics.asp
Once again, COM basics. The grammar is bad, but it will help reinforce the basics.

5. http://www.codeproject.com/com/Inside_COM.asp
Another in-depth Introduction to COM (Server and Client). This one doesn't come with a code download.

6. http://www.codeproject.com/com/mmtopo_comid.asp
Some vocabulary definitions, once again meant to reinforce the basics.

An important aspect of my project at work was the need of being able to use callback methods (aka COM events) with COM. The next links go over this:

7. http://www.samspublishing.com/articles/article.asp?p=25922&seqNum=4&rl=1
This explains the concepts of COM events.

8. http://www.codeproject.com/com/connectionpoint.asp?df=100&forumid=14124&exp=0&select=1481687
An example/tutorial going over COM events. This code simply doesn't compile on Visual Studio 2005.

And since .Net is so important now, COM interoperability is an issue. These articles give some basics on that. A component available from .NET (written in C#) is a .NET component. It is different from a COM component because it requires a few of the DLLs from the .NET Framework in order to work.

9. http://www.codeproject.com/useritems/BuildCOMServersInDotNet.asp / http://www.codeproject.com/dotnet/cominterop.asp
General stuff

10. http://support.microsoft.com/kb/828736/en-us
More about writing a .NET component.

11. http://support.microsoft.com/kb/313891
How to sink managed C# events. I believe that this is the best way to providing callbacks for COM components in C#.

The above information should be enough knowledge to understand what's going on when I get to explaining the Java/COM bridges. I'm going to provide a few more links that go a little more in depth, but you can probably do without the extra information. If you really wanted to learn COM, even this won't be enough for you. You're going to have to buckle up and buy a book. But anyway...

12. http://www.codeproject.com/com/com_in_c1.asp
To understand how COM really works, you should do it from C. This is a series of 8 tutorials (com_in_c1 to com_in_c8) that will really let you know what's going on beyond all the Visual Studio gives you through its wizards and templates.

13. http://en.wikipedia.org/wiki/Component_Object_Model
Finally, for a refresher course, read the wikipedia page. The links are nice. You may want to wiki OLE Automation, ActiveX, ATL, and IDispatch while your out it. Reviewing by reading is always beneficial, even if not as beneficial as reviewing by doing.

14. http://www.microsoft.com/com/default.mspx
Lastly, the definitive source on COM. Good luck finding your way around there, lots of scattered information.

Java Couldn't Originally Make Connections Or Method Calls.... OM.

So, after all that reading, we're finally ready to see if we can figure out the problem. The goal is to use Java to work with COM objects. No matter how you look at it, this involves interacting with native code running outside the JVM. Web Services (Java to Soap/Soap to COM) is an option, but due to the poor performance and the limited COM interaction (no events and worse), it's not a very good option. It was possible to directly call COM objects using Microsoft's own JVM in Windows, but Microsoft dropped its Java support and who really wants to lock into a specific JVM anyways? Basically, the only real option there is right now is to use JNI, which is not very fun. Our best hope is that someone else has already written the JNI for us and wrapped it up in a not-as-unfun Java API. Fortunately, this need for COM interactions is common enough that people have done that, to varying extents.

There are open-source implementations and there are (costly) commercial solutions. I'm going to point out eleven solutions that I've come across. Please pay very close attention, these solutions are very different and support COM in different ways. Make sure the solution you choose will do what you want and that it's something you can bear to work in. I want to cover each of these solutions enough so that you know if it will work for you. I'll make some comments about using it (and it's pricing) so you can decide if you can work with it. When in doubt, don't take my word for it and go to a more reliable source, specifically the project's website. Things could have definitely changed since I wrote this.

BTW, I found this gem of a study. It was very helpful to me: http://www.mit.jyu.fi/minurmin/javacom/javacom_report.html

Comparisons

I hunted down as many solutions as I could find, but ended up only reviewing three of them. Feel free to do more research on your own.

Summaries

Hopeful #1
: Jacob, http://danadler.com/jacob/ -> http://sourceforge.net/projects/jacob-project/
The potentially big problems with Jacob are its difficult error prone syntax, the lack of vtable call support, and inconsistencies in being able to access all the COM objects it should. The benefits are the cost (open source) and the maturity of the project.

Jacob uses a lot of Reflection and does its mappings based on strings provided. That means that typos are deadly and very difficult to track down because everything will compile right... it just won't work. The Jacob API is very verbose: a Jacob call can easily be twice as many lines of code as opposed to a VBScript call. Just another thing that makes debugging difficult. There is no built in way to create wrappers, but I read about a technique of modifying Jacob to use wrappers. I don't think it's worth the effort though.

Jacob has a lack of vtable call support, which means that it can not support custom interfaces. The COM objects must support the IDispatch interface (meaning ActiveX/OLE objects).

Another problem that I've encountered seems to be an incompatibility with certain COM objects. The calls won't work with certain objects (I've had very inconsistent experience with COM objects supporting callbacks in particular.) I highly suspect that it's due to badly formed COM Objects, but as of yet, I haven't tracked down the specific problem. It's not just my COM objects either: Callbacks won't work with Microsoft Office 2000, but will work with Microsoft Office 2003.

The benefits of using this solution is that it's open source and it's the most mature open source solution. It has decently active forums for minor issues and plenty of people have reported success using it. A commercial solution would be much, much better, but a Jacob solution will probably be manageable.

Hopeful #2: Jsegue, http://jsegue.sourceforge.net/
This solution seems pretty good. Unlike Jacob, it supports vtable calls. However, its API is far worse and the wrapper solution is no good (requires C++ code.)

Hopeful #3: J-Integra, http://j-integra.intrinsyc.com/
This seems to be the overall best tool I've found so far. In addition, its API is easier to use than the other solutions. It is a commercial product with all the disadvantages (cost) and benefits (support) associated.

For Java to COM support, J-Integra basically works by using their comtojava tool to create a Java Wrapper for a specified COM object. The resulting wrapper class and a J-Integra jar are imported by the class that needs COM to Java interaction. All COM calls are called as method calls of the wrapping class. The comtojava tool is easy to use and doesn't require much more than specifiying the COM class to wrap.

COM events (callbacks) are implemented using Java's standard listener registration technique. J-Integra connects with .NET components as well as COM components.

J-Integra provides support for many more Java/COM bridge needs. It allows Java to be called from COM, it allows native interfaces to be used for speed (as opposed to a native COM implementation). Probably the best things about J-Integra are its documentation and technical support. Overall, J-Integra seems to be the best way to go for a Java/COM bridge, as long as you can afford it.

Hopeful #4: com4j, https://com4j.dev.java.net/

Hopeful #5: EXJCom, http://www.ezjcom.com/purchase.html

Hopeful #6: JCOM, http://sourceforge.net/projects/jcom/
Open source and abandoned (in all but name.)

Hopeful #7: JNIWrapper, http://weblog.janek.org/Archive/2004/06/27/JNIWrapper-JavaDesktopInt.html

Hopeful #8: JaWin, http://jawinproject.sourceforge.net/jawin.html

Hopeful #9: Java2Com, http://www.nevaobject.com/default.htm

Hopeful #10: Bridge2Java, http://www.alphaworks.ibm.com/tech/bridge2java

Hopeful #11: DTJCB, http://www.alphaworks.ibm.com/tech/dtjcb

Hopeful #12: http://sourceforge.net/project/showfiles.php?group_id=174727
Not mature yet, but maybe later. It might be worth looking into later.

Comclusion

And that's all I came up with for my own study. Hopefully, it will help you in whatever you're doing.