Tehnologia componentelor externe (). Tehnologia componentelor externe () Tehnologia componentelor externe 8.3

OLEG FILIPOV, ANT-Inform, șef adjunct departament dezvoltare, [email protected]

Extinderea funcționalității 1C:Enterprise
Partea 1: Componente COM externe

Vine un moment în viața fiecărui dezvoltator 1C când sarcinile care îi sunt atribuite depășesc capacitățile platformei 1C. Să ne uităm la modalități de a „depăși granița posibilului”

Dezvoltatorii 1C experimentați probabil au ghicit deja din primele trei cuvinte ale titlului articolului că vom vorbi despre componente externe pentru 1C:Enterprise. Această tehnologie există încă din zilele platformei 1C:Enterprise 7.7 (sau mai devreme). Din punct de vedere istoric, a apărut în principal pentru a rezolva probleme de interacțiune cu echipamentele de retail (scanere de coduri de bare, cântare electronice, case de marcat), timp în care a devenit necesară ca platforma 1C să proceseze anumite evenimente inițiate de echipament. Astfel de evenimente nu sunt altceva decât secvențe de octeți care sosesc pe porturile COM/LPT. Desigur, adaptarea platformei 1C pentru a funcționa cu astfel de mecanisme de nivel scăzut nu ar fi foarte corectă, așa că am venit cu tehnologia componentelor externe pentru 1C: Enterprise.

În această serie de articole nu vom vorbi doar despre componente externe: componentele externe sunt un mecanism destul de puternic și complex, așa că folosirea lor pentru a rezolva unele probleme este văzută ca „împușcarea vrăbiilor cu un tun”. Cu toate acestea, componentele externe sunt instrumentul cel mai universal pentru a rezolva tot felul de probleme care depășesc domeniul de aplicare al platformei 1C:Enterprise, așa că vom începe cu ele. Articolul va discuta despre cea mai veche, cel mai frecvent utilizată și testată tehnologie pentru crearea de componente externe - bazată pe COM.

Care sunt componentele externe

Componentele externe, așa cum am menționat mai sus, au apărut în 1C:Enterprise începând cu versiunea 7.7. Inițial, conform tradiției, dezvoltatorii platformei 1C „nu s-au deranjat”, iar componentele externe erau un obiect cu anumite proprietăți și metode obligatorii. Componentele există încă în aceeași formă până astăzi. Adică, componentele scrise pentru 1C:Enterprise 7.7 vor funcționa (teoretic) și cu 1C:Enterprise 8.3.

În practică, există o serie de subtilități: dacă o componentă externă interacționează activ cu platforma în sine, atunci funcțiile acesteia vor fi specializate pentru o anumită versiune.

Începând cu versiunea 8.2, 1C:Enterprise a introdus o nouă tehnologie pentru dezvoltarea componentelor externe, așa-numita NativeAPI. Componentele scrise folosind această tehnologie nu mai sunt obiecte COM. Pe de o parte, acesta este un plus - aceste componente nu necesită înregistrare, pe de altă parte, utilizarea lor în altă parte decât platforma 1C:Enterprise este imposibilă. Dezvoltarea componentelor externe a devenit ceva mai complicată, acestea trebuie să suporte un număr puțin mai mare de interfețe necesare. Dar despre asta vom vorbi în următorul articol.

Componente externe și tehnologie COM

Nu voi descrie în detaliu tehnologia COM în sine, deoarece există multă literatură pe această temă. Probabil ar trebui să spunem că mulți oameni „confundă” crearea unui server inproc obișnuit cu crearea de componente externe pentru 1C:Enterprise, dar acest lucru nu este departe de adevăr. De foarte multe ori te poți descurca doar cu această funcționalitate. Cum se creează un obiect COM în Visual Studio este descris în multe surse diferite. În special, persoana a scris în detaliu cum se face acest lucru, cu un exemplu de apel de la 1C:Enterprise.

Dacă totuși doriți să faceți o componentă externă COM cu drepturi depline pentru 1C:Enterprise (dintre opțiunile de ce ar putea fi necesar acest lucru, mă gândesc doar la una - componenta trebuie să interacționeze activ cu sistemul de pe platforma 1C, să notifice utilizatorii, schimbați linia de stare, afișați casete de dialog și etc.), atunci trebuie să utilizați tehnologia componentelor externe direct. Asadar, haideti sa începem.

Acest articol este dedicat lucrului cu componente externe, și anume conectării acestora. În prezent, pentru a extinde capacitățile 1C Enterprise, sunt utilizate două tehnologii de componente externe:

  • 1 Utilizarea API-ului nativ
  • 2 Utilizarea tehnologiei COM
În acest articol, am decis să evidențiez lucrul cu componentele API native.
Deci, să începem, de la simplu la complex:
Extras din ITS

1. Să presupunem că VK-ul nostru se află într-un anumit director de pe disc:

Poate fi folosit în „Client gros (aplicație obișnuită)”;

Acesta este cel mai simplu exemplu de lucru cu componenta nativă. Vă rugăm să rețineți că acest tip de componentă nu necesită înregistrare în sistem, ceea ce simplifică foarte mult administrarea.

2. Exemplul discutat mai sus nu este deloc realist. Cel mai adesea, componenta este plasată într-un aspect. Aspectul trebuie să conțină o arhivă zip cu fișiere componente și un fișier MANIFEST.xml
Exemplu de fișier manifest:

3. Când lucrați într-un client subțire și web, asigurați-vă că utilizați metoda.
Citat din ITS:

Explicaţie:
%APPDATA%\1C\1Cv82\ExtCompT- director pentru instalarea componentelor pentru Thick și Thin clients.
%APPDATA%\Roaming\Mozilla\Extensions- extensii de director (în cazul meu) pentru Mozilla FF/
Când utilizați metoda SetExternalComponent(), în funcție de clientul utilizat, extensiile vor fi despachetate în directorul corespunzător.

Un exemplu de procedura de instalare a componentelor externe:

InstallExternalComponent- metoda trebuie apelată numai în timpul instalării inițiale a componentei și în cazul în care este necesară actualizarea versiunii instalate a componentei.

În cazul clientului subțire și gros:
Este suficient să reexecuți operația de instalare a componentei externe folosind metoda InstallExternalComponent().

În cazul unui client web pentru a actualiza o componentă:

  • Este necesar să eliminați pluginul prin mecanismul de lucru cu suplimente de browser web (Mozilla FF)
  • Utilizați metoda InstallExternalComponent
Pentru a conecta un VK, puteți utiliza următoarea procedură:

Dacă componenta nu a fost instalată, va fi aruncată o excepție.

2. Există cazuri când o componentă trebuie instalată din stocarea temporară (fișierul a fost primit de la o sursă terță parte, procesare externă), în acest caz, primii parametri din metodele Conectare componentă externă și Instalare componentă externă sunt adresa arhivei aflate în stocare temporară. Mai jos este un exemplu posibil al modului în care funcționează:

&OnClient VariableAddressArchiveComponent; Componenta variabilă &OnClient; &OnClient Procedure OnOpen(Failure) // adresa, conține un șir (link de navigare către datele binare ale arhivei zip în // stocare temporară) ComponentArchiveAddress = GetArchiveAddressInTemporaryStorage(); EndProcedure // WhenOpen() &OnServer // metode ConnectExternalComponent, SetExternalComponent poate lua ca // primul parametru un șir în formatul „link de navigare” // (URL către o componentă externă ambalată într-o arhivă ZIP, într-un format similar cu // GetNavigationLink ). Funcția GetArchiveAddressInTemporaryStorage()ProcessingObject = FormAttributesValue("ProcessingObject"); Link arhivă = PlaceInTemporaryStorage(ProcessingObject.GetLayout("MIKO_phone_IP"), New UniqueIdentifier); ReturnLinkToArchive; EndFunction // GetArchiveAddressInTemporaryStorage() &OnClient // Procedura trebuie apelată o singură dată, dacă componenta nu este încă instalată // sau trebuie actualizată Procedură InstallComponent(Command) Încercarea de a InstallExternalComponent(ArchiveComponentAddress); Raport de excepție ("Eșuare la instalarea componentei externe."); EndTempt; Sfârșitul procedurii // InstallComponent() &OnClient // procedură principală pentru inițializarea unei componente Procedură Initialize(Command) Încercarea de a conecta o componentă externă(ComponentArchiveAddress,"Comp" ,ExternalComponentType.Native); Component = New("AddIn.Comp.MIKO_phone_IP"); Exception Report("Excepție de inițializare. Este posibil ca componenta să nu fi fost instalată încă."); EndTempt; Sfârșitul procedurii
  • Tutorial

Introducere

Acest articol oferă o idee despre cum funcționează componentele externe în sistemul 1C: Enterprise.
Va fi afișat procesul de dezvoltare a unei componente externe pentru versiunea 8.2 a sistemului 1C: Enterprise, care rulează sub sistemul de operare Windows cu un mod de funcționare fișier. Această opțiune este utilizată în majoritatea soluțiilor concepute pentru întreprinderile mici. VK va fi implementat în limbajul de programare C++.

Componente externe „1C: Enterprise”

„1C: Enterprise” este un sistem extensibil. Pentru a extinde funcționalitatea sistemului, sunt utilizate componente externe (EC). Din punctul de vedere al dezvoltatorului, un VC este un obiect extern care are proprietăți și metode și poate genera, de asemenea, evenimente pentru procesare de către sistemul 1C: Enterprise.
Componentele externe pot fi folosite pentru a rezolva o clasă de probleme care sunt dificil sau chiar imposibil de implementat în limbajul de programare integrat în 1C: Enterprise. În special, această clasă include sarcini care necesită interacțiune la nivel scăzut cu sistemul de operare, de exemplu, pentru a lucra cu echipamente specifice.
Sistemul 1C: Enterprise folosește două tehnologii pentru a crea componente externe:
  • folosind Native API
  • folosind tehnologia COM
Având în vedere restricțiile date, diferența dintre cele două tehnologii mai sus menționate este nesemnificativă, așa că vom lua în considerare dezvoltarea jocurilor video folosind API-ul nativ. Dacă este necesar, dezvoltările implementate pot fi aplicate dezvoltării de software de calculator folosind tehnologia COM și, de asemenea, cu modificări minore, aplicate pentru utilizare în sistemul 1C: Enterprise cu alte opțiuni de operare, altele decât modul de operare fișier.
Structura VK
Componenta externă a sistemului 1C: Enterprise este prezentată sub forma unei biblioteci DLL. Codul bibliotecii descrie clasa descendentă IComponentBase. Clasa creată trebuie să definească metode responsabile de implementarea funcțiilor componentei externe. Metodele anulate vor fi descrise mai detaliat mai jos pe măsură ce materialul este prezentat.

Lansarea unui demo VK

Sarcină:
  1. Asamblați o componentă externă furnizată cu un abonament ITS și destinată să demonstreze principalele capacități ale mecanismului de componentă externă în 1C
  2. Conectați componenta demo la configurația 1C
  3. Asigurați-vă că funcțiile declarate funcționează corect
Compilare
VK-ul demo este situat pe discul de abonament ITS în directorul „/VNCOMP82/example/NativeAPI”.
Pentru a construi VC demonstrativ vom folosi Microsoft Visual Studio 2008. Alte versiuni ale acestui produs nu acceptă formatul de proiect Visual Studio utilizat.


Deschideți proiectul AddInNative. În setările proiectului, includem directorul cu fișierele de antet necesare pentru a construi proiectul. În mod implicit, acestea sunt localizate pe discul ITS din director /VNCOMP82/include.
Rezultatul build-ului este fișierul /bind/AddInNative.dll. Aceasta este biblioteca compilată pentru conectarea la configurația 1C.
Se conectează configurația VK la 1C
Să creăm o configurație 1C goală.
Mai jos este codul pentru modulul aplicației gestionate.
variabila DemoComp; Procedură la pornirea sistemului() Conectare componentă externă("...\bind\AddInNative.dll", "DemoVK", Tipul componentei externe.Native); DemoComp = New ("AddIn.DemoVK.AddInNativeExtension"); Sfârșitul procedurii
Dacă nu a fost raportată nicio eroare la pornirea configurației 1C, atunci VK-ul a fost conectat cu succes.
Ca urmare a executării codului de mai sus, un obiect apare în vizibilitatea globală a configurației DemoComp, care are proprietăți și metode care sunt definite în codul componentei externe.
Demonstrarea funcționalității încorporate
Să verificăm funcționalitatea demo-ului VK. Pentru a face acest lucru, să încercăm să setăm și să citim unele proprietăți, să apelăm unele metode VK și, de asemenea, să primim și să procesăm mesajul VK.
Documentația furnizată pe discul ITS precizează următoarea funcționalitate a VC-ului demo:
  1. Gestionarea stării obiectelor componente
    Metode: Porniți, Opriți
    Proprietăți: Inclus
  2. Gestionarea temporizatorului
    În fiecare secundă, componenta trimite un mesaj către sistemul 1C: Enterprise cu parametri Componentă, Temporizatorși o linie de contor al ceasului de sistem.
    Metode: StartTimer, Stop Timer
    Proprietăți: Există un cronometru
  3. Metodă ShowInStatusLine, care afișează în linia de stare textul trecut metodei ca parametri
  4. Metodă Încarcă imagine. Încarcă o imagine din fișierul specificat și o transferă în sistemul 1C: Enterprise sub formă de date binare.
Să ne asigurăm că aceste funcții funcționează. Pentru a face acest lucru, rulați următorul cod:
variabila DemoComp; Procedura la pornirea sistemului() ConnectExternalComponent(...); DemoComp = New ("AddIn.DemoVK.AddInNativeExtension"); DemoComp.Disable(); Raport (DemoComp.Activat); DemoComp.Enable(); Raport (DemoComp.Activat); DemoComp.StartTimer(); Sfârșitul procedurii Procesare externă a evenimentelor (Sursă, Eveniment, Date) Raport (Sursă + " " + Eveniment + " " + Date); Sfârșitul procedurii
Rezultatul rulării configurației este afișat în imagine


Panoul „Mesaje” afișează rezultatele apelurilor de metodă DemoComp.Disable()Și Demo.Comp.Enable(). Rândurile ulterioare din același panou conțin rezultatele procesării mesajelor primite de la VK - Sursă, EvenimentȘi Date respectiv.

Nume personalizat de componentă externă

Sarcină: Schimbați numele componentei externe într-unul arbitrar.
Secțiunea anterioară a folosit identificatorul AddInNativeExtension, al cărui sens nu a fost explicat. În acest caz AddInNativeExtension- acesta este numele extensiei.
Codul VK definește o metodă RegisterExtensionAs, returnând numele sistemului 1C: Enterprise, care este necesar pentru înregistrarea ulterioară a VK-ului în sistem. Se recomandă specificarea unui identificator care dezvăluie într-o oarecare măsură esența componentei externe.
Iată codul complet al metodei RegisterExtensionAs cu numele extensiei schimbat:
bool CAddInNative::RegisterExtensionAs(WCHAR_T** wsExtensionName) ( wchar_t *wsExtension = L"SomeName"; int iActualSize = ::wcslen(wsExtension) + 1; WCHAR_T* dest = 0; if (m_iMemory) ( if(m_iMemory) ( if(m_iMemory) ((void**)wsExtensionName, iActualSize * sizeof(WCHAR_T))) ::convToShortWchar(wsExtensionName, iActualSize) returnează false;
În exemplul dat, numele VK este schimbat în SomeName. Apoi, atunci când vă conectați VK, trebuie să specificați un nume nou:
DemoComp = New("AddIn.DemoVK.SomeName");

Extinderea listei de proprietăți VK

Sarcină:
  1. Studiați implementarea proprietăților VK
  2. Adăugați o proprietate de citire/scriere de tip șir
  3. Adăugați o proprietate șir de citire/scriere care stochează tipul de date al ultimului set de proprietăți. Nu se întreprinde nicio acțiune la setarea valorii proprietății

Pentru a determina proprietățile componentei care se creează, dezvoltatorul trebuie să implementeze următoarele metode în codul bibliotecii AddInNative.cpp:
GetNProps
Returnează numărul de proprietăți ale acestei extensii, 0 dacă nu există proprietăți
FindProp
Returnează numărul de serie al proprietății al cărei nume este trecut în parametri
GetPropName
Returnează numele proprietății după numărul de serie și după identificatorul de limbă transmis
GetPropVal
Returnează valoarea proprietății cu numărul ordinal specificat
SetPropVal
Setează valoarea proprietății cu numărul ordinal specificat
IsPropReadable
Returnează indicatorul de lizibilitate al proprietății cu numărul de secvență specificat
IsPropWritable
Returnează indicatorul de scriere al proprietății cu numărul de secvență specificat


Să luăm în considerare implementarea metodelor de clasă de mai sus CAddInNative.
În VC demonstrativ, sunt definite 2 proprietăți: InclusȘi Există un cronometru (Este activatȘi IsTimerPresent).
În domeniul global al codului bibliotecii, sunt definite două matrice:
static wchar_t *g_PropNames = (L"IsEnabled", L"IsTimerPresent"); static wchar_t *g_PropNamesRu = (L"Activat", L"Există un temporizator");
care stochează nume de proprietate în limba rusă și engleză. În fișierul antet AddInNative.h enumerarea este definită:
Enum Props ( ePropIsEnabled = 0, ePropIsTimerPresent, ePropLast // Întotdeauna ultimul );
ePropIsEnabledȘi ePropIsTimerPresent, respectiv având valorile 0 și 1, sunt folosite pentru a înlocui numerele de serie ale proprietăților cu identificatori semnificativi. ePropLast, care are valoarea 2, este folosit pentru a obține numărul de proprietăți (folosind metoda GetNProps). Aceste nume sunt folosite numai în codul componentei și nu sunt disponibile din exterior.
Metodele FindProp și GetPropName efectuează căutări în matrice g_PropNamesȘi g_PropNamesRu.
Pentru a stoca valorile câmpurilor din modulul bibliotecă, clasa CAddInNative are proprietăți care stochează valoarea proprietăților componente. Metode GetPropValȘi SetPropVal returnați și setați valoarea acestor proprietăți în consecință.
Metode IsPropReadableȘi IsPropWritableși întoarce-te Adevărat sau fals, în funcție de numărul de secvență transmis al proprietății în conformitate cu logica aplicației.
Pentru a adăuga o proprietate personalizată, trebuie să:

  1. Adăugați numele proprietății care este adăugată la matrice g_PropNamesȘi g_PropNamesRu(fişier AddInNative.cpp)
  2. A lista Recuzită(fişier AddInNative.h) inainte de ePropLast adăugați un nume care identifică în mod unic proprietatea adăugată
  3. Organizați memoria pentru stocarea valorilor proprietăților (creați câmpuri componente ale modulului care stochează valorile corespunzătoare)
  4. Faceți modificări metodelor GetPropValȘi SetPropVal pentru a interacționa cu memoria alocată în pasul anterior
  5. În conformitate cu logica aplicației, efectuați modificări metodelor IsPropReadableȘi IsPropWritable
Punctele 1, 2, 5 nu au nevoie de explicații. Detalii despre implementarea acestor pași pot fi găsite studiind anexa la articol.
Să dăm nume proprietăților de testare TestȘi Verificare tip respectiv. Apoi, ca rezultat al pasului 1, avem:
static wchar_t *g_PropNames = (L"IsEnabled", L"IsTimerPresent", L"Test", L"TestType"); static wchar_t *g_PropNamesRu = (L"Activat", L"Există un temporizator", L"Test", L"Verificare tip");
Transfer Recuzită va arata ca:
Enum Props ( ePropIsEnabled = 0, ePropIsTimerPresent, ePropTest1, ePropTest2, ePropLast // Întotdeauna ultimul);
Pentru a simplifica semnificativ codul, vom folosi STL C++. În special, pentru lucrul cu șiruri WCHAR, haideți să conectăm biblioteca wstring.
Pentru a salva valoarea unei metode Test, definim în clasă CAddInNativeîn domeniul privat:
testul șirurilor1;
Pentru a transfera parametrii șir între componentele 1C: Enterprise și externe, se folosește 1C: Manager de memorie Enterprise. Să aruncăm o privire mai atentă la munca lui. Funcțiile sunt folosite pentru a aloca și, respectiv, elibera memorie AllocMemoryȘi Memorie libera definite în dosar ImemoryManager.h. Dacă este necesar să se transmită un parametru șir sistemului 1C: Enterprise, componenta externă trebuie să aloce memorie pentru acesta apelând funcția AllocMemory. Prototipul său arată astfel:
virtual bool ADDIN_API AllocMemory (void** pMemory, unsigned long ulCountByte) = 0;
Unde pMemorie- adresa pointerului în care va fi plasată adresa zonei de memorie alocată,
ulCountByte- dimensiunea zonei de memorie alocată.
Un exemplu de alocare de memorie pentru un șir:
WCHAR_T *t1 = NULL, *test = L"TEST_STRING"; int iActualSize = wcslen(test1)+1; m_iMemory->AllocMemory((void**)&t1, iActualSize * sizeof(WCHAR_T)); ::convToShortWchar(&t1, test1, iActualSize);
Pentru confortul lucrului cu tipuri de date șir, vom descrie funcția wstring_to_p. Primește un șir wstring ca parametru. Rezultatul funcției este o structură umplută tVariant. Codul funcției:
bool CAddInNative::wstring_to_p(std::wstring str, tVariant* val) ( char* t1; TV_VT(val) = VTYPE_PWSTR; m_iMemory->AllocMemory((void**)&t1, (str.length()+1) * sizeof(WCHAR_T)); memcpy(t1, str.c_str(), (str.length()+1) * sizeof(WCHAR_T)); returneaza adevarat)
Apoi, secțiunea de caz corespunzătoare a instrucțiunii switch a metodei GetPropVal va lua forma:
caz ePropTest1: wstring_to_p(test1, pvarPropVal); pauză;
Metodă SetPropVal:
caz ePropTest1: dacă (TV_VT(varPropVal) != VTYPE_PWSTR) returnează fals; test1 = std::wstring((wchar_t*)(varPropVal -> pstrVal)); pauză;
Pentru a implementa a doua proprietate, definim un câmp de clasă CaddInNative
uint8_t last_type;
în care vom salva tipul ultimei valori transferate. Pentru a face acest lucru, adăugați comanda la metoda CaddInNative::SetPropVal:
ultimul_tip = TV_VT(varPropVal);
Acum, când solicităm citirea valorii celei de-a doua proprietăți, vom returna valoarea ultimul_tip, ceea ce necesită sarcina desemnată.
Să verificăm funcționalitatea modificărilor făcute.
Pentru a face acest lucru, să prezentăm aspectul configurației 1C după cum urmează:
variabila DemoComp; Procedură la pornirea sistemului() Conectare componentă externă("...", "DemoVK", Tip componentă externă.Native); DemoComp = New("AddIn.DemoVK.SomeName"); DemoComp.TypeCheck = 1; Raport(String(DemoComp.TypeCheck)); DemoComp.Test = "Vasya"; Raport(String(DemoComp.Test)); DemoComp.Test = "Petya"; Raport(String(DemoComp.Test)); Raport(String(DemoComp.TypeCheck)); Sfârșitul procedurii
Ca urmare a lansării, vom primi o secvență de mesaje:
3
Vasia
Petru
22

Al doilea și al treilea mesaj sunt rezultatul citirii proprietății stabilite în pasul anterior. Primul și al doilea mesaj conțin codul de tip al ultimului set de proprietăți. 3 corespunde unei valori întregi, 22 unei valori șir. Corespondența tipurilor și a codurilor acestora se stabilește în dosar tipuri.h, care se află pe discul ITS.

Extinderea listei de metode

Sarcină:
  1. Extindeți funcționalitatea componentei externe cu următoarea funcționalitate:
  2. Explorați modalități de implementare a metodelor componentelor externe
  3. Adăugați o metodă de funcție Funcția 1, care ia două șiruri de caractere („Parameter1” și „Parameter2”) ca parametru. Rezultatul este un șir de genul: „Verificare. Parametrul 1, Parametrul 2"
  4. Asigurați-vă că modificările pe care le faceți funcționează.

Pentru a defini metodele componentei care se creează, dezvoltatorul trebuie să implementeze următoarele metode în codul bibliotecii AddInNative:
GetNMethods, FindMethod, GetMethodName
Conceput pentru a obține numărul corespunzător de metode, căutați numărul și numele metodei. Similar cu metodele corespunzătoare pentru proprietăți
GetNParams
Returnează numărul de parametri ai metodei cu numărul de secvență specificat; dacă o metodă cu acest număr este absentă sau nu are parametri, returnează 0
GetParamDefValue
Returnează valoarea implicită a parametrului specificat al metodei specificate
HasRetVal
Returnează indicatorul dacă metoda cu valoarea de returnare ordinală specificată are o valoare returnată: true pentru metodele cu o valoare returnată și fals in caz contrar
CallAsProc
fals, apare o eroare de rulare și execuția modulului 1C: Enterprise este încheiată. Memoria pentru matricea de parametri este alocată și eliberată de 1C: Enterprise.
CallAsFunc
Execută metoda cu numărul de ordine specificat. Dacă metoda revine fals, apare o eroare de rulare și execuția modulului 1C: Enterprise este încheiată. Memoria pentru matricea de parametri este alocată de 1C: Enterprise. Dacă valoarea returnată este un șir sau un tip de date binare, componenta alocă memorie cu funcția AllocMemory manager de memorie, scrie acolo datele și stochează această adresă în câmpul corespunzător al structurii. 1C: Întreprinderea va elibera această memorie prin apel Memorie libera.
O descriere completă a metodelor, inclusiv o listă de parametri, este descrisă în detaliu în documentația furnizată pe discul ITS.
Să luăm în considerare implementarea metodelor descrise mai sus.
În codul componentelor, sunt definite două matrice:
static wchar_t *g_MethodNames = (L"Activare", L"Dezactivare", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture"); static wchar_t *g_MethodNamesRu = (L"Activare", L"Dezactivare", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadImage");
si enumerare:
Metode enum ( eMethEnable = 0, eMethDisable, eMethShowInStatusLine, eMethStartTimer, eMethStopTimer, eMethLoadPicture, eMethLast // Întotdeauna ultimul);
Sunt folosite în funcții GetNMethods, FindMethodȘi GetMethodName, prin analogie cu descrierea proprietăților.
Metode GetNParams, GetParamDefValue, HasRetVal comutatorul instrumentului, în funcție de parametrii trecuți și de logica aplicației, returnează valoarea necesară. Metodă HasRetValîn codul său are o listă de numai metode care pot returna un rezultat. Pentru ei se întoarce Adevărat. Pentru toate metodele de oțel returnări fals.
Metode CallAsProcȘi CallAsFunc conţin cod executabil direct al metodei.
Pentru a adăuga o metodă care poate fi apelată doar ca funcție, trebuie să faceți următoarele modificări la codul sursă al componentei externe:
  1. Adăugați numele metodei la matrice g_MethodNamesȘi g_MethodNamesRu(fişier AddInNative.cpp)
  2. Adăugați un identificator de metodă semnificativ la enumerarea Metode (fișier AddInNative.h)
  3. Efectuați modificări la codul funcției GetNParamsîn conformitate cu logica programului
  4. Dacă este necesar, faceți modificări la codul metodei GetParamDefValue, dacă doriți să utilizați valorile implicite ale parametrilor metodei.
  5. Efectuați modificări ale funcției HasRetVal
  6. Faceți modificări în logica funcțiilor CallAsProc sau CallAsFunc, plasând acolo codul executabil direct al metodei
Să prezentăm matricele g_MethodNamesȘi g_MethodNamesRu, precum și listare Metode la forma:
static wchar_t *g_MethodNames = (L"Activare", L"Dezactivare", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture", L"Test"); static wchar_t *g_MethodNamesRu = (L"Activare", L"Dezactivare", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture", L"Test");

Metode de enumerare ( eMethEnable = 0, eMethDisable, eMethShowInStatusLine, eMethStartTimer, eMethStopTimer, eMethLoadPicture, eMethTest, eMethLast // Întotdeauna ultimul);
Să edităm funcția GetNProps astfel încât să returneze numărul de parametri ai metodei „Test”:
long CAddInNative::GetNParams(const long lMethodNum) ( switch(lMethodNum) (case eMethShowInStatusLine: return 1; case eMethLoadPicture: return 1; case eMethTest: return 2; implicit: return 0; ) return 0; )
Să facem modificări în funcție:
bool CAddInNative::GetParamDefValue(const long lMethodNum, const long lParamNum, tVariant *pvarParamDefValue) ( ​​​​TV_VT(pvarParamDefValue)= VTYPE_EMPTY; switch(lMethodNum) (case eMethEnable:case eMeth Timer: caz eMethStopTimer: caz eMethTest : / / Nu există valori ale parametrilor implicit break: return false;
Mulțumită liniei adăugate
caz eMethTest:
dacă lipsesc unul sau mai multe argumente, parametrii corespunzători vor avea o valoare goală ( VTYPE_EMPTY). Dacă aveți nevoie de o valoare implicită pentru un parametru, ar trebui să o setați în secțiune eMethTest instrucțiunea de comutare a funcției CAddInNative::GetParamDefValue.
Deoarece metoda Test poate returna o valoare, trebuie să faceți modificări codului funcției HasRetVal:
bool CAddInNative::HasRetVal(const long lMethodNum) ( switch(lMethodNum) (case eMethLoadPicture: case eMethTest: return true; default: return false; ) return false; )
Și adăugați codul executabil al metodei la funcție CallAsFunc:
bool CAddInNative::CallAsFunc(const long lMethodNum, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray) (... std::wstring s1, s2; switch(lMethodNum) (case eMethLoadPicture: ...T break; if (!lSizeArray || !paParams) return s1 = (paParams) -> pwstrVal = (paParams+1) -> pwstring_to_p(std::wstring(s1+s2), pvarRetValue ; ;
Să compilam componenta și să aducem codul de configurare la forma:
variabila DemoComp; Procedură la pornirea sistemului() Conectare componentă externă("...", "DemoVK", Tip componentă externă.Native); DemoComp = New("AddIn.DemoVK.SomeName"); lane = DemoComp.Test ("Bună ziua," "Lumea!"); Raport (per); Sfârșitul procedurii
După lansarea configurației, vom primi mesajul: „Hello, World!”, care indică faptul că metoda a funcționat cu succes.

Temporizator

Sarcină:
  1. Studiați implementarea temporizatorului în demo VK
  2. Modificați metoda „StartTimer” adăugând posibilitatea de a trece în parametri intervalul de răspuns al temporizatorului (în milisecunde)
  3. Asigurați-vă că modificările pe care le faceți funcționează.

În WinAPI, puteți folosi mesajul pentru a lucra cu timpul WM_TIMER. Acest mesaj va fi trimis programului dumneavoastră la intervalul de timp pe care l-ați setat la crearea cronometrului.
Pentru a crea un cronometru, utilizați funcția SetTimer:
UINT SetTimer(HWND hWnd, // descriptor de fereastră UINT nIDevent, // identificator timer (număr) UINT nElapse, // întârziere TIMERPROC lpTimerFunc); // pointer către funcție
Sistemul de operare va trimite un mesaj WM_TIMERîn program cu intervalul specificat în argument nElapse(în milisecunde). În ultimul parametru puteți specifica o funcție care va fi executată de fiecare dată când se declanșează cronometrul. Antetul acestei funcții ar trebui să arate astfel (numele poate fi orice):
void __stdcall TimerProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
Să luăm în considerare implementarea unui cronometru în VC demonstrativ.
Deoarece luăm în considerare procesul de dezvoltare a unei componente externe pentru familia de sisteme de operare Windows, nu vom lua în considerare implementarea temporizatorului în alte sisteme de operare. Pentru sistemul de operare GNU/Linux, în special, implementarea va diferi în sintaxa funcției SetTimerȘi TimerProc.
Codul executabil apelează metoda SetTimer, căruia i se trece funcția MyTimerProc:
m_uiTimer = ::SetTimer(NULL,0,100,(TIMERPROC)MyTimerProc);
ID-ul temporizatorului creat este plasat într-o variabilă m_uiTimer pentru a putea fi dezactivat ulterior.
Funcţie MyTimerProc după cum urmează:
VOID CALLBACK MyTimerProc(HWND hwnd, // handle of window for timer messages UINT uMsg, // WM_TIMER message UINT idEvent, // timer identifier DWORD dwTime // time system current) (dacă (!pAsyncEvent) return; wchar_t *who = L "ComponentNative", *what = L"Timer"; wchar_t *wstime = new wchar_t if (wstime) ( wmemset(wstime, 0, TIME_LEN); ::_ultow(dwTime, wstime, 10); pAsyncEvent->ExternalEvent(who); , ce, wstime);
Esența funcției este că metoda este numită Eveniment extern, care trimite un mesaj către sistemul 1C: Enterprise.
Pentru a extinde funcționalitatea metodei StartTimer Să facem următoarele:
Modificarea codului metodei GetNParams astfel încât să fie pentru metodă eMethStartTimer valoarea returnată 1:
caz eMethStartTimer: return 1;
Aici este codul metodei CallAsProc la forma:
case eMethStartTimer: if (!lSizeArray || TV_VT(paParams) != VTYPE_I4 || TV_I4(paParams)<= 0) return false; pAsyncEvent = m_iConnect; #ifndef __linux__ m_uiTimer = ::SetTimer(NULL,0,TV_I4(paParams),(TIMERPROC)MyTimerProc); #else // код для GNU/Linux #endif break;
Acum să verificăm funcționalitatea. Pentru a face acest lucru, vom scrie codul în modulul aplicației gestionate al configurației:
variabila DemoComp; Procedură la pornirea sistemului() Conectare componentă externă("...", "DemoVK", Tip componentă externă.Native); DemoComp = New("AddIn.DemoVK.SomeName"); DemoComp.StartTimer(2000); Sfârșitul procedurii
După pornirea configurației, programul va primi mesaje la intervale de 2 secunde, ceea ce indică faptul că temporizatorul funcționează corect.

Interacțiunea cu sistemul 1C: Enterprise

Pentru a interacționa între componenta externă și sistemul 1C: Enterprise, metodele clasei IAddInDefBase, descrise în fișier AddInDefBase.h. Le enumerăm pe cele mai frecvent utilizate:
Generarea unui mesaj de eroare
virtual bool ADDIN_API AddError(unsigned short wcode, const WCHAR_T* source, const WCHAR_T* descr, long code)
wcode, cod- coduri de eroare (o listă de coduri de eroare cu descrieri poate fi găsită pe discul ITS)
sursă- sursa erorii
descr- descrierea erorii
Trimiterea unui mesaj către sistemul 1C: Enterprise
virtual bool ADDIN_API ExternalEvent(WCHAR_T* wszSource, WCHAR_T* wszMessage, WCHAR_T* wszData) = 0;
wszSource- sursa mesajului
wszMessage- Mesaj text
wszData- datele transmise
Interceptarea mesajelor este efectuată prin procedura de procesare externă a evenimentelor
Înregistrarea unei componente externe în sistemul 1C: Enterprise
virtual bool ADDIN_API RegisterProfileAs(WCHAR_T* wszProfileName)
wszProfileName- denumirea componentei.
Aceste metode sunt suficiente pentru o interacțiune completă între VK și 1C. Pentru a primi date de către o componentă externă de la sistemul 1C: Enterprise și invers, componenta externă trimite un mesaj special, care la rândul său este interceptat de sistemul 1C și, dacă este necesar, apelează metodele componentei externe pentru a transmite date înapoi. .

tVariant tip de date

La schimbul de date între componenta externă și sistemul 1C: Enterprise, este utilizat tipul de date tVariant. Este descris în fișierul types.h, care poate fi găsit pe discul ITS:
struct _tVariant ( _ANONYMOUS_UNION union ( int8_t i8Val; int16_t shortVal; int32_t lVal; int intVal; unsigned int uintVal; int64_t llVal; uint8_t ui8Val; uint16_t ushortVal; uint32_t ult32_t; uint32_t; bVal; wchar_t struct _tVal; struct tm tmVal; ANT_NAME_1 ; matrice dimensională în pvarVal TYPEVAR vt);
Tip tVariant este o structură care include:
  • amestec (unire) destinat direct stocării datelor
  • identificatorul tipului de date
În general, lucrul cu variabile de tip tVariant are loc conform următorului algoritm:
  1. Determinarea tipului de date stocate în prezent într-o variabilă
  2. Accesați câmpul de amestec corespunzător pentru a accesa direct datele
Folosind tipul tVariant simplifică semnificativ interacțiunea dintre sistemul 1C: Enterprise și componentele externe

Aplicație

Directorul „exemple” conține exemple pentru articol
exemple/1 - lansați componenta demo
exemple/2 - demonstrarea extinderii listei de proprietăți
exemple/3 - demonstrarea extinderii listei de metode
Fiecare director conține un proiect VS 2008 și o configurație 1C gata făcută.

Titlul articolului include expresia „pentru manechin”. Prin ceainic mă refeream, în primul rând, pe mine însumi. Toate cunoștințele mele de C++ au rămas la nivelul de 3-4 ani de universitate, când am pornit pe calea strâmbă a 1C. Și totul ar fi bine, dar recent a apărut o sarcină care necesita scrierea unei componente externe. A trebuit să-mi amintesc amintirile și să-mi șterg cunoștințele C++. Se dovedește că totul nu este atât de înfricoșător. Vreau să vă ofer o scurtă introducere în scrierea componentelor externe.

Componente șablon pe ITS

Discul ITS conține documentație completă despre mecanismul componentelor externe, completată de un proiect exemplu și un șablon pentru propria dezvoltare. Materialul se numește „Tehnologia componentelor externe”. Documentația este grozavă, dar trebuie totuși să o înțelegeți, iar timpul, ca de obicei, este scurt. De fapt, există doar câteva puncte cheie cărora merită să le acordați atenție, restul este decăderea și vanitatea :)

Materiale necesare

Pentru a crea o componentă externă vom avea nevoie de:

  1. Material „Tehnologie pentru crearea componentelor externe” aflat pe ITS
  2. Șablon de componentă externă goală inclus cu materialul
  3. MS Visual Studio. Versiunea Express este gratuită și mai mult decât suficientă pentru nevoile noastre.
  4. Având cunoștințe de bază despre sintaxa C++ și anume:
  • Abilitatea de a distinge o declarație variabilă de o buclă sau condiție
  • Înțelegând că șirurile în forma lor pură nu există în C++, există șiruri pentru care în mod clar trebuie să vă deranjați cu memoria
  • Ei bine, desigur, este necesară capacitatea de a implementa sarcina în limba specificată. Cel puțin, capacitatea de a apela o bibliotecă terță parte din C++ care va face totul singură.

Să începem să sapăm

Documentația pentru API-ul nativ este destul de detaliată. Pentru a rezuma, spune următoarele:

  1. O componentă externă vă permite să extindeți limbajul încorporat cu un nou obiect (sau mai multe). Acestea. vom crea o anumită clasă pe care o putem crea folosind operatorul „New” și vom apela metodele acestui obiect din limbajul încorporat.
  2. Pentru ca obiectul nostru să funcționeze, platforma va „comunica” cu acesta folosind un anumit protocol, pe care suntem obligați să-l furnizăm.
  3. Codul componentei în sine constă în mod convențional din două părți: prima este înregistrarea componentei în sine în sistem, a doua este funcționarea noii clase și interacțiunea acesteia cu platforma.

Nu vom intra în specificul implementării, epuizăm termenele limită și nu avem suficientă competență. Trebuie să înțelegem rapid unde trebuie să introducem liniile noastre pentru ca componenta să funcționeze. Pentru a face acest lucru, luați șablonul de componentă cu ITS și deschideți-l în Visual Studio. Șablonul se află în folderul șablon al arhivei despachetate. Să vedem ce avem aici.

Suntem interesați de fișierul AddInNative.cpp. Toată realizarea constă în ea. Conține șabloane pentru toate metodele necesare, trebuie doar să le personalizați ușor. Cu toate acestea, s-a dovedit că a fost mai ușor să nu folosiți un șablon gol ca bază, ci să vă ocupați de un exemplu de lucru. Are mai multe clopote și fluiere utile care nu sunt incluse în șablonul gol. Când ajungeți la înțelegere, va trebui să luați un șablon gol și să-l perfecționați cu cunoștințe despre subiect. Un exemplu de componentă de lucru se află în folderul example\NativeAPI, iar un șablon gol este localizat în folderul șablon.

Să deschidem proiectul din folderul exemplu și în el - fișierul AddInNative.cpp

La începutul fișierului există declarații de constante și funcții auxiliare. Suntem interesați de următoarele linii:

Obiectul nostru, ca unul „adevărat”, va suporta metode scrise atât în ​​rusă, cât și în engleză. În acest scop, numele scrise ale proprietăților și metodelor sunt declarate în două limbi. Rama albastra este pentru bai englezesti, rama rosie pentru bai rusesti. Imaginea arată că exemplul implementează deja o serie de metode și proprietăți. Sarcina noastră este să le eliminăm și să le introducem pe ale noastre.

Linia în care este declarat numele clasei este evidențiată cu un cadru verde. Sincer să fiu, nu am înțeles ce înseamnă. Dacă îl schimbi, nimic nu funcționează. Din moment ce inițial au făcut rezervarea că sunt un „fachin”, pot fi iertat. :)

Astfel, dacă obiectul nostru conține metoda „RunCalculation” și proprietatea „Destination”, atunci trebuie să descriem acest nume în matricele g_MethodNamesRu și, respectiv, g_PropNamesRu.

Apeluri din limba 1C

Deci, obiectul nostru va conține o metodă și o proprietate de citire-scriere.

Să avem următorul scenariu de utilizare:

OurObject = New ("AddIn. MyComponent. DataSender"); Obiectul nostru. Destinație = „un email@server. com";
Obiectul nostru. RunCalculation(PaymentAmount, „Pentru utilități”);

Există o proprietate șir și o metodă cu un parametru numeric și un parametru șir. Pentru ca toate acestea să funcționeze, 1C realizează aproximativ următorul protocol de comunicare cu componenta:

Platforma apelează funcții predefinite pe obiectul nostru și răspunde la acesta și își execută comenzile. Situația este similară cu metodele, doar că acolo, pe lângă numărul de metodă, se solicită și numărul de parametri, prezența unei valori returnate și prezența parametrilor opționali.

Să revenim la codul nostru. Pentru a evita „numerele magice”, în clasa CAddInNative sunt declarate două enumerații, care sunt responsabile pentru determinarea numărului de metode și proprietăți. Să deschidem fișierul CAddInNative.h și să le vedem chiar de la început:

Șablonul gol nu conține aceste enumerari și nu există nici un exemplu pentru separarea apelurilor în rusă de apelurile în non-rusă. Această abordare este opțională. Este important să urmăriți interfața, iar dacă vor exista sau nu transferuri rămâne la latitudinea dvs. de a decide.

Șiruri Unicode

Mulți oameni știu probabil că platforma funcționează pe caractere pe doi octeți în format Unicode. Șablonul declară un tip special WCHAR_T în acest scop. Acest tip este un wrapper multiplatform și asigură aceeași dimensiune a caracterelor pe Windows și Linux. Tipul standard wchar_t poate varia în dimensiune pe diferite sisteme. Vă rugăm să rețineți că toate literalele șir sunt declarate cu un prefix al literei L. Aceasta înseamnă că un astfel de șir este de tip wchar_t.

Există o regulă simplă: în interior, componentele șirului sunt procesate ca wchar_t (pe Linux pot fi 4 octeți, pe Windows - 2), dar de îndată ce transferăm șirul în 1C sau îl primim de acolo, avem nevoie de WCHAR_T (strict 2 octeți pe toate sistemele).

Pentru a converti un tip de șir în altul, șablonul oferă funcții auxiliare:

Primul formează WCHAR_T din wchar_t standard:

uint32_t convToShortWchar(WCHAR_T** Dest, const wchar_t* Source, uint32_t len ​​​​= 0);

Al doilea este invers. Formează wchar_t de la WCHAR_T.

uint32_t convFromShortWchar(wchar_t** Dest, const WCHAR_T* Source, uint32_t len ​​​​= 0);

Când interacționați cu platforma, se folosește întotdeauna numai WCHAR_T.

Tipul variantei

Un alt lucru interesant este tipul de date generic Variant. Ne permite să interacționăm cu limbajul 1C, care, după cum știți, nu este tastat și fiecare variabilă din el poate conține orice. Acest tip este utilizat la schimbul de valori. Trecem doi parametri metodei PerformCalculation - un număr și un șir. Componenta va primi două valori Variant. Este responsabilitatea noastră să verificăm tipul lor real. Nimeni nu te va împiedica să treci nu un număr componentului, ci, să zicem, un tabel de valori.

Deși, se pare că mă înșel. Mi se pare că încă nu va fi posibil să transferați Tabelul de Valori la NativeAPI, deoarece... nu se află în lista de tipuri permise, dar, cu toate acestea, puteți trece Date în loc de String. Nici asta nu este bine. Trebuie să verificăm tipul real al variabilei care a venit din 1C.

Tipul Variant este simplu. Aceasta este o structură ale cărei proprietăți sunt valori de diferite tipuri. Există proprietăți precum DATE, wchar_t, int și altele. Partea principală a unei Variante este proprietatea „vt”, care stochează tipul real al variabilei și din care puteți înțelege exact cum să interpretați această Variantă. În plus, au fost declarate o serie de macrocomenzi auxiliare pentru a simplifica lucrul cu tipul Variant.

Treci la subiect

Se pare că asta e tot cu introducerea. Propun să luăm în considerare un exemplu de implementare a unei componente externe. TK va fi un exemplu de componentă de pe discul ITS. Acest exemplu descrie următoarele caracteristici:

  • Afișarea textului în bara de stare a ferestrei principale;
  • Trimiterea unui eveniment timer extern;
  • Transferarea datelor binare către 1C:Enterprise;
  • Implementarea proprietăților;
  • Implementarea procedurilor;
  • Implementarea functiilor;

Componenta are următorul API:

  • Proprietăți:
    • Activat/Este Activat;
    • IsTimer/IsTimerPresent;
    • Metode:
      • Permite;
      • Dezactivare/Dezactivare;
      • ShowInStatusLine;
      • EnableTimer/StartTimer;
      • Opriți Timer/StopTimer;
      • LoadPicture/LoadPicture;

Un eveniment extern are loc conform unui cronometru, la care se poate abona din codul 1C.

Ghidați de cunoștințele pe care le avem, să privim componenta de la bun început.

Componente de înregistrare

Obiectul nostru este implementat ca o clasă separată C++, în acest caz CAddInNative. Pentru ca 1C să vadă clasa noastră, biblioteca dll trebuie să exporte 3 funcții:

  • GetClassObject
  • DestroyObject
  • GetClassNames

Aceste exporturi pot fi văzute în fișierul AddInNative.def din arborele proiectului VisualStudio. Să ne uităm la codul acestor funcții:

Cea mai simplă - funcția GetClassNames - spune platformei 1C ce clase sunt în componenta noastră. Să mă corecteze guru-ii C++, mi se pare că aici platforma trebuie să răspundă la numele claselor C++ ca să le poată importa. Exact pentru asta este folosită matricea g_kClassNames, cea cu „cadru” verde. Nu l-am verificat în mod specific, dacă trebuie doar să faceți componenta să funcționeze, atunci ar trebui să lăsați totul așa cum este în exemplu. Deja funcționează, nu e nevoie să te chinuiești deocamdată.

Deci, GetClassNames returnează platformei o serie de nume de clasă care implementează obiecte utile ale componentei externe. În exemplul nostru, componenta va returna platformei o matrice de un element cu numele de clasă CAddInNative.

Vă rugăm să rețineți că platforma va primi o valoare de tip WCHAR_T, iar numele clasei din matricea g_kClassNames este de tip wchar_t. Prin urmare, distribuția este efectuată folosind funcția de ajutor discutată mai sus.

Următoarea funcție este GetClassObject. Apelat când am scris „Nou” în codul întreprinderii. Platforma ne cere să creăm o nouă instanță a clasei și să returnăm un pointer către noul obiect.

Din nou, rețineți că primul parametru pe care ni-l spune platforma este ce clasă să creăm (dintre cele date de metoda GetClassNames). Deoarece avem o singură clasă, acest nume nu este deloc verificat aici, un obiect este pur și simplu creat prin new și returnat prin parametrul de ieșire pInterface.

Și ultima funcție de export necesară este DestroyObject. Numele vorbește de la sine. Când un obiect nu mai este necesar de către platformă, acesta trebuie șters. Ni se dă un pointer către un obiect creat anterior. Îl eliberăm folosind ștergerea și resetarea indicatoarelor inutile.

Implementările descrise sunt destul de universale. Dacă componenta noastră implementează o singură clasă (ca în exemplu), atunci aceste funcții trebuie să fie pur și simplu copiate în sine. Singura condiție este să creați clasa corectă în funcția GetClassObject, dacă numele dvs. nu este CAddInObject, ci altceva.

Inițializarea/terminarea unei componente

După crearea unei clase care implementează o componentă, platforma apelează metodele acestei clase. Înainte de a începe lucrul, platforma ne va spune un obiect „în sine”, cu care putem numi anumite metode ale platformei în sine. Acest lucru se întâmplă în metoda Init. În exemplu, obiectul platformă este stocat în variabila m_iConnect.

O altă metodă importantă este setMemManager. Vă permite să alocați blocuri de memorie pe care platforma în sine le va elibera. Se implementează după cum urmează:

Stocăm pur și simplu un pointer către managerul de memorie pe care ni-l transmite platforma. Apoi, cu acest manager vom aloca memorie eliberată de platforma în sine.

Și din nou, ca și în cazul funcțiilor de export, metodele de inițializare sunt destul de universale, puteți să le copiați pur și simplu și să nu vă faceți griji să le „terminați” până când devine cu adevărat necesar.

Încărcătură utilă. Metode și proprietăți ale obiectului component

Înregistrare

Ei bine, desigur, am creat componenta nu de dragul inițializării ei, ci de dragul unor funcționalități utile. Este timpul să vedem cum este implementat.

În primul rând, trebuie să înregistrăm un obiect care poate fi creat și apelat din limbajul 1C. Acest obiect este înregistrat în metoda RegisterExtensionAs.

În această metodă, informăm platformei numele clasei noastre, deoarece va fi vizibil din limbajul 1C. Cu acest nume îl vom crea prin „Nou”. În acest caz, crearea obiectului se va realiza cu următorul cod:

ConnectExternalComponent(Fișier, „MyComponent”, ExternalComponentType. Nativ);
ObjectComponents = New( „AddIn.MyComponent.AddInNativeExtension”);

Conform documentației, memoria pentru șirul cu numele clasei este alocată de managerul de memorie, iar numele este scris la această adresă - „AddInNativeExtension”. Aici îți poți scrie numele fără durere. Vă rugăm să rețineți că, din nou, există o conversie de la wchar_t la platforma WCHAR_T.

Utilizare

După cum am scris mai sus, platforma interogează componenta pentru diferite caracteristici ale limbii. Proprietatea specificată există, poate fi scrisă, parametrul funcției are o valoare implicită, are o valoare returnată etc. Dacă luăm exemplul de cod dat mai devreme:

OurObject = Nou( „AddIn.MyComponent.DataSender”); // DataSender este numele din funcția RegisterExtensionAs (discută mai jos).
Obiectul nostru. Destinatar = " [email protected]" ;
Obiectul nostru. Efectuați calculul (suma de plată, "Pentru utilitati");

atunci se va efectua următorul sondaj:

  1. Există o proprietate „Destinație”?
  2. Acceptă înregistrarea?
  3. Există o metodă numită RunCalculation?
  4. Cati parametri are?
  5. Are o valoare de returnare
  6. Care sunt valorile implicite pentru parametrii opționali (dacă există)

Cel mai util lucru de făcut aici este să te uiți la un exemplu și să verifici documentația. Implementarea tuturor acestor sondaje este destul de simplă. O întreagă grădină zoologică de metode este responsabilă de interacțiune. Nu voi lua în considerare totul, sunt destul de bine documentate și, în plus, sunt ușor de implementat. Vor fi luate în considerare doar momentele cele mai semnificative, în care noi, în calitate de proști, va trebui să punem mâna pe ele :). Abordarea de bază este următoarea: prima dată când este menționată o proprietate sau o metodă, platforma ne va cere să o căutăm după nume. Va trebui să răspundem cu numărul unic al acestei proprietăți (metodă). Orice comunicare ulterioară va avea loc numai prin numere. Aici vă vor ajuta enumerarile menționate care stochează aceste numere.

Proprietăți

Primul lucru de luat în considerare este infrastructura proprietății. Platforma solicită existența unei proprietăți folosind metoda FindProp

Platforma ne transmite numele proprietății pe care o căutăm sub forma WCHAR_T. Este convertit în wchar_t folosind o metodă auxiliară, iar acest text este căutat mai întâi în termeni în limba engleză, apoi în cei în limba rusă. Trebuie să returnăm numărul proprietății. Rețineți că funcția helper findName este implicată aici. Implementarea sa este în exemplu, dar componenta nu este în șablonul gol. Pare potrivit să îl trageți spre dvs. dacă intenționați să aveți termeni bilingvi în componenta dvs.

Apoi, metoda GetPropName realizează sarcina inversă, obținând numele proprietății după numărul său. Șirul de nume este, de asemenea, alocat prin managerul de memorie al companiei. Bănuiesc că metoda GetPropName împreună cu GetNProps este folosită atunci când extindem proprietățile unui obiect cu semnul plus în depanator. Apoi platforma va primi numărul total de proprietăți și va cere un nume pentru fiecare dintre ele.

Următoarea pereche de metode este IsPropReadable/IsPropWritable. Totul este simplu aici, pentru numărul de proprietate specificat trebuie să spunem dacă poate fi citit/scris.

Recepția și scrierea valorilor se efectuează folosind metodele GetPropVal/SetPropVal. Merită să intri în mai multe detalii aici. Începem să lucrăm cu tipuri 1C:Enterprise, ceea ce înseamnă că Variant intră în scenă.

Șablonul de componentă definește un set de macrocomenzi auxiliare pentru a simplifica lucrul cu Variant. Prima este verificarea tipului de valoare. De exemplu, macro-ul TV_VT vă permite să verificați/setarea tipului unei valori. Constantele numite sunt, de asemenea, definite pentru fiecare dintre tipurile acceptate. Aceste constante și corespondența lor cu tipurile 1C:Enterprise sunt enumerate în documentație.

Macrocomanda TV_BOOL primește o valoare booleană de la varianta cu care puteți lucra. Prin analogie, se obțin valori întregi (TV_INT), șiruri de caractere (TV_WSTR) și altele. Valorile exacte sunt în cod, le puteți vedea oricând.

Punctul important este că nu este suficient să alocați o valoare unei variante trebuie să atribuiți și un tip real. Acordați atenție GetPropVal. În plus față de atribuirea TV_BOOL = adevărată, există o atribuire de tip: TV_VT = VTYPE_BOOL. Dacă tipul nu este atribuit, platforma nu va ști ce tip de valoare i-a fost returnată. Desigur, puteți da greșelii și puteți seta tipul greșit. Acest lucru este adesea însoțit de căderea platformei.

Să rezumam cele de mai sus:

Obținem valoarea din opțiunea:

bool someVariable = TV_BOOL(pVariant);

Scrieți valoarea opțiunii:

TV_VT(pVariant) = VTYPE_BOOL; // tip de date valid

TV_BOOL(pVariant) = someBooleanVariable; // setează valoarea în sine

Și acum - metode cocoșate!

Metodele sunt puțin mai complicate, dar în general sunt asemănătoare proprietăților. În primul rând, există exact aceeași funcție de căutare a unei metode după nume, obținerea numărului total de metode, obținerea numelui după număr. Cu toate acestea, pe lângă cele de mai sus, se adaugă următoarele caracteristici:

  • Dacă o metodă poate returna o valoare, atunci aceasta poate fi folosită în „Calculate” și scrisă în dreapta operațiunii de atribuire în limbajul 1C. Dacă nu, atunci este o procedură și lucruri de genul acesta vor genera o excepție „Utilizarea unei proceduri ca funcție”
  • Metoda are parametri. Platforma trebuie să le cunoască numărul. Dacă apelul specifică mai multe argumente decât sunt specificate în semnătura metodei, apare eroarea „Prea mulți parametri”.
  • Dacă nu sunt transmise suficiente argumente unei metode, atunci unele dintre ele pot fi opționale, iar dacă nu există parametri opționali, atunci apare o eroare „Parametri insuficienti”.
  • Când este apelat, dacă este o procedură, atunci nu poate exista o valoare returnată. Dacă este o funcție, atunci există o valoare returnată. De asemenea, trebuie procesat.

Există o serie de metode simple, al căror scop este clar din numele lor și din documentație. Acestea includ HasRetVal, GetNParams, GetParamDefValue. Propun să nu le luăm în considerare un exemplu este mai mult decât suficient. Interesul nostru va fi îndreptat către implementarea directă a sarcinii utile. Este implementat în metodele CallAsProc și CallAsFunc. Primul este responsabil pentru procedurile de apelare, al doilea este responsabil pentru funcțiile de apelare. Ele diferă prin faptul că CallAsFunc are un parametru de ieșire suplimentar, în care vom trece valoarea de returnare a funcției către platformă.

Apelul se efectuează după cum urmează: platforma ne transmite numărul metodei apelate, o serie de parametri reali și numărul acestora. Trebuie să analizăm numărul metodei și să-i transmitem parametrii trecuți. În cazul unei funcții, trebuie să scriem și ceva în valoarea returnată.

În exemplu, numărul metodei este analizat în comutator/caz și, în funcție de număr, se execută logica metodei. Pentru metodele Activare/Dezactivare, pur și simplu bifați o casetă de selectare. Metoda ShowInStatusLine este interesantă. Arată ce i s-a transmis în bara de stare a ferestrei 1C:Enterprise. Pentru a face acest lucru, folosim obiectul de conectare la platforma m_iConnect, cel care ne-a fost „emis” la înregistrarea componentei. O listă completă a capabilităților sale este descrisă în documentație.

Interesant punct. Aici, în exemplu, tipul valorii care vine de la 1C nu este verificat, dar SetStatusLine este pur și simplu apelat cu partea șir Variant. Bănuiesc că dacă apelezi metoda componentei din limbajul 1C, trecând acolo un număr sau o dată (în loc de un șir), atunci nimic nu va funcționa... Din nou, lasă-i pe guru să corecteze, dar se pare că pointerul pwstrVal va indica la Dumnezeu știe de unde dacă a venit de la întreprindere să zicem un număr, nu un șir cinstit. La apelarea SetStatusLine, platforma va încerca să citească o linie de la o adresă necunoscută și, cel mai probabil, se va bloca. Este mai bine să verificați întotdeauna tipul așteptat. Nu stii niciodata.

Funcția LoadImage din exemplu este implementată într-un mod mai interesant, ia în considerare posibilitatea de a schimba șiruri și date binare cu platforma.

În primul rând, numărul de parametri trecuți este verificat aici. Dacă nu sunt prezenți, atunci apelul este considerat nereușit. Returnează false, care este interpretată de platformă ca o eroare de apel.

Apoi, tipul parametrului transmis este verificat aici. Dacă acesta este un șir îngust (VTYPE_PSTR), atunci se utilizează partea char a variantei. Exemplul spune paParam->pstrVal, dar poți folosi macrocomanda TV_STR, va fi la fel, dar se va menține și uniformitatea lucrului cu opțiunea.

Dacă este un șir larg (VTYPE_PWSTR), atunci conversia se face mai întâi la wchar_t și apoi la char. Faptul este că calea către fișier este transmisă din limbajul 1C către această metodă, care este apoi folosită în funcția fopen(char*). Această funcție necesită un tip char* ca intrare, iar WCHAR_T ne va fi trimis de pe platformă. Pentru o funcționare corectă, se efectuează conversii de șiruri.

Și, în sfârșit, dacă acesta nu este deloc un șir, atunci apelul este considerat nereușit și este returnat false.

Alocam memorie pentru datele binare folosind un manager de memorie. Acest lucru este logic; datele binare vor deveni un obiect cu drepturi depline în cadrul platformei și trebuie gestionate de aceasta. Memoria este alocată pentru varianta pvarRetValue, care este valoarea returnată a funcției componente externe.

În plus, întregul fișier este citit în buffer-ul alocat; Neapărat dimensiunea octetului este specificată în proprietatea opțiunii strLen și tipul de date al opțiunii VTYPE_BLOB. Dacă memoria este alocată cu succes, atunci revenim true ca semn al unui apel reușit la întreaga funcție.

Astfel, când în limbajul 1C este scris:

BinaryData = Componentă. Încarcă imagine("C:\pic.jpg");

Metoda CallAsFunc a obiectului component va fi apelată, trecând calea și returnând date binare așa cum este descris mai sus.

Dacă are succes, variabila BinaryData va conține un obiect limbaj 1C cu drepturi depline. Când iese din domeniul de aplicare, toată memoria pe care a ocupat-o va fi eliberată de platformă. De aceea a fost alocat prin intermediul managerului de memorie.

Concluzie

Povestea a fost scrisă de un ceainic pentru manechini, prin urmare, cel mai probabil, este plină de inexactități terminologice. Cu toate acestea, scopul acestui articol este de a oferi o introducere rapidă în componentele externe. Dacă trebuie să realizați rapid o componentă într-un timp scurt, fără bătăi de cap inutile, fără discuții îndelungate, atunci sper că acest articol vă va ajuta. Dacă vreuna dintre greșelile mele te face să te simți rău, ca guru C++, te rog să-mi spui în comentarii și le vom corecta.

Vă mulțumim pentru atenție.

ansmirnov 22 august 2013 la 14:12

Componente externe în 1C 8.2

  • programare,
  • C++
  • Tutorial

Introducere

Acest articol oferă o idee despre cum funcționează componentele externe în sistemul 1C: Enterprise.
Va fi afișat procesul de dezvoltare a unei componente externe pentru versiunea 8.2 a sistemului 1C: Enterprise, care rulează sub sistemul de operare Windows cu un mod de funcționare fișier. Această opțiune este utilizată în majoritatea soluțiilor concepute pentru întreprinderile mici. VK va fi implementat în limbajul de programare C++.

Componente externe „1C: Enterprise”

„1C: Enterprise” este un sistem extensibil. Pentru a extinde funcționalitatea sistemului, sunt utilizate componente externe (EC). Din punctul de vedere al dezvoltatorului, un VC este un obiect extern care are proprietăți și metode și poate genera, de asemenea, evenimente pentru procesare de către sistemul 1C: Enterprise.
Componentele externe pot fi folosite pentru a rezolva o clasă de probleme care sunt dificil sau chiar imposibil de implementat în limbajul de programare integrat în 1C: Enterprise. În special, această clasă include sarcini care necesită interacțiune la nivel scăzut cu sistemul de operare, de exemplu, pentru a lucra cu echipamente specifice.
Sistemul 1C: Enterprise folosește două tehnologii pentru a crea componente externe:
  • folosind Native API
  • folosind tehnologia COM
Având în vedere restricțiile date, diferența dintre cele două tehnologii mai sus menționate este nesemnificativă, așa că vom lua în considerare dezvoltarea jocurilor video folosind API-ul nativ. Dacă este necesar, dezvoltările implementate pot fi aplicate dezvoltării de software de calculator folosind tehnologia COM și, de asemenea, cu modificări minore, aplicate pentru utilizare în sistemul 1C: Enterprise cu alte opțiuni de operare, altele decât modul de operare fișier.
Structura VK
Componenta externă a sistemului 1C: Enterprise este prezentată sub forma unei biblioteci DLL. Codul bibliotecii descrie clasa descendentă IComponentBase. Clasa creată trebuie să definească metode responsabile de implementarea funcțiilor componentei externe. Metodele anulate vor fi descrise mai detaliat mai jos pe măsură ce materialul este prezentat.

Lansarea unui demo VK

Sarcină:
  1. Asamblați o componentă externă furnizată cu un abonament ITS și destinată să demonstreze principalele capacități ale mecanismului de componentă externă în 1C
  2. Conectați componenta demo la configurația 1C
  3. Asigurați-vă că funcțiile declarate funcționează corect
Compilare
VK-ul demo este situat pe discul de abonament ITS în directorul „/VNCOMP82/example/NativeAPI”.
Pentru a construi VC demonstrativ vom folosi Microsoft Visual Studio 2008. Alte versiuni ale acestui produs nu acceptă formatul de proiect Visual Studio utilizat.


Deschideți proiectul AddInNative. În setările proiectului, includem directorul cu fișierele de antet necesare pentru a construi proiectul. În mod implicit, acestea sunt localizate pe discul ITS din director /VNCOMP82/include.
Rezultatul build-ului este fișierul /bind/AddInNative.dll. Aceasta este biblioteca compilată pentru conectarea la configurația 1C.
Se conectează configurația VK la 1C
Să creăm o configurație 1C goală.
Mai jos este codul pentru modulul aplicației gestionate.
variabila DemoComp; Procedură la pornirea sistemului() Conectare componentă externă("...\bind\AddInNative.dll", "DemoVK", Tipul componentei externe.Native); DemoComp = New ("AddIn.DemoVK.AddInNativeExtension"); Sfârșitul procedurii
Dacă nu a fost raportată nicio eroare la pornirea configurației 1C, atunci VK-ul a fost conectat cu succes.
Ca urmare a executării codului de mai sus, un obiect apare în vizibilitatea globală a configurației DemoComp, care are proprietăți și metode care sunt definite în codul componentei externe.
Demonstrarea funcționalității încorporate
Să verificăm funcționalitatea demo-ului VK. Pentru a face acest lucru, să încercăm să setăm și să citim unele proprietăți, să apelăm unele metode VK și, de asemenea, să primim și să procesăm mesajul VK.
Documentația furnizată pe discul ITS precizează următoarea funcționalitate a VC-ului demo:
  1. Gestionarea stării obiectelor componente
    Metode: Porniți, Opriți
    Proprietăți: Inclus
  2. Gestionarea temporizatorului
    În fiecare secundă, componenta trimite un mesaj către sistemul 1C: Enterprise cu parametri Componentă, Temporizatorși o linie de contor al ceasului de sistem.
    Metode: StartTimer, Stop Timer
    Proprietăți: Există un cronometru
  3. Metodă ShowInStatusLine, care afișează în linia de stare textul trecut metodei ca parametri
  4. Metodă Încarcă imagine. Încarcă o imagine din fișierul specificat și o transferă în sistemul 1C: Enterprise sub formă de date binare.
Să ne asigurăm că aceste funcții funcționează. Pentru a face acest lucru, rulați următorul cod:
variabila DemoComp; Procedura la pornirea sistemului() ConnectExternalComponent(...); DemoComp = New ("AddIn.DemoVK.AddInNativeExtension"); DemoComp.Disable(); Raport (DemoComp.Activat); DemoComp.Enable(); Raport (DemoComp.Activat); DemoComp.StartTimer(); Sfârșitul procedurii Procesare externă a evenimentelor (Sursă, Eveniment, Date) Raport (Sursă + " " + Eveniment + " " + Date); Sfârșitul procedurii
Rezultatul rulării configurației este afișat în imagine


Panoul „Mesaje” afișează rezultatele apelurilor de metodă DemoComp.Disable()Și Demo.Comp.Enable(). Rândurile ulterioare din același panou conțin rezultatele procesării mesajelor primite de la VK - Sursă, EvenimentȘi Date respectiv.

Nume personalizat de componentă externă

Sarcină: Schimbați numele componentei externe într-unul arbitrar.
Secțiunea anterioară a folosit identificatorul AddInNativeExtension, al cărui sens nu a fost explicat. În acest caz AddInNativeExtension- acesta este numele extensiei.
Codul VK definește o metodă RegisterExtensionAs, returnând numele sistemului 1C: Enterprise, care este necesar pentru înregistrarea ulterioară a VK-ului în sistem. Se recomandă specificarea unui identificator care dezvăluie într-o oarecare măsură esența componentei externe.
Iată codul complet al metodei RegisterExtensionAs cu numele extensiei schimbat:
bool CAddInNative::RegisterExtensionAs(WCHAR_T** wsExtensionName) ( wchar_t *wsExtension = L"SomeName"; int iActualSize = ::wcslen(wsExtension) + 1; WCHAR_T* dest = 0; if (m_iMemory) ( if(m_iMemory) ( if(m_iMemory) ((void**)wsExtensionName, iActualSize * sizeof(WCHAR_T))) ::convToShortWchar(wsExtensionName, iActualSize) returnează false;
În exemplul dat, numele VK este schimbat în SomeName. Apoi, atunci când vă conectați VK, trebuie să specificați un nume nou:
DemoComp = New("AddIn.DemoVK.SomeName");

Extinderea listei de proprietăți VK

Sarcină:
  1. Studiați implementarea proprietăților VK
  2. Adăugați o proprietate de citire/scriere de tip șir
  3. Adăugați o proprietate șir de citire/scriere care stochează tipul de date al ultimului set de proprietăți. Nu se întreprinde nicio acțiune la setarea valorii proprietății

Pentru a determina proprietățile componentei care se creează, dezvoltatorul trebuie să implementeze următoarele metode în codul bibliotecii AddInNative.cpp:
GetNProps
Returnează numărul de proprietăți ale acestei extensii, 0 dacă nu există proprietăți
FindProp
Returnează numărul de serie al proprietății al cărei nume este trecut în parametri
GetPropName
Returnează numele proprietății după numărul de serie și după identificatorul de limbă transmis
GetPropVal
Returnează valoarea proprietății cu numărul ordinal specificat
SetPropVal
Setează valoarea proprietății cu numărul ordinal specificat
IsPropReadable
Returnează indicatorul de lizibilitate al proprietății cu numărul de secvență specificat
IsPropWritable
Returnează indicatorul de scriere al proprietății cu numărul de secvență specificat


Să luăm în considerare implementarea metodelor de clasă de mai sus CAddInNative.
În VC demonstrativ, sunt definite 2 proprietăți: InclusȘi Există un cronometru (Este activatȘi IsTimerPresent).
În domeniul global al codului bibliotecii, sunt definite două matrice:
static wchar_t *g_PropNames = (L"IsEnabled", L"IsTimerPresent"); static wchar_t *g_PropNamesRu = (L"Activat", L"Există un temporizator");
care stochează nume de proprietate în limba rusă și engleză. În fișierul antet AddInNative.h enumerarea este definită:
Enum Props ( ePropIsEnabled = 0, ePropIsTimerPresent, ePropLast // Întotdeauna ultimul );
ePropIsEnabledȘi ePropIsTimerPresent, respectiv având valorile 0 și 1, sunt folosite pentru a înlocui numerele de serie ale proprietăților cu identificatori semnificativi. ePropLast, care are valoarea 2, este folosit pentru a obține numărul de proprietăți (folosind metoda GetNProps). Aceste nume sunt folosite numai în codul componentei și nu sunt disponibile din exterior.
Metodele FindProp și GetPropName efectuează căutări în matrice g_PropNamesȘi g_PropNamesRu.
Pentru a stoca valorile câmpurilor din modulul bibliotecă, clasa CAddInNative are proprietăți care stochează valoarea proprietăților componente. Metode GetPropValȘi SetPropVal returnați și setați valoarea acestor proprietăți în consecință.
Metode IsPropReadableȘi IsPropWritableși întoarce-te Adevărat sau fals, în funcție de numărul de secvență transmis al proprietății în conformitate cu logica aplicației.
Pentru a adăuga o proprietate personalizată, trebuie să:

  1. Adăugați numele proprietății care este adăugată la matrice g_PropNamesȘi g_PropNamesRu(fişier AddInNative.cpp)
  2. A lista Recuzită(fişier AddInNative.h) inainte de ePropLast adăugați un nume care identifică în mod unic proprietatea adăugată
  3. Organizați memoria pentru stocarea valorilor proprietăților (creați câmpuri componente ale modulului care stochează valorile corespunzătoare)
  4. Faceți modificări metodelor GetPropValȘi SetPropVal pentru a interacționa cu memoria alocată în pasul anterior
  5. În conformitate cu logica aplicației, efectuați modificări metodelor IsPropReadableȘi IsPropWritable
Punctele 1, 2, 5 nu au nevoie de explicații. Detalii despre implementarea acestor pași pot fi găsite studiind anexa la articol.
Să dăm nume proprietăților de testare TestȘi Verificare tip respectiv. Apoi, ca rezultat al pasului 1, avem:
static wchar_t *g_PropNames = (L"IsEnabled", L"IsTimerPresent", L"Test", L"TestType"); static wchar_t *g_PropNamesRu = (L"Activat", L"Există un temporizator", L"Test", L"Verificare tip");
Transfer Recuzită va arata ca:
Enum Props ( ePropIsEnabled = 0, ePropIsTimerPresent, ePropTest1, ePropTest2, ePropLast // Întotdeauna ultimul);
Pentru a simplifica semnificativ codul, vom folosi STL C++. În special, pentru lucrul cu șiruri WCHAR, haideți să conectăm biblioteca wstring.
Pentru a salva valoarea unei metode Test, definim în clasă CAddInNativeîn domeniul privat:
testul șirurilor1;
Pentru a transfera parametrii șir între componentele 1C: Enterprise și externe, se folosește 1C: Manager de memorie Enterprise. Să aruncăm o privire mai atentă la munca lui. Funcțiile sunt folosite pentru a aloca și, respectiv, elibera memorie AllocMemoryȘi Memorie libera definite în dosar ImemoryManager.h. Dacă este necesar să se transmită un parametru șir sistemului 1C: Enterprise, componenta externă trebuie să aloce memorie pentru acesta apelând funcția AllocMemory. Prototipul său arată astfel:
virtual bool ADDIN_API AllocMemory (void** pMemory, unsigned long ulCountByte) = 0;
Unde pMemorie- adresa pointerului în care va fi plasată adresa zonei de memorie alocată,
ulCountByte- dimensiunea zonei de memorie alocată.
Un exemplu de alocare de memorie pentru un șir:
WCHAR_T *t1 = NULL, *test = L"TEST_STRING"; int iActualSize = wcslen(test1)+1; m_iMemory->AllocMemory((void**)&t1, iActualSize * sizeof(WCHAR_T)); ::convToShortWchar(&t1, test1, iActualSize);
Pentru confortul lucrului cu tipuri de date șir, vom descrie funcția wstring_to_p. Primește un șir wstring ca parametru. Rezultatul funcției este o structură umplută tVariant. Codul funcției:
bool CAddInNative::wstring_to_p(std::wstring str, tVariant* val) ( char* t1; TV_VT(val) = VTYPE_PWSTR; m_iMemory->AllocMemory((void**)&t1, (str.length()+1) * sizeof(WCHAR_T)); memcpy(t1, str.c_str(), (str.length()+1) * sizeof(WCHAR_T)); returneaza adevarat)
Apoi, secțiunea de caz corespunzătoare a instrucțiunii switch a metodei GetPropVal va lua forma:
caz ePropTest1: wstring_to_p(test1, pvarPropVal); pauză;
Metodă SetPropVal:
caz ePropTest1: dacă (TV_VT(varPropVal) != VTYPE_PWSTR) returnează fals; test1 = std::wstring((wchar_t*)(varPropVal -> pstrVal)); pauză;
Pentru a implementa a doua proprietate, definim un câmp de clasă CaddInNative
uint8_t last_type;
în care vom salva tipul ultimei valori transferate. Pentru a face acest lucru, adăugați comanda la metoda CaddInNative::SetPropVal:
ultimul_tip = TV_VT(varPropVal);
Acum, când solicităm citirea valorii celei de-a doua proprietăți, vom returna valoarea ultimul_tip, ceea ce necesită sarcina desemnată.
Să verificăm funcționalitatea modificărilor făcute.
Pentru a face acest lucru, să prezentăm aspectul configurației 1C după cum urmează:
variabila DemoComp; Procedură la pornirea sistemului() Conectare componentă externă("...", "DemoVK", Tip componentă externă.Native); DemoComp = New("AddIn.DemoVK.SomeName"); DemoComp.TypeCheck = 1; Raport(String(DemoComp.TypeCheck)); DemoComp.Test = "Vasya"; Raport(String(DemoComp.Test)); DemoComp.Test = "Petya"; Raport(String(DemoComp.Test)); Raport(String(DemoComp.TypeCheck)); Sfârșitul procedurii
Ca urmare a lansării, vom primi o secvență de mesaje:
3
Vasia
Petru
22

Al doilea și al treilea mesaj sunt rezultatul citirii proprietății stabilite în pasul anterior. Primul și al doilea mesaj conțin codul de tip al ultimului set de proprietăți. 3 corespunde unei valori întregi, 22 unei valori șir. Corespondența tipurilor și a codurilor acestora se stabilește în dosar tipuri.h, care se află pe discul ITS.

Extinderea listei de metode

Sarcină:
  1. Extindeți funcționalitatea componentei externe cu următoarea funcționalitate:
  2. Explorați modalități de implementare a metodelor componentelor externe
  3. Adăugați o metodă de funcție Funcția 1, care ia două șiruri de caractere („Parameter1” și „Parameter2”) ca parametru. Rezultatul este un șir de genul: „Verificare. Parametrul 1, Parametrul 2"
  4. Asigurați-vă că modificările pe care le faceți funcționează.

Pentru a defini metodele componentei care se creează, dezvoltatorul trebuie să implementeze următoarele metode în codul bibliotecii AddInNative:
GetNMethods, FindMethod, GetMethodName
Conceput pentru a obține numărul corespunzător de metode, căutați numărul și numele metodei. Similar cu metodele corespunzătoare pentru proprietăți
GetNParams
Returnează numărul de parametri ai metodei cu numărul de secvență specificat; dacă o metodă cu acest număr este absentă sau nu are parametri, returnează 0
GetParamDefValue
Returnează valoarea implicită a parametrului specificat al metodei specificate
HasRetVal
Returnează indicatorul dacă metoda cu valoarea de returnare ordinală specificată are o valoare returnată: true pentru metodele cu o valoare returnată și fals in caz contrar
CallAsProc
fals, apare o eroare de rulare și execuția modulului 1C: Enterprise este încheiată. Memoria pentru matricea de parametri este alocată și eliberată de 1C: Enterprise.
CallAsFunc
Execută metoda cu numărul de ordine specificat. Dacă metoda revine fals, apare o eroare de rulare și execuția modulului 1C: Enterprise este încheiată. Memoria pentru matricea de parametri este alocată de 1C: Enterprise. Dacă valoarea returnată este un șir sau un tip de date binare, componenta alocă memorie cu funcția AllocMemory manager de memorie, scrie acolo datele și stochează această adresă în câmpul corespunzător al structurii. 1C: Întreprinderea va elibera această memorie prin apel Memorie libera.
O descriere completă a metodelor, inclusiv o listă de parametri, este descrisă în detaliu în documentația furnizată pe discul ITS.
Să luăm în considerare implementarea metodelor descrise mai sus.
În codul componentelor, sunt definite două matrice:
static wchar_t *g_MethodNames = (L"Activare", L"Dezactivare", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture"); static wchar_t *g_MethodNamesRu = (L"Activare", L"Dezactivare", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadImage");
si enumerare:
Metode enum ( eMethEnable = 0, eMethDisable, eMethShowInStatusLine, eMethStartTimer, eMethStopTimer, eMethLoadPicture, eMethLast // Întotdeauna ultimul);
Sunt folosite în funcții GetNMethods, FindMethodȘi GetMethodName, prin analogie cu descrierea proprietăților.
Metode GetNParams, GetParamDefValue, HasRetVal comutatorul instrumentului, în funcție de parametrii trecuți și de logica aplicației, returnează valoarea necesară. Metodă HasRetValîn codul său are o listă de numai metode care pot returna un rezultat. Pentru ei se întoarce Adevărat. Pentru toate metodele de oțel returnări fals.
Metode CallAsProcȘi CallAsFunc conţin cod executabil direct al metodei.
Pentru a adăuga o metodă care poate fi apelată doar ca funcție, trebuie să faceți următoarele modificări la codul sursă al componentei externe:
  1. Adăugați numele metodei la matrice g_MethodNamesȘi g_MethodNamesRu(fişier AddInNative.cpp)
  2. Adăugați un identificator de metodă semnificativ la enumerarea Metode (fișier AddInNative.h)
  3. Efectuați modificări la codul funcției GetNParamsîn conformitate cu logica programului
  4. Dacă este necesar, faceți modificări la codul metodei GetParamDefValue, dacă doriți să utilizați valorile implicite ale parametrilor metodei.
  5. Efectuați modificări ale funcției HasRetVal
  6. Faceți modificări în logica funcțiilor CallAsProc sau CallAsFunc, plasând acolo codul executabil direct al metodei
Să prezentăm matricele g_MethodNamesȘi g_MethodNamesRu, precum și listare Metode la forma:
static wchar_t *g_MethodNames = (L"Activare", L"Dezactivare", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture", L"Test"); static wchar_t *g_MethodNamesRu = (L"Activare", L"Dezactivare", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture", L"Test");

Metode de enumerare ( eMethEnable = 0, eMethDisable, eMethShowInStatusLine, eMethStartTimer, eMethStopTimer, eMethLoadPicture, eMethTest, eMethLast // Întotdeauna ultimul);
Să edităm funcția GetNProps astfel încât să returneze numărul de parametri ai metodei „Test”:
long CAddInNative::GetNParams(const long lMethodNum) ( switch(lMethodNum) (case eMethShowInStatusLine: return 1; case eMethLoadPicture: return 1; case eMethTest: return 2; implicit: return 0; ) return 0; )
Să facem modificări în funcție:
bool CAddInNative::GetParamDefValue(const long lMethodNum, const long lParamNum, tVariant *pvarParamDefValue) ( ​​​​TV_VT(pvarParamDefValue)= VTYPE_EMPTY; switch(lMethodNum) (case eMethEnable:case eMeth Timer: caz eMethStopTimer: caz eMethTest : / / Nu există valori ale parametrilor implicit break: return false;
Mulțumită liniei adăugate
caz eMethTest:
dacă lipsesc unul sau mai multe argumente, parametrii corespunzători vor avea o valoare goală ( VTYPE_EMPTY). Dacă aveți nevoie de o valoare implicită pentru un parametru, ar trebui să o setați în secțiune eMethTest instrucțiunea de comutare a funcției CAddInNative::GetParamDefValue.
Deoarece metoda Test poate returna o valoare, trebuie să faceți modificări codului funcției HasRetVal:
bool CAddInNative::HasRetVal(const long lMethodNum) ( switch(lMethodNum) (case eMethLoadPicture: case eMethTest: return true; default: return false; ) return false; )
Și adăugați codul executabil al metodei la funcție CallAsFunc:
bool CAddInNative::CallAsFunc(const long lMethodNum, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray) (... std::wstring s1, s2; switch(lMethodNum) (case eMethLoadPicture: ...T break; if (!lSizeArray || !paParams) return s1 = (paParams) -> pwstrVal = (paParams+1) -> pwstring_to_p(std::wstring(s1+s2), pvarRetValue ; ;
Să compilam componenta și să aducem codul de configurare la forma:
variabila DemoComp; Procedură la pornirea sistemului() Conectare componentă externă("...", "DemoVK", Tip componentă externă.Native); DemoComp = New("AddIn.DemoVK.SomeName"); lane = DemoComp.Test ("Bună ziua," "Lumea!"); Raport (per); Sfârșitul procedurii
După lansarea configurației, vom primi mesajul: „Hello, World!”, care indică faptul că metoda a funcționat cu succes.

Temporizator

Sarcină:
  1. Studiați implementarea temporizatorului în demo VK
  2. Modificați metoda „StartTimer” adăugând posibilitatea de a trece în parametri intervalul de răspuns al temporizatorului (în milisecunde)
  3. Asigurați-vă că modificările pe care le faceți funcționează.

În WinAPI, puteți folosi mesajul pentru a lucra cu timpul WM_TIMER. Acest mesaj va fi trimis programului dumneavoastră la intervalul de timp pe care l-ați setat la crearea cronometrului.
Pentru a crea un cronometru, utilizați funcția SetTimer:
UINT SetTimer(HWND hWnd, // descriptor de fereastră UINT nIDevent, // identificator timer (număr) UINT nElapse, // întârziere TIMERPROC lpTimerFunc); // pointer către funcție
Sistemul de operare va trimite un mesaj WM_TIMERîn program cu intervalul specificat în argument nElapse(în milisecunde). În ultimul parametru puteți specifica o funcție care va fi executată de fiecare dată când se declanșează cronometrul. Antetul acestei funcții ar trebui să arate astfel (numele poate fi orice):
void __stdcall TimerProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
Să luăm în considerare implementarea unui cronometru în VC demonstrativ.
Deoarece luăm în considerare procesul de dezvoltare a unei componente externe pentru familia de sisteme de operare Windows, nu vom lua în considerare implementarea temporizatorului în alte sisteme de operare. Pentru sistemul de operare GNU/Linux, în special, implementarea va diferi în sintaxa funcției SetTimerȘi TimerProc.
Codul executabil apelează metoda SetTimer, căruia i se trece funcția MyTimerProc:
m_uiTimer = ::SetTimer(NULL,0,100,(TIMERPROC)MyTimerProc);
ID-ul temporizatorului creat este plasat într-o variabilă m_uiTimer pentru a putea fi dezactivat ulterior.
Funcţie MyTimerProc după cum urmează:
VOID CALLBACK MyTimerProc(HWND hwnd, // handle of window for timer messages UINT uMsg, // WM_TIMER message UINT idEvent, // timer identifier DWORD dwTime // time system current) (dacă (!pAsyncEvent) return; wchar_t *who = L "ComponentNative", *what = L"Timer"; wchar_t *wstime = new wchar_t if (wstime) ( wmemset(wstime, 0, TIME_LEN); ::_ultow(dwTime, wstime, 10); pAsyncEvent->ExternalEvent(who); , ce, wstime);
Esența funcției este că metoda este numită Eveniment extern, care trimite un mesaj către sistemul 1C: Enterprise.
Pentru a extinde funcționalitatea metodei StartTimer Să facem următoarele:
Modificarea codului metodei GetNParams astfel încât să fie pentru metodă eMethStartTimer valoarea returnată 1:
caz eMethStartTimer: return 1;
Aici este codul metodei CallAsProc la forma:
case eMethStartTimer: if (!lSizeArray || TV_VT(paParams) != VTYPE_I4 || TV_I4(paParams)<= 0) return false; pAsyncEvent = m_iConnect; #ifndef __linux__ m_uiTimer = ::SetTimer(NULL,0,TV_I4(paParams),(TIMERPROC)MyTimerProc); #else // код для GNU/Linux #endif break;
Acum să verificăm funcționalitatea. Pentru a face acest lucru, vom scrie codul în modulul aplicației gestionate al configurației:
variabila DemoComp; Procedură la pornirea sistemului() Conectare componentă externă("...", "DemoVK", Tip componentă externă.Native); DemoComp = New("AddIn.DemoVK.SomeName"); DemoComp.StartTimer(2000); Sfârșitul procedurii
După pornirea configurației, programul va primi mesaje la intervale de 2 secunde, ceea ce indică faptul că temporizatorul funcționează corect.

Interacțiunea cu sistemul 1C: Enterprise

Pentru a interacționa între componenta externă și sistemul 1C: Enterprise, metodele clasei IAddInDefBase, descrise în fișier AddInDefBase.h. Le enumerăm pe cele mai frecvent utilizate:
Generarea unui mesaj de eroare
virtual bool ADDIN_API AddError(unsigned short wcode, const WCHAR_T* source, const WCHAR_T* descr, long code)
wcode, cod- coduri de eroare (o listă de coduri de eroare cu descrieri poate fi găsită pe discul ITS)
sursă- sursa erorii
descr- descrierea erorii
Trimiterea unui mesaj către sistemul 1C: Enterprise
virtual bool ADDIN_API ExternalEvent(WCHAR_T* wszSource, WCHAR_T* wszMessage, WCHAR_T* wszData) = 0;
wszSource- sursa mesajului
wszMessage- Mesaj text
wszData- datele transmise
Interceptarea mesajelor este efectuată prin procedura de procesare externă a evenimentelor
Înregistrarea unei componente externe în sistemul 1C: Enterprise
virtual bool ADDIN_API RegisterProfileAs(WCHAR_T* wszProfileName)
wszProfileName- denumirea componentei.
Aceste metode sunt suficiente pentru o interacțiune completă între VK și 1C. Pentru a primi date de către o componentă externă de la sistemul 1C: Enterprise și invers, componenta externă trimite un mesaj special, care la rândul său este interceptat de sistemul 1C și, dacă este necesar, apelează metodele componentei externe pentru a transmite date înapoi. .

tVariant tip de date

La schimbul de date între componenta externă și sistemul 1C: Enterprise, este utilizat tipul de date tVariant. Este descris în fișierul types.h, care poate fi găsit pe discul ITS:
struct _tVariant ( _ANONYMOUS_UNION union ( int8_t i8Val; int16_t shortVal; int32_t lVal; int intVal; unsigned int uintVal; int64_t llVal; uint8_t ui8Val; uint16_t ushortVal; uint32_t ult32_t; uint32_t; bVal; wchar_t struct _tVal; struct tm tmVal; ANT_NAME_1 ; matrice dimensională în pvarVal TYPEVAR vt);
Tip tVariant este o structură care include:
  • amestec (unire) destinat direct stocării datelor
  • identificatorul tipului de date
În general, lucrul cu variabile de tip tVariant are loc conform următorului algoritm:
  1. Determinarea tipului de date stocate în prezent într-o variabilă
  2. Accesați câmpul de amestec corespunzător pentru a accesa direct datele
Folosind tipul tVariant simplifică semnificativ interacțiunea dintre sistemul 1C: Enterprise și componentele externe

Aplicație

Directorul „exemple” conține exemple pentru articol
exemple/1 - lansați componenta demo
exemple/2 - demonstrarea extinderii listei de proprietăți
exemple/3 - demonstrarea extinderii listei de metode
Fiecare director conține un proiect VS 2008 și o configurație 1C gata făcută.