Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

cannot load 32-bit dll c:\windows\system32\rpcrt4.dll

Status
Not open for further replies.

cfsjohn

Programmer
Sep 1, 2016
66
US
Hello all,

First, congrats to Olaf. I know he has helped me soooo many times. I apologize for providing so little help myself. I've been trying to get my head above water with my app for 16 years now and I guess I don't really believe that will ever happen.

I am having an issue that showed up Sunday for no known reason. I say unknown but it could be related to the fact one of my customers got ransomware and because I had connected I overkilled and had Norton do a deep scan of my system. That was a month ago and I believe I have been paying for that ever since. They did not find anything wrong but I am about 99% sure it changed some things (long story).

I have a VFE/VFP 9.0 SP2 app with native FoxPro databases. On my development computer, Win 7 Pro x64, I made some modifications (that's pretty much 365 days a year). I revalidated my databases and recompiled my executables to upgrade a customer. One thing I always do is run my Balance Sheet report (my app is an accounting system) to ensure everything equals the last time it was run in the live environment just to be sure all is well. This report does write to one of my tables so it generates guid's as all my tables are keyed on guids. It uses the following VFE code to generate guids:

*==============================================================================
* Procedure: GUID
* Purpose: Generates a Globally Unique 16 or 32 Character Id
* Author: F1 Technologies
* Parameters: tlLong, Indicates if the
* Returns: lcGuid, the unique id
* Modifications:
* 01/12/2002 Added support for the tnLength parameter to return 32, 36
* and 36 characters.
*==============================================================================
FUNCTION GUID(tnLength)
LOCAL ;
lnLength, ;
lcGUID, ;
lcString1, ;
lcString2, ;
lcString3, ;
lcString4

IF VARTYPE(tnLength) <> T_NUMERIC OR ;
NOT INLIST(tnLength, 16, 32, 36, 38)
lnLength = 16
ELSE
lnLength = tnLength
ENDIF

lcGuid = UUID()
DO CASE
CASE lnLength = 16
*--Convert the binaries to hex values
lcString1 = BINTOHEX(Substr(lcGUID,1,4))
lcString2 = BINTOHEX(Substr(lcGUID,5,4))
lcString3 = BINTOHEX(Substr(lcGUID,9,4))
lcString4 = BINTOHEX(Substr(lcGUID,13,4))

lcGUID = lcString1 + lcString2 + lcString3 + lcString4

lcGUID = Left(lcGUID,8) + ;
SUBSTR(lcGUID,10,4) + ;
SUBSTR(lcGUID,15,4)
CASE lnLength = 32
lcGUID = BINTOHEX(lcGuid)
CASE lnLength = 36
lcGUID = TRANSFORM(BINTOHEX(lcGuid), "@R XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
CASE lnLength = 38
lcGUID = [{] + TRANSFORM(BINTOHEX(lcGuid), "@R XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") + [}]
ENDCASE
RETURN lcGUID
ENDFUNC
*===========================================================================
* End: GUID
*===========================================================================


*==============================================================================
* Procedure: UUID
* Purpose: Generates a Universally Unique 16 Character Binary Id
* Author: F1 Technologies
* Parameters:
* Returns: lcGuid, the unique id
* Notes: If the results of this function are stored in a field the
* field should be a Character Binary field with a size of 16.
* Added: 11/10/99
*==============================================================================
FUNCTION UUID
LOCAL ;
lcUUID

IF NOT IsFunctionLoaded("UUIDCreate")
DECLARE INTEGER UuidCreate IN rpcrt4.dll String @ UUID
ENDIF

lcUUID = Space(16)

* Pass lcGUID to the UUIdCreate function
UUIDCreate(@lcUUID)
RETURN lcUUID

ENDFUNC
*===========================================================================
* End: UUID
*===========================================================================


*==============================================================================
* Procedure: IsFunctionLoaded
* Purpose: Determines if a DLL Function has already been loaded with
* the DECLARE statement
* Author: F1 Technologies
* Parameters: tcFunction, the name of the Function check
* Returns: Logical
* Added: 09/02/2004
*==============================================================================
FUNCTION IsFunctionLoaded
LPARAMETERS ;
tcFunction

LOCAL ARRAY ;
laFunctions[1]

RETURN NOT (ADLLS(ladlls)=0 or ASCAN(ladlls,tcFunction,1,0,1,15)=0)
*==============================================================================
* IsFunctionLoaded
*==============================================================================

Intermittently,(still working on finding the pattern), I get the error cannot load 32-bit DLL c:\windows\system32\rpcrt4.dll on the line UUIDCreate(@lcUUID)

As I said, I am still trying to find the scenario where it occurs every time but as yet have been unable to do so. I am hoping someone will say, Hey, did you look at this? This has my development completely shut down so it is pretty critical. I have read everything on the internet that I can find with no luck.

Thanks,
John

 
I don't know the rpcrt4.dll, is that part of your own development? If you only use it for UUID creation I would just stop using this and go for one of several onboard mechanisms of Windows for GUID generation.

If there's more to it than GUID creation I think the core problem is to load the DLL, right? I'd first use Dependency Walker to find out dependencies and then perhaps use a backup of it, as such an error hints on it being damaged somehow. Maybe only a dependent DLL is missing, maybe something was put into quarantine. It's unusual to have a 32bit system and if this is a 64bit system a 32bit DLL would go into C:\Windows\SysWow64. So maybe it's also that. Most of the time a self-made DLL would perhaps not have any dependencies and then it doesn't matter where you locate it, it could of course also be put into the application directory so even if itself also depends on system DLLs that will find them anyway, but I'd sort that out correctly anyway and install it into SywsWow64. If you have a 32bit installer and tell that the target is system32, you'll find it in SysWow64 anyway, as there is a redirection of System32 access through any 32bit application.

To replace GUID creation this might be a start: faq184-230
The easier way in faq184-2486 would likely slow down the creation of records pretty much, I posted a way in MSDN Foxpro Forums:
Code:
* Test output
_Screen.FontName = "Courier"
? FullGUID()
? uniqueidentifier()
? BinaryGUID()

Procedure BinaryGUID()
   * create a binary Char(16) GUID
   Local lcBuffer
   
   lcBuffer = Space(16)+Chr(0)
   =CoCreateGuid(@lcBuffer)
   Return Left(lcBuffer,16)
Endproc

Procedure FullGUID()
   * create GUID in the format for a Char(38) field
   * {4DDF8EA2-30F5-4016-9554-5B9C16FA2D91}
   Local lcBuffer
   
   lcBuffer = Space(128)
   =StringFromGUID2( BinaryGUID(), @lcBuffer, Len(lcBuffer) )
   
   Return Left(Chrtran(lcBuffer,Chr(0),''),38)
Endproc

Procedure UniqueIdentifier()
   * create GUID in the format, which SQL Server displays and returns, needs Char(36)
   * 4DDF8EA2-30F5-4016-9554-5B9C16FA2D91
   Return Chrtran(FullGUID(),'{}','')
Endproc

Procedure CoCreateGuid(tcBuffer)
   Declare Integer CoCreateGuid In ole32;
      STRING @ pguid

   Return CoCreateGuid(@tcBuffer)
Endproc

Procedure StringFromGUID2(tcGUID, tcBuffer, tnMaxlen)
   Declare Integer StringFromGUID2 In ole32;
      STRING    rguid,;
      STRING  @ lpsz,;
      INTEGER   cchMax   

   Return StringFromGUID2(tcGUID, @tcBuffer, tnMaxlen)
Endproc

You can also learn something from this: Don't check whether a DLL is loaded, simply do the DECLARE you need. Here it's still only done once, making use of the way VFP prioritizes what to execute. If there is a declared DLL function that has priority over a VFP user-defined function or procedure. If not, the procedure is called. Therefore creating some procedures with exactly the same name as DLL functions you can do the declare there and then do what looks like a recursive call. It'll not be, because as the API DLL function is declared, this will not cause it to recurse into the VFP procedure but call the DLL function.

That's specifically nice for use in stored procs, where you can't rely on initialization and also don't want to fail on functions unloaded via CLEAR DLLs. If you want this to be upward compatible to later easily switch to an MSSQL backend you best use UniqueIdentifier(), which gives it the same string format that is automatically converted to the uniqueidentifier in T-SQL code.

If you need your rpcrt4.dll working, better cheeck out an older backup and at least dependency walker:
Bye, Olaf.

Olaf Doschke Software Engineering
 
Good Morning Olaf.

I had downloaded dependencywalker but wasn't sure if it was something people still use today or something old that should no longer be used. The fact you suggest using it tells me I should try using it. I will do that today.

I did not write any of the code that was in my initial post. It was all written by F1/Visual FoxExpress many years ago. I assume rpcrt4.dll is a microsoft dll because it seems to be on every computer. I could be wrong about that.

I see a copy of rpcrt4.dll in both the system32 and the syswow64 folders on my development computer. It is on my list to check all my customer sites this morning. I have looked at one local server and one cloud server and both of them have this dll on the system32 and syswow64 folders. Is it the same on your computer?

I appreciate all the info you provided but it will take me some hours to fully comprehend. In the meantime I would like to ask:

Sometimes my app declares the dll correctly and returns a guid and all works perfectly. I assume it is using the dll in the syswow64 folder when working correctly because that is where 32-bit dlls are located on a 64 bit machine. I do not know how I would prove which one FoxPro used. Other times my app tries to declare the one in the system32 folder and that is when I get the error. What tells FoxPro which dll to use when there is one in both the system32 folder and the syswow64 folder?

Thanks,
John
 
OK, learned something myself, this way. Googling rpcrt4 I find this DLL is about remote procedure call and indeed an onboard system DLL.

You ask because the code you show doesn't use any path, right?

I think it does, but I don't know, whether DECLARE will look into system32 when no path is given. But if so, the redirection mechanism of Windows UAC will redirect VFP runtime access of system32 to Syswow54. Try this:
Code:
StrToFile("VFP test from"+TtoC(DateTime()),"C:\Windows\system32\test.txt")

You'll find this new test.txt file in Syswow64. Un less you turned UAC off.

Usually, your file system access is redirected when working with paths. Access to several folders will be redirected in different ways. Declaring the function with DECLARE INTEGER UuidCreate IN C:\Winodws\System32\rpcrt4.dll String @ UUID works here for me on a 64bit system also can be used because UAC redirects that. But on systems with UAC turned off you have to use the correct path yourself - or none.


So the big bummer is a system with UAC turned off. Not sure whether it is a good strategy to use DECLARE pathless or even not specify the DLL and use the IN WIN32API clause at least for declaring functions to determine system bitness, version and system paths. IN WIN32API only covers some system DLLs and surely not rpcrt4.dll, but it could be sufficient to make some declarations of functions to determine the system folder. Unless it doesn't redirect to Syswow64 itself, even if UAC is off. And I don't know that and - sorry - won't turn UAC off to see.



At this point, for the record, UAC should be what to look into in systems failing. Is UAC turned off on such systems? VFP should look into the correct system folder even when its legacy runtime code wasn't changed in VFP9 and is still looking into system32 by default. At least when UAC is turned on, that's always the key for correct redirection, here.

And UAC should be on anyway. I'm not sure whether actually, VFP runtime would just use C:\Windows\System32 hardcoded, as the Windows folder isn't necessarily on C:\ drive. It may use %Windir%+"\system32" and then depend on UAC for correct redirection or it may even use an API call to determine the system folder. Then using no folder is a good strategy.

I guess you can simply tell customers your application needs UAC turned on and call it a day. And that's a strong recommendation anyway. Not only on desktop clients also and especially for servers. Admins saying this is under their full control and UAC just slows the system should perhaps look into a bit of MS recommendations and developer guidelines. But you might not convince them and eventually have to live with DevOps decisions.


So - what's failsafe if you want to be independent on UAC folder redirection on both pure 32bit systems, 64bit systems and also in future systems? We'll need to determine the correct system folder and then declare in that path explicitly. It's not that easy, as it seems, though.

Microsoft Docs said:
Note As of Windows Vista, this function is merely a wrapper for SHGetKnownFolderPath. The CSIDL value is translated to its associated KNOWNFOLDERID and then SHGetKnownFolderPath is called. New applications should use the known folder system rather than the older CSIDL system, which is supported only for backward compatibility.

That means you could use that even if you need to support XP, the redirection to SHGetKnownFolderPath will work independently on UAC until SHGetFolderPathA is not part of the API anymore, which is the bummer when thinking of future compatibility. If you use OS() to determine whether to use SHGetFolderPathA or SHGetKnownFolderPath to not depend on that, you still have the problem both functions declarations, of course, are in the one or other system folder. And if you declare them without folder you depend on UAC (I assume). This problem seems to always redirect to itself.

There is a special GetSystemWow64Directory API function, but that again is in System32 and SysWow64 and you can only declare the correct one if UAC is on.

So after all that, a test for DIRECTORY(ADDBS(GETENV("windir"))+"SysWow64") should always work to see whether that exists, it just depends on that folder name and the relative path in the Windows folder to never change, but that's the least dependency I can think of. Dependency on system environment variables is a very low dependency, these environment variables will exist on any system, even back to NT.

After knowing whether to uze GETENV("windir"))+"SysWow64" or GETENV("windir"))+"System32" you could double check and try to declare SHGetKnownFolderPath and/or SHGetFolderPathA. Just remember the major system path still is System32 and you have to ask the right question to these special folder functions to see what system folder your exe should use.


Overall, I am suzrely overcautious here, Doug Henning has the following suggestion in
Code:
if Is64Bit()
lcSystem = GetSystemWow64Dir()
else
lcSystem = GetSystemDir()
endif

Note: That code needs some declarations, again, made with [tt]in Win32API[/tt].

A bit self-referential and to be pedantic also about this once more, this relies on the WIN32API clause to find the right path to enable you to determine it with the declared functions. So, in the end, you could also make that a configuration option to keep full control about this for your customers, then you even don't depend on the name staying SysWow64. Ideally, there would be an OLE class you can use without knowing system32, syswow64 or any future name, but even if there would be, you then depend on that to exist on all OS versions you want to support, including future ones.

I think the solution for other programming languages is early binding within compiling/linking instead of late DLL binding as is the only way for VFP.

And lastly, to be fair I implicitly called it a day to rely on UAC being on, i.e. I never had that problem because my customers were not turning it off. See my GUID determinng code above, I neither use paths nor IN WIN32API, just DLL names.

If you connect to systems with UAC turned off you're in the lucky situation you can test with such systems. As said I won't turn that off just to see. But finally the good news might be this isn't related to changes by ransomware.

Bye, Olaf.

Olaf Doschke Software Engineering
 
WOW! That is a lot of pondering.

I did the test from VFP command window and Yes, test.txt shows up in SysWow64. UAC is set to "Never" which I believe means it is Off but perhaps it is not Off but just set to have minimal effect or something. I would have to research that.

I am going to add the code you provided to my app so that is occurring from the same "environment" as the dll declare is occurring. I will report back on that.

BTW, I did not get hit by ransomware. I was worried because I had connected to a server that DID get hit by ransomware. I should not have been worried but I worry about everything. I called the doctor (Norton) for a full exam on my computer and the doctor ran a deep disk scan and it changed some things in my registry and in some form (I could not tell you how if I had to) UAC was effected or at least involved. So the doctor forgot rule number one, First, Do No Harm. I have had to do two system restores since Norton did whatever they did.

There is a lot to think about in what you have written and I really appreciate that. I will spend the time comprehending and applying what I can today and report back.

Thanks,
John
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top