Using Non-Rev DLLs With Revolution
Windows Only (Windows Only)

> But I wonder: what do you want to mean by 'The DLL must be compiled
> specifically for RunRev'?

Let me step in here - there's actually two things to talk about: the first is what it means to be 'compiled specifically for RunRev', and the other is how to work with "normal" Windows DLLs from Rev.

Here's the basic situation: When you build a DLL under Windows, you need to provide information in the DLL that provides other applications the ability to call on the function(s) in the DLL (its API). DLLs that are used by most third party Windows apps have a common (standard) API.

Revolution, on the other hand, requires a specific API be used that is not the same one used by other third-party DLLs, and is designed so that RunRev can "attach" the DLL to a stack and you can just naturally call the functions inside it (like the revXML DLL). You can download the Externals toolkit from RunRev and I believe there's information in there about what is required.

As to third-party DLLs that use the common API (which I'll call "Non-Rev DLLs"), you can use them with Revolution, it's just that it takes an intermediary 'agent' to take the request from Rev, pass it off to the DLL, and return a result. I have done this with DLLs that I've created in Visual Basic - the intermediary 'agent' in this case was the execution of a VBS script from Rev (more on this in a minute).

To register a Non-Rev DLL, you use the 'regsvr32' command line. The DLL doesn't have to be in the Windows directory; it can be anywhere - you just will be passing in the path to the DLL when you register it (and DON'T MOVE IT after it's been registered! :-)

Here's the handlers I use for registering and unregistering these DLLs (watch line wraps):

on stsRegisterDLL pDLLPath,pClass
  if pClass ="" then put "Main" into pClass
  set the itemDel to "/"
  put last item of pDLLPath into tDLLName
  put char 1 to (length(tDLLName) - 4) of tDLLName into tShortDLLName
  delete last item of pDLLPath
  replace "/" with "\" in pDLLPath

  if not(_stsIsRegistered(tShortDLLName,pClass)) then
    set the hideConsoleWindows to true
    get shell("cd" && q(pDLLPath)  && "& regsvr32 /s" && tDLLName)
    -- Note that calling regsvr32 with the /s param will NOT return
    -- any errors back to the command line, so we'll need to
    -- check AGAIN to make sure it was registered properly
    if not(_stsIsRegistered(tShortDLLName,pClass)) then
      return "Error: Could not register DLL."
    end if
  end if
end stsRegisterDLL

on stsUnregisterDLL pDLLPath,pClass
  if pClass ="" then put "Main" into pClass
  set the itemDel to "/"
  put last item of pDLLPath into tDLLName
  put char 1 to (length(tDLLName) - 4) of tDLLName into tShortDLLName
  delete last item of pDLLPath
  replace "/" with "\" in pDLLPath

  if _stsIsRegistered(tShortDLLName,pClass) then
    set the hideConsoleWindows to true
    get shell("cd" && q(pDLLPath)  && "& regsvr32 /u /s" && tDLLName)
    -- Note that calling regsvr32 with the /s param will NOT return any
    -- errors back to the command line, so we'll need to
    -- check AGAIN to make sure it was unregistered properly
    if _stsIsRegistered(tShortDLLName,pClass) then
      return "Error: Could not unregister DLL."
    end if
  end if
end stsUnregisterDLL

function _stsIsRegistered pShortDLLName,pClass
  -- First check for the key at HKCR\<DLLName>.<Method>
  put queryRegistry("HKEY_CLASSES_ROOT\" & pShortDLLName & "." & pClass & "\CLSID\") into tCLSID
  put true into tRetVal
  if (tCLSID= "bad key") or (tCLSID="") then
    put false into tRetVal
  else
    -- Check CLSID key to make sure
    put queryRegistry("HKEY_CLASSES_ROOT\CLSID\" & tCLSID & "\") into tResult
    if tResult = "bad key" then
      put false into tRetVal
    end if
  end if
  return tRetVal
end _stsIsRegistered

Calling a Non-Rev DLL From Revolution

Now to actually call a Non-Rev DLL from Rev, you need to do six things:

  1. Move the DLL file to where you want it to reside (could be inside the WIndows or Windows/System32 directory, or anywhere you like so long as you don't move it)
  2. Create a class name that you'll use to register and call on the DLL. It can be anything you like, but must be one word (AFAICT).
  3. Register the DLL with the handler I provided above, passing in the path to the DLL and the class name you want to use.
  4. Create a VBS file that attaches to the DLL and executes a function in the DLL, returning the result.
  5. Run the VBS script from Rev, and get your result back.
  6. Delete the VBS script (just to be neat and clean).

Here's an example of a VB DLL that I wrote that will get the type of a file... the VB function name is called "GetFileType" and calls on the OS to return the type of a file (this is a string like "Microsoft Word document"). This uses the Windows FileSystemObject and looks like this inside VB:

Public Function GetFileType(ByVal pFilePath As String) As String
    Dim fso As New Scripting.FileSystemObject
    Set tFile = fso.GetFile(pFilePath)
    GetFileType = tFile.Type
End Function
The name of the DLL I created from VB was called "STSFile.dll", and the class I picked to use was "FileMgr".

So with the DLL created, and the class name selected, I created this script in a button in Rev (I had already filled the global gDLLPath with the path to the STSFile.dll file):

global gDLLPath

on mouseUp
  answer file "Pick a file:"
  if it <> "" then
    put it into tFile
    stsRegisterDLL gDLLPath,"FileMgr"
    answer GetFileType(tFile)
  end if
end mouseUp

function GetFileType pPath
  replace "/" with "\" in pPath
  put "On Error Resume Next" & cr into tVBS
  put tVBS & "Dim obj" & cr into tVBS
  put tVBS & "Set obj=CreateObject(" & q("STSFile.FileMgr") & ")" & \
    cr into tVBS
  put tVBS & "tResult = obj.GetFileType(" & q(pPath) & ")" & cr into tVBS
  put tVBS & "If Err.Number = 0 Then" & cr into tVBS
  put tVBS & "WScript.Echo tResult" & cr into tVBS
  put tVBS & "Else" & cr into tVBS
  put tVBS & "WScript.Echo" && q("Error:") & " & Err.Number" & cr into tVBS
  put tVBS & "End If" into tVBS
  
  return stsDoVBS(tVBS)
end GetFileType

function stsDoVBS pVBScript,pDirectConsole
  if pDirectConsole = "" then
    put "C:\VBS_temp.vbs" into tVBSPath
    put pVBScript into url ("file:" & tVBSPath)
    set the hideConsoleWindows to true
    get shell("cscript.exe //nologo" && tVBSPath)
  else
    set the hideConsoleWindows to true
    get shell("cscript.exe" && pVBScript)
  end if
  put it into tResult
  if there is a file tVBSPath then
    send "delete file" && quote & tVBSPath & quote to me in 1 second
  end if
  if tResult <> "" then return tResult
end stsDoVBS

The key things to point out are:

  1. The "stsRegisterDLL" command needs the path to the DLL and the class name passed (in this case "FileMgr").
  2. In the VBS created in the GetFileType handler, you'll notice that it has "CreateObject" in it - you need to put in the name of the DLL (minus the extension), followed by a dot, and then the class. So if your DLL is called "EricTest.dll" and the class you picked was "Sample", you'd do:
    put tVBS & "Set obj=CreateObject(" & q("EricTest.Sample") & ")" & cr into tVBS
  3. The stsDoVBS function handles the creation of the script, execution, and deletion of the file, so you don't need to manage the cleanup.
  4. Since the VBS file only connects to a DLL and executes a function, there's no way for Windows to know whether the function is bad or good, so virus protection software won't intercept the attempt to call the DLL (so you don't get any "suspicious warning" dialogs like you might get if you called the FileSystemObject directly from a VBS script file).
  5. When you register your DLL, it will go into the registry into HKEY_CLASSES_ROOT, under the extension listings, and will have a key of <dllname>.<class> - so in my example above, there would be a registry key called "STSFile.FlieMgr".

You can call other third-party Non-Rev DLLs the same way - you just need to know if they are registered, and if so, the name of the function inside the DLL you need, and finally, what class has been picked by the developer for the DLL (which you can find in the Registry as well in the same location and format as I mentioned in (5) above).

Anyway, that's it - it's probably better to create a Rev-specific DLL that you can call directly from inside Rev, but if you don't know how to do that, or you need to access other third-party Non-Rev DLLs, you can use the approach I mention above.

Enjoy!

Ken Ray
Sons of Thunder Software

Posted 11/15/05 by Ken Ray to the Use Revolution List