OpenOffice Automation with Win32 program lazarus / 67-Windows ---------------------------------------------------------------------- aAccord · 2014-12-04 22:32 We have a program that sends a paste command to an already opened document in OpenOffice's Writer program to paste in some clipboard text. The core functionality is here: Code: [Select] PROCEDURE PasteIntoWordProcessor (); VAR Server, desktop, dispatcher: variant;     FUNCTION variantArray(): Variant;     BEGIN       variantArray:= VarArrayCreate([0, -1], varVariant);     END; BEGIN   Server := CreateOleObject('com.sun.star.ServiceManager'); desktop := Server.createInstance('com.sun.star.frame.Desktop');   dispatcher := Server.createInstance('com.sun.star.frame.DispatchHelper');   dispatcher.executeDispatch(desktop.CurrentFrame, '.uno:Paste', '', 0, variantArray());   dispatcher:= unassigned;   desktop:= unassigned;   Server:= unassigned; END;If we build a simple Forms-based application, this code works great to paste in text already in the Windows clipboard. Here is a sample app that works with a form with a single button on a form wired up to Button1Click: Code: [Select]unit Unit1; //{$mode objfpc}{$H+} {$mode macpas} interface uses   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,   Comobj, Variants; type   { TForm1 }   TForm1 = class(TForm)     Button1: TButton;     procedure Button1Click(Sender: TObject);   private     { private declarations }   public     { public declarations }   end; var   Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject); var   Server, desktop, dispatcher: variant;   FUNCTION variantArray(): Variant;   BEGIN     variantArray:= VarArrayCreate([0, -1], varVariant);   END; begin   Server := CreateOleObject('com.sun.star.ServiceManager');   desktop := Server.createInstance('com.sun.star.frame.Desktop');   dispatcher := Server.createInstance('com.sun.star.frame.DispatchHelper');   dispatcher.executeDispatch(desktop.CurrentFrame, '.uno:Paste', '', 0, variantArray());   dispatcher:= unassigned;   desktop:= unassigned;   Server:= unassigned; end; end.This code pastes and does not crash. But our app is a Win32 app. So we build a pretty bare-bones Win32 app with the same calls in PasteIntoWordProcessor: Code: [Select]program try2; {$APPTYPE GUI} USES   Interfaces, // this includes the LCL widgetset   Variants, ComObj, windows; CONST   AppName = 'WinHello';   IDC_FILE_NEW = 3001;   IDC_FILE_EXIT = 3003; VAR   mainMenu, fileMenu: HMENU;   m_Instance : HANDLE; {$R mytry2.res} PROCEDURE PasteIntoWordProcessor (); VAR Server, desktop, dispatcher: variant;     FUNCTION variantArray(): Variant;     BEGIN       variantArray:= VarArrayCreate([0, -1], varVariant);     END; BEGIN   Server := CreateOleObject('com.sun.star.ServiceManager'); desktop := Server.createInstance('com.sun.star.frame.Desktop');   dispatcher := Server.createInstance('com.sun.star.frame.DispatchHelper');   dispatcher.executeDispatch(desktop.CurrentFrame, '.uno:Paste', '', 0, variantArray());   dispatcher:= unassigned;   desktop:= unassigned;   Server:= unassigned; END; FUNCTION WindowProc(Window: HWnd; AMessage: UINT; WParam : WPARAM; theLParam: LPARAM): LRESULT; stdcall; export;   VAR ps : PAINTSTRUCT; wmId : integer; BEGIN   CASE AMessage OF WM_COMMAND:       BEGIN       wmId := LOWORD(wParam);         IF wmId = IDC_FILE_NEW THEN       BEGIN PasteIntoWordProcessor; // FAILS HERE, AFTER SUCCESSFUL PASTE!!!       END       ELSE         WindowProc := DefWindowProc(Window, AMessage, WParam, theLParam);       END; WM_PAINT: BEGIN BeginPaint(Window, @ps); EndPaint(Window, @ps); END;     WM_DESTROY:       BEGIN          PostQuitMessage(0);          Exit;       END; END;   WindowProc := DefWindowProc(Window, AMessage, WParam, theLParam); END; FUNCTION WinRegister(className: LPSTR): Boolean; VAR   WindowClass: WndClass; BEGIN   WindowClass.Style := cs_hRedraw OR cs_vRedraw;   WindowClass.lpfnWndProc := WndProc(@WindowProc);   WindowClass.cbClsExtra := 0;   WindowClass.cbWndExtra := 0;   WindowClass.hInstance := m_Instance;   WindowClass.hIcon := 0;   WindowClass.hCursor := WINDOWS.LoadCursor(0, MAKEINTRESOURCE(32512));   WindowClass.hbrBackground := GetStockObject(WHITE_BRUSH);   WindowClass.lpszMenuName := NIL;   WindowClass.lpszClassName := className;   IF RegisterClass(@WindowClass) = 0 THEN   WinRegister:= false;   WinRegister:= TRUE; END; PROCEDURE updateMenu(); VAR   mi: MENUINFO;   mii: MENUITEMINFO; BEGIN mainMenu := CreateMenu(); mi.cbSize:= sizeof(MENUINFO); mi.fMask:= MIM_STYLE;   fileMenu := CreatePopupMenu();   AppendMenu(fileMenu, MF_STRING, IDC_FILE_NEW, '&New'+#9+'Shift+N');   AppendMenu(fileMenu, MF_STRING, IDC_FILE_EXIT, 'Exit');   mii.cbSize:= sizeof(MENUITEMINFO);   mii.fMask:= MIIM_STRING or MIIM_ID or MIIM_SUBMENU;   mii.fType:= MFT_STRING;   mii.wID:= 0;   SetMenuInfo(mainMenu, @mi);   mii.hSubMenu:= fileMenu;   mii.dwTypeData:= LPSTR('&File');   WINDOWS.InsertMenuItem(mainMenu, 0, FALSE, mii); END; FUNCTION InitInstance(hInstance: HANDLE; nCmdShow: Integer): BOOLEAN; VAR myHwnd: HWND; BEGIN    myHwnd := CreateWindow(AppName, AppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, mainMenu, hInstance, NIL);    IF myHwnd = 0 THEN      return FALSE;    ShowWindow(myHwnd, nCmdShow);    UpdateWindow(myHwnd);    return TRUE; END; VAR   AMessage: Msg; BEGIN m_Instance := HINSTANCE();   IF NOT WinRegister(AppName) THEN   BEGIN     MessageBox(0, 'Register App Name failed', nil, mb_Ok);     Exit;   END;   updateMenu();   IF NOT InitInstance(m_Instance, SW_SHOWNORMAL) THEN   Exit;   WHILE GetMessage(@AMessage, 0, 0, 0) DO   BEGIN   IF (NOT TranslateMDISysAccel(AMessage.hwnd, AMessage)) THEN     BEGIN         TranslateMessage(AMessage);         DispatchMessage(AMessage);     END;   END;   Halt(AMessage.wParam); END. A bit more house-keeping, but the core function is the same. (Pick 'New' off the File menu to invoke the core call.) With this Win32 code, the paste will actually occur, but then the Win32 app will crash. Oddly, the automation we have set up for MS-Word works fine in our main Win32 program. Just OpenOffice Writer gives us this issue. Can anyone explain why it would work so well with a Forms based app and not Win32? Or what we can do to make it work with Win32? I hope I've provided enough code so that others can test these scenarios. It is a long post already, let me know if you need more details. ---------------------------------------------------------------------- rvk · 2014-12-05 09:43 I'm not sure if this is valid pascal: Code: [Select] IF myHwnd = 0 THEN      return FALSE; But when changing it to "result := true/false" your example-program works correctly for me. Lazarus 1.3 r46441 FPC 2.7.1 i386-win32 Windows 7 Ultimate with LibreOffice 4.2.5.2 Aaaah... just noticed the "{$mode macpas}" (which is NOT in your barebone app). which provides the validation of your return true/false. (You learn something new everyday ) Are you using the latest version of OpenOffice? (Did you try LibreOffice too?) You say your app is a Win32 app... This could also be a Forms-based application. Is your Win32 app not form based?? Did you try that forms-based as 32bit ? (What is your Lazarus/Fpc version and development OS ?) ---------------------------------------------------------------------- aAccord · 2014-12-05 16:34 Thanks for the reply! Yes, in Project, Options, the Parsing is set for Mac Pascal. As is our Win32 program. We have not yet tried it with LibreOffice. The OpenOffice version is 4.1.1, which I believe is the most recent. We have tried this with Laz 1.2.6 / FPC 2.6.4 (latest) an as old as Laz 1.1 / FPC 2.6.1 and the behaviors appear the same. Our Win32 app is not forms based. The first forms example is (2nd code example) is, unless I misnunderstand, a Win32 forms application, which does not crash for us. Only the pure Win32 non-forms app crashes. After pasting quite nicely. [Bang head on wall emoticon goes here.] Development OS is Windows 8.1. (Two different machines for the newer / older Laz/FPC setups.) I hope that helps answer the questions you were asking. Thanks again. Let me know if further info or clarification is needed. ---------------------------------------------------------------------- Timewarp · 2014-12-05 17:22 Cannot reproduce (I use fpc2.7.1) But are these lines really needed / have you tested without? Code: [Select]  dispatcher:= unassigned;   desktop:= unassigned;   Server:= unassigned; ---------------------------------------------------------------------- aAccord · 2014-12-05 17:43 Thanks for the reply. Quote from: Timewarp on December 05, 2014, 06:22:36 pmCannot reproduce (I use fpc2.7.1) But are these lines really needed / have you tested without? Code: [Select]  dispatcher:= unassigned;   desktop:= unassigned;   Server:= unassigned; Interesting. This is the third code sample that is the pure Win32 app that you could not reproduce the crash? Those lines are supposedly nice house-keeping. If anyone is interested, the corresponding OpenOffice thread on this (back when we thought it was purely an OpenOffice problem) is here: https://forum.openoffice.org/en/forum/viewtopic.php?f=47&t=73866&sid=2 2be8c5b4a36214a658ca2059f0662da and this is the post within that thread with the error and call stack info: https://forum.openoffice.org/en/forum/viewtopic.php?f=47&t=73866&sid=2 2be8c5b4a36214a658ca2059f0662da#p333746 if that helps. And we have tested without those unassigned lines with the same results. ---------------------------------------------------------------------- rvk · 2014-12-05 19:10 Did you test this with debugging on in the ide or stand alone app? (I tested it in the ide) If debugging is on which debugger do you use? Maybe a zip of your complete sample with settings could help us test this. ---------------------------------------------------------------------- aAccord · 2014-12-05 19:24 Debugging within Lazarus and also outside the ide crashes for us. The debugger is whatever comes integrated with Lazarus, I think? (Not smart enough to know if there is a better answer to that.) I tried attaching a zip here, but it didn't go through. Maybe too big? 3.84MB. Thanks again. ---------------------------------------------------------------------- rvk · 2014-12-05 22:37 I'm not sure why you have included a resource in your "bare-bone" win32 app. But since I don't have that resource I commented it out. Code: [Select]//{$R mytry2.res} That's probably also the reason your zip is 3,84MB. I can't reproduce your crash on either my work-machine or home- machine. Both Windows 7 Ultimate 64bit with Lazarus 1.3 FPC 2.7.1 i386-win32 and LibreOffice 4.2.5.2 and 4.1.0.4. I even tried a very bare-bone version of a console app myself without any problems: Code: [Select]program try3; uses   Variants,   ComObj,   Windows;   procedure PasteIntoWordProcessor();   var     Server, desktop, dispatcher: variant;     function variantArray(): variant;     begin       variantArray := VarArrayCreate([0, -1], varVariant);     end;   begin     Server := CreateOleObject('com.sun.star.ServiceManager');     desktop := Server.createInstance('com.sun.star.frame.Desktop');     dispatcher := Server.createInstance('com.sun.star.frame.DispatchHelper');     dispatcher.executeDispatch(desktop.CurrentFrame, '.uno:Paste', '', 0, variantArray());     dispatcher := Unassigned;     desktop := Unassigned;     Server := Unassigned;   end; begin   PasteIntoWordProcessor(); end. (no menus, gui's etc, just run it when a document open and something on the clipboard) When this simple program also crashes we could try initializing the thread-system. You can do this with a dummy-thread which you wouldn't have to use. It's the only thing I can think of where your win32 bare- bone is different from a form-gui app. Edit: Also no problems with OpenOffice 4.1.1 here. (needed to update my LibreOffice so I thought I'd give OpenOffice a try before re- installing the latest LibreOffice) ---------------------------------------------------------------------- rvk · 2014-12-06 08:37 Another thought that just popped into my head: Shouldn't there be a CoInitialize (or CoInitializeEx) in there somewhere (and a corresponding CoUninitialize)? (Or is that standard done in comobj initialization part?) You could try adding then just to test and be sure. ---------------------------------------------------------------------- aAccord · 2014-12-08 16:54 Thanks for the reply and help. OK, I had a huge unit included before that was ballooning the project.  So a much smaller one is attached. I also had to delete the generated .res file to get it small enough to attach. Anyway, it has two different 'main()' branches, the fuller one is commented out: Code: [Select]BEGIN CoInitialize(NIL); PasteIntoWordProcessor; CoUninitialize(); END. //BEGIN // m_Instance := HINSTANCE(); //  IF NOT WinRegister(AppName) THEN //  BEGIN //    MessageBox(0, 'Register App Name failed', nil, mb_Ok); //    Exit; //  END; //  updateMenu(); //  IF NOT InitInstance(m_Instance, SW_SHOWNORMAL) THEN //  Exit; //  WHILE GetMessage(@AMessage, 0, 0, 0) DO //  BEGIN //  IF (NOT TranslateMDISysAccel(AMessage.hwnd, AMessage)) THEN //    BEGIN //        TranslateMessage(AMessage); //        DispatchMessage(AMessage); //    END; //  END; //  Halt(AMessage.wParam); //END. All versions still crash for me. I haven't tried a console app yet, but I'll try that next. But the uncommented version is pretty close to what you had as your console app, I think. I also added the CoInitialize stuff, which didn't help or hurt, as best I can tell. I did have to add ActiveX to the USES section to get CoInitialize to be recognized. I'll go try a console app, but hopefully this attachment will help a bit. ---------------------------------------------------------------------- aAccord · 2014-12-08 17:21 Console version worked. [Bang head on wall emoticon goes here.] Code (more skeleton than required, but it worked) is attached. ---------------------------------------------------------------------- rvk · 2014-12-08 17:30 Your try2 (project1.exe) gives an error here too. Code: [Select]Runtime error 217 at $004096D0   $004096D0   $00409929   $00401A44  main,  line 125 of try2.lpr   $0040E711 You say that the console app works but i see you used TCustomApplication (which would be essentially the same as Application). That's probably why it works. But it should also work in you other example (try2). If i compile try2 it works correctly here (while your .exe version crashed). That leads me to believe it is a problem in your Lazarus version (or installation). (I use laz1.3/fpc 2.7.1) Could you try 1 more thing... adding SysUtils to your uses in try2 project? It installs an exception handler. Quote217 Unhandled exception occurred An exception occurred, and there was no exception handler present. The sysutils unit installs a default exception handler which catches all exceptions and exits gracefully. ---------------------------------------------------------------------- aAccord · 2014-12-08 17:38 Yes with SysUtils and the fuller core code, I get 'RunError(217)' as you note. I'll try it on my other Laz/FPC setup that is a bit newer than I have on this machine. ---------------------------------------------------------------------- aAccord · 2014-12-08 17:46 I get the same failure on my 'other' machine. That one is Laz 1.2.6 / FPC 2.6.4. My main machine is Laz 1.1 / FPC 2.6.1. I thought the Laz 1.2.6 / FPC 2.6.4 combo was the latest?!? Or it was a week or two ago when I put it on the 'other' machine. Both of my machines are Win 8.1, if that matters. ---------------------------------------------------------------------- Timewarp · 2014-12-08 18:01 Tested using same computer. It crashes 1.26/2.6.4, but works 1.3/2.7.1 -> Update. ---------------------------------------------------------------------- rvk · 2014-12-08 18:04 I'm using the latest development version (trunk). Windows 8.1 shouldn't matter. I'm on Windows 7 and your .exe crashes here too (and I can't imagine the compilation is different on Windows 8.1 and Windows 7). So if your .exe crashes here too it's not the OS. When compiling try2.lpr in the ide here it is 1.8Mb in size (named project1.exe). On the command prompt with fpc.exe it becomes try2.exe and is 252Kb in size. I just tried the latest stable version lazarus-1.2.6-fpc-2.6.4-win32 and the project try2 crashed here too. So there is indeed a problem in 1.2.6/2.6.4 which is fixed in trunk. (need to investigate further to actually know what.) ---------------------------------------------------------------------- rvk · 2014-12-08 18:24 So it seems to have been resolved in 1.3 but i still have a question... Why are you using macpas-mode? When I change your project to {$mode objfpc} (or in the project options) it works correctly here (laz1.2.6/fpc2.6.4). Is that the case for you also? I think you would only need macpas-mode if you have ancient mac-pascal code in your project. But your crash is definitely due to the macpas- mode. ---------------------------------------------------------------------- aAccord · 2014-12-08 19:38 The macpas decision is above my pay grade , but as I understand it, we still support some older 'classic' Mac systems and use the same code with that development environment. (I don't play in that playground, so I'm fuzzy on the specifics.) So we use the macpas to provide syntax compatibility across dev platforms. And yes, if I change to objpfc, it doesn't crash. ---------------------------------------------------------------------- rvk · 2014-12-08 19:46 You could try putting this specific code into a separate unit with {$mode objfpc} at the top. As long as you compile this with Lazarus it's going to be fine. Your other units can stay the same. You don't need macpas in Lazarus on OSX. You just need macpas for Mac-Pascal code units (for things like return x instead of result etc.). I assume you don't need to compile this code with another pascal compiler. And if you do you could add {$mode objfpc} in conditional defines in that separate unit. Code: [Select]unit pasteintoopenoffice; interface {$ifdef fpc}   {$mode objfpc}{$H+} {$endif} procedure PasteIntoWordProcessor; implementation uses   Variants, ComObj, ActiveX, Windows; procedure PasteIntoWordProcessor; // ... etc ---------------------------------------------------------------------- aAccord · 2014-12-08 20:29 Thank you. Yes, we will try putting this into a separate unit compiled with objfpc. We'll let you know if we run into problems there. We're pretty confident this will behave, so we're going to push our luck and just try it with our main app, not one of these test apps. I do appreciate the help you and others have given here. I was beginning to question my sanity. Thanks much.