Revolution and DLLs (was Re: DLL registering) [Long]

Ken Ray kray at sonsothunder.com
Tue Nov 15 11:25:49 EST 2005


On 11/15/05 3:03 AM, "Eric Chatonet" <eric.chatonet at sosmartsoftware.com>
wrote:

> Thanks a lot.
> 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 REV
=======================

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 pScript
  return _doVBS(pScript)
end stsDoVBS

function _doVBS 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 _doVBS
---

The key things to point out are:

1) The "stsRegisterDLL" command needs the path to the DLL class name passed
(in this case "FileMgr").

2) That in the VBS created in the GetFileType handler, you'll notice that
has "CreateObject" on 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 _doVBS 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
Web site: http://www.sonsothunder.com/
Email: kray at sonsothunder.com




More information about the use-livecode mailing list