Normal Ol’ DLLs from VB.net

7
Filed under .NET, Arcade, Code Garage, MSBuild, VB Feng Shui

Every once in a while, I find a need to do something a bit off the wall. Recently, I had another one of those situations.

I’ve spent a lot of time working with some of the old arcade emulators that are floating around (the most famous of which is MAME, or Multi Arcade Machine Emulator).

Mame itself is pretty utilitarian, so there are a number of front ends  that are essentially menuing systems to provide a user with an easy to browse interface for selecting games to play and among the more popular front ends is MaLa.

image

MaLa Main screen (using one of many available skins) showing list of games, and a screenshot of the selected game

One nice aspect of MaLa is that it supports plugins, and there are a number of them out there, to control LED lights, play speech, etc.

I had had a few ideas about possible MaLa plugins for awhile, but the MaLa plugin architecture centers around creating a standard Win32 DLL with old fashioned C styled Entrypoints, and, well, I kinda like working in VB.net these days.

Gone Hunting

Eventually, curiousity got the better of me, and I started looking for ways to expose standard DLL entry points from a .net assembly. I ended up finded Sevin’s CodeProject entry called ExportDLL that allowed just that. Essentially, it works by:

  1. You add a reference in your project to a DLL he created, that only contains a single Attribute for marking the functions you want to export.
  2. Create the functions you want to export as shared functions in a MODULE
  3. You mark those functions with the Attribute
  4. You compile your DLL
  5. You then run ExportDLL against your freshly compiled DLL
  6. ExportDLL then decompiles your DLL into IL, tweaks it, and recompiles the IL code back into a DLL

It sounds complicated but it’s really not.

I set it all up and had things working in about 30 minutes.

Gone South

Unfortunately, all was not quite right. MaLa requires 2 entry points (among a host of them) defined with a single integer argument passed on the stack. Pretty simple stuff. So I coded up:

    <ExportDLL("MaLaOrientationSwitch", CallingConvention.Cdecl)> _
    Public Shared Sub EntryPoint_MaLaOrientationSwitch(ByVal Orientation As Integer)

But when I ran the DLL within MaLa, it crashed immediately after calling this function, even with NO CODE in the function itself.

What this meant is that something about the export process was trashing the stack. I spent a solid day hunting for clues as to what might be failing. I did find that eliminating the argument from the exposed entrypoint allowed MaLa to work properly AND call my entrypoint, but, being unable to get the passed Orientation value, the call was basically useless.

Gone Around Again

In digging through it all, I happened to notice a comment on the CodeProject page for Sevin’s article pointing to a similar library by Robert Giesecke. I’m not sure if the two were developed independently or not, but Robert’s is certainly a more polished set of deliverables. He even went so far as to put together a C# project template that makes it ridiculously easy to kick off a project using his technique.

It turns out, not only is Robert’s approach cleaner, it actually properly exports the MaLaOrientationSwitch function above with no problems. MaLa can call it, pass in the argument and all is good.

Gone Fishing

One big difference between the two techniques is the Robert actually defines an MSBuild targets file to patch his DLL directy into the Visual Studio build process. Very cool! But, his build step happens AFTER the PostBuildEvent target, and it was in that target that I’d setup some commands to copy the DLL into a file called *.MPLUGIN, which is what MaLa specifically looks for. Hooking that process into the build itself makes debugging things quite natural, but, Mr. Giesecke’s target wasn’t allowing for that.

Here’s Robert’s targets file:

<Project
  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="RGiesecke.DllExport.MSBuild.DllExportTask"
             AssemblyFile="RGiesecke.DllExport.MSBuild.dll"/>
  <Target Name="AfterBuild"
          DependsOnTargets="GetFrameworkPaths"
          >
      <DllExportTask Platform="$(Platform)"
                   PlatformTarget="$(PlatformTarget)"
                   CpuType="$(CpuType)"
                   EmitDebugSymbols="$(DebugSymbols)"
                   DllExportAttributeAssemblyName="$(DllExportAttributeAssemblyName)"
                   DllExportAttributeFullName="$(DllExportAttributeFullName)"
                   Timeout="$(DllExportTimeout)"
                   KeyContainer="$(KeyContainerName)$(AssemblyKeyContainerName)"
                   KeyFile="$(KeyOriginatorFile)"
                   ProjectDirectory="$(MSBuildProjectDirectory)"
                   InputFileName="$(TargetPath)"
                   FrameworkPath="$(TargetedFrameworkDir);$(TargetFrameworkDirectory)"
                   LibToolPath="$(DevEnvDir)\..\..\VC\bin"
                   LibToolDllPath="$(DevEnvDir)"
                   SdkPath="$(FrameworkSDKDir)"/>
  </Target>
</Project>

I’d worked with MSBuild scripts before, so I knew what it was capable of, I just couldn’t remember the exact syntax. A few google searches jogged my memory, and I ended up here at a great post describing exactly how you can precisely inject your own targets before or after certain other predefined targets.

I modified Robert’s targets file and came up with this:

<Project
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <UsingTask TaskName="RGiesecke.DllExport.MSBuild.DllExportTask"
             AssemblyFile="RGiesecke.DllExport.MSBuild.dll"/>

  <!-- Add to the PostBuildEventDependsOn group to force the ExportDLLPoints
       target to run BEFORE any post build steps (cause it really should) -->
  <PropertyGroup>
    <PostBuildEventDependsOn>
      $(PostBuildEventDependsOn);
      ExportDLLPoints
    </PostBuildEventDependsOn>
  </PropertyGroup>

  
  <Target Name="ExportDLLPoints"
          DependsOnTargets="GetFrameworkPaths"
          >
    <DllExportTask Platform="$(Platform)"
                   PlatformTarget="$(PlatformTarget)"
                   CpuType="$(CpuType)"
                   EmitDebugSymbols="$(DebugSymbols)"
                   DllExportAttributeAssemblyName="$(DllExportAttributeAssemblyName)"
                   DllExportAttributeFullName="$(DllExportAttributeFullName)"
                   Timeout="$(DllExportTimeout)"
                   KeyContainer="$(KeyContainerName)$(AssemblyKeyContainerName)"
                   KeyFile="$(KeyOriginatorFile)"
                   ProjectDirectory="$(MSBuildProjectDirectory)"
                   InputFileName="$(TargetPath)"
                   FrameworkPath="$(TargetedFrameworkDir);$(TargetFrameworkDirectory)"
                   LibToolPath="$(DevEnvDir)\..\..\VC\bin"
                   LibToolDllPath="$(DevEnvDir)"
                   SdkPath="$(FrameworkSDKDir)"/>
  </Target>
</Project>

Now, I can perform the compile, and execute my postbuild event to copy the DLL over to the MaLa Plugins folder and give it the requisite MPLUGIN name, all completely automatically.

And, the icing on the cake is that I can build a MaLa plugin completely in VB.net, with no C or C# forwarding wrapper layer and with a fantastic XCOPY-able single DLL application footprint (save for the .net runtime, of course<g>).

It’s a wonderful thing.

Old School Advice That Still Rings True

0
Filed under Rants

Earlier today, I was reading an excerpt from an interview with Dan Bricklin (the guy that invented the spreadsheet, for those that might not know).

He was asked about his approach to building programs back in the day (the mid 80’s, all legwarmers and The A Team).

His response was quite interesting, very “agile” when agile only meant an adjective to describe gymnasts.

One thing I’ve always done, for many years — I know Bob Frankston did, too — you have to figure out a path through the whole thing and implement that first. And then you go back to add more. Getting the path through is like building a scaffold. A lot more is put on [the application] before it’s a real product, but you have the critical path in place. It’s always building off something that’s working.

I thought that was fantastic. Yet another example of how programming seems to be a lot of doing the same thing over and over, just with each generation coming up with trendy new names for it all in the process.

Word’s Compatibility Options

224
Filed under .NET, Code Garage, Office, Word

One element you’ll eventually run up against when dealing with Word documents is "compatibility”.

image

You can see one small indicator of compatibility in the above screenshot. When you open any old format DOC file in Word 2007 or 2010, it’ll open in compatibility mode.

But what does that mean, really?

Compatibility in a Nutshell

Word’s Compatibility mode actually encompasses a fairly significant number of tweaks to how Word renders a document. You can see those options at the bottom of the Advanced tab on Word’s Options dialog.

image

a small sampling of compatibility options available

Word sets those options in a number of different combinations depending on the source of the original document. You can select the source document manually via the below dropdown, but most of the time, Word will choose an appropriate selection automatically when it opens the original  document.

image

The problem is, many of those options can cause Word to render a document in very strange, unpredictable ways. In fact, most Word experts advise to manually turn OFF all compatibility options (and so force Word to layout the document using it’s most recent rules, either those set for Word 2007 or for Word 2010).

Controlling Compatibility Programmatically

Manipulating those settings manually via the Options dialog is fine, but if you’ve got thousands of documents to deal with, that may not be your best approach.

Why not automate it with a bit of .net code?

    Private Sub ForceCompatibility(ByVal Doc As Word.Document)
        Doc.Convert()

        Doc.Application.Options.DisableFeaturesbyDefault = False
        For Each e As Word.WdCompatibility In [Enum].GetValues(GetType(Word.WdCompatibility))
            Dim res As Boolean = False
            Select Case e
                Case Word.WdCompatibility.wdDontULTrailSpace
                    res = True
                Case Word.WdCompatibility.wdDontAdjustLineHeightInTable
                    res = True
                Case Word.WdCompatibility.wdNoSpaceForUL
                    res = True
                Case Word.WdCompatibility.wdExpandShiftReturn
                    res = False
                Case Word.WdCompatibility.wdLeaveBackslashAlone
                    res = True
                Case Word.WdCompatibility.wdDontBalanceSingleByteDoubleByteWidth
                    res = True
            End Select
            Doc.Compatibility(e) = res
        Next
    End Sub

So, what’s going on here?

First, I pass in a Document variable to the function. This variable contains a reference to the Document object you need to fix compatibility on. You can easily obtain teh Active document object with the Application.ActiveDocument property.

The function first calls the Doc.Convert method. This converts the document to the latest format available to the version of Word you’re running and enables all new features. This conversion happens in memory. Nothing is saved to disk at this point. Also, you’d think that this would be enough to turn off all compatibility tweaks, but alas, Word leaves many of them still turned of after this conversion.

Next, it uses the DisableFeaturesbyDefault property to enable all available features.

Then, it enumerates all the wdCompability options to clear or set them as necessary. This handy bit of trickery can come in handy in a number of situations. Essentially, it uses a little reflection here:

[Enum].GetValues(GetType(Word.WdCompatibility))

to retrieve an array of all the possible values of the wdCompatibility enumeration.

Then, the For loop simply iterates through all the values in that array.

The SELECT CASE then matches up a few specific enumeration values that don’t work quite like you might expect, and handles them with special cases.

For instance, the wdNoSpaceForUL option is equivalent to the “Add Space for Underlines” option in the Word Options Dialog. However, the logic is reversed.

Leave it to a word processing program to make use of double negatives like this!

Odd Man Out

The wdExpandShiftReturn option is the one exception. When cleared, this option prevents Word from expanding spaces on lines that end with a soft Return (a Shift-Return). Many Word experts indicate that this is preferable to Word’s default behavior of expanding those spaces, so I’ve set it here accordingly. Your mileage may vary.

And finally, be sure the SAVE the document in xml format, using the Save method like this:

Doc.SaveAs FileName:="doc.xml", FileFormat:=wdFormatXML

So there you have it. A quick and easy way to force a document into the latest Word file format and turn off any errant compatibility options in the process.

WindowsXP periodically disconnects from Windows7

5
Filed under Networking, Troubleshooting, Windows 7

Very strange, and utterly frustrating issue recently.

After I got hit by lightning, I ended up replacing an Iomega NAS storage server with a simple Win7 box running 2 raided 2TB harddrives. It was a quick build and was a little cheaper than an equivalent 4TB Iomega unit, plus it’s running real Windows, so I can do more stuff with it.

But, I’ve got several XP Virtual Machines (under VMWare Workstation), that would, very consistently, disconnect from the Win7 “server”.

I googled for days, and tried at least a dozen different suggestions but none worked.

Essentially, if I rebooted the Win7 machine, the XP machines could connect to and work with the server for a while. But eventually, the WinXP machines would loose connection to the Win7 machine and the ONLY way to get them connected again was to reboot the Win7 machine.

The main problem was that the failure was very generic: something along the lines of “The server does not have enough resources to complete the request”.

Not much to go on.

On a whim, I happened to check in the event log at one point and came across this:

Source: srv
Event ID: 2017
Level: Error
The server was unable to allocate from the system nonpaged pool because the server reached the configured limit for nonpaged pool allocations.

Googling “srv error 2017” turned up a number of new suggestions I hadn’t seen before, but one in particular on Alan LaMielle’s blog looked very promising, though he was talking about connected via SAMBA.

The Bottom Line

In a nutshell, the problem is that if you use a “normal” version of Windows more like a server (lots a machines connecting to it for files), normal Windows installs aren’t configured for that.

You need to change 2 registry entries (on the SERVER machine, in my case the Win7 machine):

HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\LargeSystemCache

should be set to ‘1’, and:

HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\Size

should be set to ‘3’.

Then, either reboot, or use the Services control panel to restart the ‘SERVER’ service.

Since I’ve done that, I’ve connected several XP systems up, and run full backups overnight without a single hiccup.

ShiftThisSwift SMTP Configuration for WordPress

0
Filed under Blogging, WordPress

I’m in the throes of conversion from dasBlog (a great ASP.net based blogging engine, btw) to WordPress, as you can no doubt tell from the sometimes random gobbledygook littering these pages!

One bit that was particular annoying was that when comments come in, the administrator is supposed to be emailed a notification of such. But this wasn’t working.

I hit all the pages on Google pertaining to the problem (and it has been a problem for some time, apparently, centered around hosting WordPress on various webhosts), but still no luck.

Finally, I came across a mention of the ShiftThis Swift SMTP Plugin for WordPress. Looked like exactly what I was needing.

A quick download (and upload) later, and I was looking at the configuration screen.

Unfortunately, my first set of config didn’t work. Actually, it through a big, nasty exception all over the screen.

So did rounds 2 and 3. Round 4 got a little closer, the exception indicated that “authorization had failed for the username and password”.

Better.

Eventually, I worked out this set of config values (I’ve annoted the config screen below), and they DO seem to work just fine with my webhost (hosting under Windows).

image

Of course, your mileage may vary.

The key points for me, anyway, were:

  • The Server Address. It needs to be the name of the SMTP server OF THE WEB HOSTING MACHINE your website is hosted on. For instance, if WordPress is configured for the site MyDomain.com, this will likely be smtp.mydomain.com.
  • The Username. For me, it needed to be the FULLNAME (with the @domain.com part) of a mailbox configured ON THE WEB HOSTING MACHINE your website is hosted on. Following from the above example, I might use mail@mydomain.com
  • The Password. Even though my mailbox has a password, I had to leave this blank, or authentication would fail every time.

And there you have it!

Code Garage – Escaping and UnEscaping XML Strings

0
Filed under Code Garage, XML

image If you work much with XML, eventually, you’ll end up needing to take raw string data and either Escape it (replace characters that are illegal in XML in the string with legal XML markup) or UnEscape it (replace any XML markup in the string with the represented characters).

For instance, a “>” (greater than) symbol is illegal in the content of an XML node, and must be represented by &gt;

There are any number of approaches out there to handle this. Most involve simple brute force string search and replaces. They work, but I thought there must be a more elegant (and already thought out) solution to this problem.

However, after quite a bit of searching, I gave up and wrote my own, making use of the XML functions already defined in the .net framework.

To ENCODE a string into legal XML node content…

    Public Function EncodeXML(ByVal s As String) As String
        If Len(s) = 0 Then Return ""
        Dim encodedString = New StringBuilder()
        Dim writersettings = New System.Xml.XmlWriterSettings
        writersettings.ConformanceLevel = Xml.ConformanceLevel.Fragment
        Using writer = System.Xml.XmlWriter.Create(encodedString, writersettings)
            writer.WriteString(s)
        End Using
        Return encodedString.ToString
    End Function

To reverse the process and Decode a string…

    Public Function DecodeXML(ByVal s As String) As String
        If Len(s) = 0 Then Return ""
        Dim decodedString As String = ""
        Dim readersettings = New System.Xml.XmlReaderSettings
        readersettings.ConformanceLevel = Xml.ConformanceLevel.Fragment
        Dim ms = New System.IO.StringReader(s)
        Using reader = System.Xml.XmlReader.Create(ms, readersettings)
            reader.MoveToContent()
            decodedString = reader.ReadString
        End Using
        Return decodedString
    End Function

No, I haven’t performed any exhaustive performance measures on these. I’ve generally not used them in any kind of ultra high volume situations, but they’re clean, simple and leverage existing code that already performs the necessary functions.

Software Design Pattern Quick Reference

1
Filed under Software Architecture, VB Feng Shui

I’m a somewhat reluctant believer in software design patterns. On the one hand, it can be handy to have a lingua franco to discuss common patterns with. On the other, the concept has seemed to me to be so wildly hyped over the past few years, that you’d think it was the brand new answer to everything.

Truth is, I’m guessing most every programmer who’s been working for any length of time recognizes most of the common patterns, and has applied them innumerable times over the years.

Still, I’ve never spent much time truly digging into the meat and potatoes of each pattern.

Then I came across Jason McDonald’s Design Pattern Quick Reference PDF here.

image

To the right is a reduced view of one of the pages.

Really well done reference sheets for those that want to put the names with the patterns they likely already know quite well.

Highly recommended!

Word and the Disappearing Cell

0
Filed under Office, Troubleshooting, VB Feng Shui

image

So I was off working on a new bit of code today when an email came through:

“The tool is blanking out a cell when it’s not supposed to!”

Ack.

No big deal though. I fire up the project, load up the document in question and give it a run.

Sure enough, a cell that contains some text and a merge field (under my control) is being completely cleared out. Only the merge field should be being cleared.

I dig a little more and eventually narrow the culprit down to a single function.

Private Function MyFunc(Rng as Range)
   ....
   Rng.Delete
   ....
End Function

When I hit that Rng.Delete line, the ENTIRE CELL was being cleared. Including text that had nothing to do with my merge field (other than the fact that it was also in the same table cell).

After a bit more single stepping through code, I discovered that the Range being deleted was actually empty. In other words, it’s Start position was the same as it’s End position.

Now, I’ve know from dealing with Word for far too many years that when you’re working with an empty range like that, there are a good number of things that don’t work quite right. Many will throw errors, but I was getting no error here.

Then it hit me. As a test, I added a few chars of text at the VERY END of the cell, after my merge field.

Sure enough, everything worked, even though the range being deleted was still empty.

The End-of-Cell Marker.

Word stores an End-of-Cell marker character at the end of each cell in a table. The marker is a Chr(7) if you retrieve it, but you wouldn’t normally ever even bother with it.

However, if you end up with an empty Range object that is situated right at the end of a cell, then that Range is effectively pointing at the End of Cell marker, and if you invoke the Delete method on the Range, you’ll end up invoking it on the End of Cell Marker.

And what happens if you try and delete an End of Cell Marker?

Well, duh. The cell is cleared.

Moral of the Story

The short version of all this is simple:

If you plan on deleting a Range, make sure that the Range.Start <> Range.End before you do:

If Range.Start <> Range.End Then
   Range.Delete
End If

Technically speaking, if start = end, then there’s nothing in the range anyway and so there shouldn’t be anything there to delete. But doing so can definitely have detrimental effects in some cases.

And, as usual, your mileage may vary.

Phillips Comes Through

0
Filed under Rants

image It’s been almost two months since we got hit by lightning that took out almost all the electronics in the house. A very looonnnngggg two months. Far more time that I would ever want to spend hassling with insurance adjusters, repairmen, UPS vendors, etc.

Still, it could have been worse.

Fortunately, a little good news came in the mail today.

I’d built a very custom computer to power a gaming rig I’m building. It was connected to a Philips Surge suppressor with a “master” outlet, basically, a surge suppressor power strip with one plug that is the “master” plug. When whatever is plugged into that plug is turned on, all the other plugs come on. When turned off, all the other plugs completely power down. They’re a great way to cut down on vampire devices that suck energy even when “off”.

Well, the computer was fried by the lightning. As was the Altec Lansing speaker/subwoofer system connected to it.

I’d called Philips to try and file a claim against their “25,000$ connected equipment guarantee”. Obviously, my losses weren’t that much but every little bit would help.

They sent out a UPS mailing label to return the surge suppressor (it still worked but they wanted engineering to investigate it).

So I boxed it up and shipped it, along with the rundown the the fried equipment.

It took about 2 weeks, but today I get a package in the mail. They sent me a new surge suppressor (not sure how much the old one helped, as it was fried and my PC was fried, but that’s another story) and a check to cover the damages.

I’d had my doubts whether anything would come of that guarantee, but they did indeed come through, so hats off to Philips!

HP G85 and JetDirect for the Win!

1
Filed under Hardware, Rants

image I’ve always been a bit of an HP fan when it comes to their printers. Ages ago, my dad bought a LaserJet that worked for what seemed like forever.

So, back when I first started telecommuting, when I needed a printer, I naturally thought of HP and ended up with a G85 Inkjet/Copier/Scanner.

I also picked up a JetDirect box to let me hook it up directly to an ethernet network. This was all circa 2000. Yep, seriously olden days.

But, that printer is still going strong. Absolutely no problems with it. I’ve dropped it. Moved twice. And most recently, the house got struck by lightning. I lost computers, a SAN drive, network switches, hubs, a TIVO, even UPS’s and my AC units.

But that printer and the Jet Direct box survived unscathed.

So what?

Here’s the thing. The G85 is a great scanner, BUT, the software is hopelessly outdated and HP hasn’t updated it since the XP days. So running it under Vista or Win7 has been a pain.

After the lightning, I was re-setting up new machines, when I happened to try browsing over to the printer’s IP address, basically on a whim.

Guess what I found?

image

That JetDirect box has a webserver built into this WHOLE TIME!

Not just that, but check that scan button…

Click it, and it leads you to this page…

image

Yep, that’s right, you can initiate a scan right here, from the browser, then download the resulting image. No drivers to load, no compatibility issues. Nothing but good ol’ fashioned sweetness.

I’ve had this thing literally ten years and never knew that was there. Certainly never came across it in the docs or any online info.

Turns out you can teach an old dog new tricks (or get him to do tricks you’ve never seen before <g>)