Solved

COM - Delphi equivalent to VB's CreateObject

Posted on 1999-01-17
14
3,800 Views
Last Modified: 2008-03-10
In VB you can create ActiveX objects like this:

Dim MyObj
Set MyObj = CreateObject("VBScript")

and then use the object as you would any other, with dot notation access to methods and properties.

How does one go about this in Delphi?

The purpose of the exercise to add VBScript to a Delphi application as a macro language.

ComObj.pas exports CreateComObject(aGUID:TGUID):IUnknown;

but I don't really know what I'm supposed to do with a pointer to an IUnknown interface.
0
Comment
Question by:wamoz
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 8
  • 6
14 Comments
 
LVL 12

Accepted Solution

by:
rwilson032697 earned 100 total points
ID: 1362282
The IUnknown return value is so that you can assign it to any interface object:

eg:  var MyApp : IMyApp;

MyApp := CreateCOMObject(IMyApp_CLASS) as IMyApp;

where IMyApp_CLASS is its GUID.

Then you can call methods on the interface:

if MyApp.Dosomething(Arg) then
.

You can also use CreateOleObject which will return a dispatch interface...

Cheers,

Raymond.

0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1362283
Here is an instance of using CreateOleObject...

procedure TForm1.InsertBtnClick(Sender: TObject);
var
  S, Lang: string;
  MSWord: Variant;
  L: Integer;
begin
  try
    MsWord := CreateOleObject('Word.Basic');
  except
    ShowMessage('Could not start Microsoft Word.');
    Exit;
  end;
  try
    { Return Application Info. This call is the same for English and
     French Microsoft Word. }
    Lang := MsWord.AppInfo(Integer(16));
  except
    try
      { for German Microsoft Word the procedure name is translated }
      Lang := MsWord.AnwInfo(Integer(16));
    except
      { if this procedure does not exist there is a different translation of
       Microsoft Word }
       ShowMessage('Microsoft Word version is not German, French or English.');
       Exit;
    end;
  end;
  with Query1 do
  begin
    Form1.Caption := Lang;
    Close;
    Params[0].Text := Edit1.Text;
    Open;
    try
      First;
      L := 0;
      while not EOF do
      begin
        S := S + Query1Company.AsString + ListSeparator +
          Query1OrderNo.AsString + ListSeparator + Query1SaleDate.AsString + #13;
        Inc(L);
        Next;
      end;
      if (Lang = 'English (US)') or (Lang = 'English (United States)') or
         (Lang = 'English (UK)') or (Lang = 'German (Standard)') or
         (Lang = 'French (Standard') then
      begin
        MsWord.AppShow;
        MSWord.FileNew;
        MSWord.Insert(S);
        MSWord.LineUp(L, 1);
        MSWord.TextToTable(ConvertFrom := 2, NumColumns := 3);
      end;
      if Lang = 'Français' then
      begin
        MsWord.FenAppAfficher;
        MsWord.FichierNouveau;
        MSWord.Insertion(S);
        MSWord.LigneVersHaut(L, 1);
        MSWord.TexteEnTableau(ConvertirDe := 2, NbColonnesTableau := 3);
      end;
      if (Lang = 'German (De)') or (Lang = 'Deutsch') then
      begin
        MsWord.AnwAnzeigen;
        MSWord.DateiNeu;
        MSWord.Einfügen(S);
        MSWord.ZeileOben(L, 1);
        MSWord.TextInTabelle(UmWandelnVon := 2, AnzSpalten := 3);
      end;
    finally
      Close;
    end;
  end;
end;


Cheers,

Raymond.
0
 
LVL 1

Author Comment

by:wamoz
ID: 1362284
Thanks, Ray.

Particularly for your additional comments, in which you answer the question I would have asked had I known the answer to it. <g>
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 1

Author Comment

by:wamoz
ID: 1362285
Ok, your example works a treat. But when I try this on VBScript an error is reported EOleSysError: 'No such interface supported.' which I imagine means that it's not an OLE object.

That implies that I have to use CreateComObject. Presumably I therefore also have to use interfaces. How does one go about obtaining the interface information from an object? I know about IUnknown.QueryInterface(aGUID:TGUID) but I don't know where to obtain the GUID for IPERSIST never mind the rest of the interfaces. Is it possible to extract this information from wshom.ocx (Window Scripting Host Object Model) using something like the Type Library editor?

Does it also mean that there isn't a dispatch interface?
0
 
LVL 1

Author Comment

by:wamoz
ID: 1362286
When I try to import wshom.ocx the TypeLib editor spits the dummy because a Dispatch interface is not present. I have a horrible feeling this is going to be tougher than I expected.

I tried using VB4 CreateObject("VBScript") and it reports that "Class doesn't support OLE Automation" so I'm a bit flummoxed.
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1362287
Wamoz: I am surprised that the Typelib editor spat the dummy.

Did you try importing a type library? (Project|Import type library). This SHOULD create a nice unit for you with the interfaces all nicely set up and ready to go...

Raymond.
0
 
LVL 1

Author Comment

by:wamoz
ID: 1362288
It should but it doesn't. It reports the absence of a Dispatch interface and abends.

I have since hunted down the necessary declarations and guids in MSDN, and while I can now get hold of IActiveScript and successfully invoke its InitNew method, calling its ParseScriptText method returns a "Catastrophic OLE Error".

Following is the source. If you could try it out, I'd be very grateful if you can tell me what's wrong with it. If you want to check my function declarations against the original C header it's called activscp.h and it's in MSDN. I lifted the definition for TExcepInfo from ActiveX.pas so I doubt that it contains the problem.

One item that worries me is the return value VARIANT. I sincerely doubt that MFC variants are the same internal format as Delphi Variants, and if you can point me in the right direction there I'd also be grateful.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  OleCtrls, ComObj, ComServ;

type
  PExcepInfo = ^TExcepInfo;

  TFNDeferredFillIn = function(ExInfo: PExcepInfo): HResult stdcall;

  TExcepInfo = record
    wCode: Word;
    wReserved: Word;
    bstrSource: WideString;
    bstrDescription: WideString;
    bstrHelpFile: WideString;
    dwHelpContext: Longint;
    pvReserved: Pointer;
    pfnDeferredFillIn: TFNDeferredFillIn;
    scode: HResult;
  end;

  IACTIVESCRIPTPARSE = interface(IUNKNOWN)
    function InitNew:HRESULT; safecall;
    function AddScriptlet(
      pstrDefaultName:PChar;
      pstrCode:PChar;
      pstrItemsName:PChar;
      pstrEventName:PChar;
      pstrDelimiter:PChar;
      dwSourceContextCookie:DWORD;
      ulStartingLineNumber:ULONG;
      dwFlags:DWORD;
      pbstrName:PWideString;
      var pexcepinfo:TEXCEPINFO):HRESULT; safecall;
    function ParseScriptText(
      pstrCode:PChar;
      pstrItemName:PChar;
      punkContext:IUNKNOWN;
      pstrDelimiter:PChar;
      dwSourceContextCookie:DWORD;
      ulStartingLineNumber:ULONG;
      dwFlags:DWORD;
      pvarResult:PVariant;
      pexcepinfo:PExcepInfo):HRESULT; safecall;
    end;
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    iuVBS:IUNKNOWN;
    iuActiveScriptParse:IACTIVESCRIPTPARSE;
    v:variant;
    anExcepInfo:TExcepInfo;
    aPExcepInfo:PExcepInfo;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

const
  SCRIPTTEXT_ISVISIBLE = $000000002;
  SCRIPTTEXT_ISEXPRESSION = $000000020;
  SCRIPTTEXT_ISPERSISTENT = $000000040;
  guidVBS:TGUID = '{B54F3741-5B07-11cf-A4B0-00AA004A55E8}';
  guidActiveScriptParse:TGUID = '{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}';
  src:PChar =
    'sub Main()'#13#10+
    'x=5'#13#10+
    'end sub';

procedure TForm1.FormCreate(Sender: TObject);
begin
aPExcepInfo := @anExcepInfo;
iuVBS := CreateComObject(guidVBS);
iuVBS.QueryInterface(guidActiveScriptParse,iuActiveScriptParse);
iuActiveScriptParse.InitNew;
iuActiveScriptParse.ParseScriptText(
  src,
  nil,
  nil,
  nil,
  0,
  0,
  0,
  nil,
  aPExcepInfo);
showmessage(v)
end;

end.


0
 
LVL 1

Author Comment

by:wamoz
ID: 1362289
...And I've tried including ActiveX.pas and updating the method interface declarations as follows (but it didn't help):

type
  IACTIVESCRIPTPARSE = interface(IUNKNOWN)
    function InitNew:HRESULT; safecall;
    function AddScriptlet(
      pstrDefaultName:POleStr;
      pstrCode:POleStr;
      pstrItemsName:POleStr;
      pstrEventName:POleStr;
      pstrDelimiter:POleStr;
      dwSourceContextCookie:DWORD;
      ulStartingLineNumber:ULONG;
      dwFlags:DWORD;
      var pbstrName:TBSTR;
      var pexcepinfo:TEXCEPINFO):HRESULT; safecall;
    function ParseScriptText(
      pstrCode:POleStr;
      pstrItemName:POleStr;
      punkContext:IUNKNOWN;
      pstrDelimiter:POleStr;
      dwSourceContextCookie:DWORD;
      ulStartingLineNumber:ULONG;
      dwFlags:DWORD;
      pvarResult:PVariantArg;
      var pexcepinfo:TExcepInfo):HRESULT; safecall;
    end;
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    iuVBS:IUNKNOWN;
    iuActiveScriptParse:IACTIVESCRIPTPARSE;
    v:variant;
    anExcepInfo:TExcepInfo;
    aPExcepInfo:PExcepInfo;
  public
    { Public declarations }
  end;

0
 
LVL 1

Author Comment

by:wamoz
ID: 1362290
It occurs to me that maybe Borland has already addressed this. If you have Delphi 4 C/S then perhaps you have the relevant declarations.
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1362291
No, I do not have D4 C/S (mores the pity!)

Your orignal code example compiles but gives the same error you report for me. The second version doesn't compile...

I am not sure if calling it like this is asking for trouble:

uActiveScriptParse.ParseScriptText(
         src,
         nil,
         nil,
         nil,
         0,
         0,
         0,
         nil,
         aPExcepInfo);

Perhaps there are one or more arguments you need to fill in. In particular the nil for the PVariantArg seems suspicious.

I don't otherwise see anything obvious here that you are doing wrong. The fact you are getting the Interface from the COM object is very encouraging.

Is PVariantArg the correct declaration? You could try POleVariant.

Cheers,

Raymond.

0
 
LVL 1

Author Comment

by:wamoz
ID: 1362292
1) I don't think so but until it runs I'm in no position to argue. The documentation I have here says "pvarResult [out] Address of a buffer that receives the results of scriptlet processing, or NULL if the caller expects no result (that is, the SCRIPTTEXT_ISEXPRESSION flag is not set)."

2) I didn't notice POleVariant. I think you are right about that. The type declared in the C++ code is VARIANT, not VARIANTARG.
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1362293
It would be interesting to know what is throwing the error, ie: is it the control itself, or part of the COM machinery in behind objecting to the way it is being called...

Raymond.
0
 
LVL 1

Author Comment

by:wamoz
ID: 1362294
Ray the error is actually in the COM object.

I found in a newsgroup a URL for a Delphi Informant download from Feb98 in which the author has succeeded in doing exactly what I am trying to do - use a script language, VBScript or JScript or any other conforming to the MS Script Engine spec.

There wasn't anything wrong with my code per se but there are a bunch of ancillary objects and interfaces that must be prepped, in particular a script site must be prepared. This is a special object to own the script. Where I had InitNew I should have had all this (note his code allows interactive selection of VBS or JS):

  if tmVBScript.Checked then
    CatID := CatID_VBScript
  else
    CatID := CatID_JScript;

  ActiveScript := IActiveScript(CreateComObject(CatID));

  { Get Interface pointer for IActiveScriptParse Interface }
  if (ActiveScript.QueryInterface(IID_IActiveScriptParse,
      ActiveScriptParse) <> S_OK) then
    Exit;

  { COM Host Object Implementation  }
  ActiveScriptSite := IActiveScriptSite(TActiveScriptSite.Create);

  { Register Site Object with the ActiveScript Engine }
  if ActiveScript.SetScriptSite(ActiveScriptSite) <> S_OK then
    Exit;

  { initialize the engine }
  if (ActiveScriptParse.InitNew <> S_OK) then
    Exit;

and after that my code was pretty much identical.

I must say that neither the Microsoft doco on the topics "IActiveScriptParse" nor on "ActiveX Scripting" mentions the need to prep a ScriptSite object. I'm going to look in the "IActiveScript" topic and if it's described properly there I shall scream.

Eventually (the next month or two, I hope) I shall rewrite the example code as a VCL. Are you interested in a copy?
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1362295
I am glad to see you have triumphed despite Microsofts best efforts!

If you are going to wrap this example up as a VCL I would be interested in a copy - as I am sure would a lot of other people. Perhaps you could post it on your web site...

Cheers,

Raymond.
0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
In this video, viewers are given an introduction to using the Windows 10 Snipping Tool, how to quickly locate it when it's needed and also how make it always available with a single click of a mouse button, by pinning it to the Desktop Task Bar. Int…
There's a multitude of different network monitoring solutions out there, and you're probably wondering what makes NetCrunch so special. It's completely agentless, but does let you create an agent, if you desire. It offers powerful scalability …

691 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question