⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch27.htm

📁 好书《C++ Builder高级编程技术》
💻 HTM
📖 第 1 页 / 共 5 页
字号:
}



void 
__fastcall TForm1::SquareBtnClick(TObject *Sender)

{

  try

  {

    ShowMessage(V.OleFunction("Square", Edit1->Text));

  }

  catch(Exception &E)

  {

    ShowMessage(E.Message);

  }

}



void __fastcall 
TForm1::FormDestroy(TObject *Sender)

{

  CoUninitialize();  

}

</FONT></PRE>
<P>The code declares the CLSID created by the BCB Automation expert in the preceding
section of this chapter:</P>
<PRE><FONT COLOR="#0066FF">DEFINE_GUID(ClassID, 
0xE2674A60, 0x2DF2, 0x11D0, 0x92,0xC5,

            0x00,0x00,0x00,0x00,0x00,0x00);

</FONT></PRE>
<P>This is the CLSID associated with the server half of this DCOM project, and you
need to include it here in the client program. Additional information 
about CLSIDs
and the registration process will be presented later in this chapter.

<DL>
	<DT></DT>
</DL>



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>You need to include the standard
	Windows API <TT>initguids.h</TT> 
file in projects that use GUIDs. 
<HR>


</BLOCKQUOTE>

<P>The actual call to automate the object is nearly identical to the call you would
make if you wanted to automate an object on your local machine. The only difference
is that you call 
<TT>CreateRemoteOleObject</TT> rather than <TT>CreateOleObject</TT>.
Here is the way to call the object locally:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::GetLocalObjectBtnClick(TObject *Sender)

{

  V = 
CreateOleObject(&quot;EasyDCOM.SimpleDCOM&quot;);

  ShowMessage(V.OleFunction(&quot;GetName&quot;));

}

</FONT></PRE>
<P>This code assumes that the variable <TT>V</TT>, of type <TT>Variant</TT> is a
field of <TT>TForm1</TT>. The code calls a 
built-in function of the VCL called <TT>CreateOleObject</TT>.
This function takes the <TT>ProgID</TT> of an object, looks up its CLSID in the Registry,
finds the place on the hard drive where the program that owns the object is located,
launches the 
program, and retrieves the object in a <TT>Variant</TT>.</P>
<P>The code then uses the <TT>OleFunction</TT> method of the <TT>Variant</TT> object
to call one of the methods of the OLE server. <TT>OleFunction</TT> takes one or more
parameters, 
specifying the name of the function you want to call and any parameters
you want to pass to it. BCB does not have support for named parameters.</P>
<P>Later in the program, you can call the <TT>Square</TT> method of the Automation
server:</P>

<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::SquareBtnClick(TObject *Sender)

{

  try

  {

    ShowMessage(V.OleFunction(&quot;Square&quot;, Edit1-&gt;Text));

  }

  catch(Exception &amp;E)

  {

    ShowMessage(E.Message);

  }

}


</FONT></PRE>
<P>Again, I use the <TT>OleFunction</TT> method to make the call to the <TT>Square</TT>
method via <TT>OleAutomation</TT>. As you can see, I wrap the function call in a
<TT>try..except</TT> block because chances are good that the user 
might click the
Square button before initializing the object with a call to <TT>CreateOleObject</TT>
or <TT>CreateRemoteOleObject</TT>. Notice that this time <TT>OleFunction</TT> takes
two parameters, one stating the name of the OLE server method to 
be called and the
second specifying a parameter to be passed to that method.</P>
<P>Here is the method of <TT>GetDCOM</TT> that I use to summon the remote server:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::GetRemoteObjectBtnClick(TObject 
*Sender)

{

  Screen-&gt;Cursor = crHourGlass;

  if (CreateRemoteObject(ClassID, &quot;143.186.149.228&quot;, V))

  {

    ShowMessage(V.OleFunction(&quot;GetName&quot;));

  }

  else

  {

    ShowMessage(&quot;Failed&quot;);

  }

  
Screen-&gt;Cursor = crDefault;

}

</FONT></PRE>
<P>This function is no different in substance from the <TT>GetDCOM</TT> routine that
retrieves the local object. The only difference is that I call <TT>CreateRemoteObject</TT>
rather than 
<TT>CreateOleObject</TT>. Remember that you can call <TT>CreateOleObject</TT>
to retrieve remote objects if you are on an NT machine or if you have set the Windows
95 server machine into User Access mode via the Network applet in the Control Panel.


<DL>
	<DT></DT>
</DL>



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Let me just reiterate that you need
	to pass in the IP address, or server name, of the machine on which your server is
	located. Here I type in the IP 
address of my NT server: <TT>143.186.149.228</TT>.
	You replace this number with the name or number of your server. If you're confused
	by the topic of IP addresses, you might be able to glean some information from the
	discussion of TCP/IP in Chapter 
8, &quot;Database Basics and Database Tools.&quot;
	
<HR>


</BLOCKQUOTE>

<P><TT>CreateRemoteObject</TT> is a custom function I have written; it looks like
this:</P>
<PRE><FONT COLOR="#0066FF">BOOL CreateRemoteObject(GUID ClassID, char *Server, 
Variant &amp;V)

{

  Ole2::IClassFactory *ClassFactory;

  Ole2::IUnknown *Unknown1;

  COSERVERINFO Info;

  OLECHAR Dest[MAX_PATH];

  int i = MultiByteToWideChar(CP_ACP, 0, Server, -1, Dest, MAX_PATH);

  if (i &lt;= 0)

    return FALSE;

  
ClassFactory = NULL;

  Info.dwReserved1 = 0;

  Info.pAuthInfo = NULL;

  Info.dwReserved2 = 0;

  Info.pwszName = Dest;

  HRESULT hr = CoGetClassObject(ClassID, CLSCTX_REMOTE_SERVER, &amp;Info,

                 Ole2::IID_IClassFactory, (void 
**)&amp;ClassFactory);

  OleCheck(hr);



  if (ClassFactory != NULL)

  {

    hr = ClassFactory-&gt;CreateInstance(NULL,

      Ole2::IID_IUnknown, (void **)&amp;Unknown1);

    OleCheck(hr);

    V = VarFromInterface(Unknown1);

    
ClassFactory-&gt;Release();

    if (VarType(V) != varNull)

      return True;

    else

      return False;

  }

  return FALSE;

}

</FONT></PRE>
<P>This routine is declared in the <TT>CodeBox</TT> unit found in the <TT>Utils</TT>
subdirectory on 
the CD that accompanies this book. As I stated earlier, you need
to add the <TT>CodeBox</TT> unit to your project; otherwise, it will not compile.
Alternatively, you can simply copy this routine into your project. However, keeping
it in a separate 
unit makes sense because you might want to call it from multiple
applications.</P>
<P>Whether you understand this routine is not really important. You can just plug
it into your applications the same way you do <TT>CreateOleObject</TT>. However,
I 
will talk about it briefly for those who are interested.</P>
<P>The <TT>CreateRemoteObject</TT> routine takes three parameters. The first contains
the ID of the object you want to obtain, and the second contains the name of the
server where the object 
resides. The last parameter contains a variant that will
hold the instance of <TT>IDispatch</TT> retrieved from the system. (Sometimes you
might have to use the IP address itself rather than the name of the server.) <TT>CreateRemoteObject</TT>
returns 
a variant that &quot;contains&quot; a copy of the object that you want to
call. You can use this variant to call all the methods in the <TT>automated</TT>
section of your object.</P>
<P>Variants are special BCB types that can contain a wide variety of 
data types,
including OLE objects. I discussed variants at some length in Chapter 3, &quot;C++Builder
and the VCL.&quot;</P>
<P>When you call the methods of an OLE object off a variant, no runtime checking
for the calls occurs. BCB just assumes you 
know what you're doing, and if the call
fails, you won't know until runtime. This problem is addressed in Delphi 3, and so
I assume it will be addressed in future releases of C++Builder.</P>
<P>The key call in <TT>CreateRemoteOleObject</TT> is to 
<TT>CoGetClassObject</TT>:</P>
<PRE><FONT COLOR="#0066FF">HRESULT hr = CoGetClassObject(ClassID, CLSCTX_REMOTE_SERVER, &amp;Info,

                              Ole2::IID_IClassFactory, (void **)&amp;ClassFactory);

OleCheck(hr);

</FONT></PRE>

<P>This routine has long been a part of COM, but it has been altered slightly to
support DCOM. Here is how the routine is currently declared in <TT>ObjBase.h</TT>:</P>
<PRE><FONT COLOR="#0066FF">WINOLEAPI  CoGetClassObject(

  REFCLSID rclsid,    // 
The ID of the object you want

  DWORD dwClsContext, // In process, local or remote server?

  LPVOID pvReserved,  // Previously reserved, now used for CoServerInfo

  REFIID riid,        // Usually IID_IClassFactory

  LPVOID FAR* ppv);   // Where 
the class factory is returned

</FONT></PRE>
<P>The function returns an <TT>HRESULT</TT> variable containing information on the
outcome of the call. If <TT>HRESULT</TT> is set to zero, then the call succeeded.
Most other values represent an error in 
the form of a number. You can retrieve a
human-readable string by passing that number to a VCL function called <TT>OleCheck</TT>.</P>
<P>The third parameter to <TT>CoGetClassObject</TT>, previously reserved, is now
the place where you pass in the name 
of the server you want to access. The server
is usually designated with either a string or a literal IP address, such as <TT>143.186.149.111</TT>.
You would pass in the IP address in the form of a string. That is, don't try to pass
a number; just put 
the IP address in quotation marks and pass it in as a string.
Here is the new declaration for <TT>CoGetClassObject</TT>, as found in the MSDN:</P>
<PRE><FONT COLOR="#0066FF">STDAPI CoGetClassObject(

  REFCLSID rclsid,            //CLSID associated 
with the class object

  DWORD dwClsContext,         //Context for running executable code

  COSERVERINFO * pServerInfo, // Machine on which object is to be instantiated

  REFIID riid,                //Reference to the identifier of the interface

  
LPVOID * ppv                //Indirect pointer to the interface

 );

</FONT></PRE>
<P>In particular, here is the record you pass in for the third parameter:</P>
<PRE><FONT COLOR="#0066FF">typedef struct _COSERVERINFO {

  DWORD     dwSize;   // must 
be set to sizeof(COSERVERINFO)

  OLECHAR*  pszName;  // machine name

} COSERVERINFO;

</FONT></PRE>
<P>The first field of this record is just a version check field that should contain
the size of the <TT>TCoServerInfo</TT> record. The second 
parameter contains a Unicode
string that has the name of the server or its IP address embedded in it. Use the
<TT>MultiByteToWideChar</TT> Windows API function to convert a standard BCB string
into a Unicode string:</P>
<PRE><FONT 
COLOR="#0066FF">OLECHAR Dest[MAX_PATH];

int i = MultiByteToWideChar(CP_ACP, 0, Server, -1, Dest, MAX_PATH);

if (i &lt;= 0)

  return FALSE;

</FONT></PRE>
<P>The call to <TT>CoGetClassObject</TT> retrieves a <TT>ClassFactory</TT>. After
you have the 
<TT>ClassFactory</TT> back from the server, you can use it to retrieve
an instance of the object you want to call. What you retrieve back, of course, is
an instance of <TT>IDispatch</TT>. You can convert this instance into a variant by
calling the BCB 
routine <TT>VarFromInterface</TT>, which is found in the <TT>OleAuto</TT>
unit that ships with BCB.</P>
<P>If you want, you can simplify this call by using <TT>CoCreateInstanceEx</TT>.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -