Solved

COM - Delphi equivalent to VB's CreateObject

Posted on 1999-01-17
14
3,665 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
  • 8
  • 6
14 Comments
 
LVL 12

Accepted Solution

by:
rwilson032697 earned 100 total points
Comment Utility
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
Comment Utility
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
Comment Utility
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
 
LVL 1

Author Comment

by:wamoz
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 1

Author Comment

by:wamoz
Comment Utility
...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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

744 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

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now