home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland Pascal with Objects 7.0 / OLE.ZIP / SERVER.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-10-27  |  34.0 KB  |  1,003 lines

  1. {***************************************************}
  2. {                                                   }
  3. {   Windows 3.1 OLE Server Demonstration Program    }
  4. {   Server Object Unit                              }
  5. {   Copyright (c) 1992 by Borland International     }
  6. {                                                   }
  7. {***************************************************}
  8.  
  9. { This unit defines the Server and Document objects, which
  10.   represent the Ole Server and Ole Document, respectively.
  11.   The Server interfaces with the Client application at the
  12.   highest level, managing the creation and manipulation of
  13.   Documents.
  14.  
  15.   Interaction between the Client and these objects is carried
  16.   out through a series of callback functions, which are also
  17.   defined here.
  18.  
  19.   NOTE that we only have one document per server. if yours
  20.   was an MDI app, then you would have a list of documents.
  21.  
  22.   Note: To compile the OLE Server demo, set Compile|Primary File to OLESERVR.PAS
  23. }
  24.  
  25. unit Server;
  26.  
  27. interface
  28.  
  29. uses WinTypes, CommDlg, Ole, Objects, OWindows, OleTypes, OleObj;
  30.  
  31. type
  32.  
  33. { The following record types represent the Server and Document
  34.   objects within the OLE library.  They are based on the
  35.   standard structures defined in Ole.pas, and each adds one
  36.   field to provide access back to the TPW object which represents
  37.   it.
  38. }
  39.   POleServerObj = ^TOleServerObj;
  40.  
  41.   PAppServer = ^TAppServer;
  42.   TAppServer = record
  43.     OleServer: TOleServer;
  44.     Owner    : POleServerObj;
  45.   end;
  46.  
  47.   POleDocument  = ^TOleDocument;
  48.  
  49.   PAppServerDoc = ^TAppServerDoc;
  50.   TAppServerDoc = record
  51.     OleServerDoc: TOleServerDoc;
  52.     Owner       : POleDocument;
  53.   end;
  54.  
  55. { TOleServerObj }
  56.  
  57. { This object represents the OLE Server, wrapping useful
  58.   behaviors around the basic TOleServer structure that is
  59.   used within OLE to represent a Server.  This structure
  60.   is represented by the AppServer data field, which is of
  61.   the TAppServer type defined in oleservr.pas, and which
  62.   includes an additional field to point back to Self so
  63.   that our callback functions can reference this object.
  64. }
  65.   TOleServerObj = object(TObject)
  66.     AppServer : TAppServer;
  67.     ServerHdl : LHServer;       { Registration handle returned
  68.                                   by server library}
  69.     Document  : POleDocument;  
  70.     IsReleased: Boolean;        { True if Release method has been called}
  71.  
  72.     constructor Init(App: PApplication; Embedded: Boolean);
  73.     constructor InitFromFile(App: PApplication; Path: PChar);
  74.  
  75.     function Initialize(App: PApplication): Boolean;
  76.  
  77.     function RegisterWithDatabase: Boolean; virtual;
  78.     function WantsToRegister: Boolean; virtual;
  79.   end;
  80.  
  81.  
  82. { TOleDocument }
  83.  
  84. { This object represents the OLE ServerDoc, wrapping useful
  85.   behaviors around the basic TOleServerDoc structure that is
  86.   used within OLE to represent a document.  This structure
  87.   is represented by the AppServerDoc data field, which is of
  88.   the TAppServerDoc type defined in oleservr.pas, and which
  89.   includes an additional field which points back to Self so
  90.   that our callback functions can reference this object.
  91. }
  92.   TOleDocument = object(TObject)
  93.     AppServerDoc: TAppServerDoc;
  94.     ServerDoc   : LHServerDoc;     { Registration handle returned by
  95.                                      server library }
  96.     DocType     : TDocType;
  97.     Name        : PChar;
  98.     OleObject   : POleObjectObj;
  99.     IsDirty     : Boolean;
  100.     IsReleased  : Boolean;  { True if Release method has been called }
  101.  
  102.     constructor Init(Server: POleServerObj; Doc: LHServerDoc;
  103.       Path: PChar; Dirty: Boolean);
  104.  
  105.     procedure Setup(Path: PChar; MaxPathLen: Integer;
  106.       var FNStruct: TOpenFileName); virtual;
  107.     function  LoadFromFile(Path: PChar): Boolean; virtual;
  108.     procedure SaveDoc; virtual;
  109.     procedure SaveAs; virtual;
  110.     procedure Reset(Path: PChar); virtual;         
  111.     procedure SetDocumentName(NewName: PChar;
  112.       ChangeCaption: Boolean); virtual;
  113.     function  PromptForOpenFileName(Path: PChar): Boolean; virtual;
  114.   end;
  115.  
  116. function TOleServerObj_InitVTbl(Inst: THandle): Boolean;
  117. function TOleDocument_InitVTbl(Inst: THandle): Boolean;
  118.  
  119.  
  120. implementation
  121.  
  122. uses Strings, WinProcs, ServrWin, OleApp, ShellAPI;
  123.  
  124. { Global variables }
  125.  
  126. var
  127.   OleServerVTbl   : TOleServerVTbl;
  128.   OleServerDocVTbl: TOleServerDocVTbl;
  129.  
  130.   Filter          : array [0..100] of Char;   { Used in Setup }
  131.   SimpleName      : array [0..13]  of Char;
  132.  
  133. const
  134.   UnnamedDoc: PChar = '(Untitled)';
  135.  
  136.  
  137.  
  138.  
  139. { Server Callback Functions }
  140.  
  141. { The first parameter to each callback is a pointer to the TOleServer
  142.   structure that defines this document.  In each case, we know that it
  143.   will really be a pointer to a TAppServer record, which includes a
  144.   pointer to the Pascal object which owns the TOleServer record.  We
  145.   can therefore use a typecast to access that object, and thus find our
  146.   way back to Self.
  147. }
  148.  
  149. { Handles the Open callback. The user has activated a linked object in an
  150.   OLE client by calling OleActivate.  Similar to CreateFromTemplate in that
  151.   we need to create a document, initialize it with the contents of file 
  152.   'DocName', and save the file name for later use.
  153.  
  154.   WHAT TO DO:
  155.     - Create a TOleDocument of class 'ClassName' (since we only have one
  156.       class we can ignore the class name)
  157.     - Initialize the document with the contents of file 'DocName'
  158.     - Associate handle 'Doc' with the document
  159.     - Store the pointer to the TOleDocument in 'ServerDoc'
  160.     - Save file name 'DocName'
  161.     - Return ole_Ok if successful, ole_Error_Open otherwise
  162. }
  163. function Open(Server: POleServer; Doc: LHServerDoc; DocName: PChar;
  164.               var ServerDoc: POleServerDoc): TOleStatus; export;
  165. var
  166.   SelfPtr: POleServerObj;
  167.   NewDoc : POleDocument;
  168. begin
  169.   SelfPtr := PAppServer(Server)^.Owner;
  170.  
  171.   NewDoc := New(POleDocument, Init(SelfPtr, Doc, DocName, False));
  172.   if NewDoc = nil then
  173.     Open := ole_Error_Edit
  174.   else 
  175.   begin
  176.     ServerDoc := @NewDoc^.AppServerDoc;
  177.     Open      := ole_Ok;
  178.   end;
  179. end;
  180.  
  181. { Handles the Create callback.  Called by the server library when a client
  182.   application has created a new embedded object by calling OleCreate.
  183.  
  184.   WHAT TO DO:
  185.     - Create an *untitled* TOleDocument of class 'ClassName' (since we
  186.       only have one class we can ignore the class name) and mark it as dirty
  187.     - Associate handle 'Doc' with the document
  188.     - Store the pointer to the TOleDocument in 'ServerDoc'
  189.     - Return ole_Ok if successful, ole_Error_New otherwise
  190.  
  191.   If your app is an MDI application then you would also allocate a window
  192.   here, but since this app isn't the window is already created.
  193.  
  194.   'DocName' is the name of the document as it appears in the client
  195.   class. DON'T use this to change the title bar, use what you get when
  196.   the document is sent the message 'SetHostNames'.
  197.  
  198.   NOTE: Since we only have one document we could have created it during
  199.         initialization
  200. }
  201. function Create(Server: POleServer; Doc: LHServerDoc;
  202.                 Class, DocName: PChar;
  203.                 var ServerDoc: POleServerDoc): TOleStatus; export;
  204. var
  205.   SelfPtr: POleServerObj;
  206.   NewDoc : POleDocument;
  207. begin
  208.   SelfPtr:= PAppServer(Server)^.Owner;
  209.  
  210.   NewDoc := New(POleDocument, Init(SelfPtr, Doc, nil, True));
  211.   if NewDoc = nil then
  212.     Create := ole_Error_New
  213.   else 
  214.   begin
  215.     ServerDoc := @NewDoc^.AppServerDoc;
  216.     PServerWindow(Application^.MainWindow)^.BeginEmbedding;
  217.     Create := ole_Ok;
  218.   end;
  219. end;
  220.  
  221. { Handles the CreateFromTemplate callback.  Called by the server library 
  222.   when a client application has created a new linked object specifying a 
  223.   template by calling OleCreateFromTemplate. What this really means is that
  224.   we need to create a document and initialize it with the contents of a file.
  225.   'DocName' is the name of the document as it appears in the client class.
  226.   DON'T use this to change the title bar, use what you get when the document
  227.   is sent message 'SetHostNames'
  228.  
  229.   WHAT TO DO:
  230.     - Create a TOleDocument of class 'ClassName' (since we only have one
  231.       class we can ignore the class name)
  232.     - Initialize the document with the contents of file 'TemplateName'
  233.     - Associate handle 'Doc' with the document
  234.     - Store the pointer to the TOleDocument in 'ServerDoc'
  235.     - Return ole_Ok if successful, ole_Error_Template otherwise
  236.  
  237.     If your app is an MDI application then you would also allocate a window
  238.     here, but since this app isn't the window is already created.
  239.  
  240.     NOTE: since we only have one document we could have created it during
  241.           initialization
  242. }
  243. function CreateFromTemplate(Server: POleServer; Doc: LHServerDoc;
  244.   Class, DocName, TemplateName: PChar;
  245.   var ServerDoc: POleServerDoc): TOleStatus; export;
  246. var
  247.   SelfPtr: POleServerObj;
  248.   NewDoc : POleDocument;
  249. begin
  250.   SelfPtr:= PAppServer(Server)^.Owner;
  251.  
  252.   NewDoc := New(POleDocument, Init(SelfPtr, Doc, TemplateName, False));
  253.   if NewDoc = nil then
  254.     CreateFromTemplate := ole_Error_New
  255.   else 
  256.   begin
  257.     ServerDoc := @NewDoc^.AppServerDoc;
  258.     PServerWindow(Application^.MainWindow)^.BeginEmbedding;
  259.     CreateFromTemplate := ole_Ok;
  260.   end
  261. end;
  262.  
  263. { Handles the Edit callback.  Called by the server library when a client
  264.   application has activated an embedded object for editing.  This is exactly
  265.   like 'Create' except that the document will receive a 'GetData' message to
  266.   create the object, and the object will receive a 'SetData' message to 
  267.   initialize itself
  268.  
  269.   'DocName' is the name of the document as it appears in the client class.
  270.   DON'T use this to change the title bar, use what you get when the document
  271.   is sent message 'SetHostNames'
  272.  
  273.   WHAT TO DO:
  274.     - Create a TOleDocument of class 'ClassName' (since we only have one
  275.       class we can ignore the class name)
  276.     - Associate handle 'Doc' with the document
  277.     - Store the pointer to the TOleDocument in 'ServerDoc'
  278.     - Return ole_Ok if successful, ole_Error_Edit otherwise
  279. function Edit(Server: POleServer; Doc: LHServerDoc; Class, DocName: PChar;
  280.   var ServerDoc: POleServerDoc): TOleStatus; export;
  281. var
  282.   SelfPtr: POleServerObj;
  283.   NewDoc : POleDocument;
  284. begin
  285.   SelfPtr:= PAppServer(Server)^.Owner;
  286.   NewDoc := New(POleDocument, Init(SelfPtr, Doc, nil, False));
  287.   if NewDoc = nil then
  288.     Edit := ole_Error_Edit
  289.   else 
  290.   begin
  291.     ServerDoc := @NewDoc^.AppServerDoc;
  292.     PServerWindow(Application^.MainWindow)^.BeginEmbedding;
  293.     Edit := ole_Ok;
  294.   end;
  295. end;
  296.  
  297. { Handles the Exit callback.  We have been instructed by the library to 
  298.   exit immediately because of a fatal error.
  299.  
  300.   WHAT TO DO:
  301.     - Hide the window to prevent user interaction
  302.     - Call OleRevokeServer and ignore a return of ole_Wait_For_Release
  303.     - Terminate the application immediately
  304.     - Return ole_Ok if successful, ole_Error_Generic otherwise
  305. }
  306. function Exit(Server: POleServer): TOleStatus; export;
  307. var
  308.   SelfPtr: POleServerObj;
  309. begin
  310.   SelfPtr := PAppServer(Server)^.Owner;
  311.  
  312.   Application^.MainWindow^.Show(sw_Hide);
  313.  
  314.   OleRevokeServer(SelfPtr^.ServerHdl);
  315.  
  316.   PostAppMessage(GetCurrentTask, wm_Quit, 0, 0);
  317.   Exit := ole_Ok;
  318. end;
  319.  
  320. { Handles the Release callback.  This routine gets called by the server
  321.   library after the server has called OleRevokeServer and when the DDE 
  322.   conversation with the client has been successfully closed.  This tells
  323.   us that there are no connections to the server, its documents, or their
  324.   objects and that we are free to terminate.
  325.  
  326.   WHAT TO DO:
  327.     - Set a flag to indicate that 'Release' has been called
  328.     - If the application is hidden and we *haven't* called OleRevokeServer
  329.       then we *must* terminate by posting a wm_Close message
  330.     - Free any resources allocated including documents, but *not* the
  331.       TOleServer structure
  332.     - Return ole_Ok if successful, Ole_Error_Generic otherwise
  333.  
  334.   NOTE: this routine is tricky because it is invoked under different
  335.   circumstances:
  336.     - User brought up the server and then closes it, which causes us
  337.       to call OleRevokeServer which means the server will eventually
  338.       receive a 'Release' message
  339.  
  340.     - The server was started to perform an invisible update for a client
  341.       (i.e. the server has always been hidden). In this case the server will
  342.       receive a 'Release' message and we must tell ourselves to close
  343.       because there is no user interaction.
  344. }
  345. function Release(Server: POleServer): TOleStatus; export;
  346. var
  347.   SelfPtr: POleServerObj;
  348. begin
  349.   SelfPtr := PAppServer(Server)^.Owner;
  350.  
  351.   { If we haven't been sent a 'Release' message yet and our main window is
  352.     hidden then we post a quit message.  NOTE: Call PostMessage and not 
  353.     PostQuitMessage because PostQuitMessage might bypass your application's
  354.     necessary cleanup procedures.
  355.   }
  356.   if (not SelfPtr^.IsReleased) and
  357.       (not IsWindowVisible(Application^.MainWindow^.HWindow)) then
  358.     PostMessage(Application^.MainWindow^.HWindow, wm_Close, 0, 0);
  359.  
  360.   SelfPtr^.IsReleased := True;
  361.  
  362.   Release := ole_Ok;
  363. end;
  364.  
  365. { Handles the Execute callback. If your app supports DDE execution
  366.   commands then you would handle this event. Since we don't we return
  367.   ole_Error_Command.
  368. }
  369. function Execute(Server: POleServer; Commands: THandle): TOleStatus; export;
  370. begin
  371.   Execute := ole_Error_Command;
  372. end;
  373.  
  374.  
  375. { TOleServerObj Methods }
  376.  
  377. { Constructs an untitled instance of the OLE server document.
  378. }
  379. constructor TOleServerObj.Init(App: PApplication; Embedded: Boolean);
  380. begin
  381.   if Initialize(App) and (not Embedded) then
  382.     Document := New(POleDocument, Init(@Self, 0, nil, False));
  383. end;
  384.  
  385. { Constructs an instance of the Server Object, creating an OLE document
  386.   and initializing it from file 'Path'.
  387. }
  388. constructor TOleServerObj.InitFromFile(App: PApplication; Path: PChar);
  389. begin
  390.   if Initialize(App) then
  391.     Document := New(POleDocument, Init(@Self, 0, Path, False));
  392. end;
  393.  
  394. { Completes the construction of Self, attaching Self to the given
  395.   application.  Returns True if successful, False if not.
  396. }
  397. function TOleServerObj.Initialize(App: PApplication): Boolean;
  398. var
  399.   Status: TOleStatus;
  400. begin
  401.   AppServer.OleServer.lpvtbl:= @OleServerVTbl;
  402.   AppServer.Owner           := @Self;
  403.  
  404.   IsReleased := False;
  405.  
  406.   { Attach Self to the containing application.
  407.   }
  408.   POleApp(App)^.Server := @Self;
  409.  
  410.   { Since we can't handle multiple documents (MDI), request that we use
  411.     multiple instances to support multiple objects
  412.   }
  413.   Status := OleRegisterServer(ClassKey, @AppServer, ServerHdl, HInstance,
  414.     ole_Server_Multi);
  415.  
  416.   Initialize := True;
  417.   if Status = ole_Error_Class then
  418.   begin
  419.     if RegisterWithDatabase then
  420.       OleRegisterServer(ClassKey, @AppServer, ServerHdl, HInstance,
  421.         ole_Server_Multi)
  422.     else
  423.       Initialize := False;
  424.   end;
  425. end;
  426.  
  427. { Displays an action message prompting the user to see if they want to
  428.   register Application^.Name with the system registration database.
  429.   Returns True if user says YES and False is users says NO.  If user
  430.   says NO we terminate the app.
  431. }
  432. function TOleServerObj.WantsToRegister: Boolean;
  433. var
  434.   Buf: array [0..255] of Char;
  435. begin
  436.   StrCopy(Buf, Application^.Name);
  437.   StrCat(Buf, ' is not registered as an OLE server in the ' +
  438.     'system registration');
  439.   StrCat(Buf, ' database. Do you want to register it?');
  440.  
  441.   if MessageBox(0, Buf, Application^.Name, mb_YesNo or
  442.       mb_IconQuestion) = idYes then
  443.     WantsToRegister := True
  444.   else 
  445.   begin
  446.     PostAppMessage(GetCurrentTask, wm_Quit, 0, 0);
  447.  
  448.     { We also need to make sure that the main window doesn't get displayed.
  449.       We have a couple of choices: set 'CmdShow' to sw_Hide or set 'Status'
  450.       to non-zero.  Since the user electing not to register isn't really an
  451.       error, let's set 'CmdShow'.
  452.     }
  453.     CmdShow := sw_Hide;
  454.     WantsToRegister := False;
  455.   end;
  456. end;
  457.  
  458. { Registers us as an OLE server with the system registration database.
  459.   This would typically be done during *installation* of the app and not
  460.   when the app runs.
  461.  
  462.   NOTE: We first prompt the user to see if they want us to register. if so
  463.         we register and if not we terminate the app.
  464. }
  465. function TOleServerObj.RegisterWithDatabase: Boolean;
  466. var
  467.   Buf  : array [0..127] of Char;
  468.   Path : array [0..255] of Char;
  469. begin
  470.   if not WantsToRegister then
  471.     RegisterWithDatabase := False
  472.   else 
  473.   begin
  474.     StrCopy(Buf, '.');
  475.     StrCat(Buf, FileExt);
  476.     RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, ClassKey, StrLen(ClassKey));
  477.     RegSetValue(hkey_Classes_Root, ClassKey, Reg_Sz, ClassValue,
  478.       StrLen(ClassValue));
  479.  
  480.     { Register verb actions EDIT and PLAY with EDIT being the primary verb.
  481.     }
  482.     StrCopy(Buf, ClassKey);
  483.     StrCat(Buf, '\protocol\StdFileEditing\verb\0');
  484.     RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, 'Edit', 4);
  485.   
  486.     StrCopy(Buf, ClassKey);
  487.     StrCat(Buf, '\protocol\StdFileEditing\verb\1');
  488.     RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, 'Play', 4);
  489.  
  490.     { Register a full pathname to the executable with the database.
  491.     }
  492.     GetModuleFileName(HInstance, Path, SizeOf(Path));
  493.     StrCopy(Buf, ClassKey);
  494.     StrCat(Buf, '\protocol\StdFileEditing\server');
  495.     RegSetValue(hkey_Classes_Root, Buf, Reg_Sz, Path, StrLen(Path));
  496.   
  497.     { Inform the user that we have registered as an OLE server by displaying
  498.       an information message.
  499.     }
  500.     StrCopy(Buf, Application^.Name);
  501.     StrCat(Buf, ' successfully registered as an OLE server with the system '+
  502.       'registration database.');
  503.   
  504.     MessageBox(0, Buf, Application^.Name, mb_Ok or mb_IconInformation);
  505.     RegisterWithDatabase := True;
  506.   end
  507. end;
  508.  
  509. { Creates the instance thunks for the OleServer callback tables.
  510. }
  511. function TOleServerObj_InitVTbl(Inst: THandle): Boolean;
  512. begin
  513.   @OleServerVTbl.Open    := MakeProcInstance(@Open,    Inst);
  514.   @OleServerVTbl.Create  := MakeProcInstance(@Create,  Inst);
  515.   @OleServerVTbl.CreateFromTemplate
  516.                          := MakeProcInstance(@CreateFromTemplate, Inst);
  517.   @OleServerVTbl.Edit    := MakeProcInstance(@Edit,    Inst);
  518.   @OleServerVTbl.Exit    := MakeProcInstance(@Exit,    Inst);
  519.   @OleServerVTbl.Release := MakeProcInstance(@Release, Inst);
  520.   @OleServerVTbl.Execute := MakeProcInstance(@Execute, Inst);
  521.  
  522.   TOleServerObj_InitVTbl := (@OleServerVTbl.Open <> nil) and
  523.                             (@OleServerVTbl.Create <> nil) and
  524.                             (@OleServerVTbl.CreateFromTemplate <> nil) and
  525.                             (@OleServerVTbl.Edit <> nil) and
  526.                             (@OleServerVTbl.Exit <> nil) and
  527.                             (@OleServerVTbl.Release <> nil) and
  528.                             (@OleServerVTbl.Execute <> nil);
  529. end;
  530.  
  531.  
  532. { Document Callback Functions }
  533.  
  534. { The first parameter to each callback is a pointer to the TOleServerDoc
  535.   structure that defines this document.  In each case, we know that it
  536.   will really be a pointer to a TAppServerDoc record, which includes a
  537.   pointer to the Pascal object which owns the TOleServerDoc record.  We
  538.   can therefore use a typecast to access that object, and thus find our
  539.   way back to Self.
  540. }
  541.  
  542. { Handles the Save callback.  This method is only used when the server is
  543.   editing a linked object: the client application is closing and the user
  544.   has requested saving the client document which contains a linked object.
  545.  
  546.   WHAT TO DO:
  547.     - Save the document to the filename which was passed in when the document
  548.       was opened for linking
  549.     - Return Ole_Ok if successful, ole_Error_Generic otherwise
  550. }
  551. function Save(Doc: POleServerDoc): TOleStatus; export;
  552. var
  553.   SelfPtr: POleDocument;
  554. begin
  555.   SelfPtr := PAppServerDoc(Doc)^.Owner;
  556.  
  557.   if SelfPtr^.DocType <> DoctypeFromFile then
  558.     Save := Ole_Error_Generic
  559.   else
  560.   begin
  561.     SelfPtr^.SaveDoc;
  562.     Save := Ole_Ok;
  563.   end;
  564. end;
  565.  
  566. { Handles the Close callback.  We have been requested to close the document
  567.   because the client that contains a link (embedding or linking) to that 
  568.   document has shut down.  This is always called *before* the document's
  569.   'Release' callback is called.
  570.  
  571.   WHAT TO DO:
  572.     - Call OleRevokeServerDoc and *don't* free any resources until
  573.       'Release' is called
  574.     - Return the value of OleRevokeServerDoc
  575. }
  576. function Close(Doc: POleServerDoc): TOleStatus; export;
  577. var
  578.   SelfPtr: POleDocument;
  579. begin
  580.   SelfPtr:= PAppServerDoc(Doc)^.Owner;
  581.  
  582.   Close := OleRevokeServerDoc(SelfPtr^.ServerDoc);
  583. end;
  584.  
  585. { Responds to the SetHostNames callback.  The server library is calling
  586.   to provide the server with the name of the client's document and the
  587.   name of the object in the client application.  These names should be
  588.   used to make the necessary window title bar and menu changes.
  589.  
  590.   This is only called for embedded objects because linked objects display
  591.   their filename in the title bar.
  592.  
  593.    WHAT IT DOES:
  594.     - Change the title bar and File menu
  595.     - Store the object and client names for later use
  596.     - Return Ole_Ok is successful, Ole_Error_Generic otherwise
  597.  
  598.    PARAMETERS:
  599.     - 'Client' is the name of the client application document
  600.     - 'Doc' is the name of the object in the client application
  601. }
  602. function SetHostNames(Doc: POleServerDoc; Client,
  603.   DocName: PChar): TOleStatus; export;
  604. var
  605.   SelfPtr: POleDocument;
  606.   Title  : array [0..63] of Char;
  607. begin
  608.   SelfPtr := PAppServerDoc(Doc)^.Owner;
  609.   PServerWindow(Application^.MainWindow)^.UpdateFileMenu(DocName);
  610.  
  611.   { Store the document name, but don't update the title bar; we will do that
  612.     below
  613.   }
  614.   SelfPtr^.SetDocumentName(DocName, True);
  615.  
  616.   { Set the caption to be <App Name> - <Object Name> in <Client App Document>
  617.   }
  618.   StrCopy(Title, Application^.Name);
  619.   StrCat (Title, ' - ');
  620.   StrCat (Title, DocName);
  621.   StrCat (Title, ' in ');
  622.   StrCat (Title, Client);
  623.   PWindow(Application^.MainWindow)^.SetCaption(Title);
  624.  
  625.   SetHostNames := Ole_Ok;
  626. end;
  627.  
  628. { Handles the DocSetDimensions callback. The client is informing us how
  629.   big the object should be. 'Rect' is in mm_HiMetric units (all OLE
  630.   libraries express the size of every object in mm_HiMetric).  This
  631.   function is not supported.
  632. }
  633. function SetDocDimensions(Doc: POleServerDoc;
  634.   var Bounds: TRect): TOleStatus; export;
  635. begin
  636.   SetDocDimensions := Ole_Ok;
  637. end;
  638.  
  639. { Handles the GetObject callback. The server library calls this method
  640.   whenever a client application creates an object using a function like
  641.   OleCreate.  If 'ObjName' is nil, that means we are being called for an
  642.   embedded object after the server was sent 'Create', 'Edit', or
  643.   'CreateFromTemplate' and the server library requests the entire document.
  644.  
  645.   If 'ObjName' isn't nil then the server has already received a 'Open'
  646.   message to activate the linked object
  647.  
  648.   WHAT TO DO:
  649.     - Allocate a TOleObject if 'Item' is nil, or look up 'Item'
  650.       in the list of objects if it isn't nil
  651.     - Store the pointer to the TOleObject in 'OleObject' for return
  652.     - Store 'Client' so we can send notifications to the client
  653.       (used for linked objects)
  654.     - Return ole_Ok if successful, ole_Error_Name if 'Item' isn't
  655.       recognized, or ole_Error_Memory if the object could not be
  656.       allocated
  657.  
  658.   NOTE:
  659.     - We only have one object and it is created when the document is
  660.       created. Therefore, we don't actually create anything here.
  661.     - 'Client' resides in the server library and is used on behalf of
  662.       a client application
  663. }
  664. function GetObject(Doc: POleServerDoc; Item: PChar;
  665.   var OleObject: POleObject; Client: POleClient): TOleStatus; export;
  666. var
  667.   SelfPtr: POleDocument;
  668. begin
  669.   SelfPtr := PAppServerDoc(Doc)^.Owner;
  670.  
  671.   { In either case (whether 'ObjName' is nil or not) we just return
  672.     the object associated with the document.  NOTE that we return a
  673.     pointer to its AppObject field, not to the object itself.
  674.   }
  675.   OleObject := POleObject(@SelfPtr^.OleObject^.AppObject);
  676.  
  677.   { If 'Item' isn't nil then we associate 'Client' with it.
  678.   
  679.     NOTE: We only have one object. if you have multiple objects then you
  680.           would have to search your objects to find the one that matched
  681.           'Item'
  682.   }
  683.   if Item <> nil then
  684.     SelfPtr^.OleObject^.AddClientLink(Client);
  685.  
  686.   GetObject := Ole_Ok;
  687. end;
  688.  
  689. { Handles the Release callback.  The server library calls this routine when
  690.   all conversations to the object have been closed.  At this point the server
  691.   has called either OleRevokeServerDoc or OleRevokeServer.
  692.  
  693.   There will be no more calls to the document's methods.  It is thus okay to
  694.   free the document's objects, but *not* the TOleDocument yet.
  695.  
  696.   WHAT TO DO:
  697.     - Free the document's objects and resources (e.g. atoms) but *not* the
  698.       document itself
  699.     - Set a flag to indicate that 'Release' has been called
  700.     - Return Ole_Ok if successful, Ole_Error_Generic otherwise
  701.  
  702.   NOTE:
  703.     - Since we only have one document and one object within the
  704.       document we don't delete the object here.  However, you
  705.       might want to.
  706.     - This procedure is not called 'Release' because it appears in the
  707.       same scope as the Release callback for the TOleServerObj.
  708. }
  709. function ReleaseDoc(Doc: POleServerDoc): TOleStatus; export;
  710. var
  711.   SelfPtr: POleDocument;
  712. begin
  713.   SelfPtr := PAppServerDoc(Doc)^.Owner;
  714.  
  715.   SelfPtr^.IsReleased := True;
  716.   ReleaseDoc := Ole_Ok;
  717. end;
  718.  
  719. { Handles the SetColorScheme callback.  Not supported.
  720. }
  721. function SetColorSchemeDoc(Doc: POleServerDoc; var Palette: TLogPalette): TOleStatus; export;
  722. begin
  723.   SetColorSchemeDoc := Ole_Error_Generic;
  724. end;
  725.  
  726. { Handles the Execute callback.  If your app supports DDE execution commands
  727.   then you would handle this event.  Since we don't, we return
  728.   Ole_Error_Command.
  729. }
  730. function ExecuteDoc(Doc: POleServerDoc;
  731.   Commands: THandle): TOleStatus; export;
  732. begin
  733.   ExecuteDoc := ole_Error_Command;
  734. end;
  735.  
  736.  
  737. { TOleDocument Methods }
  738.  
  739. { Constructs an instance of the OLE Document. If 'Path' is nil then we
  740.   create an untitled document and default object.  The type is 'DoctypeNew'
  741.   if 'ServerDoc' is nil and 'DoctypeEmbedded' if 'ServerDoc' is non-nil.
  742.   If 'Path' is non-nil we create a document of type 'DoctypeFromFile'
  743.   and initialize it from file 'Path'
  744.     
  745.   If 'ServerDoc' is nil then we call OleRegisterServerDoc, otherwise we
  746.   just use 'ServerDoc' as our registration handle.
  747. }
  748. constructor TOleDocument.Init(Server: POleServerObj; Doc: LHServerDoc; 
  749.                               Path: PChar; Dirty: Boolean);
  750. begin
  751.   Name      := nil;
  752.   IsReleased:= False;
  753.   IsDirty   := Dirty;
  754.  
  755.   AppServerDoc.OleServerDoc.lpvtbl:= @OleServerDocVTbl;
  756.   AppServerDoc.Owner              := @Self;
  757.  
  758.   { Attach this document to the owning server.
  759.   }
  760.   POleServerObj(Server)^.Document := @Self;
  761.  
  762.   { Since we only have one object we can create it now.
  763.   }
  764.   OleObject := New(POleObjectObj, Init);
  765.  
  766.   if Path <> nil then
  767.     LoadFromFile(Path)
  768.   else
  769.   begin
  770.     SetDocumentName(UnnamedDoc, True);
  771.  
  772.     if Doc <> 0 then
  773.       DocType := DoctypeEmbedded
  774.     else
  775.       DocType := DoctypeNew;
  776.   end;
  777.  
  778.   if Doc <> 0 then
  779.     ServerDoc := Doc  { Use registration handle we were given }
  780.   else
  781.     OleRegisterServerDoc(Server^.ServerHdl, Name, @AppServerDoc, ServerDoc);
  782. end;
  783.  
  784. { Changes the instance variable 'Name' and changes the window caption to
  785.   those given.
  786. }
  787. procedure TOleDocument.SetDocumentName(NewName: PChar;
  788.   ChangeCaption: Boolean);
  789. var
  790.   Title: array[0..63] of Char;
  791. begin
  792.   StrDispose(Name);
  793.   Name := StrNew(NewName);
  794.  
  795.   if ChangeCaption then
  796.   begin
  797.     StrCopy(Title, Application^.Name);
  798.     StrCat (Title, ' - ');
  799.     StrCat (Title, NewName);
  800.     PWindow(Application^.MainWindow)^.SetCaption(Title);
  801.   end;
  802. end;
  803.  
  804. { Loads from the given file name.  Returns True if successful and False
  805.   otherwise.  If successful sets DocType to 'DoctypeFromFile' and sets
  806.   'Name' to 'Path'.
  807. }
  808. function TOleDocument.LoadFromFile(Path: PChar): Boolean;
  809. var
  810.   Msg     : array [0..255] of Char;
  811.   Key     : array [0..40]  of Char;
  812.   InStream: TBufStream;
  813. begin
  814.   InStream.Init(Path, stOpen, 1000);
  815.   if InStream.Status = stInitError then
  816.   begin
  817.     StrCopy(Msg, 'Cannot open file ');
  818.     StrCat(Msg, Path);
  819.     MessageBeep(0);
  820.     MessageBox(Application^.MainWindow^.HWindow, Msg,
  821.                Application^.Name, mb_OK or mb_IconExclamation);
  822.     LoadFromFile := False;
  823.   end
  824.   else
  825.   begin
  826.     { Read in the signature.  Read the number of characters we
  827.       would expect, then see if we got them.  If not, then abandon
  828.       the attempt.  Note that the Read will not get in a NUL; we
  829.       put that on manually.  Also note that we read StrLen(ClassKey)+1
  830.       characters to consume the extra blank written out.
  831.     }
  832.     InStream.Read(Key, StrLen(ClassKey)+1);
  833.     Key[StrLen(ClassKey)] := #0;
  834.     if StrComp(Key, ClassKey) <> 0 then
  835.     begin
  836.       StrCopy(Msg, 'File ');
  837.       StrCat(Msg, Path);
  838.       StrCat(Msg, ' is not an "');
  839.       StrCat(Msg, Application^.Name);
  840.       StrCat(Msg, '" file!');
  841.       MessageBeep(0);
  842.       MessageBox(Application^.MainWindow^.HWindow, Msg, Application^.Name,
  843.                  mb_OK or mb_IconExclamation);
  844.       LoadFromFile := False;
  845.     end
  846.     else
  847.     begin
  848.       OleObject:= POleObjectObj(InStream.Get);
  849.       DocType  := DoctypeFromFile;
  850.       SetDocumentName(Path, True);
  851.       LoadFromFile := True;
  852.     end;
  853.   end;
  854.   InStream.Done;
  855. end;
  856.  
  857. { Resets the document so that we can re-use the document object.  If your
  858.   app doesn't then you would delete the old object and create a new one.
  859.   Sets 'IsDirty' flag to False and 'IsReleased' to False. If 'ServerDoc'
  860.   is nil then calls OleRegisterServerDoc.
  861. }
  862. procedure TOleDocument.Reset(Path: PChar);
  863. begin
  864.   IsDirty    := False;
  865.   IsReleased := False;
  866.  
  867.   if Path <> nil then
  868.     if not LoadFromFile(Path) then 
  869.     begin
  870.       PServerWindow(Application^.MainWindow)^.ShapeChange(ObjEllipse);
  871.  
  872.       OleObject^.Native.NativeType := ObjEllipse;
  873.       OleObject^.Native.Version    := 1;
  874.  
  875.       DocType := DoctypeNew;
  876.       SetDocumentName(UnnamedDoc, True);
  877.     end;
  878.  
  879.   if ServerDoc = 0 then
  880.     OleRegisterServerDoc(POleApp(Application)^.Server^.ServerHdl, Name,
  881.       @AppServerDoc, ServerDoc);
  882. end;
  883.  
  884. { Sets up a TOpenFileName structure for use with the File Open Common
  885.   Dialog.  The caller passes in a structure which is filled in as
  886.   required, and a pointer to the array to receive the full path name.
  887.   Uses the Filter and SimpleName variables defined above, which are
  888.   global to allow this to be used from several places.
  889. }
  890. procedure TOleDocument.Setup(Path: PChar; MaxPathLen: Integer;
  891.   var FNStruct: TOpenFileName);
  892. begin
  893. { Set up a filter buffer to look for '*.oos' files only.  Recall that filter
  894.   buffer is a set of string pairs, with the last one terminated by a
  895.   double-null.
  896. }
  897.   FillChar(Filter, SizeOf(Filter), #0);  { Set up for double null at end }
  898.   StrCopy(Filter, 'OWL OLE Server');
  899.   StrCopy(@Filter[StrLen(Filter)+1], '*.oos');
  900.  
  901.   StrCopy(Path, '*.');
  902.   StrCat (Path, FileExt);
  903.  
  904.   FillChar(FNStruct, SizeOf(TOpenFileName), #0);
  905.  
  906.   with FNStruct do
  907.   begin
  908.     hInstance     := HInstance;
  909.     hwndOwner     := Application^.MainWindow^.HWindow;
  910.     lpstrDefExt   := FileExt;
  911.     lpstrFile     := Path;
  912.     lpstrFilter   := Filter;
  913.     lpstrFileTitle:= SimpleName;
  914.     Flags         := ofn_HideReadOnly or ofn_PathMustExist;
  915.     lStructSize   := SizeOf(TOpenFileName);
  916.     nFilterIndex  := 1;       {Use first Filter String in lpstrFilter}
  917.     nMaxFile      := MaxPathLen;
  918.   end;
  919. end;
  920.  
  921. { Activates the File/Open common dialog, and returns the result.
  922.   Puts the obtained file name into the given Path parameter, which
  923.   is assumed to point to a buffer big enough to contain a TFilename
  924.   sized string.
  925. }
  926. function TOleDocument.PromptForOpenFileName(Path: PChar): Boolean;
  927. var
  928.   FNStruct: TOpenFileName;
  929. begin
  930.   Setup(Path, SizeOf(TFilename), FNStruct);
  931.   PromptForOpenFileName := GetOpenFileName(FNStruct);
  932. end;
  933.  
  934. { Calls the common Windows dialog function to prompt the user for the
  935.   filename to use.
  936. }
  937. procedure TOleDocument.SaveAs;
  938. var
  939.   Path    : TFilename;    { Result of GetSaveFileName }
  940.   FNStruct: TOpenFileName;
  941. begin
  942.   Setup(Path, SizeOf(Path), FNStruct);
  943.  
  944.   if GetSaveFileName(FNStruct) then
  945.   begin
  946.     DocType := DoctypeFromFile;
  947.     SetDocumentName(Path, True);  { We must do this BEFORE we call SaveDoc }
  948.     SaveDoc;
  949.  
  950.     { Now inform the server library that we have renamed the document
  951.     }
  952.     OleRenameServerDoc(ServerDoc, Name);
  953.   end;
  954. end;
  955.  
  956. { Saves the document to file 'Name' and marks the document as no
  957.   longer 'dirty'.
  958. }
  959. procedure TOleDocument.SaveDoc;
  960. var
  961.   OutStream: TBufStream;
  962.   Blank    : Char;
  963. begin
  964.   if DocType = DoctypeNew then
  965.     SaveAs
  966.   else
  967.   begin
  968.     OutStream.Init(Name, stCreate, 1000);
  969.     OutStream.Write(ClassKey^, StrLen(ClassKey));
  970.     Blank := ' ';
  971.     OutStream.Write(Blank, 1);
  972.     OutStream.Put(OleObject);
  973.     IsDirty := False;
  974.     OutStream.Done;
  975.   end;
  976. end;
  977.  
  978. { Creates thunks for TOleServerDoc method callback tables
  979. }
  980. function TOleDocument_InitVTbl(Inst: THandle): Boolean;
  981. begin
  982.   @OleServerDocVTbl.Save            := MakeProcInstance(@Save,              Inst);
  983.   @OleServerDocVTbl.Close           := MakeProcInstance(@Close,             Inst);
  984.   @OleServerDocVTbl.SetHostNames    := MakeProcInstance(@SetHostNames,      Inst);
  985.   @OleServerDocVTbl.SetDocDimensions:= MakeProcInstance(@SetDocDimensions,  Inst);
  986.   @OleServerDocVTbl.GetObject       := MakeProcInstance(@GetObject,         Inst);
  987.   @OleServerDocVTbl.Release         := MakeProcInstance(@ReleaseDoc,        Inst);
  988.   @OleServerDocVTbl.SetColorScheme  := MakeProcInstance(@SetColorSchemeDoc, Inst);
  989.   @OleServerDocVTbl.Execute         := MakeProcInstance(@ExecuteDoc,        Inst);
  990.  
  991.   TOleDocument_InitVTbl := (@OleServerDocVTbl.Save <> nil) and
  992.                            (@OleServerDocVTbl.Close <> nil) and
  993.                            (@OleServerDocVTbl.SetHostNames <> nil) and
  994.                            (@OleServerDocVTbl.SetDocDimensions <> nil) and
  995.                            (@OleServerDocVTbl.GetObject <> nil) and
  996.                            (@OleServerDocVTbl.Release <> nil) and
  997.                            (@OleServerDocVTbl.SetColorScheme <> nil) and
  998.                            (@OleServerDocVTbl.Execute <> nil);
  999. end;
  1000.  
  1001. end.
  1002.