Friday, December 19, 2008

Sharp (#) Problem :)

Hiya all,

my holidays just started... weeeeeeeeeeeeeeeeeeeeeee... and ran head on into a problem with the stupid *.chm files I got myself from work (yeah yeah yeah... holidays and such, I know).

Buuut... none of these things would open, displaying a nasty 'Page cannot be displayed' on the right hand side of the window.
Now there are quite a few solutions to this, of which none worked.. always got that dreaded IE comment about my *.chm... rats.

Though, on a lonely site in the MS databases if found an interesting, yet vital fact... listen up people, there's going to be a test :)

IF the path to the CHM contains a '#' it won't display.

Now imagine my path: 'C:\Data\AD-Migration\C#\Tools\GroupBouncer\Help'... great

Exchanged every occurence of '#' with 'Sharp' (using a quick-hack-prog in CSharp :) ) and BEHOLD THE EMPEROR'S NEW... *looks at scroll again*... errrrr... OLD CHM's!

I hope I at least save somebody's time with this little note... drop me a line... or a beer... well.... thinking that over... AND a beer :)

Regards,
Dirk

Starting to fiddle with LINQ... I think...

Thursday, November 13, 2008

License and Registration, please

As promised the next post is here... this time about checking Groupmembership in a logonscript using VBS.

First of all I have to say this is not the most flexible way of doing it, especially if you have a few different domains to check for, but it should give you a lead on 'how-to'.

  1. Option Explicit

  2. On Error Resume Next

  3. Dim oWS : Set oWS = WScript.CreateObject("WScript.Shell")
  4. Dim oFS : Set oFS = CreateObject("Scripting.FileSystemObject")
  5. Dim oWN : Set oWN = CreateObject("Wscript.Network")

  6. Const ADS_SCOPE_SUBTREE = 2
  7. Set objConnection = CreateObject("ADODB.Connection")
  8. Set objCommand = CreateObject("ADODB.Command")
  9. objConnection.Provider = "ADsDSOObject"
  10. objConnection.Open "Active Directory Provider"

  11. Set objCommand.ActiveConnection = objConnection
  12. objCommand.Properties("Page Size") = 1000
  13. objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE

  14. objCommand.CommandText = "Select ADsPath, Name From 'LDAP://dc=my,dc=domain,dc=com' Where objectCategory = 'Group' AND Name='AD-Migration'"
  15. Set objRecordSet = objCommand.Execute
  16. objRecordSet.MoveFirst
  17. Set objGroup = GetObject(objRecordSet.Fields("ADsPath").Value)

  18. Dim UserInGroup
  19. UserInGroup=False

  20. For Each strUser in objGroup.Member
  21. set objUser=GetObject("LDAP://"+struser)

  22. if (LCase(objUser.samaccountname)=LCase(oWN.UserName)) then
  23. UserInGroup=True
  24. end if
  25. Next

  26. if(UserInGroup=True)then
  27. oWS.Run("wscript.exe .\AD_UserProfileMigration\migrate_station.vbs")
  28. else
  29. Wscript.Quit(0)
  30. end if
Actually pretty straigtforward. The only thing that cost me quite some time is the fact that the contents of the 'UserObject' are referenced by a '.' and the 'RecordSet' uses 'Field()'. That threw me off track and was one of the main points of cursing :)

I'll just assume you know your programming bits and go on from top to bottom:
Create default objects for Filesystem, Scriptshell and Network. Though I do not need all of them in this one, the main logonscript has all of those in use. So I just included them here too.

Next thing we do is setting up a query for LDAP to our domain where we look for the specific group 'AD-Migration'. All members of that group have to be migrated to the new Domain.
The beauty of this is that you can stuff a new member in there on the On-Site-DC and the change is immideatly done.. no relog.. no gpupdate... just restart the script.

Next step is to interate through all members and see if their current logonname matches the logonname in the group. Next Gotcha... MS calls this 'SamAccountName' in the AD-Schema. And... great praise to whoever dreamed that up... the description in the schema is... *drumroll* 'SamAccountName' DUH...

So... we set a boolean to 'true' if we found the name and start the script(s) accordingly... no magic there...

I hope this did help you a bit on your way... I have some recommended reading though:
  • The windows script Help (v5.6 at the moment). Won't link it here, because it is likely to change in URL. Just search on MS.com.
  • Windows Server Support Tools. These bring the 'Active Directory Schema' Add-In for the Managementconsole
With these two things and a bit of fiddling I am sure you'll handle some of the more advanced thingies that will cross your way. I am still on my quest for less fuss for the user and will surely try to blog my cur... thoughts here :)

Questions welcome :)

See you soon,
Dirk

P.S.:
Yes, actual workingcode directly from our logonscript. I works, honestly!

Thursday, November 6, 2008

Long time no C[++,#]

Busy, busy, busy... and still am :)

Recently we were merging our domain(s) to one, big domain... which is not easy when the impact for the users should be zero (best case that is).

So, what did I learn?
First and foremost VBS *shiver* for the logonscripts and other neato thingies that needed to be done while the user cannot do much about them... which is most important, as these pesky people tend to stop whatever is running just to get to their 'Lotus Notes'.

Second... I did not find a way to make a simple, standalone program to set various (temporary) rights for the users in C#. And I *tried*... HARD... which brought me back to my trusty (and rusty) C++ Builder (I *hate* that registration process thing, Borland! And I *hate* the new help, get the old one back... much niiiicer)

Third... don't trust MS with their docs either... I found a few very *wrong* things they put into the Active Directory Schema docs... well... the Domain will recover :)

What is to expect next from me?
Weeeeeeell... a few posts with tips on what to do and what THE HECK NOT to do in Domainmigration, some neato things I got hopping in VBS (in your *face* Central Team! I *did* get it working, noobs), a neato thing discovered in C# (Yes, Kurt, still fiddling with it and not abandoned) and general riffraff I need to get out :)

There were some questions about me abandoning the blog... yeah... riiiight.

So... I try to post more regularly and keep you updated :)

Thursday, August 28, 2008

RSS/ATOM feed extensions made easy

The easiest way to add custom elements to a syndicated (ATOM/RSS feed) is to use an XElement, like this:

SyndicationFeed feed = new SyndicationFeed("FeedName", "Feed description");

//fill feed with items (omitted)

XNamespace ns = "http://tempuri.org/myapp";

feed.ElementExtensions.Add(new XElement(ns + "myCustomProperty", "hello"));

return new Atom10FeedFormatter(feed) // to return as an atom feed


Happy feeding!

Wednesday, August 6, 2008

70-561 bookable

You can now book for 70-561. ADO.NET 3.5. Wich is what i'll do this week.

Sunday, August 3, 2008

Entity Framework will hit with VS2008 SP1

Finally its coming! With Visual Studio SP1, the final version of entity framework will be included.
I'm expecting the .NET 3.5 certification exam to go live shortly after, finally!

Wednesday, July 30, 2008

70-504 passed

Lets hope MS get that ADO .NET 3.5 done soon otherwise i'll have to do WPF!

Friday, July 18, 2008

Grab that icon with .NET

Many people just dont know there's a very easy way to get any program icon you need.
Here's the code
Icon icon = Icon.ExtractAssociatedIcon(@"C:\Program Files\Internet Explorer\iexplore.exe");
using (FileStream fs = new FileStream(@"c:\explorer.ico", FileMode.Create))
{
icon.Save(fs);
}

Sunday, July 13, 2008

Embedded resources and clean folder structure - yes you can.

If you have a bunch of embedded resources in a folder, the single most efficient way to get a stream to them is to create a dummy class in that folder. Aside from it being internal or public, it doesn't have to do a thing.
You then use the type of this object in calls to get this resource, eg Assembly.GetManifestResourceStream. With this type, the namespace is looked up for the folder, and the embedded items in it are effortlessly resolved.
eg:
Assembly.GetExecutingAssembly().GetManifestResourceStream(typeof(ImageResourceResolver), "myicon.ico");

Saturday, July 12, 2008

//You do not need to understand this

Maintenance programming... hated by most, yet essential.

Good things to do for the poor sod who has to maintain your code after you left:
  • Comment clearly and not too much
  • Put in a clear roundup what the function is supposed to do
  • ... add some explanation for your parameters
  • Keep speaking variablenames, but go easy on the length :)
  • Be consistent in these names.. p preceeds a pointer, s a string, i an integer and so on
  • Avoid pressing complicated statements into one line
Bad things to do:
  • Ignore all of the above
  • i is good, use often like nested for{} statements with i for the outer loop and ii for the next :)
  • Take extremely good care of your pointers and never set them to NULL when you free()
  • Place comments like //Taking care of X... when you do nothing the like

heheeh.... maintenance programming has its ups and downs. But you can make it hell if you like. If you happen to hop into a job where you get thrown at code that looks like the latter... I do not envy you :)

Saturday, July 5, 2008

Electronic flyswatter: The Art of Debugging

Among the less popular things about programming in general is debugging. It is a hassle to most people I know, yet an essential skill to master.

The first step to find a bug is to think while coding. Hacking away without reason and just grinding code without exercising your brainmuscles is futile. Believe me when I say that a well thought through codeflow is saving you quite some headaches in debugging.

Before you start coding make yourself a clear picture of what you want, how it should look and how it should flow. Expect that to be hard :)
Do whatever helps you, don't rely on what others tell you too much, as there is no perfect way. Use what works for you.. diagrams, UML or Post-It notes. It saves you time and gives you an overview of the program-to-be.

Step two is translating that into code. Here I mostly choose to have my own collection of ironcode. WTF? 'Ironcode' is code I use all the time, well tested and throught through (...ehhehe... in most cases well though through). It minimizes the possibility of errors... most of the time :)
KISS. Keep It Simple Stupid. Don't optimize too much. It is easier to get a working program to run fast than a fast program to run.

Avoid one-liners in the first iteration. I love oneliners like:
string ASCII = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(srLogFile.ReadLine()));
While it surely is not the most complicated thingy to do, it is surely stupid to do that untested. Try, for debugging, to split that into three or more lines. It is easier to debug (yes, and to read, I hear you, but maintenance programming is another topic).
When your code is ready to be shooed out of the sandbox, you can always tighten up things.

Step three is 'Know your tools'. Know your tools. Know how the debugger works, when to hit which key and watch your locals. Locals is your friend, the callstack tells you quite a lot about what happened before you crash.. or did not crash.. also a possibility in a multithreaded environment.
Speaking of which.. create a decent logger that is alive in your debug build. In most of the cases you cannot just 'stop, drop and roll' your program, e.g. Games. There a good logger is making your live a lot easier.

Step four is 'know the 'usual suspects''. I, when programming, have a list in my mind of those things that can go wrong. I build and test every function I create for obvious errors then stuff in a few lines of loggingcode to be prepared.

And the last, most important, step: 'Don't give up'!
See it this way: At least you have the sourcecode, you know the one who committed the cirme and you know all the things that sourround your program. The worst thing that can happen to you is reverseengineering buggy code with not the slightest idea what that program needs... believe me.. been there, done that, got the T-Shirt.

All in all this is not the yellow brick road to follow in debugging. Never thought of it that way, but it should take away the highest hurdles. Of course it is easy to say such things while having quite a bit of experience in that field. As always. But then... I also started sometime... heheeh... most people forget that...

Thursday, July 3, 2008

Division by Cucumber?

Today I let loose my latest thingy, which reads some logs and parses them into a FoxPro table. See my post on it here.

It was working fine while in the sandbox but screamed hell when I set it loose in the wild. It threw a 'DivideByZeroException' all the time. After a while of debugging and crawling through my code I ended up here:
  1. System.Data.OleDb.OleDbCommand Com = new System.Data.OleDb.OleDbCommand(Instrucions[i]);
  2. Com.Connection = conn;
  3. Com.ExecuteNonQuery();
Line 3 threw the exception... but why? The instruction looked like 'insert into tablename (Col1, Col2, Col3) values ('One','Two','Three other things added here')'.
Thing was that the first round with another table went fine and that SQLstatement was a lot more complicated! So... what gives?

Took me about 2 hours of fiddling, cursing and sniffing through my code I found out the cause:
'Col3' is a FoxPro Memofield... sheesh.. which creates the need for the corresponding 'tablename.FPT' in the directory where the free tables reside. After that it worked like a charm.

So... all the crap in the web about some complicated parameteradding and such is safely discarded. The only thing is that you should chop your strings that should go into the memofield into 255 charater pieces as FoxPro works with stringlaterals that hold 255 chars max :)

Hope I could push you a step further to enlightenment :)

Wednesday, July 2, 2008

Beauty is only GUIdeep

Softwareguy: 'So did you decide on the purchase of our NiftySoftware v2.01?'
Customer2b: 'Yes. And no, we won't buy. We'll buy Competitor v1.1.'
Softwareguy: 'Oh... well.. ok. May I ask what influenced your decision?'
Customer2b: 'Sure, because it looks a lot better and it feels better.'
Softwareguy: 'But we are faster, more refined and our databasehandling is top-of-the-line'
Customer2b: 'Well, but it looks ugly and I do not find anything I need.'

Now what is that all about? And what should we learn from this conversation?

Well... as much as your code may roxxor, your GUI is the face that sells the Application. A lot of books have been written about GUI designe, do's and don't's and quite a few by Pschologists who explain how it reflects in various ways on the workflow of ones brain.

So how to design the perfect GUI? Easy: You don't!
There is nothing like a 'perfect' [insert whatever here]. The thing I learned over all of these years is that you should, in the best possible case, be proficient in the trade. And I do not mean GUI design or programming. I am aiming at the profession you write the programm for. Let me elaborate on that:
You can write a CRS software, an accounting software or a Qualityassurance software without special training. You just get book (well... not 'just', but you get the hang of it) and read up on the procedures and algorythms involved and hack away. There is always a logic pattern you can beat out of things and translate to code.
But (<- the catch) you do not know what the user looks for. In everyday usage of a program some things need to be visible to the trained eye. And some things need to be accessible even for the... uh... how to say... not-so-techy-people-who-barely-know-how-to-logon.
Everybody has their own workpace and -path. But somethings are universal to the trade. Get to know those. Have a feel for the work, the job and the people.

Hm... not sure if I made my point.... to put it short: The customer does not give the slightest bit of a poo if your code beats 100,000 SQL instructions into a database in 10 seconds. He also is not too impressed if your code is 100 lines less than your competitors.
He sees: 'Clean design, nice grouping of the [important things]'

Realworld example:
We designed a program to manage the workload in a carrepairshop. By looking how they work we designed the GUI to display the free workers on almost 70% of the screen. The customer loved that 'I can see who has the most time on their hands!'. After he doubleclicked a worker, assigned a task and got the option to priorize the work the thingy was sold.


For the finale:
I am not sure this helps you. We work that way and we really get a bang out of it. And... well... for all it is worth we have a 99% customersatisfaction rating for the 5th year in a row (that is how long I am with this company now). And I am proud about that :)

XmlUrlResolver: a free stream to almost anything

Well, I'm not sure if its a good practise, or if there are any better classes to achieve this goal.
But I often use an XmlResolver (wich is the abstract type, or the concrete version is the XmlUrlResolver) to get a stream to any file or uri alike without the fuss of handling http requests etc.

Just use the XmlResolver function like this:
XmlUrlResolver resolver = new XmlUrlResolver();
using (Stream stream = resolver.GetEntity(new Uri(filename), string.Empty, typeof(Stream)))
{
//do stuff with the file
}

This saves gives you a lot of extra flexibility in terms of where you can put your files. Eg some files can now come over FTP or HTTP.

On a lighter note, I scheduled my Workflow Foundation MCTS exam. Wish me luck.

Tuesday, July 1, 2008

Declarative workflows: Could not deserialize object. The type '' could not be resolved.

This is mainly a reminder for myself.
When you get this error
a) check that you've added a reference to the needed assembly using a type provider for your workflow
b) check the xml itself of your workflow: the namespace of the item that couldnt be deserialized is possibly incomplete. It needs to be a fully qualified namespace. And this is where the editor screws up a little if it's not in the GAC.

For example, the editor has a tendency to create only:
xmlns:ns0="clr-namespace:Company.Lib.Activities
but it should create
xmlns:ns0="clr-namespace:Company.Lib.Activities;Assembly=Company.Lib.Activities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

(it's missing the assembly qualifier in the namespace declaration, wich you need to resolve the activity). Not specifying version, culture and publickeytoken works just the same.

With these two tips you can generally resolve the 'Could not deserialize object'-errors.

Dinner's ready, I already filled the FoxPro table!

Fighting my way through C# and its various... uh... specialities I ran head on into the problem of filling a FoxPro table. Being the smart guy I ventured to the Internet and looked for a solution.

Problem:
A directory of free tables to which I want to connect via C#.

Internet solution:
  1. Download the Visual FoxPro 9.0 OLEDB driver
  2. Install (duh :) )
  3. 'using System.Data;'
  4. Jump around to Connectionstrings.com and get the correct connectionstring
  5. Start filling that table!
Well so far so good, this is what I got (edited so it fits this layout):

  1. string vfpConnectionString="Provider=vfpoledb.1;Data Source=C:\\temp\\fptest\\;Collating Sequence=general;";

  2. conn = new System.Data.OleDb.OleDbConnection(vfpConnectionString);
  3. conn.Open();
  4. System.Data.OleDb.OleDbCommand Com = new System.Data.OleDb.OleDbCommand("[SQL goes here]);

  5. Com.Connection = conn
  6. Com.ExecuteNonQuery();
  7. conn.Close();
Now then... away with testing and... behold... nothing. WTF? The driver is not registered? Okies... go intall again... poo... no luck... what gives? After another round of doing the same... the same.. hehehe... not registered [instert heavy cursing on MS, SQL, FoxPro and Windows].
'Oh crap on FoxPro... I go to bed'

[Birds chirping] Next day at work:
Installed the same sequence again... WOW! Works! What gives?

Now then... education starting, sit down, be quiet and read up:

If you have, like me, a Vista64 Ultimate at home and a Vista32 Business at work... you get the drift? Yes! No 64bit with the FoxPro drivers! [claps] Very good! Get a Snickersbar and be proud.

Solution? Easy:
  1. Create your FoxPro-Accessing-Application-to-be
  2. Go to 'Build' -> 'Configuration Manager...'
  3. Find your Project (should be easy if you only have one in there)
  4. Click on the 'Platform' DropDown and choose ''
  5. 'New Platform' and choose 'x86' and '>'
  6. 'Ok' everything, make sure 'x86' is set as target CPU
  7. Build and frolic
Now you can use the FoxProtables as if they were 'normal' tables in a database. Meaning:
'Select * from ste_q3 where current=3.35'

Oh and by the way I recommend DBFManager which is quite a powerful (and inexpensive) way to sniff around in those tables without having FoxPro installed.

I hope I saved at least a bit of someone's troubles with this :)

Oh.. and I just read up on static variables in functions and C#... I go on with that in the next post, because it is a separate thing to rant about :)

Monday, June 30, 2008

Don't hide my error! A note on business apps and exception handling

Suppose you get some dlls to use by your foreign fellow developer team. But there you go, who would have guessed. The thing throws an error. In the real world you'd look at the stack trace, the exception type, the inner exception and the error message for starters.
I have had architects tell me "but we cannot give you this information, we use the same classes for our client", and more annoyingly "this is what business applications do: hide the error from the user".
First off, I agree that no user should receive a stack trace or too cryptic error message. A user would be better served with a tip on how to possibly resolve the issue (maybe the file is locked, so he could be advised to close all other copies of the file before saving over it for example).
But, and here it javascript:void(0)
Bericht publicerencomes: I'm not a user. I'm a developer. The project is in development. I am entitled to either a clearly written error that helps me resolve any programming errors I make, or at least to a full stack trace so i can at least attempt to figure out where it went wrong. I am entitled to an inner exception and an exception type.

So, on to a real world example, wich is also the worst case scenario, sadly:

01. Peanuts p = new Peanuts();
02. try
03. {
04. ErrorCausingClass ecc = new ErrorCausingClass();
05. ecc.CausePossibleErrorByReadingTheDatabase();
06. ecc.CausePossibleErrorBySendingEmailWithBusinessLayer();
07. ecc.CausePossibleErrorByConnectingToCommunicationFoundationService
08. } catch (Exception e)
09. {
10. p.Eat();
11. throw new Exception(e.Message);
12. }

in this scenario, when an exception occurs on line 7, the error is caught on line 8, and a new error is generated (see the 'new'? that means 'new error'). So what does 'new error' mean?
a) its a generic (untyped) error. all you know its an error, but not what type of error, for example a FilenotFound when reading the configs, but you wouldn't know.
b) the stacktrace is gone. the actual error did not occur on line 7, but somewhere deeper in the class used on that line.
c) the inner exception is gone. Have you ever encountered an error message "check inner exception for details". Well, it's "null" now. Oops, no details.
d) the location where the error occurred is gone too. The line registered for the error returned by this piece of code is line 11, where the new error is generated.
e) the error message is returned and eventually bubbled up to the user.

To recap: useless for developers, users get to see needless error message as it cannot be used for debugging.

The solution is plain and simple:
01. Peanuts p = new Peanuts();
02. try
03. {
04. ErrorCausingClass ecc = new ErrorCausingClass();
05. ecc.CausePossibleErrorByReadingTheDatabase();
06. ecc.CausePossibleErrorBySendingEmailWithBusinessLayer();
07. ecc.CausePossibleErrorByConnectingToCommunicationFoundationService
08. } catch (Exception e)
09. {
10. p.Eat();
11. throw e;
12. }

In this example, no error information is lost to the developer, and no-one is counting on it that the error is ready-for-use by the end user. A developer can provide a logical, sense-making error message for a few different types of error as the error type is not lost. Debugging is a lot easier with the stacktrace and inner exception still present.

What's even better then in some cases? Take a look at this (assume this is in a function called 'DoStuff')

01. Peanuts p = new Peanuts();
02. try
03. {
04. ErrorCausingClass ecc = new ErrorCausingClass();
05. ecc.CausePossibleErrorByReadingTheDatabase();
06. ecc.CausePossibleErrorBySendingEmailWithBusinessLayer();
07. ecc.CausePossibleErrorByConnectingToCommunicationFoundationService
08. } catch(TimeoutException te)
09. {
10. p.Eat();
11. throw new DoStuffFailedException("DoStuff failed due to a timeout. Check the inner exception for details", te);
12. }
13. } catch (Exception e)
14. {
15. p.Eat();
16. throw new DoStuffFailedException("Do stuff failed. Check the inner exception for details.", e);
17. }

In some cases it's preferrable to wrap a new custom exception around the original exception. This is mostly the case when a larger, logical part has failed, or when you will be handling other errors in identical ways. For example, if you custom implement a login system, a good practise would be to not return a 'FileNotFound' or a Timeout' that creates the inability to login, but you would focus on the inability to login, so throw a LoginFailedException. Developers use the inner exception to log and debug the problem. As close as possible to the user interface, the actual messagebox can contain a custom message tailored to the user. Wich could also be based on the inner exception, that is up to the developer of the UI.

A good practise when using smart clients or WCF is that the web service does not return detailed error information to the client. However, generally this can be enabled or disabled easily through config. Disabling detailed error information while development is taking place is asking for people saying your service doesn't work and they can't fix it.

Conclusion:
Developers should never be deprived of the information needed to solve the actual issue. If an application is written to hide all the exception information, developers using the dll will end up using something else. If you re too lazy/dont have enough time to write useful messages for the developers to use, keep the original error at the very least. Don't pretend they don't need it and don't hide behind the fact that a business application usually doesn't show the message to the user. Nowhere does that imply that an error has to be masked by an new one and all useful information thrown away.

nOOb

Being actually a newbie with the Visual Studio IDE I was wondering how to change my default application icon to a more favourable one... say.. nicer one. 'On with it!' I thought and sailed through the various searchengines and wondered about the great answers to the simple question:
'How can I change the shortcut icon for my Application in VS2005?'

My favourite:
Create a shortcut on Desktop and choose 'Change Icon' then choose the program you want the icon from.
The most harebrained:
Replacing the standard App.ico in the VS.
And the most obvious:
Go look in the Helpfile

And the easy one (really obtained through deduction from my Borland projects):
Go to the 'Project'-menu, choose '[Projectname] Properties' and behold the 'Application' Tab with the magic 'Resources' at the bottom, containing 'Icon'. Change that and you have a neato (or not so neato) shortcuticon :)

Prepare for more discoveries in the world of VS2005 and C# (I was looking for some decent examples for delegates... and oh boy, there are so many, yet so absolutely not-helping) :)

Bye