To recap from Part 1, the goals of this rabbit hole project were to use DuckDB’s fast processing engine alongside an existing Visual FoxPro 9 (VFP) application, to take advantage of DuckDB’s import/query speed on large datasets (100M+ rows, ~2GB) while keeping VFP as the primary application layer. The primary technical obstacle was that DuckDB only ships a 64-bit ODBC driver, and VFP9 only supports 32-bit ODBC which ruled out a direct database connection.
Have I told you lately I’ve not been fan of AI and definitely not of “vibe coding?”
Perhaps I’ve dropped some hints along the way or maybe, I’ve alluded to my personal bias an oblique way as I’ve shared these posts, but in case you missed it, up until this point my exploration of AI over the past two years has resulted in less than stellar results.
I’m old enough to remember the PC boom in the early 80’s when every company out there jumped in and started selling computers. Remember, the TI-99, the Commodore 64, The Osborne I, or how about the Timex Sinclair? The market shot up and everyone was flying high until the IBM PC won the market and all those companies all but disappeared.
I’m old enough to remember the internet boom of the late 90’s when huge amounts of money were invested by companies in this nebulous idea of connecting computers all over the world and it really seemed like everyone was lighting $100 dollar bills on fire and spending like drunken sailors on the promise of what the internet might offer. It was an amazing time to be alive, at least until the bust came. It would then take at least 10 more years before the internet actually started to deliver on those early promises.
And, now I’m sitting here watching companies like OpenAI, X, Anthropic and others light $100,000 dollar bills on fire in an effort to win the AI race. I’ve tested ChatGPT, Grok, Duck.AI and several others trying to find useful way to take advantage of the promises of LLMs. In particular, I spent couple of week earlier this year trying have an AI help me refactor some of my utilities to get more performance out of my older code. The results generally were mixed and I usually ended up debugging the code the AI generated because it sometimes included commands in the code which were not even VFP code.
But my mother didn’t raise a quitter, and after hearing some other VFP programmers rave about the results they were getting with Claude.AI, I thought this rabbit hole project might be a great way to test Claude with specific tasks which wouldn’t be a great loss if it all came to nothing.
As I have already mentioned in these posts, so far, the greatest benefit in working with Claude.AI has been feeling I had another experienced programmer working beside me who is bringing knowledge I’m lacking to the project I’ve envisioned. It’s been like having a personal research assistance who has the ability to read and understand my coding, the key documentation and all of the related documents that could be part of the right answers.
Even before this project started, I have been opposed to the idea of letting AI write code, preferring to try and use AI to refactor existing code. The news reports of some spectacular failures that have happened to other programmers was a cautionary tale to me. The moral of the story being that if you give AI too much control, your project will come to ruin. But…the code snippets Claude has been providing to help explain technical topics along the way have been really helpful. I love reading documentation and programming manuals, but sometimes, nothing beats seeing the general logic of a plan laid out, then I can take the logic outline and embellish it in my own way. Unlike any other AI tool I’ve worked with, I haven’t had to debug any of the code Claude suggested other than that one time when Claude recommended a command which included a “&&” I documented at the end of Part 6. Claude has been great at helping me find bugs in my code and is especially good at finding and helping me fix syntax errors with things like DuckDB commands with which I’m not as familiar.
I know it sounds stupid but coming up with the fix for “&&” bug made me feel like I was still relevant to the programming process. So far, 98% of the code I’d put together for this project was mine. From time to time, I would upload my code and Claude would offer suggestions if I had errors, but otherwise, I was still in the drivers seat when it came to the test programs being written. I was also in control of the direction this whole project was taking because in the end, I was the one asking the questions as I found new questions to ask. It wasn’t like I told Claude what I wanted and it handed it all to me on a silver platter. It was a collaborative effort to explore possibilities of how to turn my goals for this project into something functional and useable.
But, I was about to confront a situation where I ended up using some code Claude wrote as an example for me because I could find no reason to re-write it from scratch.
If you recall, Part 6 ended with Claude trying to tell me how to hide the black command window by using Win32 API to cycle through all open windows on the desktop and then hide the one running the DuckDB instance. Honestly, that suggestion was an abject failure and really not a good long term solution for a lot of reasons. So after trying my best to make it work, I went back to Claude and explained that the idea didn’t work and I could never hide the window.
Claude then responded that the only real, permanent fix was to stop relying on find-and-hide entirely and prevent the window from ever being created in the first place — which required bypassing WshShell.Exec and calling CreateProcess directly from VFP with the CREATE_NO_WINDOW flag. While this was counter to the suggestion Claude made before (after all, it was Claude’s idea in the first place), this new direction seemed more solid to me for some reason.
Claude said that if I wanted full control over hiding the command window which opens every time I spawn a DuckDB instance AND still have the named pipe object available to send commands directly into the DuckDB CLI, then I needed to work at an even lower area in the API and make direct calls to the Win32 CreateProcess API instead of using the higher level wrappers like WshShell.Run or WshShell.Exec.
But, Houston, now we had a new and bigger problem. I had no idea how to write the kind of code required to setup and make the calls required to make this happen. I didn’t even know where to start. It would have been like asking me to write code in Russian for all I knew about the Win32 API calls. Therefore, I turned once again to my wingman, Claude, and I posted that since the find/hide window option was not working and didn’t seem feasible, I was probably going to have to use the low level CreateProcess to make this work like I wanted. So, I wanted to create a VFP9 wrapper for the CreateProcess call to make things a little bit easier and could Claude help me with that?
I was expecting Claude to give me some code snippets like before and then I could take those broad strokes and turn them into something useable going forward. What I didn’t expect Claude’s response:
Sure — let’s wrap all of that plumbing into a reusable class so you don’t have to hand-manage the byte-packing every time. I’ll build it as a .prg you can SET PROCEDURE TO or DO FORM-include as needed, with methods for start/send/status/stop.
And a few seconds later, a download link appeared for a .PRG file named “HiddenShellProcess” and Claude displayed the following VFP code snippet to show me how to use it:
SET PROCEDURE TO HiddenShellProcess ADDITIVE
loProc = CREATEOBJECT("HiddenShellProcess")
IF loProc.Start(".\duckdb.exe .\SQLWork\datawork.db", "C:\SQLWork", "C:\SQLWork\duckdb_console.log")
* loProc.Start(tcCommandLine, tcWorkingDir, tcLogFile)
loProc.SendCommand(".read C:\jobs\job_0001.sql")
* ... keep it around across your event loop / job dispatch as jobs arrive ...
loProc.SendCommand(".read C:\jobs\job_0002.sql")
* ... when finished ...
loProc.Stop()
ELSE
? "CreateProcess failed, Win32 error: " + TRANSFORM(loProc.nLastError)
ENDIF
Literally in seconds, Claude generated a procedure which did all of the heavy lifting of calling the low-level CreateProcess to fire off a hidden instance of DuckDB CLI and setup the named pipe I could use to push commands directly into the CLI. Click the link and download the HiddenShellProcess.zip file which contains the .PRG Claude created and use it to your heart’s content. I cannot claim this as my own since Claude wrote it, but perhaps someone else out there can use it as well.
Claude even provide some helpful tips for using the HiddenShellProcess:
- Destroy cleans up automatically. If loProc goes out of scope or gets RELEASEd while still running, it sends .exit, waits up to 500ms, and force-terminates if DuckDB hasn’t exited by then — so you shouldn’t leak orphaned duckdb.exe processes even if your calling code forgets to call Stop() explicitly.
- Stop() is graceful-then-forceful. It tries .exit first (letting DuckDB close its database file cleanly), waits up to 2 seconds by default, and only calls TerminateProcess as a last resort if it’s still alive — you can pass a different wait time as loProc.Stop(5000) if your jobs sometimes take a moment to finish flushing.
- IsRunning() is there so you can poll process health in whatever loop is feeding it jobs, without needing to touch stdout/stderr at all.
- No stdout capture on purpose — output goes straight to the log file you specify, avoiding the pipe-buffer-fills-up problem entirely. If you later want to programmatically read DuckDB’s responses rather than just log them, that’s a bigger addition (a second pipe plus something to drain it without blocking VFP).
I did not ask Claude to write the code. I asked Claude to help me build something like this, but I guess Claude took our full conversation into account and decided to offer this bit of low-level coding without me asking for it directly. Putting my bias against using AI for coding aside for a few minutes, I revised my test program to call the HiddenShellProcess and it worked.
Perfectly. The first time.
I admit I was conflicted. I was excited because a problem we’d been experiencing from the beginning was solved. But I was bummed Claude did everything for me and just handed it to me. I know it would have taken days for me to research all of the bits Claude just built into the HiddenShellProcess procedure. I’m not sure I would have thought to include all of the things Claude automatically included. But I was grateful that I didn’t have to go through all of that and I could stay focused on the larger rabbit hole project without distraction. I didn’t get to learn all of the details of making calls to the Win32 API, but now I have a procedure I can review at my own pace and dig deeper if I want to. I weirdly felt like Claude just showed me up, but at the same time, I felt like I was working on a team with another programmer who was experienced with technical skills I was missing. I don’t know if we would have arrived at the same place if we hadn’t been doing all of this testing together and providing feedback about what worked and what didn’t. In fact, at the beginning of this experiment, I had no clue I needed something like the HiddenShellProcess and yet, here Claude and I were.
I’m still a skeptic. That’s my nature. I still believe a lot of companies are selling sizzle and not the steak when it comes to the idea of “artificial intelligence.” I know as a tech person AI is really a large language model (LLM) and not really sentient. And I know we are not even close to any AI being sentient even if it feels that way sometimes.
However, after two years of trying and failing to find a way to utilize AI in an effective way, not to replace my work in programming, but to augment it; Claude just proved it was really possible for AI to be a real asset to the programming process when used in the correct way. I made a joke in earlier posts calling Claude my new best “wingman” but it is no longer a joke. Claude rightfully has earned that moniker and I will no longer say it with my tongue firmly into my cheek.
I still believe AI companies have to find a way to make AI profitable and they cannot continue to burn through capital like it is gasoline. There will come a reckoning, we just don’t know when and we don’t know who the winners and losers will be. It’s happened every time there has been a fundamental shift in technology.
I’ve always said for many years that most technological advances are evolutionary not revolutionary, but on rare occasions, the revolution is real.
Now it’s just a matter of surviving as a buggy whip maker in world of flying cars.
If I get nothing else out of this experiment, I’m starting to feel like Dr. Strangelove. I’m learning to stop worrying and love SkyNet.
GuruGraffiti Writing On The Walls Of The Internet
