Automating Excel through the PIA makes VBA go squiffy - c#

I have absolutely no idea how to start diagnosing this, and just wondered if anyone had any suggestions. I'm generating an Excel spreadsheet by calling some Macros from a C# application, and during the generation process it somehow breaks. I've got a VBA class containing all of my logging/error-handling logic, which I instantiate using a singleton-esque accessor, shown here:
Private mcAppFramework As csys_ApplicationFramework
Public Function AppFramework() As csys_ApplicationFramework
If mcAppFramework Is Nothing Then
Set mcAppFramework = New csys_ApplicationFramework
Call mcAppFramework.bInitialise
End If
Set AppFramework = mcAppFramework
End Function
The above code works fine before I've generated the spreadsheet, but afterwards fails. The problem seems to be the following line;
Set mcAppFramework = New csys_ApplicationFramework
which I've never seen fail before. If I add a watch to the variable being assigned here, the type shows as csys_ApplicationFramework/wksFoo, where wksFoo is a random worksheet in the same workbook. What seems to be happening is that while the variable is of the right type, rather than filling that slot with a new instance of my framework class, it's making it point to an existing worksheet instead, the equivalent of
Set mcAppFramework = wksFoo
which is a compiler error, as one might expect. Even more bizarrely, if I put a breakpoint on the offending line, edit the line, and then resume execution, it works. For example, I delete the word 'New' move off the line, move back, re-type 'New' and resume execution. This somehow 'fixes' the workbook and it works happily ever after, with the type of the variable in my watch window showing as csys_ApplicationFramework/csys_ApplicationFramework as I'd expect.
This implies that manipulating the workbook through the PIA is somehow breaking it temporarily. All I'm doing in the PIA is opening the workbook, calling several macros using Excel.Application.Run(), and saving it again. I can post a few more details if anyone thinks that it's relevant.
I don't know how VBA creates objects behind the scenes or how to debug this. I also don't know how the way the code executes can change without the code itself changing.
As previously mentioned, VBA has frankly gone a bit squiffy on me... Any thoughts?

If you look in task manager, are there any instances of excel running in the background? Just curious if it is creating an Excel object and not disposing of it properly.

I would suggest that somehow the PIA's are not working correctly. I would recommend unregistering them, removing all instances of them from your PC, and then regenerating them.
Of course this is not the rational explanation I would like to give, but it seems like sometimes COM just doesn't want to behave. I would love to know what really happens when things break down like this, but the only thing I have ever seem work is lots of stabs in the dark, followed by an attempt to retrofit a rational explanation once the weird refusal to work randomly disappears again.
Sorry for the lack of a 'REAL' answer

Related

Can't fill CheckedListBox

I am trying retrieve data from a database. I have added a .sdf file and wrote the code shown below. My table name is info and it has three columns: id, name, and code.
What I want to do is to populate a CheckedListBox with this data, but nothing happens when I execute my code. CheckedListBoxis empty. What am I doing wrong?
SqlCeDataReader dr;
SqlCeConnection con;
SqlCeCommand cmd;
void loadData()
{
cmd.CommandText = "select column_name from Information_schema.columns where table_name='info' order by ordinal_position";
con.Open();
dr = cmd.ExecuteReader();
if (dr.HasRows)
{
while (dr.Read()) {
checkedListBox1.Items.Add(dr[0].ToString());
}
dr.Close();
con.Close();
}
private void Form1_Load(object sender, EventArgs e)
{
con = new SqlCeConnection();
con.ConnectionString=#"Data Source=c:\users\xxx\documents\visual studio 2012\Projects\WindowsFormsApplication1\WindowsFormsApplication1\Database1.sdf";
cmd = new SqlCeCommand();
cmd.Connection = con;
loadData();
}
You are basically asking for your code snippet to be debugged. This is tricky for a couple of reasons. Firstly your code calls out to a database, and does a slightly weird query in that it is getting information on columns from a system database rather than something from your own table. No-one here knows exactly what the result of that will be. It might be possible to replace that line with something else which has a similar effect (eg a list which is initialized with some dummy values in the code), but by doing so we might change the behavior so that the bug disappears, and we would be none the wiser. We will come back to this idea in a minute.
A question like this is not ideal for StackOverflow. One reason is that which I have just outlined - it could be very difficult to replicate exactly what your code does in order to help you. Therefore you might not get a useful answer. Another, slightly opposed reason is that someone (slightly more diligent and observant than me) might spot a simple typo or gotcha in your code. This might have already happened while I type this. They will post an answer or a comment pointing this out to you, and you will fix your code. However, the question is then not particularly useful to anyone else. No-one is likely to make quite the same typo or slip as you. Even if they do, it will probably be in a slightly different context, unrelated to CheckedListBox or SqlCeDataReader, and they will never find this question.
Those type of questions-and-answers essentially add nothing to this site. They also won't be particularly helpful to you, and this is why: you won't learn to debug.
In my experience, great programmers are almost always good debuggers. Most of us make a lot of small mistakes - finding and fixing these quickly is the difference between average productivity and great productivity. Being a good debugger also means you have a different relationship with your thoughts and your (or someone else's) code. You are able to think more flexibly, holding many different cases and possibilities in your mind at the same time. This is called divergent thinking, and is slowly being recognized to be just as important as convergent thinking, thinking which leads to an answer.
What you should do to progress with your problem and as a programmer, is debug properly. If you had done this and were still faced with a difficult question (or maybe a bug in your tools), you would have a much shorter code snippet to post, and would be able to describe the unintended behavior much better.
There are basically two ways of debugging code like this - stepping through it, and 'print lines'. (Weird and difficult code like kernel code, multithreading and message passing might be much harder to debug - these two techniques will get you a long way with everything else.) The main idea in both is that you look at intermediate values of your variables, at different stages of evaluation. Debugging support for stepping through C# code in Visual Studio is very good, but to keep things simple and reasonably language independent, I will use print lines as an example.
Just add a line anywhere in your program, which you want to know if the execution flow gets there or not. For example
Console.WriteLine("We do have some rows");
should go after the if (dr.HasRows) { line. If this is a console program and you run it, you will see this output appear (or not) in the console window. Otherwise you could use Debug.Print to send the text to the 'Output' window of VS, or a Winforms MessageBox to display the text in an alert window.
You will find out straightaway if your query result has any rows or not. If it doesn't, you have a problem with your query or the way you execute it. You should first of all run the same query on the same database in a different way, for example sqlcmd or Sql Server Management Studio. The fact that you hadn't done this, and that you didn't know if dr.HasRows was true or not, showed me immediately that you weren't debugging properly. [Edit: #Leonardo also pinpointed exactly this same thing in a comment.] If you get some valid response rows when running the query elsewhere, but not when running this code, there is some problem with how you set up the connection or run the query in C#. If you don't get any rows, there is a problem with your query itself. Try different queries directly until you get the right one, then put it back into the C# code. (The third possibility is that 'no rows' is the right response, and that your code should know how to deal with this case properly.)
Suppose on the other hand that dr.HasRows is true. There is some problem with dr.Read or with adding to the checkbox. To eliminate the former, try using a printline in the inner loop. This time output the value you are interested in:
Debug.Print("Value to be added is: " + dr[0].ToString());
You will quickly see if these look reasonable or not. If they do, try and find out what is going wrong with checkListBox1. Print the value of checklistBox1 and/or checkListBox1.Items after each iteration of the loop. Try writing some different values, possible a string constant or a hard-coded list of strings to Items instead and see if it works. Make sure that checkListBox1 actually is empty, and that you actually call the code to fill it up before accessing it.
Well done, you've just learnt the main two parts of one of the two main methods of debugging. You can identify how the code loops and branches (by putting in information print lines)
Console.WriteLine("Got this far!");
and you can find out what the value of your data is (by printing the actual values of variables)
Console.WriteLine("x is currently equal to " + x.ToString());
The nice thing about printlines is that whenever you control some kind of output or logging, you can debug like this without any other tools.
Now you are going to test each step of the code, observing the data passed to it and back from it. Try and find out exactly which parts are working as expected, and which parts are already using bad information passed to them by a previous step. Any bit you are not sure about (like the db accesses), you try and re-run in a different way to how it's used in your code. For example, write a two-line program which creates and displays a ListCheckBox, to make sure you know how to do it.
If you are using some extremely experimental tool, you might end up finding a bug in it, and that your own code is fine. This is very very unlikely with what you're doing, using technology that has been extensively tried and tested by others. The most likely outcome is that you find your (probably simple) bug and fix it. Other than that, it is possible you come back to StackOverflow with a much smaller test case, asking about something non-obvious in the way you use the components or some other straightforward query that is hard to figure out yourself. (Both Winforms and SQL Server do contain many gotchas, which lead to good questions that help others..) When you do, you will be able to share the results of your debugging to let answerers know exactly where the code doesn't seem to behave as expected. It's also very likely that if you do have a problem like this, you can search and find that someone else has already come across it and had it answered.
Don't forget, once you've finished debugging you can take your working code to http://codereview.stackexchange.com to get advice about making it shorter, more elegant, better performing and more error-proof. Good luck!
Please try use the command text as below:
cmd.ComandText = "Select * from infor".

How to check XML output from DataContract

I have inherited some code that is not working properly. Of course it was working properly as of the last commit but somehow it is not returning good data now. Historic logs show the data would look like this:
[0] => SimpleXMLElement Object
(
[Patron_Key] => 412730
[x3.32] => 4A
[x3.1] => 2014-01-08T08:00:00-07:00
[x3.2] => 2014-01-22T07:59:59-07:00
)
A valid field name is x3.32. The 'x' is added to the column name on purpose. However, now the XML is coming out with leading zeroes in the field names, as shown below:
<Patron_Key>363384</Patron_Key>
<_x0033_.32>BC4S4B</_x0033_.32>
<_x0033_.1>2013-08-15T08:00:00-06:00</_x0033_.1>
<_x0033_.2>2014-05-13T07:59:59-06:00</_x0033_.2>
So far I have checked that the data coming from the DB via COM+ is valid using PowerShell. This leads me to think it is either the code or something in IIS. Whenever I log the data in the code after the COM+ calls and when it is put in a DataTable it looks fine. The data structure being used has the DataContract attribute and all the class members have the DataMember attribute. Is there a way I can check the data after it is serialized/deserialized to see if that is where the leading '003' is being added?
Any thoughts on where I might look would be appreciated. I'm working on getting a new web server spun up just to make sure it has nothing to do with IIS.
Update:
It turned out that the issue was because the first character in the column name was a number, and an XML node cannot start with a number. We found a GitHub comment that got us in the right direction, but I'm also linking to a SO post that describes in more detail the issue. Oh, and someone introduced some code that wasn't properly detecting numbers in the first character of the column - 5 months ago, so we had to dig through a whole bunch of commits to find the offending change.
Encoding XML element name beginning with a number?
I'm seeing two questions:
Is there a way I can check the data after it's serialized to see if that's where the leading '003' is being added?
Any thoughts on where I might look?
The first one is one I can definitively answer: if you are capable of running your application in debug mode and stepping through the code, you should be able to use the debugger to step through the serialization/deserialization and examine the contents of the variable using a watch. Now, you may have to involve the remote debugger depending on where the code is running, and if you have helper processes you may need to run several instances of Visual Studio with EACH portion of your application running in debug mode to properly track the issue down, but this is what the debugger is for.

No return value from C++ DLL using C#

Using a console application, I'm making use of a c++ com dll to call a function.
I have added the registered DLL as a reference for the project and then I am instantiating the object and calling the function. (I should note that I'm not using pinvoke as some other people seem to be)
I should be getting a string back as a result but I am just getting an empty string. The only way I can get any form of output is by enabling debugging for unmanaged code and from that I can see that it is executing correctly and returning a result.
I have had a search around stackoverflow and a few other sites and can't quite find anything that matches this. Any ideas what I'm doing wrong or how I can get it to return a value?
EDIT: As requested, here is the code -
COMMODCHECKLib.Modcheck mod = new COMMODCHECKLib.Modcheck();
string output = mod.check("123456");
I would suggest to first check, if the problem is within the C# code or the COM library. For that, you can use e.g. a VB-Script file (.vbs) like
(test.vbs)
Set mod = CreateObject("COMMODCHECKLib.ModCheck")
WScript.echo(mod.check("123456"))
Just run this script from the command line (entering test.vbs).
If this gives the desired output, you know at least, that the problem lies on the C# side.
Well it seems that the documentation for the DLL was incorrect and gave the check function as the one I needed but infact there was a function called checkAllocate which is the one I needed to use. Apologies guys - many thanks for your time and efforts

Running an XLL outside Excel?

I know this question has been posted before... but I haven't found any answer yet (besides from the generic answers about how XLL are actually DLL, etc).
Has anybody out there been successful calling a XLL from say C# (using DllImport) without having to load Excel with the XLL loaded as an addin?
Basically, you would have to create a special XLCALL32.DLL that simulates the Excel host. It sounds like a lot of work... has anybody done this? Or seen a product to do it?
Thanks
You're on the right track with needing to create your own XLCall32.dll and simulate Excel. That's non-trivial given what you can do via the interface that XLLs use to talk to Excel. It becomes easier the less of Excel that you need to use from within your XLL, so I guess if you have a known selection of XLLs that you need to use and you know what bits of Excel they access via the XLL interface then you can just replace the bits you need...
Why do you want to do this?
Evaluating this XLL+ library (which is not free, running on trial atm) which hels to "mock"/"simulate" XLCALC32.dll calls (as it only needs 2 methods from it as far as i understand). Will let you know if I get somewhere.

Is this utility useful enough to bother putting into CodePlex?

It was my second C# project, undertaken years ago, and it has lived on, because (imho) it is Genuinely Useful Software. It's also badly designed and the code is embarrassing.
It runs C# code. You write a method, the method name appears in a listbox, you double-click the method name to execute it. That's it.
Examples:
When I open up my C# web project at work, a method runs a couple command-window apps my project needs, and checks to confirm that the requisite service is up. I never have to remember that stuff.
I hate UPPERCASE, so I have a method that lower-cases SQL, but preserves the case of quoted strings. Another method calls a web service to beautify SQL. Those both operate on the clipboard.
One method fixes the names of MP3 files: title casing, replacing underscores and hyphens, optionally removing/inserting text or prepending numbers. Creates a playlist!
I double-click to harvest all of my Twitter links, turning them into an HTML page with hyperlinks and a jQuery-powered search.
A method searches the specified log4net.log for every operation that took longer than the specified number of milliseconds.
I can create a restore point by double-clicking a method (and open up the corresponding dialog with another method).
When my wife had to write some sorting algorithms for school, the utility was an ideal testbed. I use it to test bits of code all the time.
None of these methods is in any way impressive. No large brain stuff. Most of it is just string manipulation, file system operations -- mundane stuff. Handy though!
This morning, I wanted to format some SQL output as rows in an Excel table. I wrote a method to read the output and format it as tab-delimited columns, for import into Excel. I have no idea how else I could have done that. It took about 8 minutes to write.
I have 300 methods, perhaps 50 of which are often useful, the rest there if the occasion arises. Occasionally I move the real cruft into the Zaps group, so it's out of the way.
The utility has lots of ease-of-use features. I prefer the keyboard to the mouse, so methods are tagged into groups that are accessible from a dropdown: control-T selects a different group. Don't remember the group? You enter control-F to find all the methods matching a string. Arrow down and press to run the method. The parameters window always remembers its state: if you entered Hoytster last time, it's there this time. You can right-click a method to see its tooltip; double-right-click to see its source.
I tried to make it easy to create new methods quickly.
A method generates your new function's prototype: you enter the method's name, group tag, tooltip, etc, and the new method is created with the requisite attribute decorations. The prototype is placed in the clipboard so you can paste it into one of the utility's source files.
It's easy to prompt for parameters:
...GetParameters("*Target File", "#Report File", "Open Report [No, Yes]");
opens a window with textboxes labeled Target File and Report File, and an Open Report checkbox with text that toggles Yes and No. Strings in curly-braces become radiobuttons. The Target File must exist, because of the initial asterisk; the parameters window will not close if an invalid target file is entered. The Report File must be valid (it CAN be created) because of the #-sign.
When you run the method and the parameters window appears, it has a [Capture] button you click to generate the code needed to capture the returned parameters, putting it into the clipboard again:
string targetFile = parameters["Target File"];
...
boolean openReport = parameters["Open Report"] == "Yes";
Ach, I go on too long.
So, how ambitious should I be? CodePlex? Maybe a dedicated web site, where people can upload their methods?
Getting the utility publish-ready would be a lot of work. I have to clean up the code; remove the really dumb methods and the never-finished methods; create a screen cast of the "make a new method" process, document the teeny "meta-language" (tongue-in-cheek) that drives the parameters window.
I like the idea of y'all using my utility to be a bit more productive. I love the idea of seeing what methods you invent and share. No doubt it's out there, but I'm not aware of places on the net where people share code as simple as a method "Fix the names of my MP3s".
Would you like to have this utility?
Besides being overworked and lazy, I have never put up a web site (!) -- and y'all might mock me because my GetParameters() method has about 200 lines (my poor excuse: I started out with FORTRAN). This utility was never designed; it accreted. :)
So let me know: Do you think this utility is useful enough to put up on CodePlex (or somplace)?
Thanks in advance! - Hoytster
Put it out on CodePlex and gage the usefulness of it. If it is very useful to many people start moving forward by creating a community around it like the website you talked about. If it is going to be a lot of work and you don't know if it will be useful to people, start small with your effort level and keep moving it up.
I did this exact same thing with my URL Rewriter that I developed, that was based off of Apache mod_rewrite, for the .NET framework.
http://urlrewriter.codeplex.com
I started small and as people requested new feature and started using it more and more, the effort became easy to justify.

Categories