Just what I was looking for. Tx for writing this out. Very useful.
I read your blog and the light came on in my head. Thank you very much sir.
Thanks Rick, this is very helpful.
I normally write utility programs for my own use in ad hoc data manipulation/cleanup. I am now tasked with creating an app for someone in another dept so was looking for help exactly like this.
Some weeks ago I shared a little helper ( nfCustomEnvHelper.prg ) that automates all the steps you describe here.
You can give it a try at https://github.com/nfoxdev/nfCustomEnvHelper
Just ran into this problem on new Win11 computer. The latest OLE and ODBC installers on https://github.com/VFPX/VFPInstallers resolved the issue for me. Thank you!
The article is a good starting point for anyone new to Web Connect or like me, for someone that is setting up my first cloud server. Thank you Rick for taking the time to teach me through these writings.
Sorry to dredge up this old thread, but I recently made a discovery that affects performance. If you have a single prg with a define class, add code at the top of that prg to return the createobject of that class. That way there is no set procedure required. When the class is a subclass
dsoInventoryItems.prg IF NOT "\MYDATAOBJECT"$set("procedure") set procedure to mydataobject.prg additive endif return createobject("dsoInventoryItems")
define class dsoInventoryItems as MyDataObject
That means the set procedure is increased by only 1, and the individual data object subclasses can be instantiated directly like so:
I believe that should give as good EXE performance as having everything in one .PRG.
This reminded me of a trick I learned years ago at Eric Selje's Southwest Fox 2010 session on VFP2C32. This technique should survive a CLEAR DLLS. From Eric's whitepaper:
*Here’s a tip I remember Drew Speedie mentioning a few years back. Because API functions with the same name as FoxPro functions take precedence in the calling heirarchy, you can dynamically load API functions on-demand by wrapping them in a VFP function of the same name. The first time you call it, the VFP function will be called, the API function will load and get called, but on subsequent calls the API function will be called directly. *
? getActiveWindow() FUNCTION getActiveWindow DECLARE INTEGER GetActiveWindow IN user32 RETURN GetActiveWindow()
Thanks for the write-up! I've been using Bootstrap 3 - had planned to go to 4 but never did. Definitely want to move to 5 (primarily for the floating labels more than anything, but also to do a better job of keeping up with the latest version).
I haven't used the HTMLHelpers much, so I personally don't need an upgrade for those. Though I'd suspect there's a lot of use of them out in the real world.
Really appreciate the link to the git commit for the upgrade. Very easy to see the types of changes needed in my apps.
Where has powershell been all my life? Love this Rick.
Thanks, Rick. This is a great article. Much appreciated!. I'm still working my way through it as I learn ES5 and ES6 at the same time.
By the way, I missed you as the Virtual Fox Fest 2020.
There's a bug in your code, in GetStartupInfo() you are checking for:
IF EMPTY(lnShowWindow) lnShowWindow = 1 ENDIF
But lnShowWindow = 0 (and therefore EMPTY) is a valid value! With lnShowWindow = 0 you can start a process "hidden", invisible in task bar and invisible in gui.
So you should replace it with IF VARTYPE(lnShowWindow) != "N" or something like that...
It'll be faster if you copy the string into memory and then use SYS(2600) to read byte by byte.
LOCAL lnX, lcString lcString = REPLICATE("1234567890",100000) Declare Integer HeapAlloc in Win32Api Integer, Integer, Integer Declare Integer HeapFree in Win32APi Integer, Integer, Integer Declare Long GetProcessHeap in Win32API lnLength = LEN(lcString) TRANSFORM(lnLength,"9,999,999") IF .T. lnSecs = SECONDS() lnBase = HeapAlloc( GetProcessHeap(), 0, m.lnLength ) Sys(2600, m.lnBase, m.lnLength, m.lcString) FOR lnX = 1 TO lnLength lcVal = Sys(2600,m.lnBase-1+m.lnX,1) ENDFOR HeapFree( GetProcessHeap(), 0, m.lnBase ) ? "SYS(2600): " + TRANSFORM(SECONDS() - lnSecs ) ENDIF
That's 0.33 seconds on my machine vs 37 seconds with SUBSTR().
There's one more async operation that is native to FoxPro and great source of frustration. Dynamic properties in grids. VFP executes these expressions which can call methods or user-defined functions seemingly randomly. Mouse pointer movement and the window placement of other applications impact when a dynamic expression are triggered.
In one application we have one nasty problem that appears to be related to dynamic expressions, although we are not entirely sure yet. VFP switches the data session of one complex form to the default data session at random lines in a form methods for some customers. We have added debug code to every single SET DATASESSION command and know that none of them are executing. Same for any timer event. So frustrating.
DOEVENTS works well in EXEs. Unfortunately, it's one of those commands that are not supported in an MTDLL, because VFP doesn't own the UI thread there. This leads to all kind of strange behavior with callbacks and kernel wait states when the VFP9T runtime is used. In AFP I've mitigated this issue by implementing my own event loop using GetMessage, TranslateMessage, DispatchMessage.
VFP interrupting after a line can occur in unexpected circumstances:
When VFP executes other code outside a wait state (READ EVENTS, MessageBox, etc.) it's in my experience more likely to do so upon entering or returning from a subprocedure. It appears that the code that manages DO levels also performs some checks.
The code interrupting the SCAN loop can be hard to debug because it also depends on the optimization state of SCAN expressions and filters. VFP will not reevaluate optimized expressions in SCAN loops, but it does so with non-optimizable expressions. If one of the side effects of the code that was called asynchronously impacts the filter condition, you suddenly see errors when adding or dropping an index, even when the code in neither the handler nor the SCAN loop has changed at all.
We have a problem to solve and I ask you if has some routine or API that do something like threads.
The atual cenario:
we must use three timers to execute differents routines in different times. Example:
Timer1 -> routine A -> at each 30 seconds
Timer2 -> routine B -> At each 60 seconds
Timer3 -> routine C -> at each 10 seconds
This cenario cause many problems because one timer in execution stops the others, creating a fila. So, the 10 seconds timer stops the others that never executes.
My question: any routine or API to generate independ threads or something to simulate my cenario?
Thanks a Lot
Sorry, but this functions are NOT working correctly! Especially the GetUtcTime() is incorrect. Your function GetTimeZone() checks for daylight-savings-time, but GetUtcTime() does not! When passing a datetime as parameter to the GetUtcTime() function, you have to check, if the given datetime is in daylight-savings-time or standard-time. If you run that function in winter, it gives a different result compared to if you run it in summer!
Mapping as part of startup in group policy did the trick. Thank you very much.
@Phil - you can use wwDotnetBridge to call that .NET Component to encode your message.
Great app and nice (though somewhat cryptic) descriptions of what can be done with it.
PLEASE, PLEASE address the OAUTH 2 signature question posed by Phil on Feb 26. I am trying to accomplish the exact same thing, and I'm an absolute noob when it comes to API/JSON services.
Many thanks, Will
We are getting a similar "Clr Instance..." error but with a different hash identifier as follows:
Unable to load wwDotNetBridge: Unable to load Clr instance.0x8007000e:
We are using the WW library v.6.15 DtNetBridge DLL with a custom VFP v.09x enterprise platform on our LAN. VFP and the enterprise platform is running on Windows 2012 R12 Server and have .met v4.7 running as well.
The error is odd as it is occasional/intermittent and does not always occur. It just started occurring today.
Sorry I forgot to mention that I'm running a VFP compiled exe with createprocess()
I can get start a process using createprocess() but the lhProcess number is different to the PID listed in the TaskManager.
So when I use OpenProcess(PROCESS_QUERY_INFORMATION+PROCESS_VM_READ,0,lhProcess) it fails to open the handle.
Is there a way to get the correct pid back or is enumerating all the processes the only way to go?
Hi, thanks for sharing this.
I used in my various VFP applications directly Windows AJAX with l_oHttp = CREATEOBJECT("MSXML2.ServerXMLHTTP") ... and was biten with TLS 1.2 issue on various client PCs.
At the end, I created small EXE with Golang which only connects to server and returns result. Then I called this EXE instead of MSXML2.ServerXMLHTTP with l_oShell = CREATEOBJECT("Wscript.Shell") ...
Yes, it looks silly to call another EXE for every HTTP request, but this was only secure way to do it on older Windows.
Thank you Rick you a legend. FoxPro is so good with handling data it will be hard to find an alternative in the future unless someone reinvents it in a multithreaded form. Hopefully we can keep it going for another 20yrs or so. There is still Cobol Code out there that is 58yrs old and still running.
Thanks, Rick--great information, as always!
Rick - discovered a few minutes ago that another issue when trying to resolve this is the default settings for IE Enhanced Security Settings Configuration. If these are both active you will not be able to open this url https://api.authorize.net/xml/v1/request.api if you are running Windows 2012 R2. If you deactivate for both administrators and users the page will open.
I know we all hate flashing command windows, but:
STRTOFILE("systeminfo >systeminfo.txt","runsysteminfo.bat") RUN /n runsysteminfo.bat sysitext= FILETOSTR("systeminfo.txt")
I do like simple solutions.
I'm trying to figure out how to use wwJsonServiceClient to generate an OAUTH 2.0 signature.
Their API docs have an example: The signature is sent in a header named BDXAPI_NAME.
MessageDigestPasswordEncoder encoder = new MessageDigestPasswordEncoder("SHA-1"); StringBuilder sb = new StringBuilder(); sb.append("bdxapikey").append("=").append(bdxApiKey).append("&"); sb.append("bdxapiClientId").append("=").append(clientId).append("&"); sb.append("bdxapisecret").append("=").append(bdxApiSecret); model.setSignature(encoder.encodePassword(sb.toString(), null)); return model;
Did you ever find a resolve for this? We are migrating our website over to a new Windows 2012 Server and are not able to get the foxpro dlls to work even when we have registered them successfully?
I like the idea using the wwJsonServiceClient, however is this also possible with headers? Using HTTP call I would normally so something like that: loHTTP = CREATEOBJECT("wwHTTP") loHTTP.AppendHeader("user_session", lcUserSession) lcHTML = loHTTP.HTTPGet("http://192.168.168.155:7564/api/v1/projekte")
Very helpful - but how would I return a 301 redirect? In my 5.x app, I do this: Response.Clear() Response.Status = "404 File not found" Response.Write("Page was not found")
lnUnixEpochTime = 1500847437204 ? lnUnixEpochTime/(6060606060*60) + DATETIME()
This returns: 07/23/2017 05:16:29 PM
Using addproperty() in .Init() works around the COM signature issue.
Also, using state persistence the way you describe applied more to single server applications (logical servers such as COM objects)
Hello, i am facing the issue while using request filtering option. My website didn't work until I add '.' in allowed list. I am unable to find which particular extension I am missing. Thanks in advance.
Yes, the _screen.top and _screen.left can obtain value -32000 which is hard to find on any monitor...
This can happen when the main FoxPro window is minimized.
Thank you for your given information. Foxpro application.
Visual FoxPro is great for building desktop applications.It is a object-oriented programming to reuse components across applications.Visual FoxPro originated as a member of the class of languages commonly referred to as "xBase" languages,Visual FoxPro is a great tool for building database solutions of all sizes.
Hi Thank you, thank you are a genius, i was about to setup a machine with older OS and IE. oButton.Click(.F.) is a life saver.
Thank you Rick--great stuff as always. And a cool plug for a great band in your example (Accept)!
There now exists a command-line tool to unblock these downloaded files. PowerShell has the Unblock-File commandlet.
The link for the syntax highlighting extension has some extraneous text at the beginning of the URL, Rick.