A little trick here:
Add the Dll manually to the GAC in the Setup project (as a file rather than as a "project Output"); then set the "register" property of the Dll to "vsdraCOM". If you use "Project Output" for the Dll project it seems to (a) register the COM object only sometimes, and (b) keeps trying to stick the TLB into the GAC and giving you an error.
Thursday, August 7, 2008
Wednesday, July 23, 2008
taskdef class. org.apache.catalina.ant.InstallTask cannot be found
If you get this error trying to run your ant build, this can fix it:
Copy the "catalina-ant.jar" file from $TOMCAT_HOME\server\lib to $ANT_HOME\lib
Updated: 20 Feb 2013
If you've updated to Tomcat 7 then somewhere along the way, the "InstallTask" has been deprecated and replaced with "DeployTask". If you edit your build script and replace it, it should work. For more details, read here.
In order to get some of your other Tomcat 7 Catalina-Ant bits working (eg. StartTask, StopTask), follow Paul Grenyer's useful guide.
Copy the "catalina-ant.jar" file from $TOMCAT_HOME\server\lib to $ANT_HOME\lib
Updated: 20 Feb 2013
If you've updated to Tomcat 7 then somewhere along the way, the "InstallTask" has been deprecated and replaced with "DeployTask". If you edit your build script and replace it, it should work. For more details, read here.
In order to get some of your other Tomcat 7 Catalina-Ant bits working (eg. StartTask, StopTask), follow Paul Grenyer's useful guide.
Tuesday, July 22, 2008
Moving your VB6 COM collections to .Net under Vantive VBA
A pretty typical VB6 object model was to use a collection class to represent a group of objects. So you'd have a "Car" object with properties like "Colour", "Make", "Model" etc. and you'd store multiple of these in a "Cars" object, which would internally use a Collection and externally provide your "Add", "Count" "Remove" and enumerator methods. You could expose these as method parameters via COM and Vantive VBA would happily be able to enumerate the collection:
Skip forward a few years: Vantive is still kicking and it is time to convert your VB6 COM component to .Net. Our VB6 Collection object is gone, what will we use instead? What about an array of objects? So in .Net we have:
To access these from Vantive VBA, this should do the trick:
This works fine via VB6, but when you try to compile this in Vantive VBA, you'll get a "Cannot assign whole array" error message on the oCars=... line.
The solution is to use the ArrayList class. Like this:
Which you can then access in Vantive VBA exactly as you could with the VB6 collection, including the "Count" method:
Dim oCars as Object
Dim oCar as Object
Set oCars = oComVB6.FetchCarsByColour("blue")
For Each oCar in oCars
MsgBox "Make is " & oCar.Make
Next oCar
Skip forward a few years: Vantive is still kicking and it is time to convert your VB6 COM component to .Net. Our VB6 Collection object is gone, what will we use instead? What about an array of objects? So in .Net we have:
public class Car : ICar {...}
public Car[] FetchCarsByColour(string colour) {...}
To access these from Vantive VBA, this should do the trick:
Dim oCars() as Object
Dim oCar as Object
oCars = oComNet.FetchCarsByColour("blue")
For I = 0 to UBound(oCars)
...
This works fine via VB6, but when you try to compile this in Vantive VBA, you'll get a "Cannot assign whole array" error message on the oCars=... line.
The solution is to use the ArrayList class. Like this:
public ArrayList FetchCarsByColour(string colour) {...}
Which you can then access in Vantive VBA exactly as you could with the VB6 collection, including the "Count" method:
Dim oCars as Object
Dim oCar as Object
Set oCars = oComNet.FetchCarsByColour("blue")
For Each oCar in oCars
MsgBox "Make is " & oCar.Make
Next oCar
MsgBox "You found " & oCars.Count & " cars"
Wednesday, June 18, 2008
"TNS-12541: TNS:no listener" error proves no match for Super-useful Oracle commands
I'm running Oracle on my XP development box. After making a few changes to my network I found that Oracle no longer started. The first step to checking things was to use super-useful Oracle command #1: "tnsping". If you have the Oracle bin in your path, you can run this from anywhere:
TNS Ping Utility for 32-bit Windows: Version 10.2.0.1.0 - Production on 18-JUN-2
008 11:14:42
Copyright (c) 1997, 2005, Oracle. All rights reserved.
Used parameter files:
C:\oracle\product\10.2.0\db_1\network\admin\sqlnet.ora
Used TNSNAMES adapter to resolve the alias
Attempting to contact (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 10.0.1.1
98)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = mybase)))
TNS-12535: TNS:operation timed out
So, first problem is that the host name I'm using is hard-coded in there to be "10.0.1.1". A quick check of "ipconfig /all" reveals that, with my network tinkering, my IP address has changed. In fact, I'm running XP on a virtual machine and not using a static IP address so the IP address was going to change sometime anyway. So we'll replace my IP address with my new IP address instead. To modify this setting, find your "tnsnames.ora" file; for me it lives at C:\oracle\product\10.2.0\db_1\network\admin\tnsnames.ora.
OK, so now a "tnsping" gives me the lovely "TNS-12541: TNS:no listener" message. Checking my Windows Services, I can see that the Oracle TNS Listener service (OracleOraDb10g_home1TNSListener) is stopped. If I try to start it I get a nice generic Windows Service message "Error 1067: The process terminated unexpectedly."
So, how do I find out what is happening with the listener? By using super-useful Oracle command #2: "lsnrctl". Again you can find this in your Oracle bin directory. You can now check the listener status via "lsnrctl status". Again, this revealed the same "10.0.1.198" IP address hard-coded in the listener config. So again we need to replace that with the new IP address also. The file that controls this is "listener.ora" and it lives in the same place as your "tnsnames.ora".
I found these needed to be done in a set order:
So, to summarise, replace the hard-coded IP address with your new IP address in your "tnsnames.ora" and "listener.ora" files.
Note that I did try to use both "localhost" and "127.0.0.1" but couldn't get the listener to pick up the database√.
tnsping mybase
TNS Ping Utility for 32-bit Windows: Version 10.2.0.1.0 - Production on 18-JUN-2
008 11:14:42
Copyright (c) 1997, 2005, Oracle. All rights reserved.
Used parameter files:
C:\oracle\product\10.2.0\db_1\network\admin\sqlnet.ora
Used TNSNAMES adapter to resolve the alias
Attempting to contact (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 10.0.1.1
98)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = mybase)))
TNS-12535: TNS:operation timed out
So, first problem is that the host name I'm using is hard-coded in there to be "10.0.1.1". A quick check of "ipconfig /all" reveals that, with my network tinkering, my IP address has changed. In fact, I'm running XP on a virtual machine and not using a static IP address so the IP address was going to change sometime anyway. So we'll replace my IP address with my new IP address instead. To modify this setting, find your "tnsnames.ora" file; for me it lives at C:\oracle\product\10.2.0\db_1\network\admin\tnsnames.ora.
OK, so now a "tnsping" gives me the lovely "TNS-12541: TNS:no listener" message. Checking my Windows Services, I can see that the Oracle TNS Listener service (OracleOraDb10g_home1TNSListener) is stopped. If I try to start it I get a nice generic Windows Service message "Error 1067: The process terminated unexpectedly."
So, how do I find out what is happening with the listener? By using super-useful Oracle command #2: "lsnrctl". Again you can find this in your Oracle bin directory. You can now check the listener status via "lsnrctl status". Again, this revealed the same "10.0.1.198" IP address hard-coded in the listener config. So again we need to replace that with the new IP address also. The file that controls this is "listener.ora" and it lives in the same place as your "tnsnames.ora".
I found these needed to be done in a set order:
- Shutdown Oracle.
sqlplus user/pwd as sysdba
shutdown immediate - Stop the listener.
lsnrctl stop
- Change the IP addresses in *.ora files.
- Start the listener (notice that your database is not registered)
lsnrctl start
- Start Oracle.
sqlplus user/pwd as sysdba
startup - Check the db is registered to the listener.
lsnrctl status
- Try to login via the tnsname.
sqlplus user/pwd@mybase
So, to summarise, replace the hard-coded IP address with your new IP address in your "tnsnames.ora" and "listener.ora" files.
Note that I did try to use both "localhost" and "127.0.0.1" but couldn't get the listener to pick up the database√.
Tuesday, June 10, 2008
Debug into .Net from VB6
If you've got a VB6 app that is calling a .Net component, and you want to use the VS debugger on the .Net component then you need do the following:
Now, back in VB6, click the button (or do the action) that activates your .Net component. It will switch into the VS debugger on your breakpoint. Nice!
Be careful about which version of your .Net Dll is registered on the machine (release/debug). If you hav ethe relase one activated then it won't give you any debugging - it does warn you though. To get your debug one back, switch VS back to debug build, close your VB6 app/IDE, and rerun your .Net app. This will re-register the debug version.
For more details, try this.
Update: Very, very, VERY, VERY IMPORTANT!!!! You must enable "Managed Code" debugging when you attach to the process. So, in the "Attach to Process" window, you might have "Automatic: Native Code" showing. This didn't work for me - presumably because my Dll is managed, not native. So click the "Select..." button next to the "Attach to:" and choose "Managed" and "Native". Now attach to VB6.exe and all should work!
- Start the VB6 app
- Set a break point in your .Net app
- In VS, click on "Tools-Attach to Process..." and select the VB6 application/IDE. This will start your component running in VS.
Now, back in VB6, click the button (or do the action) that activates your .Net component. It will switch into the VS debugger on your breakpoint. Nice!
Be careful about which version of your .Net Dll is registered on the machine (release/debug). If you hav ethe relase one activated then it won't give you any debugging - it does warn you though. To get your debug one back, switch VS back to debug build, close your VB6 app/IDE, and rerun your .Net app. This will re-register the debug version.
For more details, try this.
Update: Very, very, VERY, VERY IMPORTANT!!!! You must enable "Managed Code" debugging when you attach to the process. So, in the "Attach to Process" window, you might have "Automatic: Native Code" showing. This didn't work for me - presumably because my Dll is managed, not native. So click the "Select..." button next to the "Attach to:" and choose "Managed" and "Native". Now attach to VB6.exe and all should work!
Thursday, June 5, 2008
C# Progress/Busy Window
I've been playing around with using a C# progress window. For testing I setup a project that takes three different approaches. I used a simple XML serialisation project as a base, and added in some sleep commands to make the serialisation command take about 10 seconds. The first approach uses a simple form (which doesn't work very nicely), then second uses a simple form with a background thread, and the third uses a form with a cancel button and a progress bar with a background thread.
The upshot is, that you need to use a BackgroundWorkerProcess. You need to encapsulate the stuff you want to do in the background into a single function; you need to identify where your bottleneck is. You send your job, that is going to take some time, to a background worker thread, and that keeps the UI responsive; it makes sure that your "I'm busy right now" window stays refreshed. There are two key things you need to do to get this to work:
1. Make sure that your work to be done in the background, doesn't try to touch the user interface. If you need to show progress then you can use the backgroundWorker.Progress method. Your background process should do its work and then return the data (via e.Result) which can then be populated onto the form if required.
2. Make sure you lock down the UI when you launch the work. The UI will remain active and you don't want the user to open a new screen or start the same update again etc, so you need to disable these buttons as required.
One thing you might want to do if you are waiting on a single network request (and therefore can't really determine % complete for a progress bar) is to display an animation - like some whirring cogs or some papers shuffling.
The upshot is, that you need to use a BackgroundWorkerProcess. You need to encapsulate the stuff you want to do in the background into a single function; you need to identify where your bottleneck is. You send your job, that is going to take some time, to a background worker thread, and that keeps the UI responsive; it makes sure that your "I'm busy right now" window stays refreshed. There are two key things you need to do to get this to work:
1. Make sure that your work to be done in the background, doesn't try to touch the user interface. If you need to show progress then you can use the backgroundWorker.Progress method. Your background process should do its work and then return the data (via e.Result) which can then be populated onto the form if required.
2. Make sure you lock down the UI when you launch the work. The UI will remain active and you don't want the user to open a new screen or start the same update again etc, so you need to disable these buttons as required.
One thing you might want to do if you are waiting on a single network request (and therefore can't really determine % complete for a progress bar) is to display an animation - like some whirring cogs or some papers shuffling.
Wednesday, June 4, 2008
C# Converting a Class to XML
Using a class in C# to hold data is the right idea. XML is just a transport mechanism and should only be used at the boundaries of your application. So, as soon as you grab a chunk of XML from somewhere you should turn it into a class. Similarly, when you want to send some data somewhere, encapsulate the data in a class, and then serialise the class to XML. This way, if you decide to get rid of XML and use INI files, or CSV files, or YAML files then you don't need to change your core application - you only change the routines that serialise and deserialise the class.
Q. How to easily turn a class into XML?
A. For a simple class setup, you only need about four lines of code. For a user-defined class called "cls":
Q. How to easily turn a class into XML?
A. For a simple class setup, you only need about four lines of code. For a user-defined class called "cls":
XmlSerializer serializer = new XmlSerializer(cls.GetType());
StringWriter writer = new StringWriter();
serializer.Serialize(writer, cls);
string xml = writer.GetStringBuilder().ToString();
Subscribe to:
Posts (Atom)