Excel macro om PPT te maken werkt slecht onder Citrix

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • curkey
  • Registratie: Mei 2009
  • Laatst online: 10-09 17:39
Hi,

Ik heb een MS Excel werkboek met verschillende tabs. Elke tab bevat query resultaten die vanuit SAP BW gevuld worden. Op deze BW queries zitten standaard Excel grafieken.

Voor mijn klant heb ik een VBA macro geschreven die alle tabs afgaat en de grafieken copy-paste naar een Powerpoint presentatie. Dit werkt al een aantal jaren uitstekend, maar ik heb gemerkt dat user die onder Citrix werken grote problemen ondervinden.

Sommige grafieken komen niet over, andere bevatten oude resultaten etc. Ik ben er voor 98% van overtuigd dat dit komt door clipboard problemen, dus dat het kopieren van een chart (als image!) nog bezig is als hij hem al probeert te pasten in de PPT.

Ik heb al het een en ander geprobeerd, zoals ik met onderstaande snippets probeer te verduidelijken, en het werkt al een stuk beter dan eerst, maar wie kan mij helpen dit hoofdpijndossier te sluiten??

Korte uitleg verwerking
  • Ik roep eerst de functie IsRemoteSession aan. Deze geeft een True indien je onder Citrix werkt. In dat geval zet ik ScreenUpdating op True (normaal kan deze op False staan)
  • Ik loop over de slides in mijn PPT. Per slide zoek ik de juiste chart op in de Excel en die copy-paste ik dan als image.
  • Voor en na de copy paste doe ik een expliciete ClearClipboard() aanroep, dit om te zorgen dat een vorige grafiek niet nogmaals gepaste kan worden.
  • Na elke copy/paste stap doe ik een expliciete DoEvents() om het clipboard tijd te geven te synchroniseren
  • Tenslotte nog wat move en resize operaties om de chart op de juiste plek te zetten.
Main loop
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   ' When running under Citrix, sync issues occur when copy-pasting the charts
   ' and it can be solved by leaving screenupdating switched on
   If IsRemoteSession() = True Then
      Application.ScreenUpdating = True
   Else
      Application.ScreenUpdating = False
   End If

   ' Loop over slides in PPT template
   For Each pptSlide In pptPres.Slides
      Select Case pptSlide.Name
         Case "Slide1"
            ' Title page
            Call generateSlide1(pptSlide, projectID)
            
         ' (...) Many more
      End Select
   Next


generateSlide1
Visual Basic:
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
   Private Sub generateSlide1(pptSlide As Object)
   Dim objName As String
   Dim updScreen As Boolean
   Dim shShape As Object
   Dim rng As Range, item As Range
   
   On Error Resume Next
   
   ' Copy the Gantt chart
   ' --------------------
   ActiveWorkbook.Sheets("Schedule (G)").Activate
   If Range("rngSchedule").Rows.Count > 1 Then
      
      'Preserve ScreenUpdating status
      updScreen = Application.ScreenUpdating
      Application.ScreenUpdating = True 'Otherwise CopyPicture of cell data does not work
      
      Call ClearClipboard
      ActiveSheet.Range("rngGanttChart").Select
      Selection.CopyPicture Appearance:=xlScreen, Format:=xlBitmap  ' Copy the range as xlBitmap, not xlPicture
                                                                    ' to prevent clipping when Gantt is large
      DoEvents ' Make sure clipboard can be synchronized
            
      objName = pptSlide.Shapes.Paste.Name
      
      DoEvents ' Make sure clipboard can be synchronized
      
      'Reset screenupdating to what it was
      Application.ScreenUpdating = updScreen
      
      pptSlide.Shapes(objName).LockAspectRatio = msoTrue
      pptSlide.Shapes(objName).Left = 2.5
      pptSlide.Shapes(objName).Top = 28
      
      ' Make sure the item fits in the bounding box
      pptSlide.Shapes(objName).Width = 720
      If pptSlide.Shapes(objName).Height > 62 Then
          pptSlide.Shapes(objName).Height = 62
      End If
      
      ' Clear the text box. In case the Gantt chart is smaller than whole width,
      ' part of this text becomes visible
      pptSlide.Shapes("Text Box 47").TextFrame.TextRange.Text = ""
   Else
      ' In case no Gantt could be generated
      pptSlide.Shapes("Text Box 47").TextFrame.TextRange.Text = msNODATAFOUNDREWORK
   End If

    ' (....) etc etc


De functies IsRemoteSession en ClearClipboard heb ik met wat googlen op Citrix fora en zo als volgt gedefinieerd:

IsRemoteSession
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Public Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As Long

Public Const c_SM_REMOTESESSION As Long = 4096, _
             c_SM_REMOTECONTROL As Long = 8193

Public Function IsRemoteSession() As Boolean
   Dim lSession As Long
   
   ' Test if running under WTS
   lSession = GetSystemMetrics(c_SM_REMOTESESSION)
   
   If lSession <> 0 Then
      IsRemoteSession = True
   Else
      IsRemoteSession = False
   End If
End Function


ClearClipboard
Visual Basic:
1
2
3
4
5
6
7
8
9
Public Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function EmptyClipboard Lib "user32" () As Long
Public Declare Function CloseClipboard Lib "user32" () As Long

Public Sub ClearClipboard()
    OpenClipboard (0&)
    EmptyClipboard
    CloseClipboard
End Sub

[ Voor 0% gewijzigd door curkey op 21-04-2010 12:28 . Reden: Syntax highlighting on :-) ]


Acties:
  • 0 Henk 'm!

  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 17:15

Reptile209

- gers -

Mocht je er via PPT niet uitkomen, is het dan geen optie om de plaatjes naar een PDF te laten printen en die als fullscreen 'slides' te gebruiken? Dat is waarschijnlijk een wat makkelijkere route dan waar je nu mee bezig bent. Het opmaken van de printinstellingen van een Excelsheet is een heel stuk makkelijker dan deze (toch best indrukwekkende) macro's. Een en ander hangt natuurlijk ook af van het verdere gebruik van de slides, dus dat moet je zelf even beoordelen.

Zo scherp als een voetbal!


Acties:
  • 0 Henk 'm!

  • Bolukan
  • Registratie: Oktober 2002
  • Laatst online: 23-08 23:43
Ik zie wel de copy (lijn 20) maar niet de paste. Komt dat na 49? De tijd tussen die 2 kan ook het probleem geven?

Verder kopieer je een range en niet het chartobject. Als op de citrix de clipboard wordt gemangeld, maakt het niet uit, maar gebruik van het chartobject is iets mooier.

PS: Misschien staat in de Excel van de Citrix Application.Calculation niet goed. Data wijzigen leidt dan niet tot formulewijziging die weer in de grafiek worden verwerkt. Zet Calculation aan of doe even ExcelApplication.Calculate

[ Voor 29% gewijzigd door Bolukan op 21-04-2010 16:37 ]


Acties:
  • 0 Henk 'm!

  • curkey
  • Registratie: Mei 2009
  • Laatst online: 10-09 17:39
Bolukan schreef op woensdag 21 april 2010 @ 16:33:
Ik zie wel de copy (lijn 20) maar niet de paste. Komt dat na 49? De tijd tussen die 2 kan ook het probleem geven?
Bij de Copy op regel 20 hoort de Paste op regel 24. Ik kopieer de Name van de shape gelijk naar een variabele, omdat ik de shape in de PPT na het pasten nog verder wil bewerken.
Verder kopieer je een range en niet het chartobject. Als op de citrix de clipboard wordt gemangeld, maakt het niet uit, maar gebruik van het chartobject is iets mooier.

PS: Misschien staat in de Excel van de Citrix Application.Calculation niet goed. Data wijzigen leidt dan niet tot formulewijziging die weer in de grafiek worden verwerkt. Zet Calculation aan of doe even ExcelApplication.Calculate
Hi,

Goed gezien en slecht van mij. Er is 1 tab waar een soort Gantt chart gemaakt wordt door vakjes in de Excel in te kleuren. Dat is wat ik in het voorbeeld heb staan.

My bad, maar het zou voor het eindresultaat niets uit moeten maken: van deze cellen doe ik een CopyPicture, dus op het clipboard staat gewoon een bitmap.

Hieronder een voorbeeld voor het copy-pasten van een Chart object.
Visual Basic:
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
   ActiveWorkbook.Sheets("Milestone Trend").Activate
   If ActiveSheet.ChartObjects.Count > 0 Then
               
      If Range("rngMilestoneTrend").Rows.Count > 1 Then
         Call ClearClipboard
         ActiveSheet.ChartObjects(1).Activate
         ActiveChart.ChartArea.Select
         ActiveChart.CopyPicture Appearance:=xlPrinter, Size:=xlScreen, Format:=xlPicture
                  
         DoEvents ' Make sure clipboard can be synchronized

         objName = pptSlide.Shapes.Paste.Name
                  
         DoEvents ' Make sure clipboard can be synchronized
                  
         pptSlide.Shapes(objName).LockAspectRatio = msoTrue
         pptSlide.Shapes(objName).Left = 138
         pptSlide.Shapes(objName).Top = 28
         pptSlide.Shapes(objName).Width = 715
         pptSlide.Shapes(objName).Height = 438
      Else
        pptSlide.Shapes("Text Box 58").TextFrame.TextRange.Text = msNODATAFOUNDREWORK
      End If
   Else
      pptSlide.Shapes("Text Box 58").TextFrame.TextRange.Text = msNODATAFOUNDREWORK
   End If
   Range("rngMilestoneTrend").Cells(1, 1).Offset(1, 0).Select

Acties:
  • 0 Henk 'm!

  • curkey
  • Registratie: Mei 2009
  • Laatst online: 10-09 17:39
Reptile209 schreef op woensdag 21 april 2010 @ 15:15:
Mocht je er via PPT niet uitkomen, is het dan geen optie om de plaatjes naar een PDF te laten printen en die als fullscreen 'slides' te gebruiken? Dat is waarschijnlijk een wat makkelijkere route dan waar je nu mee bezig bent. Het opmaken van de printinstellingen van een Excelsheet is een heel stuk makkelijker dan deze (toch best indrukwekkende) macro's. Een en ander hangt natuurlijk ook af van het verdere gebruik van de slides, dus dat moet je zelf even beoordelen.
Hi,

Dat is helaas geen optie. De PPT is een tempate met diverse placeholders voor de grafieken.

Verder zijn het niet de plaatjes zelf die het probleem vormen, want op een reguliere PC werkt het goed. Onder Citrix gaat het vaak goed, maar behoorlijk vaak ook niet. Helaas alleen niet reproduceerbaar. Bij sommige mensen verschijnen er geen grafieken, bij anderen op de verkeerde plaats of anderszins verminkt.

Acties:
  • 0 Henk 'm!

  • kunnen
  • Registratie: Februari 2004
  • Niet online
Haal allereerst eens die vreselijke
Visual Basic .NET:
1
On Error Resume Next
weg aub.

Acties:
  • 0 Henk 'm!

  • curkey
  • Registratie: Mei 2009
  • Laatst online: 10-09 17:39
ThomasB schreef op woensdag 21 april 2010 @ 18:51:
Haal allereerst eens die vreselijke
Visual Basic .NET:
1
On Error Resume Next
weg aub.
De code is uit een productieversie van het rapport. Op bepaalde plekken zit wel error afhandeling, maar op sommige plekken heb ik liever niet dat gebruikers de [OK] [Debug] [Cancel] te zien krijgen met een waardeloze foutmelding.

Ik heb het rapport wel eens door wat mensen laten testen waarbij de On Error eruit gehaald was, maar er kwam niet veel bruikbaars uit w.b. de issues die ik met Citrix heb ervaren.

Van alle programmeertalen die ik ken, moet ik wel zeggen dat VBA wel de grootste drol is waar ik mee heb mogen werken. Wat onder 2000 werkt, doet het niet onder 2003 of 2007, of heel anders etc etc. Maar goed, dat is weer een andere discussie waar ik nu geen flame bait voor wil uitzetten :-)

Acties:
  • 0 Henk 'm!

  • kunnen
  • Registratie: Februari 2004
  • Niet online
curkey schreef op donderdag 22 april 2010 @ 19:02:
[...]


De code is uit een productieversie van het rapport. Op bepaalde plekken zit wel error afhandeling, maar op sommige plekken heb ik liever niet dat gebruikers de [OK] [Debug] [Cancel] te zien krijgen met een waardeloze foutmelding.

Ik heb het rapport wel eens door wat mensen laten testen waarbij de On Error eruit gehaald was, maar er kwam niet veel bruikbaars uit w.b. de issues die ik met Citrix heb ervaren.

Van alle programmeertalen die ik ken, moet ik wel zeggen dat VBA wel de grootste drol is waar ik mee heb mogen werken. Wat onder 2000 werkt, doet het niet onder 2003 of 2007, of heel anders etc etc. Maar goed, dat is weer een andere discussie waar ik nu geen flame bait voor wil uitzetten :-)
Dan bouw je custom errorafhandeling in, desnoods log je de errors naar een bestand en ga je daarna door, maar in zo'n stukje code horen gewoon geen errors voor te kunnen komen. Alles doodmeppen is natuurlijk absoluut not done, een error komt niet zonder reden |:(

Acties:
  • 0 Henk 'm!

  • Bolukan
  • Registratie: Oktober 2002
  • Laatst online: 23-08 23:43
Bolukan schreef op woensdag 21 april 2010 @ 16:33:
Misschien staat in de Excel van de Citrix Application.Calculation niet goed. Data wijzigen leidt dan niet tot formulewijziging die weer in de grafiek worden verwerkt. Zet Calculation aan of doe even ExcelApplication.Calculate
Hier ook al naar gekeken?

Acties:
  • 0 Henk 'm!

  • curkey
  • Registratie: Mei 2009
  • Laatst online: 10-09 17:39
Bolukan schreef op vrijdag 23 april 2010 @ 07:49:
[...]

Hier ook al naar gekeken?
De calculate functie heb ik niet expliciet aangeroepen, maar ik vermoed dat het ook niet gaat helpen.

Ik heb namelijk al een aparte button op een sheet gezet om het genereren van de PPT manueel te triggeren. De grafieken en data areas zijn dan al gevuld door de SAP BEx add-in, die de verschillende rapporten op de sheets ververst.

Dus ondanks dat in de Excel de grafieken er goed uitzien op dat moment, komt dat niet altijd goed over in de PPT.

Acties:
  • 0 Henk 'm!

  • curkey
  • Registratie: Mei 2009
  • Laatst online: 10-09 17:39
Na een aantal dagen heftig Googlen en debuggen ben ik iets opgeschoten.

Op sommige momentel (maar helaas niet consistent reproduceerbaar) kan ik het probleem als volgt laten optreden.
  • Ik log aan op mijn remote server via RDP (terminal sessie dus).
  • Daar open ik mijn Excel werkboek en ik log aan op de SAP BW server.
  • Om de BW queries te refreshen moet ik een selectie opgeven. Ik heb op mijn lokale pc een text file met de items, die ik copy-paste naar het klembord.
  • In mijn remote sessie doe ik een paste en ik start de queries.
  • Na de refresh trigger ik het genereren van de powerpoint en aldaar zie ik errors.
De errors die ik dan voorbij zie komen zijn o.a. WinAPI errors 8 (Not enough storage is available to process this command) en 1418 (Thread does not have a clipboard open).

Het lijkt er welhaast op dat het clipboard gelockt blijft op het moment dat ik een waarde van de lokale naar de remote machine kopieer.

Ik kwam wat blogs tegen waar het probleem uitgelegd wordt:Ik weet alleen niet hoe ik dit moet tackelen vanuit een VBA macro. Iemand een briljant idee?

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Kun je niet beter het hele clipboard achterwege laten, en gewoon de activechart desnoods Export(eren) naar een plaatjesbestand wat je in powerpoint weer inlaad? Het is sowieso eigenlijk slecht dat je het clipboard kaapt van de gebruiker, en als het ook nog eens zoveel problemen geeft zou ik het al helemaal achterwege laten.. :p

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • urk_forever
  • Registratie: Juni 2001
  • Laatst online: 18:27
curkey schreef op dinsdag 11 mei 2010 @ 15:00:
Na een aantal dagen heftig Googlen en debuggen ben ik iets opgeschoten.

Op sommige momentel (maar helaas niet consistent reproduceerbaar) kan ik het probleem als volgt laten optreden.
  • Ik log aan op mijn remote server via RDP (terminal sessie dus).
  • Daar open ik mijn Excel werkboek en ik log aan op de SAP BW server.
  • Om de BW queries te refreshen moet ik een selectie opgeven. Ik heb op mijn lokale pc een text file met de items, die ik copy-paste naar het klembord.
  • In mijn remote sessie doe ik een paste en ik start de queries.
  • Na de refresh trigger ik het genereren van de powerpoint en aldaar zie ik errors.
De errors die ik dan voorbij zie komen zijn o.a. WinAPI errors 8 (Not enough storage is available to process this command) en 1418 (Thread does not have a clipboard open).

Het lijkt er welhaast op dat het clipboard gelockt blijft op het moment dat ik een waarde van de lokale naar de remote machine kopieer.

Ik kwam wat blogs tegen waar het probleem uitgelegd wordt:Ik weet alleen niet hoe ik dit moet tackelen vanuit een VBA macro. Iemand een briljant idee?
De functies die in de eerste link getoond worden zijn gewoon twee api functies:

SetClipboardViewer
GetClassName

Die kan je gewoon vanuit je macro aanroepen.

Hail to the king baby!


Acties:
  • 0 Henk 'm!

Verwijderd

je kan ofwel zelf de nodige api's (findwindow of beter: enumwindows) uit de blog die de fix meldt in vba vertalen, ofwel het gecompileerde rdpfixclip met een shellexecute oproepen.
misschien nog beter om heel het gedoe met het clipboard te omzeilen, is het tekstbestand zelf rechtstreeks inlezen met excel.

Acties:
  • 0 Henk 'm!

  • curkey
  • Registratie: Mei 2009
  • Laatst online: 10-09 17:39
Verwijderd schreef op dinsdag 11 mei 2010 @ 15:40:
je kan ofwel zelf de nodige api's (findwindow of beter: enumwindows) uit de blog die de fix meldt in vba vertalen, ofwel het gecompileerde rdpfixclip met een shellexecute oproepen.
misschien nog beter om heel het gedoe met het clipboard te omzeilen, is het tekstbestand zelf rechtstreeks inlezen met excel.
In de text file staan parameters die ik aan een add-in moet doorgeven om het SAP BW rapport te genereren. Denk bijvoorbeeld aan de rapportagemaand en de cost center waar ik getallen van wil zien.

Die waarden copy-paste ik dus naar mijn remote sessie waar mijn Excel rapport staat, en ik start de queries die mijn werkboek vullen met data.

De laatste stap is dan mijn macro, waarmee ik een powerpoint maak van de diverse grafieken en tabellen in de Excel. Dat laatste levert dus soms problemen op als je remote werkt (RDP / Citrix), omdat het clipboard dan soms blokkeert. Grafieken of tabellen worden dan niet gekopieerd.

[licht cynisch]
Ik moet wel zeggen dat ik ongevraagd een heleboel heb bijgeleerd. Ik ben als VBA-noob gestart om een "simpele" VBA macro te maken voor een dashboard, maar nu zit ik de halve WinAPI door te spitten om een clipboard fatsoenlijk werkend te krijgen... Moet ik straks maar mijn eigen RDP server gaan schrijven? 8)7
[/licht cynisch]

Maar goed, het kan nooit kwaad een beetje multi-disciplinair inzetbaar te zijn :P

Edit:
Hier even een overzicht van de error messages die zoal voorbijkwamen:

TimestampRoutineMessage
20100512 160806ClearClipboardWinAPI error: 1418 Thread does not have a clipboard open.
20100512 160806generateSlide118Error Gantt copy: 1004 Cannot empty the Clipboard.
20100512 160806generateSlide118Error Gantt paste -2147417851 Method 'Paste' of object 'Shapes' failed
20100512 161035ClearClipboardWinAPI error: 8 Not enough storage is available to process this command.

De eerste en de laatste melding zijn bij het clearen van het clipbord. De middelste twee meldingen verschenen bij het kopieren respectievelijk pasten van een chart (Copy as picture).

Verder mijn vraag: ik heb nu de functies gemaakt voor SetClipboardViewer etc en ze werken zonder fouten.

Waar ik nu vastloop door mijn gebrek aan kennis van de WinAPI is hoe ik het beste eenvoudig door de windows kan loopen om degene die van klasse RdpClipRdrWindowClass zijn opnieuw in de Clipboard chain te hangen. Tenminste, dat is wat ik denk dat er moet gebeuren, right? Of moet ik alleen maar de vensters van mijn Excel instance te doen? En hoe vind ik die dan?

Ik ga me de komende tijd verder verdiepen in de Windows API calls versus VBA, maar ik zit nu met tijdsdruk omdat het rapport deze week af moet, dus enige hulp wordt ten zeerste gewaardeerd _/-\o_

[ Voor 34% gewijzigd door curkey op 12-05-2010 16:56 ]

Pagina: 1