Σύνοψη
Αυτό το άρθρο παρέχει απαντήσεις σε συνήθεις ερωτήσεις σχετικά με την αυτοματοποίηση στο Microsoft Office από το Visual C++.
Περισσότερες πληροφορίες
Πίνακας περιεχομένων
-
Τι είναι ο αυτοματισμός;
-
Είμαι νέος χρήστης της Αυτοματοποίησης, πού μπορώ να βρω καλούς πόρους για να μάθω περισσότερα;
-
Υπάρχουν διαφορετικοί τρόποι με τους οποίους μπορώ να χρησιμοποιήσω την αυτοματοποίηση;
-
Τι είναι η COM;
-
Πώς γίνεται επισυνάψετε στην τρέχουσα παρουσία μιας εφαρμογής του Office;
-
Πώς γίνεται περάσετε προαιρετικές παραμέτρους;
-
Πώς γίνεται συμβάντα που εκτέθηκαν από τις εφαρμογές του Office;
-
Ο κώδικας αυτοματισμού μου είναι πολύ αργός. Πώς μπορώ να επιταχύνω τα πράγματα;
-
Τι σημαίνουν αυτές οι τεράστιες τιμές σφάλματος, όπως -2147352573 ή 0x80030002;
-
Τι είναι μια βιβλιοθήκη τύπων;
-
Ο κώδικας αυτοματισμού μου λειτούργησε με το Microsoft Excel 95, αλλά αποτυγχάνει με το Microsoft Excel 97. Why?
-
Γιατί η εφαρμογή που αυτοματοποιώ παραμένει στη μνήμη μετά την ολοκλήρωση του προγράμματός μου;
-
Ξέρω τι θέλω να κάνω ως χρήστης της εφαρμογής του Microsoft Office, αλλά πώς μπορώ να το κάνω αυτό μέσω προγραμματισμού χρησιμοποιώντας αυτοματοποίηση;
-
Μπορώ να αυτοματοποιήσω μια ενσωματωμένη εφαρμογή του Microsoft Office;
-
Πώς γίνεται πρόσβαση στις ιδιότητες του εγγράφου μου σε ένα έγγραφο του Microsoft Office;
Ερωτήσεις και απαντήσεις
-
Τι είναι ο αυτοματισμός;
Η αυτοματοποίηση (πρώην αυτοματοποίηση OLE) είναι μια τεχνολογία που σας επιτρέπει να επωφεληθείτε από τις λειτουργίες ενός υπάρχοντος προγράμματος και να το ενσωματώσετε στις δικές σας εφαρμογές. Για παράδειγμα, μπορείτε να χρησιμοποιήσετε τις δυνατότητες ορθογραφικού και γραμματικού ελέγχου του Microsoft Word στην εφαρμογή σας χωρίς να είναι ορατό στους χρήστες σας το Microsoft Word. Μπορείτε ακόμη και να χρησιμοποιήσετε όλα τα εργαλεία γραφημάτων, εκτύπωσης και ανάλυσης δεδομένων του Microsoft Excel. Αυτή η τεχνολογία μπορεί να απλοποιήσει και να επιταχύνει σημαντικά την ανάπτυξή σας. -
Είμαι νέος χρήστης της Αυτοματοποίησης, πού μπορώ να βρω καλούς πόρους για να μάθω περισσότερα; Το κεφάλαιο 24 του "Inside Visual C++" (ISBN:1-57231-565- 2) του David Kruglinski παρέχει μια γενική επισκόπηση καθώς και μερικά εξαιρετικά παραδείγματα. Επίσης, η Γνωσιακή βάση της Microsoft είναι μια καλή πηγή πληροφοριών.
Εάν προτιμάτε να μάθετε μέσω παραδείγματος, ανατρέξτε στο ακόλουθο άρθρο στη Γνωσιακή βάση της Microsoft:179706 HOWTO Χρήση MFC για την αυτοματοποίηση του Excel & δημιουργία/μορφοποίηση νέου βιβλίου εργασίας
-
Υπάρχουν διαφορετικοί τρόποι με τους οποίους μπορώ να χρησιμοποιήσω την αυτοματοποίηση;
Υπάρχουν τρεις βασικοί τρόποι με τους οποίους μπορείτε να χρησιμοποιήσετε την αυτοματοποίηση: MFC, #import και C/C++:-
Με το MFC, χρησιμοποιήστε το Visual C++ ClassWizard για να δημιουργήσετε "κατηγορίες περιτυλίγματος" από τις βιβλιοθήκες τύπων του Microsoft Office. Αυτές οι τάξεις, καθώς και άλλες τάξεις MFC, όπως COleVariant, COleSafeArray, COleException, απλοποιούν τις εργασίες της Αυτοματοποίησης. Αυτή η μέθοδος συνιστάται συνήθως σε σχέση με τις άλλες και τα περισσότερα παραδείγματα της Γνωσιακής Βάσης της Microsoft χρησιμοποιούν MFC.
-
#import, μια νέα οδηγία που έγινε διαθέσιμη με το Visual C++ 5.0, δημιουργεί "έξυπνους δείκτες" VC++ από μια συγκεκριμένη βιβλιοθήκη τύπων. Είναι πολύ ισχυρό, αλλά συχνά δεν συνιστάται λόγω προβλημάτων αναφοράς- καταμέτρησης που συνήθως παρουσιάζονται όταν χρησιμοποιούνται με τις εφαρμογές του Microsoft Office.
-
Η αυτοματοποίηση C/C++ είναι πολύ πιο δύσκολη, αλλά μερικές φορές είναι απαραίτητη για την αποφυγή επιβάρυνσης με MFC ή προβλημάτων με #import. Βασικά, εργάζεστε με τέτοια API όπως το CoCreateInstance() και διασυνδέσεις COM όπως το IDispatch και το IUnknown.
Είναι σημαντικό να σημειωθεί ότι υπάρχουν ορισμένες μικρές διαφορές μεταξύ της αυτοματοποίησης από C++ σε σύγκριση με την απλή C, επειδή η COM σχεδιάστηκε γύρω από την κατηγορία C++.
-
-
Τι είναι η COM;
Η αυτοματοποίηση βασίζεται στο μοντέλο αντικειμένου στοιχείου (COM). Η αρχιτεκτονική COM είναι μια τυπική αρχιτεκτονική λογισμικού που βασίζεται σε διασυνδέσεις και έχει σχεδιαστεί για να διαχωρίζεται ο κώδικας σε αυτόνομα αντικείμενα. Θεωρήστε το ως μια επέκταση του παραδείγματος αντικειμενοστραφούς προγραμματισμού (OOP), αλλά ισχύει για ξεχωριστές εφαρμογές. Κάθε αντικείμενο εκθέτει ένα σύνολο διασυνδέσεων και όλη η επικοινωνία με ένα αντικείμενο, όπως η προετοιμασία, οι ειδοποιήσεις και η μεταφορά δεδομένων, συμβαίνει μέσω αυτών των διασυνδέσεων. Η COM είναι επίσης ένα σύνολο υπηρεσιών που παρέχονται από βιβλιοθήκες δυναμικής σύνδεσης (DLL) που έχουν εγκατασταθεί με το λειτουργικό σύστημα. Η αυτοματοποίηση χρησιμοποιεί πολλές από αυτές τις υπηρεσίες. Ένα παράδειγμα είναι η υπηρεσία "Marshalling", η οποία συσκευάζει τις κλήσεις της εφαρμογής-πελάτη στις λειτουργίες μελών των διασυνδέσεων της εφαρμογής διακομιστή και τις μεταβιβάζει, με τα ορίσματα, στην εφαρμογή διακομιστή. Κάνει να φαίνεται ότι τα περιβάλλοντα εργασίας του διακομιστή εκτίθενται στο χώρο μνήμης του υπολογιστή-πελάτη, κάτι που δεν συμβαίνει όταν ο υπολογιστής-πελάτης είναι ένας .exe που εκτελείται στο δικό του χώρο διεργασίας. Η Marshalling λαμβάνει επίσης τις τιμές επιστροφής από τις μεθόδους του διακομιστή πίσω πέρα από τα όρια της διαδικασίας και με ασφάλεια στα χέρια της κλήσης του πελάτη. Υπάρχουν πολλές άλλες υπηρεσίες που είναι απαραίτητες για την αυτοματοποίηση που παρέχονται από τις διάφορες βιβλιοθήκες COM. Πηγές πληροφοριών σχετικά με αυτές περιλαμβάνουν το "Inside Ole - Second Edition" του Kraig Brockschmidt, το ISBN 1-55615-843-2, το "Inside COM" του Dale Rogerson - ISBN 1-57231-349-8 και την "Αναφορά προγραμματιστή αυτοματισμού", ISBN 1-57231-584-9. -
Πώς γίνεται επισυνάψετε στην τρέχουσα παρουσία μιας εφαρμογής του Office;
Χρησιμοποιήστε το API GetActiveObject(). Οι διακομιστές αυτοματισμού καταχωρούνται στο ROT (Running Object Table), μέσω του API RegisterActiveObject(). Τα προγράμματα-πελάτες αυτοματισμού μπορούν να φτάσουν στην τρέχουσα παρουσία με κώδικα όπως:// Translate server ProgID into a CLSID. ClsidFromProgID // gets this information from the registry. CLSID clsid; CLSIDFromProgID(L"Excel.Application", &clsid); // Get an interface to the running instance, if any.. IUnknown *pUnk; HRESULT hr = GetActiveObject(clsid, NULL, (IUnknown**)&pUnk); ASSERT(!FAILED(hr)); // Get IDispatch interface for Automation... IDispatch *pDisp; hr = pUnk->QueryInterface(IID_IDispatch, (void **)&pDisp); ASSERT(!FAILED(hr)); // Release the no-longer-needed IUnknown... pUnk->Release();
ΣΗΜΕΙΩΣΗ: Εάν υπάρχουν πολλές παρουσίες που εκτελούνται στην εφαρμογή του Office που θέλετε να επισυνάψετε, θα μπορείτε να επισυνάψετε μόνο στην πρώτη παρουσία που ξεκίνησε με χρήση του API GetActiveObject().
Θεωρητικά, μπορείτε να επαναλαμβάνετε τη ΣΉΨΗ για κάθε μεμονωμένη παρουσία, αλλά οι εφαρμογές του Office δεν καταχωρούνται μόνοι τους εάν μια άλλη παρουσία βρίσκεται ήδη στη ΣΉΨΗ, επειδή το παρατσούκλι για τον εαυτό του είναι πάντα το ίδιο (δεν μπορούσε να διακριθεί ούτως ή άλλως). Αυτό σημαίνει ότι δεν μπορείτε να επισυνάψετε καμία παρουσία εκτός από την πρώτη. Ωστόσο, επειδή οι εφαρμογές του Office καταχωρούν επίσης τα έγγραφά τους στη ΣΉΨΗ, μπορείτε να επισυνάψετε με επιτυχία σε άλλες παρουσίες, επαναληπτικά τη ΣΉΨΗ αναζητώντας ένα συγκεκριμένο έγγραφο, επισυνάπτοντας σε αυτό και, στη συνέχεια, αποδίδοντας το αντικείμενο Application από αυτό. Δεν θα χρειαστεί να το κάνετε αυτό για το PowerPoint, επειδή πρόκειται για εφαρμογή μίας παρουσίας. μπορείτε να έχετε μόνο μία παρουσία να εκτελείται. -
Πώς γίνεται περάσετε προαιρετικές παραμέτρους;
Ορισμένες μέθοδοι έχουν "προαιρετικές" παραμέτρους. Στη Visual Basic, μπορείτε να τις παραλείψετε περιστασιακά κατά την κλήση της μεθόδου. Ωστόσο, κατά την κλήση με Visual C++ πρέπει να μεταβιβάσετε μια ειδική ΜΕΤΑΒΛΗΤΗ της οποίας το πεδίο .vt είναι VT_ERROR και το πεδίο .scode είναι DISP_E_PARAMNOTFOUND. Αυτό είναι:// VARIANT used in place of optional-parameters. VARIANT varOpt; varOpt.vt = VT_ERROR; varOpt.scode = DISP_E_PARAMNOTFOUND;
Αυτό ακριβώς κάνει η Visual Basic στο παρασκήνιο.
-
Πώς γίνεται συμβάντα που εκτέθηκαν από τις εφαρμογές του Office;
Βασικά, υλοποιείτε το περιβάλλον εργασίας συμβάντος που θέλετε να πιάσετε (το "νεροχύτη") και στήνετε μια συμβουλευτική σύνδεση με την εφαρμογή (η "πηγή"). Γενικά, για να ρυθμίσετε τη συμβουλευτική σύνδεση, λαμβάνετε το IConnectionPointContainer του διακομιστή και καλείτε το FindConnectionPoint() με το IID του περιβάλλοντος εργασίας συμβάντος. Αυτό σας παρέχει ένα περιβάλλον εργασίας του IConnectionPoint και το μόνο που απομένει είναι να καλέσετε το Advise() με μια παρουσία του περιβάλλοντος εργασίας συμβάντος. Στη συνέχεια, ο διακομιστής θα καλέσει ξανά μέσω αυτού του περιβάλλοντος εργασίας όταν προκύψουν αυτά τα συμβάντα. -
Ο κώδικας αυτοματισμού μου είναι πολύ αργός. Πώς μπορώ να επιταχύνω τα πράγματα;
Μια συνήθης αιτία προβλημάτων ταχύτητας με την Αυτοματοποίηση είναι η επαναλαμβανόμενη ανάγνωση και σύνταξη δεδομένων. Αυτό είναι τυπικό για τα προγράμματα-πελάτες αυτοματοποίησης του Excel. Ωστόσο, οι περισσότεροι χρήστες δεν γνωρίζουν ότι αυτά τα δεδομένα μπορούν συνήθως να συνταχθούν ή να διαβαστούν όλα ταυτόχρονα χρησιμοποιώντας τη συνάρτηση SAFEARRAY. Ανατρέξτε στο ακόλουθο άρθρο της Γνωσιακής βάσης της Microsoft για περισσότερες πληροφορίες και ενημερωτικά παραδείγματα:179706 HOWTO: Χρησιμοποιήστε το MFC για να αυτοματοποιήσετε το Excel και να δημιουργήσετε/μορφοποιήσετε ένα νέο βιβλίο εργασίας Επίσης, είναι σημαντικό να επισημάνετε ότι η χρήση του Προχείρου μπορεί ορισμένες φορές να βελτιώσει τις επιδόσεις. Για παράδειγμα, μπορείτε να αντιγράψετε τα δεδομένα σας στο Πρόχειρο και, στη συνέχεια, να χρησιμοποιήσετε αυτοματοποίηση για να ενημερώσετε το διακομιστή για την επικόλληση. Ή το αντίστροφο. πείτε στο διακομιστή να κάνει αντιγραφή στο πρόχειρο και να κάνει επικόλληση στην εφαρμογή σας.
-
Τι σημαίνουν αυτές οι τεράστιες τιμές σφάλματος, όπως -2147352573 ή 0x80030002;
Αυτές οι τιμές είναι γνωστές ως HRESULTs και ορίζονται στο winerror.h. Οι αριθμοί είναι τόσο μεγάλοι, επειδή το πρώτο bit αντιπροσωπεύει αν πρόκειται για αποτέλεσμα σφάλματος ή όχι. Μπορείτε να χρησιμοποιήσετε το βοηθητικό πρόγραμμα ErrLook.Exe που παρέχεται με το Visual C++ για να μετατρέψετε αυτούς τους αριθμούς σε χαρακτηριστικές περιγραφές. Εάν θέλετε να λάβετε μέσω προγραμματισμού μια περιγραφή για τα σφάλματα, μπορείτε να χρησιμοποιήσετε το API FormatMessage().ΣΗΜΕΙΩΣΗ: Εάν χρησιμοποιείτε Visual C++ 6.0 και έχετε μια μεταβλητή που περιέχει αυτή την τιμή στο παράθυρο παρακολούθησης σφαλμάτων, προσαρτήστε το ", hr" (χωρίς τα εισαγωγικά) για να το μεταφράσει η Visual C++ για εσάς!
-
Τι είναι μια βιβλιοθήκη τύπων;
Μια βιβλιοθήκη τύπων είναι παρόμοια με ένα αρχείο κεφαλίδας C/C++. Περιέχει τις διασυνδέσεις, τις μεθόδους και τις ιδιότητες που δημοσιεύει ένας διακομιστής. Μπορείτε να προβάλετε τη βιβλιοθήκη τύπων με το Πρόγραμμα προβολής αντικειμένων OLE/COM (Oleview.exe) που παρέχεται με το Visual C++. Ακολουθεί μια λίστα με τα ονόματα αρχείων βιβλιοθήκης τύπων για το Microsoft Office 95, 97 και 2000: Office Application | Πληκτρολόγηση βιβλιοθήκης ------------------------+---------------- Word 95 και προηγούμενες | wb70en32.tlb Excel 95 και προηγούμενες | xl5en32.olb PowerPoint 95 και προηγούμενες | Powerpoint.tlb Access 95 και προηγούμενες | msaccess.tlb Ντοσιέ 95 | binder.tlb Προγραμματισμός+ | sp7en32.olb Project | pj4en32.olb | "Διαχείριση ομάδας" mstmgr1.olb Word 97 | msword8.olb Excel 97 | excel8.olb | του PowerPoint 97 msppt8.olb Access 97 | msacc8.olb Ντοσιέ 97 | msbdr8.olb Graph 97 | graph8.olb Outlook 97 | msoutl8.olb Outlook 98 | msoutl85.olb Word 2000 | msword9.olb Excel 2000 | excel9.olb PowerPoint 2000 | msppt9.olb Access 2000 | msacc9.olb Outlook 2000 | msoutl9.olb Word 2002 | msword.olb Excel 2002 | excel.exe PowerPoint 2002 | msppt.olb Access 2002 | msacc.olb Outlook 2002 | msoutl.olb
-
Ο κώδικας αυτοματισμού μου λειτούργησε με το Excel 95, αλλά αποτυγχάνει με το Excel 97. Τι συμβαίνει?
Το μοντέλο αντικειμένων για το Excel έκανε μια σημαντική αλλαγή από την έκδοση 95 στην 97. Το Excel 95 υλοποίησε όλες τις μεθόδους και τις ιδιότητές του σε μια ενιαία εφαρμογή του IDispatch. Αυτό σήμαινε ότι συχνά θα μπορούσατε να καλέσετε μεθόδους που προορίζονται για το αντικείμενο X, από το αντικείμενο Y. Δεν ήταν καλή σχεδίαση, επομένως, στο Office 97, κάθε αντικείμενο έχει τη δική του ξεχωριστή υλοποίηση Idispatch. Αυτό σημαίνει ότι εάν ζητήσετε μια μέθοδο ή ιδιότητα από το αντικείμενο X από ένα ξεχωριστό αντικείμενο Y, λαμβάνετε το σφάλμα 0x80020003, -2147352573, "Το μέλος δεν βρέθηκε". Για να αποφύγετε αυτό το σφάλμα, πρέπει να βεβαιωθείτε ότι το υποκείμενο περιβάλλον εργασίας IDispatch από το οποίο πραγματοποιείτε κλήσεις είναι το πολύ σωστό. -
Η εφαρμογή που αυτοματοποιώ παραμένει στη μνήμη μετά την ολοκλήρωση του προγράμματός μου. Τι συμβαίνει?
Πιθανότατα, αυτό συμβαίνει επειδή έχετε ξεχάσει να αποδεσμεύσετε μια αποκτηθείσα διεπαφή και θα πρέπει να την εντοπίσετε. Ακολουθούν ορισμένες γενικές προτάσεις και πράγματα που πρέπει να αναζητήσετε:-
Εάν χρησιμοποιείτε #import, είναι πολύ πιθανό να αντιμετωπίσετε ένα από τα σφάλματα καταμέτρησης αναφορών που σχετίζονται με αυτό. Συχνά τα σφάλματα μπορούν να επιλυθούν, αλλά συνήθως προτιμάται να χρησιμοποιήσετε μία από τις άλλες μεθόδους αυτοματοποίησης. #import δεν λειτουργεί πολύ καλά με τις εφαρμογές του Office, επειδή οι βιβλιοθήκες τύπου και η χρήση τους είναι αρκετά περίπλοκες. Επίσης, είναι δύσκολο να εντοπιστούν τέτοια προβλήματα καταμέτρησης αναφορών, επειδή πολλές από τις κλήσεις COM σε επίπεδο περιβάλλοντος εργασίας βρίσκονται στο παρασκήνιο κατά τη χρήση #import.
-
Ελέγξτε αν καλείτε μεθόδους, όπως Open ή New, που επιστρέφουν ένα IDispatch * (LPDISPATCH) και παραβλέπουν την τιμή επιστροφής. Εάν είστε, τότε εγκαταλείπετε αυτό το περιβάλλον εργασίας που επιστρέφεται και θα πρέπει να αλλάξετε τον κωδικό σας, ώστε να τον αποδεσμεύσετε όταν δεν είναι πλέον απαραίτητο.
-
Προσθέστε σταδιακά σχόλια σε ενότητες του κώδικα μέχρι να εξαφανιστεί το πρόβλημα και, στη συνέχεια, προσθέστε το ξανά με σύνεση για να εντοπίσετε πού ξεκινά το πρόβλημα.
-
Σημειώστε ότι ορισμένες εφαρμογές θα παραμείνουν σε λειτουργία εάν ο χρήστης έχει "αγγίξει" την εφαρμογή. Εάν συμβεί αυτό ενώ αυτοματοποιείτε, η εφαρμογή πιθανότατα θα παραμείνει σε λειτουργία μετά. Οι εφαρμογές του Office έχουν μια ιδιότητα "UserControl" στο αντικείμενο Application που μπορείτε να διαβάσετε/γράψετε για να αλλάξετε αυτή τη συμπεριφορά.
-
Επίσης, ορισμένες εφαρμογές θα αποφασίσουν να συνεχίσουν να λειτουργούν εάν έχει γίνει αρκετή "ενέργεια" περιβάλλοντος εργασίας χρήστη. Εάν σκοπεύετε να εξέλθετε από την εφαρμογή, καλέστε τη μέθοδο Quit() στο αντικείμενο Application. Το Word θα κλείσει ανεξάρτητα από το πλήθος αναφορών όταν καλείται τερματισμός. Αυτή δεν είναι η αναμενόμενη συμπεριφορά COM. Το Excel, ωστόσο, απλώς αποκρύπτεται, αλλά παραμένει σε λειτουργία μέχρι να κυκλοφορήσουν όλα τα εξαιρετικά περιβάλλοντα εργασίας. Γενικά, θα πρέπει να αποδεσμεύετε όλες τις εκκρεμείς αναφορές και να καλείτε το στοιχείο Quit() μόνο εάν σκοπεύετε να κλείσετε την εφαρμογή.
-
-
Ξέρω τι θέλω να κάνω ως χρήστης εφαρμογής του Office, αλλά πώς μπορώ να το κάνω αυτό μέσω προγραμματισμού μέσω αυτοματοποίησης;
Αυτό που σας ενδιαφέρει είναι ποια αντικείμενα, μεθόδους και ιδιότητες πρέπει να χρησιμοποιήσετε. Ο καλύτερος τρόπος για να μάθετε πώς μπορείτε να περιηγηθείτε στα μοντέλα αντικειμένων του Word, του Excel και του Powerpoint, με βάση αυτό που θέλετε να κάνετε ως χρήστης, είναι να χρησιμοποιήσετε την Καταγραφή μακροεντολής. Απλώς επιλέξτε Macro\'Record New Macro' από το μενού Εργαλεία, εκτελέστε την εργασία που σας ενδιαφέρει και, στη συνέχεια, επιλέξτε Macro\'Stop Recording.' Όταν ολοκληρώσετε την καταγραφή, επιλέξτε Macro\Macros από το μενού Εργαλεία, επιλέξτε τη μακροεντολή που καταγράψατε και, στη συνέχεια, κάντε κλικ στην επιλογή Επεξεργασία. Αυτό θα σας μεταφέρει στον κώδικα VBA που δημιουργείται και ο οποίος θα ολοκληρώσει την εργασία που καταγράψατε. Λάβετε υπόψη ότι η καταγεγραμμένη μακροεντολή δεν θα είναι ο καλύτερος δυνατός κώδικας στις περισσότερες περιπτώσεις, αλλά τα πάει πολύ καλά για ένα γρήγορο παράδειγμα. -
Μπορώ να αυτοματοποιήσω μια ενσωματωμένη εφαρμογή του Office;
Απολύτως. Το κόλπο είναι να λάβετε τον δείκτη IDispatch: αυτό δίνεται στη Visual C++ Technical Note 39 (TN039). -
Πώς γίνεται πρόσβαση στις ιδιότητες του εγγράφου μου σε ένα έγγραφο του Office;
Οι ιδιότητες του εγγράφου είναι προσβάσιμες μέσω αυτοματοποίησης ή απευθείας μέσω του IPropertyStorage.