Category Archives: General Coding

Old BBS list from Birmingham, AL (circa 1994)

Here’s an old list of BBS bulletin boards I found on an old hard drive I was filing away. These are from the Birmingham, Alabama area around mid-1994.  I probably used many of these at the time, although I can only recall a few of them.  Man, those were the good old days!

See how many you can remember:

 

Debugging Angular2 apps in Visual Studio Code

I couldn’t debug TypeScript from my Angular 2 app files at first…

but then I got it working. Here’s how:

(Note: The environment used here was Windows 10 and a beginner Angular 2 app cloned from the Angular Quick Start repo from the Angular Github repo. If you’re on a Mac or Linux system, I can’t say whether the solution presented he will work for those environments.)

This post explains my trouble (and solution) of using the the Microsoft VS Code Debugger for Chrome extension to debug my first beginner Angular 2 app from within VS Code.  I let VS Code create the default launch.json  file that is required (details in this link: https://code.visualstudio.com/docs/editor/debugging) , but when I started the debugger from inside VS Code, and I put a breakpoint on a line of code in a TypeScript file (.ts), VS Code complained that it could not find the corresponding .js file (therefore it could debug from the .ts file), and it put a little gray circle beside the line of code, instead of the typical red dot, with a warning:

“Breakpoint ignored because generated code not found (source map problem).”

When searching the web about this issue, all of the suggested launch.json code examples from various posts I found all looked very similar in what they suggested was required to fix the issue, but I never could get any of them to work for my app / dev environment. But, I finally found one that did work for my beginner Angular 2 project, which I had begun by cloning the Angular 2 Quick Start repo.

I found the magic solution for me in one user’s example that he posted to an issue on the VSCode GitHub project: https://github.com/Microsoft/vscode-chrome-debug/issues/367 and again the issue of where the generated .js and .js.map files are located (in a Windows environment) is addressed in this issue: https://github.com/angular/angular-cli/issues/1223

Now it works!

Here’s the part from his included code that worked for me:

IMPORTANT: Kill all current VS Code and Chome instances to start fresh.

After making this change to my launch.json, I made sure to kill all existing VS Code instances and all Chrome instances. Then I used npm start  and then I started the debugger from inside VS Code. It then launched its own instance of Chrome to start my app, and finally, VS Code debugger was able to trace into the .ts code because it was finally able to find the generated .js code.

.gitignore changes

I also found that I needed to add two folders to my .gitignore  file:

 

SSMS – Generate Scripts – Data only – Cyclic dependencies found error

When trying to generate data only scripts from a Sql Server 2012 database, I was getting this error from Sql Server Management Studio 2012:

Microsoft.SqlServer.Management.SqlScriptPublish.SqlScriptPublishException: An error occurred while scripting the objects. —> Microsoft.SqlServer.Management.Smo.SmoException: Cyclic dependencies found. at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.DependencyGraphTraversal(Int32 num, Dictionary2 dictionary, List1 sortedList, HashSet1 visited, HashSet1 current) at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.DependencyGraphTraversal(Int32 num, Dictionary2 dictionary, List1 sortedList, HashSet1 visited, HashSet1 current) at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.DependencyGraphTraversal(Int32 num, Dictionary2 dictionary, List1 sortedList, HashSet1 visited, HashSet1 current) at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.DependencyGraphTraversal(Int32 num, Dictionary2 dictionary, List1 sortedList, HashSet1 visited, HashSet1 current) at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.DependencyGraphTraversal(Int32 num, Dictionary2 dictionary, List1 sortedList, HashSet1 visited, HashSet1 current) at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.SortDictionary(Dictionary2 dictionary) at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.SortDataSet(DataSet ds) at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.SortDataSet(List1 objectList, Dictionary2 idDictionary, DataSet ds) at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.ExecuteQueryUsingTempTable(List1 objectList, List1 list, String query) at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.ResolveTableOnlyDependencies() at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.ResolveDependencies() at Microsoft.SqlServer.Management.Smo.SmoDependencyOrderer.Order(IEnumerable1 urns) at Microsoft.SqlServer.Management.Smo.ScriptMaker.DiscoverOrderScript(IEnumerable1 urns) at Microsoft.SqlServer.Management.Smo.ScriptMaker.ScriptWorker(List1 urns, ISmoScriptWriter writer) at Microsoft.SqlServer.Management.Smo.ScriptMaker.Script(Urn[] urns, ISmoScriptWriter writer) at Microsoft.SqlServer.Management.SqlScriptPublish.SqlScriptGenerator.DoScript(ScriptOutputOptions outputOptions) — End of inner exception stack trace — at Microsoft.SqlServer.Management.SqlScriptPublish.GeneratePublishPage.worker_DoWork(Object sender, DoWorkEventArgs e) at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e) at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)

 

This database was originally on Sql Server 2008, and I could script it without any problems at that time, which I did many times. Then, about a month ago, we moved it to Sql Server 2012, and when I tried to script the database again, using Sql Server Management 2012, I got this error.

I googled around and found this helpful script to find the offending circular references:

https://azure.microsoft.com/en-us/blog/finding-circular-foreign-key-references/

… which I ran in a query window to see what was wrong. Sure enough, it showed me the circular references and the results were spot on, but I wanted to keep the references in place. Everything worked fine when accessed from Entity Framework, and I was afraid to mess with the relations.   (In fact, the database was created in Sql Server 2012 by the Entity Framework using the update-database command. So, all the relations, foreign keys, indexes, etc came originally from the class definitions in EF.)

Problem Solved!

Then, on a whim, knowing that I had successfully scripted this database before, I connected to the Sql Server 2012 instance using Sql Server Management 2008 R2, and I was able to generated the scripts without error!!

Note, that I did data-only, and I had to allow it to script all database objects. I tried to select only certain tables, but that gave some other errors that I wasn’t willing to track down, so I just did the whole thing.

 

So, good luck to you… I hope this will help the next person who faces this problem.

I Love Me Some Business Objects

Here is the beautiful stack of Business Object classes for my Line of Business CRUD app (see image below).

The Job object (highlighted in the screenshot), in particular, is populated with tons of little worker methods to handle all the processes my Job oriented forms need. Most of these methods were created by taking little bits of procedural code from various forms and procedure files that had become strewn about all over my app code, and tucking it away into methods on the Job class.

Now, I can easily create a Job object with one line of code, and then call any of its handy methods to quickly create a cursor containing the desired child records, or return a value from some other calculation, or any other tasks that my business app needs to handle. 

So follow this rule: If you’re writing processing code in form methods or methods on UI controls (yikes!), you should consider creating a Business Object and adding methods to it instead. It will be much more re-usable and maintainable.

 

Code sample:

oJob = CreateBusObject(‘Job’) && Call a Dictionary method to get the class

oJob.Get(‘TheJobNumber’)

If oJob.lFound = .T.

   oJob.GetLineItemsForInvoice(‘CursorName’)

Else

   *– Handle Not Found situation

EndIf

 

SNAGHTML9aff7fe7

BarCamp Birmingham 5 Sessions

imageI attended BarCamp Birmingham 5 on Saturday, August 6 2011. This was my first time attending an un-conference, also known as a “user conference”. In a BarCamp conference, the sessions are proposed on the morning of the conference by attendees who have a topic they’d like to share. Each topic is recorded on a sticky note, and then a very informal “vote” occurs to select the session list for the day. I was a little skeptical of sacrificing a full day away from my family to attend a conference where I could not review the sessions ahead of time. However, I was very pleased with the sessions, speakers, and attendees. The entire event was great, and I was inspired by many of the speakers who were obviously as passionate about their careers and work in the software field as I am.

There were 111 registrants for the conference. It was held and UAB School of Business and Engineering complex. Every attendee received a BarCamp Birmingham T-shirt, free soft drinks, and and free lunch from Jim-N-Nick’s. The twitter hashtag is #BarCampBham.

Below, I’ve recorded the sessions that were chosen, and have listed the speaker’s name and contact info for the ones that I could reconstruct. I took iPhone photos of the sticky notes that were arranged to lay out the schedule.

If you can help me fill in any of the missing speaker info, please ping me with the information.

Morning Sessions

9:30

10:10

10:50

11:30

Room 1 The Hitchhikers Guide to Web Development at Midnight in the Garden of Good and Evil
Chris
WordPress Optimization for Shared Host Environments.

Paul OFlaherty
@pauloflaherty

Into to PhoneGap
Matt R.
5 Things You Should Know About PhotoShop
Tammy Hart
@TammyHart
Room 2 Basics of SEO
Cathy Reisenwitz
@CathyReisenwitz
Non-Technical Intro to Google+
Dave Barger
@lalunablanca
Tips From a PM:
4 reasons why programmers should NEVER talk to clients.
Daniel Walters
Group Discussion on Gamification.
Room 3 Start-ups and Screw-ups – How to Beat the Odds
Andra Walker
@andreafwalker
Filming, Editing, and Publishing Great Videos on the iPod and iPhone With No $
Chris Davis
@chriscreates
Convergence Trends and Relevancy of the Modern Still Image
Josh Self
@joshself
Brief History of Japanese Occupation of Taiwan

Jason Chucong

Afternoon Sessions

1:40

2:25

3:00

3:35

Room 1 Discussion Group –
How to Grow the Digital Community
Chris Davis
@chriscreates
Cutting the Chord – Ways to get rid of cable and Direct TV
Kevin Gatgens
@kgatgens
5 “Guidelines” of Improv
Capt. Daniel Walters
@BlazeDaDragon
PCI Compliance – Risks and why it’s so hard to achieve
Matt Hellinger
Room 2 Open Source – What I’ve learned: – Git
– VIM
– JavaScript
Tom Brander
@dartdog
Conversation about Mobile Design and UX
Brian Cauble
@briancauble
There is NO Tablet Market
Matt Hellinger
Critical Thinking 098 – How to start being less sure and more right
M. Alvis
Room 3 Discussion – What is AWESOME?

Jason

CSS – Advanced and Intro
Brian Moon
HTML Email
– Design
– Strategy
– Code

Link:
Slide DeckJulia Anderson
@Blueys
Idea Driven Design
Giovanni DiFeterici
@giodif

Selected programming job posting trends as of March 2011

Job postings chart for some programming common languages (as of 3/24/2011).

Hey – What’s that BLACK line down at the very bottom? (Answer: FoxPro)

You can see that C# has about 3 times the job postings as Ruby or Python, and that that Ruby and Python are on a path to overtake Visual Basic.

Use this link to view the chart and explore others: http://www.indeed.com/jobtrends?q=C%23%2C+Ruby%2C+Python%2C+FoxPro%2C+Visual+Basic&l=

3-24-2011 12-45-20 PM

Extracting Enum values from an assembly file using the Object Browser in FoxPro

Thanks to a helpful comment from Doug Hennig, I’ve learned how to use the Object Browser in Foxpro to extract Enum values from an assembly file right into a .h file inside of FoxPro.  By an “assembly”, I mean a dll file that is installed on your machine when you install software or API tools to communicate with various software packages.

For instance, here is the Object Browser in FoxPro taking a peek inside a dll assembly from the QuickBooks SDK ver 10:

SNAGHTML3ea8bd

I posted an article recently about communicating with QuickBooks from FoxPro, where I explained some problems I began having with the .h include file I was using, which had become out of date when we upgraded to the latest version of the QuickBooks SDK. You can see that post here if you’re interested in the original problem I was having: http://mattslay.com/adding-custom-data-to-quickbooks-invoices-from-foxpro/

The problem was that I needed the latest Enum values (“constants” in FoxPro) from the new assembly that came with the QB SDK ver 10. The Enums from a dll are exposed nicely when programming inside Visual Studio, but there is no FoxPro .h include file included in the QuickBooks SDK for us Fox programmers. I knew the information I needed to create a FoxPro .h file was hidden away in that .dll somewhere, I just didn’t know how to get them out of there in an automated fashion. In the original post linked above, I showed where I was able to see them inside of Visual Studio, and even determine the values of each one, but it was a very manual process. I was sure there was a better way. And there was. It was super-smart FoxPro MVP Doug Hennig who showed me the way, using the Object Browser in FoxPro.

So, with a little expansion on Doug’s original hint, I was eventually able to drag-and-drop the  entire Enum node from that QB assembly file right into a code file in FoxPro to get a full list of constants nicely defined and formatted in a .h include file.

It turns out that the Object Browser in FoxPro has some pretty good (but not perfect) support for this… It was close, but not exactly what I needed, since it required a LOT of manual drag-and-drop maneuvers. So I Googled around and stumbled upon the golden nugget that I needed to completely solved the problem to my liking. To dig into the solution I found, you can check it out here: http://www.dbmonster.com/Uwe/Forum.aspx/foxpro/7047/Utility-to-extract-print-all-enumerations. I am not going to repeat all the code and discussion from that thread in this post, just to avoid redundancy. There’s a lot to be learned from reading the thread yourself if you want to dig into the details, but that’s not necessary unless you want to explore the issue deeper. In short, I followed the thread and picked out the various instructions and was able to have it all working in just a few minutes.  So, to make it easy for everyone else, you can skip all the details in that post and just download the enhanced version of the Object Browser from the links below. Or, If you want to dig deeper into the code yourself, I’ve given you the steps you’ll need to follow to make the modifications yourself….

Instructions to modify the Object Browser for Enum Extraction

Fortunately, the source code for many of the FoxPro tools like the Object Browser were release to the community a few year ago so we could enhance them and keep the tools current.  That’s what the guys in the dbmonster post did to solve this problem (link below), and I’ve wrapped it all up here in an easy download file to share it with you:

Option 1 – Download the modified file from my site and start using it now:

If you do not want to modify the code yourself, you can download the .exe file that I built from here: http://mattslay.com/download-files/object-browser-modified-for-enums.exe

If you want a copy of the modified source code, you can download the whole project here: http://mattslay.com/download-files/object-browser-modified-for-enums.zip

Option 2 – Do it yourself – To update the FoxPro Object Browser yourself with this expanded functionality, follow these steps…

1. Go to the VFPX site and download the Xsource code here: http://vfpx.codeplex.com/wikipage?title=XSource. Extract the ‘obrowser’ tool from the zip and open the project file in VFP.

2. Make these changes to the source code… (follow the discussion and see the code at this link: http://www.dbmonster.com/Uwe/Forum.aspx/foxpro/7047/Utility-to-extract-print-all-enumerations

3. Next, compile the project to an .exe (name and location is your choice).

4. Now you can run your new version of the Object Browser to accomplish this task and output shown above.

Results

Here’s the beautiful output you’ve been waiting to see.  (Note: I manually added the top few lines of comments to record some notes about how the files was created and what version is.)

By the way, if you happen to need the complete QBFC10Lib.h file that I’ve extracted for with FoxPro, you can download  it here: http://mattslay.com/download-files/QBFC10Lib.h   If you’re working with a different version of the QB SDK, then you should extract the Enums yourself from the correct dll from your version of the SDK, and build your own .h file.

*** FoxPro constants from QuickBooks SDK assembly QBFC10Lib
***
*** To see the origin of this file, visit this link:
***   http://mattslay.com/adding-custom-data-to-quickbooks-invoices-from-foxpro/
***
**********************************************************************************

*— Constant Group: ENOpenMode
#DEFINE omSingleUser    0   
#DEFINE omMultiUser    1   
#DEFINE omDontCare    2   

*— Constant Group: ENRqOnError
#DEFINE roeStop    0   
#DEFINE roeContinue    1   
#DEFINE roeRollback    2   

*— Constant Group: ENReleaseLevel
#DEFINE rlPreAlpha    0   
#DEFINE rlAlpha    1   
#DEFINE rlBeta    2   
#DEFINE rlRelease    3   

*— Constant Group: ENRqResponseData
#DEFINE rdIncludeAll    0   
#DEFINE rdIncludeNone    1   

…etc…

Enjoy!

WordPress is double awesome

I’ve just got say “WOW!” on this one… I recently wanted to add a blog to one of our corporate web sites so we could begin publishing a few articles about the equipment we manufacture in that division (heavy industrial shredders). Now, I had set up a Blogger account back in 2009 for my personal blogging effort, where I post mostly about software development stuff I’m working on or studying. But for this new corporate blogging plan I knew I wanted it living on our own server that we would fully control, rather than the Blogger model where your stuff is kind of under some control of Blogger. (Not that they are evil or anything, I’m just saying it didn’t seem like the right choice for a corporate blog)

Anyway, I had heard about WordPress in the past, but never really knew what it was all about. Turns out… It’s VERY powerful, simple to install, easy to use, and free.

All you do is download the zip file from WordPress.org, then FTP it to a folder on your web hosting company, say in a /blog folder. It has great instructions on how to configure MySql to host your blog, and you have to edit a config file to wire it up once the database is setup. (MySql database is included in most web hosting accounts. Our corporate web sites live on 1and.com and the installation and configuration went very well).

The whole thing is written in PHP. Now, I know nothing about PHP but that really doesn’t matter. There’s a Control Panel that you hit with your browser to configure everything. You can create as many Authors as you want, you can create Categories for your posts, installing Themes is easy, and there are tons of useful plugins that are available to do all kinds of cool stuff. There is a huge community around this software, and I plan to dig into that a little more in the future.

I’m not trying to give a complete tutorial on how install and set it up. Those topics have been covered in tons of places already. My point is that this “tool” is awesome! I learned the basics and had it installed, configured, and live all in one stay-up-late evening. I’m sure you can too.

Oh, by the way… you can see the blog here: http://www.JordanReductionSolutions.com/blog

Model, View Model, View – No way (for now)…

I get all the Model <—> ViewModel <—> View stuff.  It’s great theory, but come on! A View Model class that has the same exact properties as the stinkin’ Model that it is wrapping, just so we can say “I used MVVM”.

My little Asp.Net CRUD app is binding the UI controls directly the data properties on a BusinessObject.Entity, where the Entity is a Typed class from a LinqToSql query. (My BO’s are based on the West Wind wwLinqToSql Business Object Wrapper from the West Wind Web Tools for Asp.Net ).  And guess, what… It works! It loads and saves data when the user clicks the buttons.  Imagine that. I’ll be darned if I’m gonna wrap a ViewModel class around that data Entity so I can bind my UI controls to that ViewModel, rather than hooking straight to the Entity properties.  Just too much stinkin’ code required.

I’ve also added a few additionally needed "presentation properties" into my Business Objects to *supplement* the Entity properties that came from the data base row. And, yes, when working with my BO’s, you have to access some properties right off the BO (the custom presentation properties I added) and some come from the main BO.Entity (the real data from the database row). So, you do have to know where to get the data you are after. By doing this, most of my UI binding is against the BusinessObject.Entity properties, and a few UI bindings go against the supplemental presentation properties I’ve added right on the BO.

So, I don’t mind some bit of a hybrid ViewModel approach, but not a separate class where I have to re-host every single property that is on the Entity. And, since this is done on the Business Object class level, all these supplemental  "presentation properties" can still be accessed by other UI apps that work with the same Business Object wrapper classes.

From a theoretical standpoint, sure, it breaks a few rules. But from a real world impact, we are talking about only a handful of supplemental properties. In all the BOs I’ve built (Customer, Invoice, Quote, Shipper, User, Employee, etc.), maybe I’ve added 3 to 5 additional properties on each one to serve what is usually one view, but sometimes two or three different views at the most. If things really get crazy, there might be as many as 10 presentation properties to handle all the views. It still seems like less of an overall burden on the application as a whole to add a few presentation properties right on the BO, rather than dropping an entire Model-to-ViewModel Mapper implementation on my app (homegrown or commercial). Talk about a burden.

Now look, these additional properties will be implemented somewhere. Why not stick them onto something that already exists (the BO) as little light-weight tag-a-longs, rather than create an entirely free-standing ViewModel object, where I put these innocent properties and then I have to REPLICATE all the real properties from the Model too. Is that how this magic ViewModel character makes things "cleaner"? Yuck! Oh, wait, I almost forgot… but look how clean my BO is! No thanks.

Here’s a use case from my app… I want to bind a UI control to the Customer Name for an Invoice Object that I have pulled from the database. Well, rather than attempting to bind through the main Invoice Entity and into the Customer sub-Entity (which LinqToSql handles nicely for me BTW) I’ve added a presentation property on the BusinessObject to access the Customer Name easily, and also guard against several null object cases that can arise.

Example: Attempting to bind a UI control to directly to this property (see below) could crash the UI generation if the Entity object or Customer Entity is null:

busInvoice.Entity.CustomerEntity.Name

Since it’s possible that the busInvoice object’s .Entity property is not even saturated yet, or that the CustomerEntity is null, this could cause problems generating the UI.  And, I certainly don’t want to test for all the potential problems in my UI pages every time I want to display that property. So, I added a property on the busInvoice object to host that value and deal will all the required testing.

Something like:

public string CustomerName
{
get
   {
     if (this.Entity != null && this.Entity.CustomerEntity != null)
       return this.Entity.CustomerEntity.Name;
     else
       return "";
   }
}

Anyway, back to my main rant about going overboard with ViewModels…

So look at this sample I found from a notable Asp.Net guru.  It is a ProductView class that returns a ProductView object from a passed in Product (that you got from your DAL somewhere), and it can return a Product object from a passed in ProductView object, basically by unwrapping the ProductView object and casting the string properties back to the original data types. Look at all this code! Especially look at the ToProduct() method. Wow!  ( code from http://weblogs.asp.net/craigshoemaker/archive/2009/11/03/vm-workshop-model-view-viewmodel-mvvm-and-the-presentation-model-pattern-in-5-ui-platforms.aspx )

It looks like a maintenance nightmare.  I need more convincing that this is the way the pros do it. Perhaps I’ll be a rookie just a little bit longer.

    public class ProductEditView : IUrlSerializer
    {
        public string ProductId { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string QuantityOnHand { get; set; }
        public string Price { get; set; }
        public string ReleaseDate { get; set; }

        public string SerializeToUrlString()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("productId={0}&", this.ProductId);
            sb.AppendFormat("title={0}&", this.Title);
            sb.AppendFormat("description={0}&", this.Description);
            sb.AppendFormat("quantityonhand={0}&", this.QuantityOnHand);
            sb.AppendFormat("price={0}&", this.Price);
            sb.AppendFormat("releasedate={0}", this.ReleaseDate);
            return sb.ToString();
        }

        public static ProductEditView ToProductEditView(Product product)
        {
            ProductEditView view = new ProductEditView();
            view.ProductId = product.ProductId.ToString();
            view.Title = product.Title;
            view.Description = product.Description;
            view.Price = string.Format("{0:c}",product.Price);
            view.QuantityOnHand = product.QuantityOnHand.ToString();
            view.ReleaseDate = product.ReleaseDate.ToShortDateString();
            return view;
        }
        public static Product ToProduct(ProductEditView view)
        {
            double db;
            int num;
            DateTime dt;

            Product product = new Product();
            product.Title = view.Title;
            product.Description = view.Description;

            if (double.TryParse(view.Price.TrimStart(‘$’), out db))
            {
                product.Price = db;
            }
            else
            {
                throw new ArgumentException("Price is malformed or not a valid value.");
            }

            if (int.TryParse(view.ProductId, out num))
            {
                product.ProductId = num;
            }
            else
            {
                throw new ArgumentException("ProductId is malformed or not a valid value.");
            }

            if (int.TryParse(view.QuantityOnHand, out num))
            {
                product.QuantityOnHand = num;
            }
            else
            {
                throw new ArgumentException("QuantityOnHand is malformed or not a valid value.");
            }

            product.ProductId = Convert.ToInt32(view.ProductId);
            product.QuantityOnHand = Convert.ToInt32(view.QuantityOnHand);

            product.ReleaseDate = Convert.ToDateTime(view.ReleaseDate);
            if (DateTime.TryParse(view.ReleaseDate, out dt))
            {
                product.ReleaseDate = dt;
            }
            else
            {
                throw new ArgumentException("ReleaseDate is malformed or not a valid value.");
            }

            return product;
        }

Adobe Acrobat PDF Printer output filename on 64-bit OS…

Now that I’ve moved over to Windows 7 64-bit on my primary workstation, I’m in a new world of IT support that I didn’t expect from those extra 32 bits of OS horsepower. My MicroTek scanner is not supported, my HP 2800 Business Inkjet is not supported, and I recently discovered that a technique I use in my Visual FoxPro business application to pre-define the PDF output filename when using the Adobe Acrobat PDF printer driver mysteriously does not work on Windows 64-bit. So, I want to show the remedy in case others encounter this same problem. I’m explaining this from the perspective of a Visual FoxPro application, but the issue applies to all Windows applications, and the remedy can come from many programming languages.

Background… It’s well know that you can prevent the Adobe PDF printer driver from prompting the user to choose a path and filename for the output file by programmatically setting a certain registry key with a string value that contains the desired path and filename of PDF file you want to output. I use this technique to automatically set the path and filename for my users for the various PDF reports we generate to send out to our customers and vendors.

This magic registry key is located at: HKEY_CURRENT_USER\Software\Adobe\Adobe Distiller\PrinterJobControl.  So, you create a string value under that key with the path and name of the *application* that is printing the PDf, and then you set the value of that string value to the path and filename of the PDF you want to create.

Now, the path and filename of the application that is doing the printing is not always easy to figure out, so I can’t solve that mystery here for any given app that you may be using, but I do know (and I forget where I learned this) that for a Visual FoxPro application (.exe), the path and application name can be discovered from the VFP variable “_VFP.ServerName”. Of course, it’s up to you to decide the path and filename of the PDF output file you’re want to create.

So, to bring it all together, you add a string value named after the app that is doing the printing (for instance) “C:\Program Files\Microsoft Visual FoxPro 9\vfp9.exe” and you set the value of that string value to the filename you want the PDF to be called; i.e. “C:\TEMP\JMC_PO_MS2741C.pdf”

Well, that’s how it works on good ‘ole 32-bit OS.  Now, enter into the 64-bit world…

The issue is that the *application* name that the 64-bit OS sees as doing the printing is no longer the actual exe the user is running,  but rather some odd Windows exe named “splwow64.exe” (some spooler-like thingy that actually handles the printing which I learned about here http://www.acrobatusers.com/forums/aucbb/viewtopic.php?pid=57794#p57794 ). So that’s the application name you have to use, and you do have to include the path. Since this spooler thingy exe runs from C:\Windows, the actual registry key that must therefore be created is “C:\Windows\splwow64.exe”.

PDF_Reg

So, with the PrinterJobControl registry key all programmatically setup as described above, when you actually fire off the Adobe PDF printer, the output name is already set and the user will not see the prompt dialog to choose the output path and filename.

Also, be aware that after *you* programmatically create and set the registry value, the Adobe PDF driver will actually delete that string value from the key right after the file is printed. Perhaps this is so the next app that prints won’t use the same name.

Finally, here’s the VFP code I use to set the values for both 32-bit and 64-bit OS’s. (This code uses an instance of the Registry class from the FoxPro FFC classes to work on the Windows registry). Again, this all works the same way whether you are using .Net, VFP, VB, or whatever. The main thing is to setup the registry value the right way.

   oRegTools=createobject(‘registry’)

        m.lcKeyPath = "Software\Adobe\Acrobat Distiller\PrinterJobControl"

       *– This RegKey setting works on 32-bit OS…
        oRegTools.OpenKey(@lcKeyPath, HKEY_CURRENT_USER , .T.)
        lcPrintingApplicationName = _VFP.ServerName
        oRegTools.SetKeyValue(lcPrintingApplicationName, “C:\TEMP\JMC_PO_MS2741C.pdf”)
        oRegTools.CloseKey()

        *– This is the "Application" name that the OS sees as doing the printing when running on a 64-bit OS…
        oRegTools.OpenKey(@lcKeyPath, HKEY_CURRENT_USER , .T.)
        lcSplWow64ProcessName = ‘C:\Windows\splwow64.exe’ && <— This is what the OS sees as what’s doing the printing…
        oRegTools.SetKeyValue(lcSplWow64ProcessName, “C:\TEMP\JMC_PO_MS2741C.pdf”)
        oRegTools.CloseKey()

 

Hopefully, this insight will save someone out there a little headache when working with the Adobe PDF printer driver on these super-duper 64-bit OS’es.