IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

API, COM et SDKs Delphi Discussion :

WaitForSingleObject Word Excel multiple ouverture


Sujet :

API, COM et SDKs Delphi

  1. #1
    Membre averti
    Avatar de castorcharly
    Homme Profil pro
    Chef de projet
    Inscrit en
    Février 2009
    Messages
    423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projet
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Février 2009
    Messages : 423
    Points : 300
    Points
    300
    Par défaut WaitForSingleObject Word Excel multiple ouverture
    Bonjour,

    J'utilise une routine qui me permet d'ouvrir un fichier word ou excel et attendre la fermeture du programme via ShellExecuteEx et WaitForSingleObject et ça fonctionne trés bien, seulement si j'ouvre un fichier de ce type et que je le laisse ouvert, je ne peux en ouvrir un autre à partir de mon programme.

    Le problème venant que l'object "surveillé" est le programme Word ou Excell et non le fichier ouvert.

    Avez-vous une idée ?

    Voici la routine,"traditionnelle" que j'utilise:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    function TFrMfMain.RunExternEx(value: string; FlagAdmin: boolean = false; aparam: string = ''): boolean;var
       SEInfo   : TShellExecuteInfoW;
       IdResult: DWORD;
       FlagNoWait: boolean;
    begin
         result:= false;
         try
            ZeroMemory(@SEInfo, SizeOf(SEInfo));
            with SEInfo do begin
                 cbSize := SizeOf(SEInfo);
                 fMask:= SEE_MASK_NOCLOSEPROCESS;
                 Wnd:= 0;
                 lpFile:= PChar(value);//value contient les chemin et nom du fichier
                 if aparam <> '' then 
                    lpParameters:= pchar(aParam);
                 lpDirectory:= nil;
                 if FlagAdmin then
                       lpVerb:= 'runas';
                 nShow:= SW_SHOWMAXIMIZED;
            end;
            FlagNoWait:= false;
            if ShellExecuteEx(@SEInfo) then begin
               repeat
               IdResult:= Winapi.Windows.WaitForSingleObject(SEInfo.hProcess, 200);
               case IdResult of
                    WAIT_ABANDONED: FlagNoWait:= true;
                    WAIT_OBJECT_0: result:= true;
                    WAIT_FAILED:
                                begin
                                     FlagNoWait:= true;
                                     if GetLastError = 6 then
                                        saveMsg('Gestion automatique non prise en compte, il n''y aura pas de sauvegarde automatique !')
                                     else
                                         savemsg(syserrormessage(GetlastError));
                                end;
               end;
               application.ProcessMessages;
               until result or FlagNoWait;
            end else savemsg(inttostr(GetlastError));
         except
               savemsg('RunExternEx ' + exception(exceptobject).Message);
         end;
    end;

  2. #2
    Membre averti
    Avatar de castorcharly
    Homme Profil pro
    Chef de projet
    Inscrit en
    Février 2009
    Messages
    423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projet
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Février 2009
    Messages : 423
    Points : 300
    Points
    300
    Par défaut
    Serais je le seul dans ce cas de figure ou personne n'a trouvé de solution a ce problème ?

    Tout ça pour faire un petit up pour vous rappeler que je suis toujours coincé avec ces .Doc et .XLS

  3. #3
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 815
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 815
    Points : 13 531
    Points
    13 531
    Par défaut
    La première chose est bien sûr de déterminer si plusieurs instances de ces programmes sont démarrées.

    Mais je ne vois pas trop comment ça pourrait fonctionner correctement à l'aide d'un seul thread. Un seul processus sera surveillé à la fois (on ne peut être bloqué que sur un seul WaitForSingleObject). Entre temps, les autres processus pourraient avoir été arrêtés faisant qu'on ne passera jamais dans WAIT_OBJECT_0... le tout avec un temps de latence qui va aller en se dégradant !

    WAIT_ABANDONED est inutile, on n'y passera jamais

  4. #4
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 675
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 675
    Points : 25 487
    Points
    25 487
    Par défaut
    Par Ole, en conservant une instance de l'interface Document,

    Méthode moche :
    On peut surveiller via test régulier la fermeture du document word, cela déclenche l'exception EOleSysError RPC_E_DISCONNECTED à la fermeture du fichier
    Cela provoque la même exception pour l'interface WordApp

    Le test régulier, soit via un Sleep(200) soit aussi via un Wait...Object
    au lieu d'avoir le process via ShellExecuteEx, on le chope via
    - FindWindow(WordOle.Caption) et GetWindowThreadProcessId
    - GetModuleHandle
    - CreateToolhelp32Snapshot


    Méthode .NET :
    Utiliser l'interface DocumentEvents2_Event
    il faut affecter une procédure de délégation de type DocumentEvents2_CloseEventHandler à la propriété Close

    Je n'ai aucune idée comment le faire en LateBinding

    Par contre en Early Binding avec l'unité Word2010 et TWordApplication, il suffit de coder un Word2010.TWordApplicationDocumentBeforeClose affecté à la propriété OnDocumentBeforeClose

    Méthode Delphi via TWordApplication

    je n'ai pas le PAS de Word2010 dans ma version, voici le prototype généré par la complétion de code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure T***Form.WordApplication1DocumentBeforeClose(
      ASender: TObject; const Doc: _Document; var Cancel: WordBool);
    begin
      // bla bla ...
    end;

  5. #5
    Membre averti
    Avatar de castorcharly
    Homme Profil pro
    Chef de projet
    Inscrit en
    Février 2009
    Messages
    423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projet
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Février 2009
    Messages : 423
    Points : 300
    Points
    300
    Par défaut
    AndNotOr,

    je retourne le problème depuis pas mal de temps et effectivement, je ne vois pas comment ce serait possible.

    Shai,

    Pour l'object Wordapplication, c'est ce que j'utilisais avant et ça me pose plein d'autre problème sur d'autre fonction,
    du genre que les events sont très mal gérés, quand ils ne me plante pas carrement mon applic, etc.

    Pour le .NET, là je ne sais pas, mon applic est terminée sous Delphi il ne me reste plus que ce problème à régler,
    alors basculer en .net galère.

    Quand à la méthode bourin que tu me proposes, en passant par findwindow etc, je ne vois pas bien où tu veux que je me dirige.
    Si je fais un WaitForSingle... je vais me retrouver au même point, non ?

  6. #6
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 815
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 815
    Points : 13 531
    Points
    13 531
    Par défaut
    Essaye en englobant tout ça dans un thread et gère OnTerminate :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    unit Unit1;
     
    interface
     
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, ActiveX, ShellAPI, Vcl.StdCtrls;
     
    type
      TRunAndWaitThread = class(TThread)
      private
        Verb   :string;
        App    :TFileName;
        Params :string;
      protected
        procedure Execute; override;
      public
        constructor Create(aApp :TFileName; aParams :string = ''; aVerb :string = 'open');
      end;
     
      TForm1 = class(TForm)
        Memo1: TMemo;
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      protected
        procedure RunAndWaitTerminated(Sender :TObject);
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    { TRunAndWaitThread }
     
    constructor TRunAndWaitThread.Create(aApp: TFileName; aParams, aVerb: string);
    begin
      inherited Create;
     
      App    := aApp;
      Params := aParam;
      Verb   := aVerb;
     
      FreeOnTerminate := TRUE;
    end;
     
    procedure TRunAndWaitThread.Execute;
    var
      SEInfo :TShellExecuteInfo;
     
    begin
      ZeroMemory(@SEInfo, SizeOf(SEInfo));
     
      with SEInfo do
      begin
        cbSize       := SizeOf(SEInfo);
        fMask        := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_NO_UI;
        lpFile       := PChar(App);
        lpParameters := PChar(Params);
        lpVerb       := PChar(Verb);
        nShow        := SW_SHOWMAXIMIZED;
      end;
     
      if ShellExecuteEx(@SEInfo) then
        case WaitForSingleObject(SEInfo.hProcess, INFINITE) of
          WAIT_OBJECT_0 : ReturnValue := ERROR_SUCCESS;
          WAIT_FAILED   : ReturnValue := GetLastError;
        end
      else ReturnValue := GetLastError;
    end;
     
    { TForm1 }
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      with TRunAndWaitThread.Create('notepad.exe') do
        OnTerminate := RunAndWaitTerminated;
    end;
     
    procedure TForm1.RunAndWaitTerminated(Sender: TObject);
    begin
      with TRunAndWaitThread(Sender) do
        Memo1.Lines.Add(App +' - ' +SysErrorMessage(ReturnValue));
    end;
     
    end.

  7. #7
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 675
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 675
    Points : 25 487
    Points
    25 487
    Par défaut
    Citation Envoyé par castorcharly Voir le message
    Quand à la méthode bourin que tu me proposes, en passant par findwindow etc, je ne vois pas bien où tu veux que je me dirige.
    Si je fais un WaitForSingle... je vais me retrouver au même point, non ?
    C'était pour coupler TWordApplication ET le Wait...Object

    Oubli cette proposition sur TWordApplication est instable !

    Voir la proposition de AndNotOr, gérant un système avec des threads !
    Au lieu d'attendre en INFINITE, je laisse un délai d'une seconde

    Et tu utilises GetActiveOleObject, cela te donnera l'instance de Word déjà lancé

    Avec la collection Documents, tu peux lister les fichiers ouverts
    Si cela contient uniquement TES fichiers, tu bloques ou que sais-je
    Si cela ne contient AUCUN de tes fichiers, tu laisses ton programme ouvrir un fichier





    Pour ma part, je le fais que peu sur Word mais plutôt sur Excel et un seul fichier à la fois et toujours en LateBinding !

    En fait, les utilisateurs ADORENT Excel, ils veulent tous des exports des listes à l'écran vers Excel !

    Pour gérer l'attente de Excel, j'ai fait un truc simpliste : MessageDlg !!!
    Le modal attend que l'on clique !
    Si l'on clique avant de fermer Excel, cela ferme Excel
    Si l'on clique après avoir fermer Excel, cela masque l'exception disconnected et continue comme si de rien n'était !

    Voici mon code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
     
    //------------------------------------------------------------------------------
    (*                SoLuTions is an Versatile Library for Delphi                 -
     *                                                                             -
     *  Version alternative publiée sur "www.developpez.net"                       -
     *  Post : "c++ builder 2007 + ole excel"                                      -
     *  Post Number : 5628011                                                      -
     *  Post URL = "http://www.developpez.net/forums/d1006909/c-cpp/outils-c-cpp/cppbuilder/cpp-builder-2007-p-ole-excel/#post5628011"
     *                                                                             -
     *  Version alternative publiée sur "www.developpez.net"                       -
     *  Post : "excel et delphi"                                                   -
     *  Post Number : 6375536                                                      -
     *  Post URL = "http://www.developpez.net/forums/d1158956/environnements-developpement/delphi/debutant/excel-delphi/#post6375536"
     *                                                                             -
     *  Copyright ou © ou Copr. "SLT Solutions", (2006)                            -
     *  contributeur : ShaiLeTroll (2012) - Renommage Fichier et Correction XE2    -
     *  contributeur : ShaiLeTroll (2012) - Documentation Insight                  -
     *  contributeur : ShaiLeTroll (2014) - Reprise de la SLT<2012> sous C++Builder XE2/XE3 vers la SLT<2013> sous Delphi XE2
     *                                                                             -
     * ShaiLeTroll@gmail.com                                                       -
     *                                                                             -
     * Ce logiciel est un programme informatique servant à aider les développeurs  -
     * Delphi avec une bibliothèque polyvalente, adaptable et fragmentable.        -
     *                                                                             -
     * Ce logiciel est régi par la licence CeCILL-C soumise au droit français et   -
     * respectant les principes de diffusion des logiciels libres. Vous pouvez     -
     * utiliser, modifier et/ou redistribuer ce programme sous les conditions      -
     * de la licence CeCILL-C telle que diffusée par le CEA, le CNRS et l'INRIA    -
     * sur le site "http://www.cecill.info".                                       -
     *                                                                             -
     * En contrepartie de l'accessibilité au code source et des droits de copie,   -
     * de modification et de redistribution accordés par cette licence, il n'est   -
     * offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,   -
     * seule une responsabilité restreinte pèse sur l'auteur du programme,  le     -
     * titulaire des droits patrimoniaux et les concédants successifs.             -
     *                                                                             -
     * A cet égard  l'attention de l'utilisateur est attirée sur les risques       -
     * associés au chargement,  à l'utilisation,  à la modification et/ou au       -
     * développement et à la reproduction du logiciel par l'utilisateur étant      -
     * donné sa spécificité de logiciel libre, qui peut le rendre complexe à       -
     * manipuler et qui le réserve donc à des développeurs et des professionnels   -
     * avertis possédant  des  connaissances  informatiques approfondies.  Les     -
     * utilisateurs sont donc invités à charger  et  tester  l'adéquation  du      -
     * logiciel à leurs besoins dans des conditions permettant d'assurer la        -
     * sécurité de leurs systèmes et ou de leurs données et, plus généralement,    -
     * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.          -
     *                                                                             -
     * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez      -
     * pris connaissance de la licence CeCILL-C, et que vous en avez accepté les   -
     * termes.                                                                     -
     *                                                                             -
     *----------------------------------------------------------------------------*)
    unit SLT.Common.Office.Excel;
     
    interface
     
    uses System.SysUtils, System.Classes, System.Variants,
      Vcl.Dialogs, Vcl.ComCtrls,
      Data.DB;
     
    type
      { Forward class declarations }
      /// <summary>Création d'un classeur excel à partir d'un TDataSet</summary>
      TSLTExcelDataSetExporter = class;
      TSLTExcelListViewer = class;
     
      /// <summary>Erreur de base liée à la création d'un classeur excel à partir d'un TDataSet</summary>
      ESLTExcelDataSetExporterError = class(Exception);
     
      /// <summary>Création d'un classeur excel à partir d'un TDataSet</summary>
      TSLTExcelDataSetExporter = class(TObject)
      private
        // Membres privés
        FDataSet: TDataSet;
        FOnProgress: TNotifyEvent;
        FOnBeforeActivateExcel: TNotifyEvent;
     
        // Méthodes privées - Evenement
        procedure DoProgress();
        procedure DoBeforeActivateExcel();
      public
        // Constructeurs
        constructor Create(ADataSet: TDataSet);
     
        // Méthodes publiques
        procedure ViewInExcel(const ASheetName: string; const AFieldsNames, ATitles: array of string);
     
        // Propriétés publiques
        property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
        property OnBeforeActivateExcel: TNotifyEvent read FOnBeforeActivateExcel write FOnBeforeActivateExcel;
      end;
     
      /// <summary>Erreur de base liée à la création d'un classeur excel à partir d'un TListView</summary>
      ESLTExcelListViewerError = class(Exception);
     
      /// <summary>Création d'un classeur excel à partir d'un TListView</summary>
      TSLTExcelListViewer = class(TObject)
      private
        // Membres privés
        FListView: TListView;
        FOnProgress: TNotifyEvent;
        FOnBeforeActivateExcel: TNotifyEvent;
        FCheckedOnly: Boolean;
     
        // Méthodes privées - Evenement
        procedure DoProgress();
        procedure DoBeforeActivateExcel();
      public
        // Constructeurs
        constructor Create(AListView: TListView);
     
        // Méthodes publiques
        procedure ViewInExcel(const ASheetName: string; const AColumnTypes: array of TVarType);
     
        // Propriétés publiques
        property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
        property OnBeforeActivateExcel: TNotifyEvent read FOnBeforeActivateExcel write FOnBeforeActivateExcel;
        property CheckedOnly: Boolean read FCheckedOnly write FCheckedOnly;
      end;
     
    implementation
     
    uses Winapi.Windows, System.Win.ComObj;
     
    //------------------------------------------------------------------------------
    constructor TSLTExcelDataSetExporter.Create(ADataSet: TDataSet);
    begin
      inherited Create();
     
      FDataSet := ADataSet;
     
      if not Assigned(FDataSet) then
        raise ESLTExcelDataSetExporterError.Create('Aucune donnée disponible pour la Visualisation sous Excel !');
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTExcelDataSetExporter.DoBeforeActivateExcel();
    begin
      if Assigned(FOnBeforeActivateExcel) then
        FOnBeforeActivateExcel(Self);
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTExcelDataSetExporter.DoProgress();
    begin
      if Assigned(FOnProgress) then
        FOnProgress(Self);
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTExcelDataSetExporter.ViewInExcel(const ASheetName: string; const AFieldsNames, ATitles: array of string);
    const
      TEXT_FORMAT: WideString = '@'; // @ indique un texte, le typage en WideString est nécessaire pour qu'Excel le comprenne correctement
    var
      ExcelOLE: OLEVariant;
      ExcelOLEWorkbook: OLEVariant;
      ExcelOLEWorkSheet: OLEVariant;
      ExcelCaption: string;
      I, R: Integer;
      Bookmark: TBookmark;
      Field: TField;
    begin
      Assert(Length(AFieldsNames) = Length(ATitles), 'TSLTExcelDataSetExporter.ViewInExcel : Length(AFieldsNames) <> Length(ATitles)');
     
      try
        // Code en LateBinding !
        ExcelOLE := System.Win.ComObj.CreateOleObject('Excel.Application');
        try
          ExcelOLE.UserControl := False;
          ExcelOLE.Visible := False;
          ExcelOLEWorkbook := ExcelOLE.WorkBooks.Add;
          try
            // Sheets.Item is one-based !
            for I := ExcelOLEWorkbook.Sheets.Count downto 2 do
              ExcelOLEWorkbook.Sheets.Item[I].Delete;
     
            ExcelOLEWorkSheet := ExcelOLEWorkbook.Sheets.Item[1];
            ExcelOLEWorkSheet.Name := ASheetName;
            // Le passage d'un Texte préfixé par ' vers une Formule permet de forcer le typage texte d'un nombre mais en masquant la guillemet !
            // Je génère Excel directement en OLE
     
            // Cells is one-based !
            R := 1;
            for I := Low(ATitles) to High(ATitles) do
              ExcelOLEWorkSheet.Cells.Item[R, I + 1].Value := ATitles[I];
     
            // Columns is one-based !
            for I := Low(AFieldsNames) to High(AFieldsNames) do
            begin
              // Force une chaine ce qui évite la perte des Zéros en début d'un texte, comme un code barre par exemple
              Field := FDataSet.FieldByName(AFieldsNames[I]);
              if Field.DataType = ftString then
                ExcelOLEWorkSheet.Columns[I + 1].NumberFormat := TEXT_FORMAT;
            end;
     
            Inc(R);
            FDataSet.DisableControls();
            try
              Bookmark := FDataSet.Bookmark;
              try
                FDataSet.First();
                while not FDataSet.EOF do
                begin
                  for I := Low(AFieldsNames) to High(AFieldsNames) do
                  begin
                    Field := FDataSet.FieldByName(AFieldsNames[I]);
                    // FormulaLocal utilise les options régionales pour traduire une valeur,
                    // cela gère ainsi le séparateur décimal correctement
                    if Field.DataType = ftFloat then
                      ExcelOLEWorkSheet.Cells[R, I + 1].FormulaLocal := Field.AsString
                    // cela gère ainsi le format de date français correctement
                    else if Field.DataType in [ftDate, ftDateTime, ftTime] then
                      ExcelOLEWorkSheet.Cells[R, I + 1].FormulaLocal := Field.AsString
                    // Force une chaine ce qui évite la perte des Zéros du RH_NUMSAL ou du MHO_CH_CODE
                    else if Field.DataType = ftString then
                      ExcelOLEWorkSheet.Cells[R, I + 1].Formula := Field.AsString
                    // Les autres données sont recopiées brutes
                    else
                      ExcelOLEWorkSheet.Cells[R, I + 1].Value := Field.AsString;
                  end;
     
                  FDataSet.Next();
                  Inc(R);
     
                  // Patience tout les 16 lignes, c'est suffisant !
                  if not ByteBool(R and $0F) then
                    DoProgress();
                end;
     
              finally
                FDataSet.Bookmark := Bookmark;
              end;
            finally
              FDataSet.EnableControls();
            end;
     
            // Redimensionnement des colonnes par rapport aux données
            for I := Low(ATitles) to High(ATitles) do
              ExcelOLEWorkSheet.Columns[I + 1].AutoFit;
     
            ExcelOLE.Visible := True;
            ExcelOLE.UserControl := True;
            ExcelCaption := ExcelOLE.Caption;
     
            DoBeforeActivateExcel();
     
            // Affiche l'excel lancé !
            SetForegroundWindow(FindWindow(nil, PChar(ExcelCaption)));
     
            // un simple message servira de système d'attente, c'est pauvre mais efficace !
            MessageDlg('Fermer Excel ?', mtConfirmation, [mbOK], 0);
     
          finally
            if not VarIsClear(ExcelOLEWorkbook) then
            begin
              ExcelOLEWorkbook.Close(SaveChanges := False);
              ExcelOLEWorkbook := Unassigned;
            end;
          end;
     
        finally
          if not VarIsClear(ExcelOLE) then
          begin
            if ExcelOLE.WorkBooks.Count = 0 then
              ExcelOLE.Quit;
            ExcelOLE := Unassigned;
          end;
        end;
     
      except
        on EOSE: EOleSysError do
          if EOSE.ErrorCode <> RPC_E_DISCONNECTED then
            raise ESLTExcelDataSetExporterError.CreateFmt('Erreur durant la Fermeture de la Visualisation sous Excel : %s', [EOSE.Message]);
        on E: Exception do
          raise ESLTExcelDataSetExporterError.CreateFmt('Erreur durant la Visualisation sous Excel : %s', [E.Message]);
      end;
    end;
     
    { TSLTExcelListViewer }
     
    //------------------------------------------------------------------------------
    constructor TSLTExcelListViewer.Create(AListView: TListView);
    begin
      inherited Create();
     
      FListView := AListView;
     
      if not Assigned(FListView) then
        raise ESLTExcelListViewerError.Create('Aucune donnée disponible pour la Visualisation sous Excel !');
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTExcelListViewer.DoBeforeActivateExcel();
    begin
      if Assigned(FOnBeforeActivateExcel) then
        FOnBeforeActivateExcel(Self);
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTExcelListViewer.DoProgress();
    begin
      if Assigned(FOnProgress) then
        FOnProgress(Self);
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTExcelListViewer.ViewInExcel(const ASheetName: string; const AColumnTypes: array of TVarType);
    const
      TEXT_FORMAT: WideString = '@'; // @ indique un texte, le typage en WideString est nécessaire pour qu'Excel le comprenne correctement
    var
      ExcelOLE: OLEVariant;
      ExcelOLEWorkbook: OLEVariant;
      ExcelOLEWorkSheet: OLEVariant;
      ExcelCaption: string;
      I, R, Rx: Integer;
    begin
      try
        // Code en LateBinding !
        ExcelOLE := System.Win.ComObj.CreateOleObject('Excel.Application');
        try
          ExcelOLE.UserControl := False;
          ExcelOLE.Visible := False;
          ExcelOLEWorkbook := ExcelOLE.WorkBooks.Add;
          try
            // Sheets.Item is one-based !
            for I := ExcelOLEWorkbook.Sheets.Count downto 2 do
              ExcelOLEWorkbook.Sheets.Item[I].Delete;
     
            ExcelOLEWorkSheet := ExcelOLEWorkbook.Sheets.Item[1];
            ExcelOLEWorkSheet.Name := ASheetName;
            // Le passage d'un Texte préfixé par ' vers une Formule permet de forcer le typage texte d'un nombre mais en masquant la guillemet !
            // Je génère Excel directement en OLE
     
            // Cells is one-based !
            Rx := 1;
            for I := 0 to FListView.Columns.Count - 1 do
            begin
              ExcelOLEWorkSheet.Cells.Item[Rx, I + 1].Value := FListView.Columns[I].Caption;
     
              // Force une chaine ce qui évite la perte des Zéros en début d'un texte, comme un code barre par exemple
              if (I <= High(AColumnTypes)) and (AColumnTypes[I] = varString) then
                ExcelOLEWorkSheet.Columns[I + 1].NumberFormat := TEXT_FORMAT;
            end;
     
            // Cells is one-based !
            // Les données en deuxieme ligne
            Rx := 2;
            for R := 0 to FListView.Items.Count - 1 do
            begin
              with FListView.Items[R] do
              begin
                // On ajoute soit
                // - c'est tout et pas seulement les cochés (not FCheckedOnly)
                // - les case à cocher ne sont pas activé dans la liste (not FListView.Checkboxes)
                // - on filtre seulement les cochés (Checked)
                if not FCheckedOnly or not FListView.Checkboxes or Checked then
                begin
                  // 1er colonne c'est le texte
                  ExcelOLEWorkSheet.Cells[Rx, 1].Value := Caption;
                  // les colonnes suivantes c'est les sous-texte
                  for I := 0 to SubItems.Count - 1 do
                    ExcelOLEWorkSheet.Cells[Rx, I + 2].FormulaLocal := SubItems[I];
     
                  Inc(Rx);
                end;
              end;
     
              // Patience tout les 16 lignes, c'est suffisant !
              if not ByteBool(R and $0F) then
                DoProgress();
            end;
     
            // Redimensionnement des colonnes par rapport aux données
            for I := 0 to FListView.Columns.Count - 1 do
              ExcelOLEWorkSheet.Columns[I + 1].AutoFit;
     
            ExcelOLE.Visible := True;
            ExcelOLE.UserControl := True;
            ExcelCaption := ExcelOLE.Caption;
     
            DoBeforeActivateExcel();
     
            // Affiche l'excel lancé !
            SetForegroundWindow(FindWindow(nil, PChar(ExcelCaption)));
     
            // un simple message servira de système d'attente, c'est pauvre mais efficace !
            MessageDlg('Fermer Excel ?', mtConfirmation, [mbOK], 0);
     
          finally
            if not VarIsClear(ExcelOLEWorkbook) then
            begin
              ExcelOLEWorkbook.Close(SaveChanges := False);
              ExcelOLEWorkbook := Unassigned;
            end;
          end;
     
        finally
          if not VarIsClear(ExcelOLE) then
          begin
            if ExcelOLE.WorkBooks.Count = 0 then
              ExcelOLE.Quit;
            ExcelOLE := Unassigned;
          end;
        end;
     
      except
        on EOSE: EOleSysError do
          if EOSE.ErrorCode <> RPC_E_DISCONNECTED then
            raise ESLTExcelListViewerError.CreateFmt('Erreur durant la Fermeture de la Visualisation sous Excel : %s', [EOSE.Message]);
        on E: Exception do
          raise ESLTExcelListViewerError.CreateFmt('Erreur durant la Visualisation sous Excel : %s', [E.Message]);
      end;
    end;
     
     
    end.

  8. #8
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 815
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 815
    Points : 13 531
    Points
    13 531
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Voir la proposition de AndNotOr, gérant un système avec des threads !
    Au lieu d'attendre en INFINITE, je laisse un délai d'une seconde
    Ce qui veut dire laisser l'application externe sans surveillance pendant quelques instants (le temps d'une boucle). Des bugs sporadiques en perspective !
    Si on veut permettre à la tâche de quitter proprement en cours de programme, il faudrait passer par WaitForMultipleObjects en ajoutant le test sur un event, mais en laissant absolument INFINITE.

    Quitter l'application ne causerait par contre aucun problème, la tâche est simplement terminée (mais abruptement il est vrai) sur ExitProcess, WaitForSingleObject ou pas

  9. #9
    Membre averti
    Avatar de castorcharly
    Homme Profil pro
    Chef de projet
    Inscrit en
    Février 2009
    Messages
    423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projet
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Février 2009
    Messages : 423
    Points : 300
    Points
    300
    Par défaut
    Merci à tous les deux de vous être penché sur mon problème.

    Je regarde vos propositions ou direction de recherche et je reviens vers vous.

  10. #10
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 675
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 675
    Points : 25 487
    Points
    25 487
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Ce qui veut dire laisser l'application externe sans surveillance pendant quelques instants (le temps d'une boucle). Des bugs sporadiques en perspective !
    Oui, on laisse sans surveillance, le temps du scan de Documents[]
    Vu ce que l'on fait entre deux Wait...Object, je ne pense pas qu'il puisse y avoir un bug lié à la surveillance, puisque l'on change de technique de surveillance (Process->OLE)

    C'est vrai un bug pourrait être présent selon comment est géré "Word.Application" et donc Documents[]

    Si "Word.Application" est global à toutes les instances de Word, cela fonctionne, on peut faire confiance à GetActiveOleObject
    Si "Word.Application" est différent pour chaque instance de Word, on ne sait pas si celle fourni par GetActiveOleObject serait celle lancé par ShellExecuteEx du thread A, celle du ShellExecuteEx du thread B ou même un Word ouvert par l'utilisateur ???


    Pour le cas ["Word.Application" est différent pour chaque instance de Word"]
    Je n'ai AUCUNE idée pour surveiller un document dans word !

    Pour le cas ["Word.Application" est global à toutes les instances de Word"]
    @AndNotOr, c''était ma seule préoccupation, la gestion au niveau Documents[] et pas au niveau Processus,
    c'est peut-être cela que tu n'as pas compris dans ma proposition

    Donc on va récupéra très rapidement reprendre une surveillance, juste le délai entre WaitForSingleObject et GetActiveOleObject
    Oui en programmation multi-thread, normalement, il faut gérer finement l'état de son event avec SetEvent, ResetEvent ... blablabla... je connais, j'ai maintenu des serveurs qui était codé ainsi pendant presque 3 ans
    Je ne vois tes "bugs sporadiques" qu'à ce moment et l'utilisation OLE devrait résoudre le problème

    Explorons les différents cas

    si Word s'est fermé, il ne fournira pas d'instance donc autant sortir du thread, on sait que Word n'existe plus
    Fini sans bug (catch de EOleSysError)

    Si Word est ouvert, il fourni un Objet Ole
    - Si Documents[] ne contient pas le document c'est que l'on a juste fermé le document mais sans fermer Word et donc autant sortir du thread, on sait que ce document n'existe plus
    Fini sans bug

    Si Word est ouvert, il fourni un Objet Ole
    - Si Documents[] contient bien le fichier que l'on surveille
    - - Si pendant que l'on utilise l'Objet Ole, Word est fermé, cela provoquera aux appels en LateBinding une EOleSysError Disconnected, donc autant sortir du thread, on sait que ce document ou ce word n'existe plus
    Fini sans bug (catch de EOleSysError)

    Si Word est ouvert, il fourni un Objet Ole
    - Si Documents[] contient bien le fichier que l'on surveille
    - - Si tout se déroule correctement avec cet Objet Ole,
    - - - Au prochain WaitForSingleObject,
    - - - Si Word est toujours, on reprend une attente
    - - - Si le handle n'existe plus plus car Word a été fermé entre le moment où l'on a relaché l'Objet Ole et l'appel de l'API, cela devrait renvoyer un WAIT_FAILED donc il faut sortir du thread, on sait que ce word n'existe plus !
    Fini sans bug

    Pour avoir ces "bugs sporadiques", il faudrait que WaitForSingleObject soit mal codé
    Exemple : si l'on tente une attente un handle en cours de fermeture et que WaitForSingleObject se plante lamentablement !
    Peu probable qu'une fonction clé de l'OS soit buggée à ce point !


    Donc le 1er truc à vérifier c'est comment ce comporte Word.Application
    Puis est-ce stable si l'on parcourt Documents[] d'un même Word.Application dans plusieurs thread séparés ?

    On peut penser que c'est bien conçu puisqu'un tas d'application peuvent utiliser GetActiveOleObject et manipuler le Word déjà lancé !


    Citation Envoyé par Andnotor Voir le message
    Si on veut permettre à la tâche de quitter proprement en cours de programme, il faudrait passer par WaitForMultipleObjects en ajoutant le test sur un event, mais en laissant absolument INFINITE
    tu évoques l'abandon de l'attente,
    je suis d'accord avec l'utilisation d'un Event indiquant que l'on arrête la surveillance,
    c'est effectivement ce que j'utilisais aussi des les programmes Serveurs que je maintenais dans mon précédent emploi.

    Mais ce n'est pas ce que j'évoquais !
    Word a une façon bien particulière de gérer ses instances et il faut séparer Word et les documents ouverts dans Word

    Car on peut fermer un document SANS fermer Word !
    Du coup, ton programme va attendre la fin d'une instance de Word qui contient peut-être maintenant d'autres fichiers ouvert par l'utilisateur mais pas le document que tu surveilles.
    ton application reste bloquer sans raison apparente

    Je n'ai pas utiliser Word via ShellExecuteEx donc il y a peut-être plusieurs choses à vérifier avant de coder !

    Est-ce qu'il va fournir autant d'instance que d'appel de ShellExecuteEx ?
    Si oui, la solution de AndNotOr avec INFINITE est tout à fait satisfaisante
    Si non, il faut donc bien vérifier dans une instance quels sont les fichiers ouverts

  11. #11
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 815
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 815
    Points : 13 531
    Points
    13 531
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    tu évoques l'abandon de l'attente,
    je suis d'accord avec l'utilisation d'un Event indiquant que l'on arrête la surveillance, ...
    Je ne doute pas que tu saches, mais ça peut donner des idées à d'autres lecteurs
    J'évoquais seulement un principe général sans penser particulièrement à MS Office. Il y a certainement mieux à faire si on reste très spécifique.

    Citation Envoyé par ShaiLeTroll Voir le message
    ...
    Mais ce n'est pas ce que j'évoquais !
    Yo ! Pas besoin de , j'ai compris

  12. #12
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 675
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 675
    Points : 25 487
    Points
    25 487
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Je ne doute pas que tu saches, mais ça peut donner des idées à d'autres lecteurs
    J'évoquais seulement un principe général sans penser particulièrement à MS Office. Il y a certainement mieux à faire si on reste très spécifique.
    AAAAAaaahhhh
    tu répondais en général sur les bonnes habitudes avec les boucle d'attente, je n'avais pas compris

    Effectivement, je mettais positionner sur la nature vicieuse de MS Office !
    Le mois dernier, j'ai aidé un collègue justement sur MS Excel pour la gestion du document actif qui a changé entre celle de 2010 et la version 2013 (et la couche 365)
    cela faisait échouer une macro affichant des images en fonction d'un code produit

    Attendons les observations de Castor Charly

  13. #13
    Membre averti
    Avatar de castorcharly
    Homme Profil pro
    Chef de projet
    Inscrit en
    Février 2009
    Messages
    423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projet
    Secteur : Biens de consommation

    Informations forums :
    Inscription : Février 2009
    Messages : 423
    Points : 300
    Points
    300
    Par défaut
    Va falloir attendre un peu, je livre d'autres applications ces jours ci...
    Je pense pouvoir me remettre sur ce problème ce WE...

    Mais encore merci, quelque soient les résultats.


Discussions similaires

  1. [AC-2003] Ouverture documents Word, Excel et Txt depuis Access
    Par pol1958 dans le forum VBA Access
    Réponses: 7
    Dernier message: 24/10/2013, 10h58
  2. Aide avec l'ouverture des documents OLE (Word, Excel, pdf etc)
    Par lordrt dans le forum API, COM et SDKs
    Réponses: 9
    Dernier message: 29/09/2009, 07h16
  3. Réponses: 6
    Dernier message: 13/03/2008, 10h47
  4. VBA WORD / EXCEL
    Par forsay1 dans le forum VBA Word
    Réponses: 2
    Dernier message: 22/12/2005, 09h58
  5. Export d'etat ACCESS vers WORD/EXCEL : pb de mise en forme
    Par leguminator dans le forum Access
    Réponses: 2
    Dernier message: 25/10/2005, 11h03

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo