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