Külső alkatrészek technológia (). Külső alkatrész technológia () Külső alkatrész technológia 8.3

Ez a cikk a külső komponensekkel való munkával foglalkozik, nevezetesen azok összekapcsolásával. Jelenleg az 1C Enterprise képességeinek bővítésére két külső komponens technológiát használnak:

  • 1 Natív API használata
  • 2 COM technológia használata
Ebben a cikkben úgy döntöttem, hogy kiemelem a natív API-komponensekkel való munkát.
Tehát kezdjük, az egyszerűtől a bonyolultig:
Részlet az ITS-ből

1. Tegyük fel, hogy a VK-nk egy adott könyvtárban található a lemezen:

Használható a "Thick Client (rendszeres alkalmazás)"-ban;

Ez a legegyszerűbb példa a natív összetevővel való munkavégzésre. Felhívjuk figyelmét, hogy az ilyen típusú összetevők nem igényelnek regisztrációt a rendszerben, ami nagyban leegyszerűsíti az adminisztrációt.

2. A fent tárgyalt példa egyáltalán nem reális. Leggyakrabban az alkatrészt elrendezésben helyezik el. Az elrendezésnek tartalmaznia kell egy zip-archívumot összetevőfájlokkal és egy MANIFEST.xml fájlt
Példa jegyzékfájlra:

3. Ha vékony és webes kliensben dolgozik, feltétlenül használja a módszert.
Idézet az ITS-től:

Magyarázat:
%APPDATA%\1C\1Cv82\ExtCompT- a Thick és Thin kliensek összetevőinek telepítési könyvtára.
%APPDATA%\Roaming\Mozilla\Extensions- könyvtár (esetemben) kiterjesztés a Mozilla FF-hez/
A módszer alkalmazásakor SetExternalComponent(), a használt klienstől függően a bővítmények kicsomagolásra kerülnek a megfelelő könyvtárba.

Példa a külső komponens telepítési eljárására:

InstallExternalComponent- a metódust csak a komponens kezdeti telepítése során és abban az esetben szabad meghívni, ha frissíteni kell a komponens telepített verzióját.

Vékony és vastag kliens esetén:
Elegendő a külső komponens telepítési műveletét a metódussal újra végrehajtani InstallExternalComponent().

Webes kliens esetén egy összetevő frissítéséhez:

  • El kell távolítani a bővítményt a webböngésző-kiegészítőkkel (Mozilla FF) való munkavégzés mechanizmusán keresztül.
  • Használja a módszert InstallExternalComponent
A VK csatlakoztatásához a következő eljárást használhatja:

Ha az összetevő nincs telepítve, kivételt dob ​​a rendszer.

2. Vannak esetek, amikor egy összetevőt ideiglenes tárolóról kell telepíteni (a fájl külső forrásból érkezett, külső feldolgozás), ebben az esetben a Külső komponens csatlakoztatása és a Külső összetevő telepítése módszerek első paraméterei a az ideiglenesen tárolt archívum címe. Az alábbiakban egy lehetséges példa látható a működésére:

&OnClient VariableAddressArchiveComponent; &OnClient változó komponens; &OnClient eljárás OnOpen(Failure) // cím, egy karakterláncot tartalmaz (navigációs hivatkozás a zip archívum bináris adataihoz // ideiglenes tárolóban) ComponentArchiveAddress = GetArchiveAddressInTemporaryStorage(); EndProcedure // WhenOpen() &OnServer // A ConnectExternalComponent, SetExternalComponent metódusai // első paraméterként egy karakterláncot vehetnek fel a következő formátumban: "navigation link" // (URL egy ZIP-archívumba csomagolt külső összetevőhöz hasonló formátumban // GetNavigationLink ). Függvény GetArchiveAddressInTemporaryStorage()ProcessingObject = FormAttributesValue("ProcessingObject"); Archív hivatkozás = PlaceInTemporaryStorage(ProcessingObject.GetLayout("MIKO_phone_IP"), New UniqueIdentifier); ReturnLinkToArchive; EndFunction // GetArchiveAddressInTemporaryStorage() &OnClient // Az eljárást csak egyszer kell meghívni, ha az összetevő még nincs telepítve // ​​vagy frissíteni kell. Exception Report("A külső összetevő telepítése sikertelen."); EndAttempt; Az eljárás vége // InstallComponent() &OnClient // fő eljárás egy összetevő inicializálására Eljárás Initialize(Command) Külső komponens csatlakoztatásának kísérlete(ComponentArchiveAddress,"Comp" ,ExternalComponentType.Native); Component = New("AddIn.Comp.MIKO_phone_IP"); Exception Report("Inicializálási kivétel. Lehet, hogy az összetevő még nincs telepítve."); EndAttempt; Az eljárás vége

A cikk címe tartalmazza a „bábuknak” kifejezést. A teáskanna alatt elsősorban magamat értettem. Minden C++ tudásom 3-4 év egyetemi szinten maradt, amikor ráléptem az 1C görbe útjára. És minden rendben is lenne, de a közelmúltban felmerült egy feladat, amihez külső komponens írása volt szükséges. Fel kellett idéznem emlékeimet, és le kellett porolni a C++ tudásomat. Kiderült, hogy nem minden olyan ijesztő. Szeretnék röviden bemutatni a külső komponensek írását.

Sablonkomponensek az ITS-en

Az ITS lemez teljes dokumentációt tartalmaz a külső komponensek mechanizmusáról, kiegészítve egy példaprojekttel és egy sablonnal a saját fejlesztéshez. Az anyagot „Külső komponens technológiának” nevezik. A dokumentáció nagyszerű, de még mindig meg kell értened, és az idő, mint általában, kevés. Valójában csak néhány kulcsfontosságú pont van, amire érdemes odafigyelni, a többi romlás és hiúság :)

Szükséges anyagok

Külső komponens létrehozásához szükségünk lesz:

  1. Az ITS-en található „Külső komponensek létrehozásának technológiája” anyag
  2. Az anyaghoz mellékelt üres külső alkatrész sablon
  3. MS Visual Studio. Az Express verzió ingyenes, és több mint elegendő az igényeinknek.
  4. Alapvető ismeretekkel rendelkezik a C++ szintaxisról, nevezetesen:
  • Képes megkülönböztetni egy változó deklarációt a ciklustól vagy feltételtől
  • Ha megértjük, hogy a karakterláncok tiszta formájukban nem léteznek a C++-ban, vannak olyan tömbök, amelyekhez egyértelműen a memóriával kell bajlódnia.
  • Nos, természetesen szükséges a feladat végrehajtásának képessége a megadott nyelven. Legalább egy harmadik féltől származó könyvtár meghívása a C++-ból, amely mindent megtesz.

Kezdjük ásni

A Native API dokumentációja meglehetősen részletes. Összefoglalva a következőket mondja:

  1. Egy külső összetevő lehetővé teszi a beépített nyelv bővítését egy új objektummal (vagy többel). Azok. létrehozunk egy osztályt, amelyet az „Új” operátor segítségével tudunk létrehozni, és meghívjuk ennek az objektumnak a metódusait a beépített nyelvből.
  2. Annak érdekében, hogy az objektumunk működjön, a platform egy bizonyos protokoll segítségével „kommunikál” vele, amelyet kötelesek vagyunk biztosítani.
  3. Maga a komponens kódja hagyományosan két részből áll: az első a komponens regisztrációja a rendszerben, a második az új osztály működése és interakciója a platformmal.

Nem fogunk belemenni a megvalósítás sajátosságaiba, kifutunk a határidőkből, és nincs kellő kompetenciánk. Gyorsan meg kell értenünk, hova kell beírnunk a sorainkat, hogy az összetevő működjön. Ehhez vegye az összetevősablont az ITS-szel, és nyissa meg a Visual Studióban. A sablon a kicsomagolt archívum sablonmappájában található. Lássuk, mi van itt.

Érdekel bennünket az AddInNative.cpp fájl. Minden felismerés benne rejlik. Az összes szükséges módszerhez sablonokat tartalmaz, csak kissé testre kell szabni őket. Kiderült azonban, hogy egyszerűbb nem egy üres sablont alapul venni, hanem egy működő példával foglalkozni. Számos hasznos harangot és sípot tartalmaz, amelyek nem szerepelnek az üres sablonban. Amikor eljön a megértés, elő kell venned egy üres sablont, és az ügy ismeretében finomítani kell rajta. Egy működő összetevőre egy példa az example\NativeAPI mappában, egy üres sablon pedig a sablonmappában található.

Nyissuk meg a projektet a példamappából, és benne az AddInNative.cpp fájlt

A fájl legelején konstansok és segédfüggvények deklarációi találhatók. A következő sorokra vagyunk kíváncsiak:

Objektumunk, mint „igazi”, támogatni fogja az orosz és angol nyelven írt módszereket. Ebből a célból a tulajdonságok és metódusok írott nevei két nyelven vannak deklarálva. A kék keret az angol fürdőhöz, a piros az orosz fürdőhöz való. A képen látható, hogy a példa már számos módszert és tulajdonságot valósít meg. A mi feladatunk ezek eltávolítása és a sajátunk behelyezése.

Az a sor, amelyben az osztálynév deklarálva van, zöld kerettel van kiemelve. Őszintén szólva nem értettem, mit jelent. Ha megváltoztatod, semmi sem működik. Mivel kezdetben azzal a fenntartással éltek, hogy „bábu vagyok”, megbocsátható. :)

Így, ha az objektumunk tartalmazza a „RunCalculation” metódust és a „Destination” tulajdonságot, akkor ezt a nevet kell leírnunk a g_MethodNamesRu és g_PropNamesRu tömbökben.

Hívások 1C nyelvről

Tehát az objektumunk egy metódust és egy olvasási-írási tulajdonságot fog tartalmazni.

Vegyük a következő használati forgatókönyvet:

OurObject = New(“AddIn. MyComponent. DataSender”); A mi Tárgyunk. Cél = "somemail@server. com";
A mi Tárgyunk. RunCalculation(PaymentAmount, "A segédprogramokhoz");

Van egy string tulajdonság és egy metódus numerikus és egy string paraméterrel. Annak érdekében, hogy mindez működjön, az 1C megközelítőleg a következő kommunikációs protokollt hajtja végre az összetevővel:

A platform előre definiált függvényeket hív meg az objektumunkon, és reagál rá, és végrehajtja a parancsait. Hasonló a helyzet a metódusokkal is, csak ott a metódusszám mellett a paraméterek számát, a visszatérési érték meglétét, illetve az opcionális paraméterek meglétét kérik.

Térjünk vissza a kódunkhoz. A „mágikus számok” elkerülése érdekében a CAddInNative osztályban két felsorolás van deklarálva, amelyek a metódusok és tulajdonságok számának meghatározásáért felelősek. Nyissuk meg a CAddInNative.h fájlt, és nézzük meg őket a legelején:

Az üres sablon nem tartalmazza ezeket a felsorolásokat, és arra sincs példa, hogy az orosz nyelvű és nem orosz nyelvű hívásokat elválassza. Ez a megközelítés nem kötelező. Fontos, hogy kövessük a felületet, és hogy lesznek-e átutalások vagy sem, azt mindenki döntse el.

Unicode karakterláncok

Valószínűleg sokan tudják, hogy a platform kétbájtos Unicode formátumú karakterekkel működik. A sablon erre a célra egy speciális WCHAR_T típust deklarál. Ez a típus egy többplatformos burkoló, és ugyanazt a karakterméretet biztosítja Windowson és Linuxon. A szabványos wchar_t típus mérete eltérő lehet a különböző rendszereken. Kérjük, vegye figyelembe, hogy minden karakterlánc-literál egy L betű előtaggal van deklarálva. Ez azt jelenti, hogy egy ilyen karakterlánc wchar_t típusú.

Van egy egyszerű szabály: belsőleg a karakterlánc-összetevők wchar_t-ként kerülnek feldolgozásra (Linuxon 4 bájt lehet, Windowson - 2), de amint átvisszük a karakterláncot 1C-be vagy megkapjuk onnan, szükségünk van WCHAR_T-re (szigorúan 2 bájt minden rendszeren).

Az egyik típusú karakterlánc másikra konvertálásához a sablon kiegészítő funkciókat biztosít:

Az első a WCHAR_T-t alkotja a szabványos wchar_t-ből:

uint32_t convToShortWchar(WCHAR_T** Cél, const wchar_t* Forrás, uint32_t len ​​= 0);

A második az ellenkezője. Űrlapok wchar_t innen: WCHAR_T.

uint32_t convFromShortWchar(wchar_t** Cél, const WCHAR_T* Forrás, uint32_t len ​​= 0);

A platformmal való interakció során mindig csak a WCHAR_T kerül felhasználásra.

Változat típusa

Egy másik érdekesség az általános Variant adattípus. Lehetővé teszi számunkra, hogy kölcsönhatásba lépjünk az 1C nyelvvel, amely, mint tudod, nincs beírva, és minden változó tartalmazhat bármit. Ezt a típust értékcserénél használják. Két paramétert adunk át a RunCalculation metódusnak - egy számot és egy karakterláncot. A komponens két Variant értéket kap. A mi felelősségünk a tényleges típusuk ellenőrzése. Senki nem fogja megakadályozni, hogy ne egy számot adjon át a komponensnek, hanem mondjuk egy értéktáblázatot.

Bár úgy tűnik, tévedek. Számomra úgy tűnik, hogy továbbra sem lehet átvinni az értéktáblázatot a NativeAPI-ba, mert... nem szerepel az engedélyezett típusok listájában, de a String helyett megadhatja a dátumot. Ez szintén nem jó. Ellenőriznünk kell az 1C-ből származó változó valódi típusát.

A Variant típus egyszerű. Ez egy olyan szerkezet, amelynek tulajdonságai különböző típusú értékek. Vannak olyan tulajdonságok, mint a DATE, wchar_t, int és mások. A Variant fő része a „vt” tulajdonság, amely a változó valós típusát tárolja, és amelyből pontosan megértheti, hogyan kell értelmezni ezt a Variantot. Ezenkívül számos kiegészítő makrót deklaráltak a Variant típussal való munka egyszerűsítésére.

Térjen a tárgyra

Úgy tűnik, a bevezetővel ennyi. Javaslom, hogy vegyünk egy példát egy külső komponens megvalósítására. A TK egy példa lesz az ITS lemez egy összetevőjére. Ez a példa a következő funkciókat írja le:

  • Szöveg megjelenítése a főablak állapotsorában;
  • Külső időzítő esemény küldése;
  • Bináris adatok átvitele az 1C:Enterprise-be;
  • Ingatlanok megvalósítása;
  • Eljárások végrehajtása;
  • Funkciók megvalósítása;

Az összetevő a következő API-val rendelkezik:

  • Tulajdonságok:
    • Enabled/IsEnabled;
    • IsTimer/IsTimerPresent;
    • Mód:
      • Engedélyezze;
      • Letiltás/Letiltás;
      • ShowInStatusLine;
      • EnableTimer/StartTimer;
      • Kapcsolja ki az Időzítőt/StopTimert;
      • LoadPicture/LoadPicture;

Az időzítő szerint külső esemény történik, amelyre az 1C kódból lehet előfizetni.

A rendelkezésünkre álló tudástól vezérelve nézzük meg a komponenst a kezdetektől fogva.

Regisztrációs összetevők

Objektumunkat különálló C++ osztályként, jelen esetben CAddInNativeként valósítjuk meg. Ahhoz, hogy az 1C lássa az osztályunkat, a dll-könyvtárnak 3 függvényt kell exportálnia:

  • GetClassObject
  • DestroyObject
  • GetClassNames

Ezek az exportok a VisualStudio projektfájának AddInNative.def fájljában láthatók. Nézzük meg ezeknek a függvényeknek a kódját:

A legegyszerűbb - a GetClassNames függvény - megmondja az 1C platformnak, hogy milyen osztályok vannak a komponensünkben. Javítsanak ki a C++ guruk, nekem úgy tűnik, hogy itt a platformnak a C++ osztályok nevével kell válaszolnia, hogy importálni tudja azokat. A g_kClassNames tömb pontosan erre szolgál, a zöld „keret” tömbhöz. Kifejezetten nem ellenőriztem, de ha csak az alkatrészt kell működőképessé tenni, akkor hagyjon mindent úgy, ahogy a példában van. Már működik, egyelőre nem kell bütykölni vele.

Tehát a GetClassNames visszaadja a platformnak a külső összetevő hasznos objektumait megvalósító osztálynevek tömbjét. Példánkban az összetevő egy elemből álló tömböt ad vissza a platformnak CAddInNative osztálynévvel.

Kérjük, vegye figyelembe, hogy a platform WCHAR_T típusú értéket kap, és a g_kClassNames tömbben lévő osztálynév wchar_t típusú. Ezért a leadást a fent tárgyalt segítő funkció segítségével hajtják végre.

A következő függvény a GetClassObject. Akkor hívják, ha a vállalati kódba beírtuk az „Új” szót. A platform megköveteli, hogy hozzuk létre az osztály új példányát, és adjunk vissza egy mutatót az új objektumhoz.

Ismét megjegyezzük, hogy a platform első paramétere az, hogy melyik osztályt hozzuk létre (a GetClassNames metódus által adott osztályokból). Mivel csak egy osztályunk van, ez a név itt egyáltalán nincs bejelölve, egyszerűen létrejön egy objektum a new-n keresztül, és a pInterface kimeneti paraméteren keresztül tér vissza.

És az utolsó szükséges export funkció a DestroyObject. A név magáért beszél. Ha egy objektumra már nincs szüksége a platformnak, törölni kell. Adunk egy mutatót egy korábban létrehozott objektumra. Felszabadítjuk a szükségtelen mutatók törlésével és visszaállításával.

A leírt megvalósítások meglehetősen univerzálisak. Ha a komponensünk csak egy osztályt valósít meg (mint a példában), akkor ezeket a függvényeket egyszerűen át kell másolni önmagába. Az egyetlen feltétel a megfelelő osztály létrehozása a GetClassObject függvényben, ha a neve nem CAddInObject, hanem valami más.

Egy komponens inicializálása/leállítása

Egy összetevőt megvalósító osztály létrehozása után a platform meghívja ennek az osztálynak a metódusait. A munka megkezdése előtt a platform közli velünk egy objektumot „önmagáról”, amellyel a platform bizonyos metódusait hívhatjuk meg. Ez az Init metódusban történik. A példában a platform objektum az m_iConnect változóban van tárolva.

Egy másik fontos módszer a setMemManager. Lehetővé teszi olyan memóriablokkok lefoglalását, amelyeket maga a platform felszabadít. Ennek végrehajtása a következőképpen történik:

Egyszerűen tárolunk egy mutatót a memóriakezelőre, amelyet a platform átad nekünk. Ezután ezzel a menedzserrel lefoglaljuk a platform által felszabaduló memóriát.

És ismét, mint az export függvények esetében, az inicializálási módszerek meglehetősen univerzálisak, egyszerűen átmásolhatod őket magadnak, és nem kell aggódnod a „befejezés” miatt, amíg az valóban szükségessé nem válik.

Hasznos teher. Az összetevő objektum módszerei és tulajdonságai

Bejegyzés

Nos, természetesen nem az inicializálás miatt hoztuk létre a komponenst, hanem valami hasznos funkció miatt. Ideje megnézni, hogyan valósul meg.

Először is regisztrálnunk kell egy objektumot, amely létrehozható és meghívható az 1C nyelvből. Ez az objektum a RegisterExtensionAs metódusban van regisztrálva.

Ebben a módszerben tájékoztatjuk a platformot az osztályunk nevéről, mivel az látható lesz az 1C nyelvből. Ezzel a névvel fogjuk létrehozni az „Új” révén. Ebben az esetben az objektum létrehozása a következő kóddal történik:

ConnectExternalComponent(Fájl, "Sajátösszetevő", Külsőkomponenstípus. Natív);
ObjectComponents = New( "AddIn.MyComponent.AddInNativeExtension");

A dokumentáció szerint az osztálynévvel rendelkező karakterlánc memóriáját a memóriakezelő lefoglalja, és a nevet erre a címre írja - „AddInNativeExtension”. Ide fájdalommentesen leírhatod a nevedet. Kérjük, vegye figyelembe, hogy ismét konverzió történik wchar_t-ről WCHAR_T platformra.

Használat

Ahogy fentebb írtam, a platform különféle nyelvi funkciókat kér le az összetevőtől. Létezik-e a megadott tulajdonság, írható-e, van-e alapértelmezett értéke a függvényparaméternek, van-e visszatérési értéke, stb. Ha a korábban megadott példakódot vesszük:

OurObject = Új( "AddIn.MyComponent.DataSender"); // A DataSender a RegisterExtensionAs függvényből származó név (lásd alább).
A mi Tárgyunk. Címzett = " [e-mail védett]" ;
A mi Tárgyunk. Számítás végrehajtása (fizetési összeg, "A közművek számára");

akkor a következő szavazást hajtjuk végre:

  1. Létezik "Cél" tulajdonság?
  2. Támogatja a felvételt?
  3. Létezik RunCalculation nevű módszer?
  4. Hány paramétere van?
  5. Van-e visszatérési értéke
  6. Melyek az opcionális paraméterek alapértelmezett beállításai (ha vannak)

Itt a leghasznosabb, ha egy példát nézünk és ellenőrizzük a dokumentációt. Mindezen felmérések végrehajtása meglehetősen egyszerű. Az interakcióért módszerek egész állatkertje felelős. Nem megyek végig mindent, elég jól dokumentáltak, ráadásul egyszerűen kivitelezhetőek. Csak a legjelentősebb pillanatok kerülnek figyelembevételre, amelyekbe nekünk, bábuknak kell a kezünket venni :). Az alapvető megközelítés a következő: amikor először említenek egy tulajdonságot vagy módszert, a platform megkér minket, hogy keressük meg név szerint. Ennek a tulajdonságnak (módszernek) egyedi számával kell válaszolnunk. Minden további kommunikáció csak számokkal történik. Ebben segítenek az említett felsorolások, amelyek ezeket a számokat tárolják.

Tulajdonságok

Az első dolog, amit figyelembe kell venni, az ingatlan infrastruktúrája. A platform a FindProp metódussal kér egy tulajdonság létezését

A platform átadja nekünk a keresett ingatlan nevét WCHAR_T formában. Egy segédmódszerrel wchar_t-re konvertálják, és ezt a szöveget először angol, majd orosz nyelvű kifejezésekkel keresik. Vissza kell adnunk az ingatlanszámot. Vegye figyelembe, hogy itt a findName segítő funkció is szerepet játszik. Megvalósítása szerepel a példában, de a komponens nincs az üres sablonban. Helyénvalónak tűnik magához húzni, ha azt tervezi, hogy kétnyelvű kifejezéseket tartalmaz az összetevő.

Ezután a GetPropName metódus végrehajtja az inverz feladatot, és megkapja a tulajdonság nevét a szám alapján. A név karakterlánc a vállalati memóriakezelőn keresztül is kiosztásra kerül. Gyanítom, hogy a GetPropName metódust a GetNprops-szal együtt használjuk, amikor egy objektum tulajdonságait pluszjellel bővítjük a hibakeresőben. Ezután a platform megkapja az ingatlanok teljes számát, és mindegyikhez nevet kér.

A következő metóduspár az IsPropReadable/IsPropWritable. Itt minden egyszerű, a megadott tulajdonságszámra meg kell mondanunk, hogy olvasható-e/írható.

Az értékek fogadása és írása a GetPropVal/SetPropVal metódusokkal történik. Itt érdemes részletesebben foglalkozni. Elkezdünk dolgozni az 1C:Enterprise típusokkal, ami azt jelenti, hogy a Variant színre lép.

Az összetevősablon kiegészítő makrók készletét határozza meg a Variant-szal való munka egyszerűsítése érdekében. Az első az értéktípus-ellenőrzés. Például a TV_VT makró lehetővé teszi egy érték típusának ellenőrzését/beállítását. Minden támogatott típushoz névvel ellátott konstansok is definiálva vannak. Ezek az állandók és az 1C:Enterprise típusoknak való megfelelésük a dokumentációban találhatók.

A TV_BOOL makró logikai értéket kap attól a változattól, amellyel dolgozni lehet. Analógia útján egész számokat (TV_INT), karakterláncokat (TV_WSTR) és másokat kapunk. A pontos értékek a kódban vannak, mindig láthatja őket.

Fontos szempont, hogy nem elég egy változathoz értéket rendelni, hanem valódi típust is hozzá kell rendelni. Ügyeljen a GetPropVal-ra. A TV_BOOL = true hozzárendelésen kívül létezik egy típus hozzárendelés is: TV_VT = VTYPE_BOOL. Ha a típus nincs hozzárendelve, a platform nem fogja tudni, hogy milyen típusú értéket adott vissza neki. Persze lehet elrontani és rossz típust beállítani. Ez gyakran együtt jár az emelvény leesésével.

Foglaljuk össze a fentieket:

Az értéket a következő opcióból kapjuk:

bool someVariable = TV_BOOL(pVariant);

Írja be az opció értékét:

TV_VT(pVariant) = VTYPE_BOOL; // érvényes adattípus

TV_BOOL(pVariant) = valami logikai változó; // állítsa be magát az értéket

És most - púpos módszerek!

A módszerek egy kicsit bonyolultabbak, de általában hasonlóak a tulajdonságokhoz. Először is, pontosan ugyanaz a funkciója van, hogy név szerint keresünk egy metódust, megkapjuk a metódusok teljes számát, és szám szerint kapjuk meg a nevet. A fentieken kívül azonban a következő szolgáltatásokkal egészül ki:

  • Ha egy metódus vissza tud adni egy értéket, akkor a „Számítás”-ban használható, és a hozzárendelési művelettől jobbra írható az 1C nyelven. Ha nem, akkor ez egy eljárás, és az ehhez hasonló dolgok "Az eljárás használata függvényként" kivételt dobnak
  • A módszernek vannak paraméterei. A platformnak tudnia kell a számukat. Ha a hívás több argumentumot ad meg, mint amennyit a metódus aláírása megad, akkor a „Túl sok paraméter” hibaüzenet jelenik meg.
  • Ha egy metódushoz nem adunk át elegendő argumentumot, akkor ezek egy része opcionális lehet, ha pedig nincsenek opcionális paraméterek, akkor "Nincs elég paraméter" hibaüzenet jelenik meg.
  • Meghíváskor, ha ez egy eljárás, akkor nem lehet visszatérési érték. Ha ez egy függvény, akkor van visszatérési érték. Azt is fel kell dolgozni.

Számos egyszerű módszer létezik, amelyek célja egyértelmű a nevükből és a dokumentációból. Ezek közé tartozik a HasRetVal, a GetNParams, a GetParamDefValue. Azt javaslom, hogy ne vegyük figyelembe őket, egy példa bőven elég. Érdeklődésünk a hasznos teher közvetlen megvalósítására irányul majd. A CallAsProc és CallAsFunc metódusokban valósul meg. Az első az eljárások hívásáért, a második a funkciók hívásáért felelős. Abban különböznek egymástól, hogy a CallAsFunc rendelkezik egy további kimeneti paraméterrel, amelyben a függvény visszatérési értékét adjuk át a platformnak.

A hívás a következőképpen történik: a platform átadja nekünk a hívott metódus számát, a tényleges paraméterek tömbjét és azok számát. Elemeznünk kell a metódusszámot, és meg kell adni az átadott paramétereket. Függvény esetén a visszatérési értékbe is be kell írnunk valamit.

A példában a metódusszám elemzése kapcsoló/esetben történik, és a számtól függően végrehajtódik a metóduslogika. A metódusok engedélyezése/letiltása esetén egyszerűen jelöljön be egy jelölőnégyzetet. Érdekes a ShowInStatusLine metódus. Az 1C:Enterprise ablak állapotsorában megmutatja, hogy mit adtak át neki. Ehhez az m_iConnect platform kapcsolati objektumot használjuk, azt, amelyet a komponens regisztrálásakor „kiadtak” nekünk. Képességeinek teljes listája a dokumentációban található.

Érdekes pont. Itt a példában az 1C-ből érkező érték típusát nem ellenőrzik, hanem a SetStatusLine-t egyszerűen meghívják a Variant karakterlánc-résszel. Gyanítom, hogy ha az 1C nyelvből hívod meg a komponens metódust, átadva ott egy számot vagy dátumot (karakterlánc helyett), akkor semmi sem fog működni... Megint javítsanak a guruk, de úgy tűnik, hogy a pwstrVal mutató mutat majd Isten tudja honnan, ha a vállalkozásból jött, mondjuk egy szám, ne egy őszinte füzér. A SetStatusLine hívásakor a platform megpróbál beolvasni egy sort egy ismeretlen címről, és valószínűleg összeomlik. Jobb mindig az elvárt típust ellenőrizni. Sose tudhatod.

A példában szereplő LoadImage függvény sokkal érdekesebb módon van megvalósítva, figyelembe veszi a stringek és bináris adatok platformmal való cseréjének lehetőségét.

Először itt ellenőrizzük az átadott paraméterek számát. Ha nincsenek jelen, akkor a hívás sikertelennek minősül. False értéket ad vissza, amit a platform hívási hibaként értelmez.

Ezután itt ellenőrizzük az átadott paraméter típusát. Ha ez egy keskeny karakterlánc (VTYPE_PSTR), akkor a változat karakteres része kerül felhasználásra. A példa azt írja, hogy paParam->pstrVal, de használhatod a TV_STR makrót, az ugyanaz lesz, de az opcióval való munka egységessége is megmarad.

Ha ez egy széles karakterlánc (VTYPE_PWSTR), akkor az átalakítás először wchar_t-re, majd char-ra történik. A helyzet az, hogy a fájl elérési útja az 1C nyelvről átkerül erre a metódusra, amelyet azután az fopen(char*) függvény használ. Ez a funkció char* típust igényel bemenetként, és a WCHAR_T elküldésre kerül nekünk a platformról. A helyes működés érdekében karakterlánc-konverziókat hajtanak végre.

És végül, ha ez egyáltalán nem karakterlánc, akkor a hívás sikertelennek minősül, és false-t ad vissza.

A bináris adatokhoz memóriakezelővel foglalunk le memóriát. Ez logikus, a bináris adatok teljes értékű objektummá válnak a platformon belül, és ennek kell kezelnie azokat. Memória van lefoglalva a pvarRetValue változathoz, amely a külső összetevő függvény visszatérési értéke.

A teljes fájl beolvasásra kerül a lefoglalt pufferbe; emellett Szükségszerűen a bájt mérete az strLen beállítás tulajdonságában és a VTYPE_BLOB beállítás adattípusában van megadva. Ha a memória lefoglalása sikeresen megtörtént, akkor a teljes függvény sikeres hívásaként igazat adunk vissza.

Így amikor az 1C nyelven ez van írva:

BinaryData = Összetevő. Kép feltöltése("C:\pic.jpg");

A komponens objektum CallAsFunc metódusa meghívásra kerül, átadja az elérési utat és visszaadja a bináris adatokat a fent leírtak szerint.

Sikeres esetben a BinaryData változó egy teljes értékű 1C nyelvi objektumot fog tartalmazni. Ha kilép a hatóköréből, az általa elfoglalt összes memóriát felszabadítja a platform. Ezért került kiosztásra a memóriakezelőn keresztül.

Következtetés

A történetet egy teáskanna írta báboknak, ezért valószínűleg tele van terminológiai pontatlanságokkal. Ennek a cikknek azonban az a célja, hogy gyors bevezetést nyújtson a külső összetevőkhöz. Ha gyorsan el kell készítenie egy alkatrészt rövid időn belül, felesleges gondok, hosszadalmas viták nélkül, akkor remélem, hogy ez a cikk segít. Ha valamelyik hibám rosszul érzed magad, mint C++ guru, kérlek jelezd kommentben és kijavítjuk.

Köszönöm a figyelmet.

  • Oktatóanyag

Bevezetés

Ez a cikk képet ad arról, hogyan működnek a külső összetevők az 1C: Enterprise rendszerben.
Megjelenik az 1C: Enterprise rendszer 8.2-es verziójának külső összetevőjének fejlesztési folyamata, amely Windows operációs rendszer alatt fut, fájl üzemmóddal. Ezt a lehetőséget a legtöbb kisvállalkozások számára tervezett megoldás használja. A VK C++ programozási nyelven kerül megvalósításra.

Külső összetevők "1C: Enterprise"

Az "1C: Enterprise" egy bővíthető rendszer. A rendszer funkcionalitásának bővítésére külső komponenseket (EC) használnak. A fejlesztő szemszögéből a VC egy külső objektum, amely tulajdonságokkal és módszerekkel rendelkezik, és eseményeket is generálhat az 1C: Enterprise rendszer általi feldolgozásra.
A külső komponensek segítségével megoldható egy olyan problémaosztály, amelyet nehéz vagy akár lehetetlen megvalósítani az 1C: Enterprise programozási nyelven. Ez az osztály különösen olyan feladatokat tartalmaz, amelyek alacsony szintű interakciót igényelnek az operációs rendszerrel, például bizonyos berendezésekkel való munkavégzés érdekében.
Az 1C: Enterprise rendszer két technológiát használ a külső összetevők létrehozására:
  • natív API használatával
  • COM technológia segítségével
Az adott megszorítások ismeretében a két fent említett technológia között elenyésző a különbség, ezért megfontoljuk a videojátékok Native API-t használó fejlesztését. A megvalósított fejlesztések szükség esetén alkalmazhatók a COM technológiát alkalmazó számítógépes szoftverek fejlesztésére, illetve kisebb módosításokkal az 1C: Enterprise rendszerben történő felhasználásra is, a fájl üzemmódtól eltérő működési lehetőségekkel.
VK szerkezet
Az 1C: Enterprise rendszer külső összetevője DLL-könyvtár formájában jelenik meg. A könyvtár kódja az IComponentBase leszármazott osztályt írja le. A létrehozott osztálynak meg kell határoznia a külső komponens funkcióinak megvalósításáért felelős metódusokat. A felülírt módszereket az alábbiakban az anyag bemutatásakor részletesebben ismertetjük.

Demó VK indítása

Feladat:
  1. Szereljen össze egy ITS-előfizetéssel szállított külső komponenst, amely az 1C külső összetevő mechanizmusának főbb képességeit hivatott bemutatni.
  2. Csatlakoztassa a bemutató komponenst az 1C konfigurációhoz
  3. Győződjön meg arról, hogy a deklarált függvények megfelelően működnek
Összeállítás
A demó VK az ITS előfizetési lemezén található a „/VNCOMP82/example/NativeAPI” könyvtárban.
A bemutató VC elkészítéséhez a Microsoft Visual Studio 2008-at fogjuk használni. A termék más verziói nem támogatják a Visual Studio használt projektformátumát.


Nyissa meg az AddInNative projektet. A projektbeállítások között megadjuk a projekt felépítéséhez szükséges fejlécfájlokat tartalmazó könyvtárat. Alapértelmezés szerint az ITS-lemezen találhatók a könyvtárban /VNCOMP82/include.
A felépítés eredménye a fájl /bind/AddInNative.dll. Ez a lefordított könyvtár az 1C konfigurációhoz való csatlakozáshoz.
A VK csatlakoztatása az 1C konfigurációhoz
Hozzunk létre egy üres 1C konfigurációt.
Az alábbiakban található a felügyelt alkalmazásmodul kódja.
változó DemoComp; Eljárás a rendszer indításakor() Csatlakoztassa a külső összetevőt ("...\bind\AddInNative.dll", "DemoVK", külső összetevő típusa.Native); DemoComp = New("AddIn.DemoVK.AddInNativeExtension"); Az eljárás vége
Ha az 1C konfiguráció indításakor nem jelentett hiba, akkor a VK sikeresen csatlakoztatva lett.
A fenti kód végrehajtása eredményeként egy objektum jelenik meg a konfiguráció globális láthatóságában DemoComp, amelynek tulajdonságai és metódusai a külső komponens kódjában vannak definiálva.
A beépített funkcionalitás bemutatása
Ellenőrizzük a demó VK működését. Ehhez próbáljunk meg beállítani és beolvasni néhány tulajdonságot, meghívni néhány VK metódust, valamint fogadni és feldolgozni a VK üzenetet.
Az ITS lemezen található dokumentáció a demo VC következő funkcióit tartalmazza:
  1. Kezelő komponens objektum állapota
    Mód: Bekapcsol, Kikapcsol
    Tulajdonságok: Beleértve
  2. Időzítő kezelés
    A komponens minden másodpercben üzenetet küld az 1C: Enterprise rendszernek paraméterekkel Összetevő, Időzítőés egy rendszeróra számlálósor.
    Mód: StartTimer, StopTimer
    Tulajdonságok: Van időzítő
  3. Módszer ShowInStatusLine, amely a metódusnak átadott szöveget paraméterként jeleníti meg az állapotsorban
  4. Módszer Kép feltöltése. Betölt egy képet a megadott fájlból, és bináris adat formájában továbbítja az 1C: Enterprise rendszerbe.
Győződjön meg arról, hogy ezek a funkciók működnek. Ehhez futtassa a következő kódot:
változó DemoComp; Eljárás a rendszer indításakor() ConnectExternalComponent(...); DemoComp = New("AddIn.DemoVK.AddInNativeExtension"); DemoComp.Disable(); Jelentés(DemoComp.Enabled); DemoComp.Enable(); Jelentés(DemoComp.Enabled); DemoComp.StartTimer(); Eljárás vége Eljárás Külső eseményfeldolgozás (forrás, esemény, adatok) jelentés (Forrás + " " + Esemény + " " + Adat); Az eljárás vége
A konfiguráció futtatásának eredménye a képen látható


Az „Üzenetek” panel megjeleníti a metódushívások eredményeit DemoComp.Disable()És Demo.Comp.Enable(). Ugyanazon panel következő sorai a VK-tól kapott üzenetek feldolgozásának eredményeit tartalmazzák - Forrás, EseményÉs Adat illetőleg.

Egyéni külső összetevő neve

Feladat: Módosítsa a külső komponens nevét tetszőlegesre.
Az előző rész az azonosítót használta AddInNativeExtension, amelynek jelentését nem magyarázták el. Ebben az esetben AddInNativeExtension- ez a kiterjesztés neve.
A VK kód meghatároz egy metódust RegisterExtensionAs, visszaadja a nevet az 1C: Enterprise rendszernek, amely szükséges a VK későbbi regisztrációjához a rendszerben. Ajánlatos olyan azonosítót megadni, amely bizonyos mértékig felfedi a külső komponens lényegét.
Itt található a módszer teljes kódja RegisterExtensionAs megváltoztatott kiterjesztéssel:
bool CAddInNative::RegisterExtensionAs(WCHAR_T** wsExtensionName) ( wchar_t *wsExtension = L"SomeName"; int iActualSize = ::wcslen(wsExtension) + 1; WCHAR_T* cél = 0; if (m-m_Memória_memória) ((void**)wsExtensionName, iActualSize * sizeof(WCHAR_T))) ::convToShortWchar(wsExtensionName, wsExtension, iActualSize); return true; ) return false; )
A megadott példában a VK név a következőre módosult SomeName. Ezután a VK csatlakoztatásakor új nevet kell megadnia:
DemoComp = New("AddIn.DemoVK.SomeName");

A VK tulajdonságok listájának bővítése

Feladat:
  1. Tanulmányozza a VK tulajdonságok megvalósítását
  2. Adjon hozzá egy karakterlánc típusú olvasási/írási tulajdonságot
  3. Adjon hozzá egy olvasási/írási karakterlánc tulajdonságot, amely az utolsó tulajdonságkészlet adattípusát tárolja. A tulajdonság értékének beállításakor semmilyen művelet nem történik

A létrehozandó komponens tulajdonságainak meghatározásához a fejlesztőnek a következő módszereket kell megvalósítania az AddInNative.cpp könyvtárkódban:
GetNprops
A kiterjesztés tulajdonságainak számát adja vissza, 0-t, ha nincsenek tulajdonságok
FindProp
Annak a tulajdonságnak a sorozatszámát adja vissza, amelynek nevét a paraméterek között átadtuk
GetPropName
Visszaadja a tulajdonság nevét annak sorozatszámával és az átadott nyelvi azonosítóval
GetPropVal
Az ingatlan értékét adja vissza a megadott sorszámmal
SetPropVal
Beállítja a tulajdonság értékét a megadott sorszámmal
IsPropReadable
A tulajdonság olvashatósági jelzőjét adja vissza a megadott sorszámmal
IsPropWritable
A megadott sorszámú tulajdonság írhatósági jelzőjét adja vissza


Tekintsük a fenti osztálymetódusok megvalósítását CAddInNative.
A demo VC-ben 2 tulajdonság van meghatározva: BeleértveÉs Van időzítő (EngedélyezveÉs IsTimerPresent).
A könyvtárkód globális hatókörében két tömb van meghatározva:
static wchar_t *g_PropNames = (L"IsEnabled", L"IsTimerPresent"); static wchar_t *g_PropNamesRu = (L"Engedélyezve", L"Van egy időzítő");
amelyek orosz és angol tulajdonneveket tárolnak. A fejlécfájlban AddInNative.h a felsorolás meghatározása:
enum Props ( ePropIsEnabled = 0, ePropIsTimerPresent, ePropLast // Mindig utolsó );
ePropIsEnabledÉs ePropIsTimerPresent A 0 és 1 értékkel rendelkező tulajdonságok sorszámát értelmes azonosítókkal helyettesítik. Az ePropLast, amelynek értéke 2, a tulajdonságok számának meghatározására szolgál (a GetNProps módszerrel). Ezeket a neveket csak a komponenskódon belül használjuk, kívülről nem érhetők el.
A FindProp és a GetPropName metódusok tömbkeresést hajtanak végre g_PropNamesÉs g_PropNamesRu.
A mezők értékeinek tárolására a könyvtármodulban a CAddInNative osztály rendelkezik olyan tulajdonságokkal, amelyek tárolják az összetevő tulajdonságainak értékét. Mód GetPropValÉs SetPropVal térjen vissza, és ennek megfelelően állítsa be ezen tulajdonságok értékét.
Mód IsPropReadableÉs IsPropWritableés vissza igaz vagy hamis, az ingatlan átadott sorszámától függően az alkalmazási logika szerint.
Egyéni tulajdonság hozzáadásához a következőket kell tennie:

  1. Adja hozzá a tömbökhöz hozzáadandó tulajdonság nevét g_PropNamesÉs g_PropNamesRu(fájl AddInNative.cpp)
  2. Felsorolni Kellékek(fájl AddInNative.h) előtt ePropLast adjon hozzá egy nevet, amely egyedileg azonosítja a hozzáadandó tulajdonságot
  3. Memória rendezése a tulajdonságértékek tárolására (hozzon létre olyan modulkomponens mezőket, amelyek tárolják a megfelelő értékeket)
  4. Módosítsa a módszereket GetPropValÉs SetPropVal az előző lépésben lefoglalt memóriával való interakcióhoz
  5. Az alkalmazási logikának megfelelően módosítsa a metódusokat IsPropReadableÉs IsPropWritable
Az 1., 2., 5. pont nem szorul magyarázatra. E lépések végrehajtásának részleteit a cikk mellékletének tanulmányozásával találhatja meg.
Adjunk neveket a teszttulajdonságoknak TesztÉs Típusellenőrzés illetőleg. Ezután az 1. lépés eredményeként a következőket kapjuk:
static wchar_t *g_PropNames = (L"IsEnabled", L"IsTimerPresent", L"Test", L"TestType"); static wchar_t *g_PropNamesRu = (L"Engedélyezve", L"Időzítő van", L"Teszt", L"Típusellenőrzés");
Átruházás Kellékekígy fog kinézni:
enum Props ( ePropIsEnabled = 0, ePropIsTimerPresent, ePropTest1, ePropTest2, ePropLast // Mindig utolsó );
A kód jelentős egyszerűsítése érdekében STL C++-t fogunk használni. Különösen a húrokkal való munkához WCHAR, csatlakoztassuk a könyvtárat wstring.
Metódusérték mentéséhez Teszt, meghatározzuk az osztályban CAddInNative magánterületen:
string teszt1;
A karakterlánc-paraméterek átviteléhez az 1C: Enterprise és a külső összetevők között az 1C: Enterprise memóriakezelőt kell használni. Nézzük meg közelebbről a munkáját. A funkciók a memória lefoglalására és felszabadítására szolgálnak AllocMemoryÉs Szabad memória fájlban meghatározott ImemoryManager.h. Ha egy karakterlánc paramétert kell átadni az 1C: Enterprise rendszernek, akkor a külső komponensnek memóriát kell lefoglalnia a függvény meghívásával. AllocMemory. A prototípusa így néz ki:
virtual bool ADDIN_API AllocMemory (void** pMemory, előjel nélküli hosszú ulCountByte) = 0;
Ahol pMemória- annak a mutatónak a címe, amelybe a lefoglalt memóriaterület címe kerül,
ulCountByte- a lefoglalt memóriaterület mérete.
Példa memóriafoglalásra egy karakterlánchoz:
WCHAR_T *t1 = NULL, *teszt = L"TEST_STRING"; int iActualSize = wcslen(teszt1)+1; m_iMemory->AllocMemory((void**)&t1, iActualSize * sizeof(WCHAR_T)); ::convToShortWchar(&t1, teszt1, iActualSize);
A karakterlánc-adattípusokkal való munka kényelme érdekében leírjuk a függvényt wstring_to_p. Paraméterként egy wstring karakterláncot kap. A függvény eredménye egy kitöltött szerkezet tVáltozat. Funkció kód:
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) * memcpy(t1, str.c_str(), (str.length()+1) * sizeof(WCHAR_T)); val -> pstrVal = t1; val -> strLen = str.length(); vissza igaz;)
Ezután a metódus switch utasításának megfelelő esetrésze GetPropVal a következő formában lesz:
eset ePropTest1: wstring_to_p(teszt1, pvarPropVal); szünet;
Módszer SetPropVal:
case ePropTest1: if (TV_VT(varPropVal) != VTYPE_PWSTR) return false; teszt1 = std::wstring((wchar_t*)(varPropVal -> pstrVal)); szünet;
A második tulajdonság megvalósításához osztálymezőt definiálunk CaddInNative
uint8_t last_type;
amelyben elmentjük az utoljára átvitt érték típusát. Ehhez adja hozzá a parancsot a CaddInNative::SetPropVal metódushoz:
utolsó_típus = TV_VT(varPropVal);
Most, amikor a második tulajdonság értékének leolvasását kéri, visszaadjuk az értéket utolsó_típus, amit a kijelölt feladat megkövetel.
Ellenőrizzük a végrehajtott változtatások működőképességét.
Ehhez mutassuk be az 1C konfiguráció megjelenését az alábbiak szerint:
változó DemoComp; Eljárás a rendszer indításakor() Csatlakoztassa a külső komponenst ("...", "DemoVK", külső összetevő típusa. Natív); DemoComp = New("AddIn.DemoVK.SomeName"); DemoComp.TypeCheck = 1; Report(String(DemoComp.TypeCheck)); DemoComp.Test = "Vasya"; Report(String(DemoComp.Test)); DemoComp.Test = "Petya"; Report(String(DemoComp.Test)); Report(String(DemoComp.TypeCheck)); Az eljárás vége
Az indítás eredményeként üzenetsorozatot kapunk:
3
Vasya
Péter
22

A második és harmadik üzenet az előző lépésben beállított tulajdonság beolvasásának eredménye. Az első és a második üzenet az utolsó tulajdonságkészlet típuskódját tartalmazza. A 3 egy egész értéknek, a 22 egy karakterlánc értéknek felel meg. A típusok és kódjaik egyezése a fájlban található típusok.h, amely az ITS lemezen található.

A módszerek listájának bővítése

Feladat:
  1. Bővítse ki a külső összetevő funkcionalitását a következő funkciókkal:
  2. Fedezze fel a külső összetevők módszereinek megvalósítási módjait
  3. Adjon hozzá függvénymódszert Funkció1, amely két karakterláncot („Parameter1” és „Parameter2”) vesz paraméterként. Az eredmény egy ilyen karakterlánc: „Ellenőrzés. Paraméter1, Paraméter2"
  4. Győződjön meg arról, hogy az elvégzett változtatások működnek.

A létrehozandó komponens metódusainak meghatározásához a fejlesztőnek a következő módszereket kell megvalósítania az AddInNative könyvtárkódban:
GetNMethods, FindMethod, GetMethodName
Úgy tervezték, hogy megkapja a megfelelő számú metódust, keresse meg a módszer számát és nevét. Hasonló a tulajdonságokra vonatkozó megfelelő metódusokhoz
GetNParams
A metódusparaméterek számát adja vissza a megadott sorszámmal; ha egy ilyen számú metódus hiányzik vagy nincs paramétere, 0-t ad vissza
GetParamDefValue
A megadott metódus megadott paraméterének alapértelmezett értékét adja vissza
HasRetVal
Azt a jelzőt adja vissza, hogy a megadott sorszámú visszatérési értékű metódusnak van-e visszatérési értéke: igaz a visszatérési értékű metódusokhoz és hamis másképp
CallAsProc
hamis, futásidejű hiba történik, és az 1C: Enterprise modul végrehajtása leáll. A paraméterek tömbjének memóriáját az 1C: Enterprise lefoglalja és felszabadítja.
CallAsFunc
Végrehajtja a metódust a megadott sorszámmal. Ha a metódus visszatér hamis, futásidejű hiba történik, és az 1C: Enterprise modul végrehajtása leáll. A paraméterek tömbjének memóriáját az 1C: Enterprise foglalja le. Ha a visszatérési érték karakterlánc vagy bináris adattípus, akkor az összetevő memóriát foglal le a függvényhez AllocMemory memóriakezelő, adatokat ír oda, és ezt a címet a struktúra megfelelő mezőjében tárolja. 1C: A vállalkozás felszabadítja ezt a memóriát hívással Szabad memória.
A módszerek teljes leírása, beleértve a paraméterek listáját, az ITS lemezen található dokumentációban található részletesen.
Tekintsük a fent leírt módszerek megvalósítását.
A komponens kódjában két tömb van definiálva:
static wchar_t *g_MethodNames = (L"Engedélyezés", L"Letiltás", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture"); static wchar_t *g_MethodNamesRu = (L"Engedélyezés", L"Letiltás", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadImage");
és felsorolás:
enum Methods ( eMethEnable = 0, eMethDisable, eMethShowInStatusLine, eMethStartTimer, eMethStopTimer, eMethLoadPicture, eMethLast // Mindig utolsó );
Funkciókban használatosak GetNMethods, FindMethodÉs GetMethodName, a tulajdonságok leírásához hasonlóan.
Mód GetNParams, GetParamDefValue, HasRetVal Az implementációs kapcsoló az átadott paraméterek és az alkalmazáslogika függvényében adja vissza a szükséges értéket. Módszer HasRetVal kódjában csak olyan metódusok listája van, amelyek eredményt adhatnak vissza. Értük visszatér igaz. Minden acél módszernél visszatér hamis.
Mód CallAsProcÉs CallAsFunc tartalmazza a metódus közvetlenül végrehajtható kódját.
Olyan metódus hozzáadásához, amely csak függvényként hívható meg, a következő módosításokat kell végrehajtania a külső összetevő forráskódjában:
  1. Adja hozzá a metódus nevét a tömbökhöz g_MethodNamesÉs g_MethodNamesRu(fájl AddInNative.cpp)
  2. Adjon hozzá egy értelmes metódusazonosítót a Methods felsoroláshoz (fájl AddInNative.h)
  3. Módosítsa a funkciókódot GetNParams programlogika szerint
  4. Ha szükséges, módosítsa a módszer kódját GetParamDefValue, ha a metódusparaméterek alapértelmezett értékeit szeretné használni.
  5. Változtassa meg a funkciót HasRetVal
  6. Módosítsa a függvények logikáját CallAsProc vagy CallAsFunc, a metódus közvetlenül végrehajtható kódját helyezve oda
Mutassuk be a tömböket g_MethodNamesÉs g_MethodNamesRu, valamint a listázás Mód az űrlaphoz:
static wchar_t *g_MethodNames = (L"Engedélyezés", L"Letiltás", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture", L"Teszt"); static wchar_t *g_MethodNamesRu = (L"Engedélyezés", L"Letiltás", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture", L"Teszt");

Enum Methods ( eMethEnable = 0, eMethDisable, eMethShowInStatusLine, eMethStartTimer, eMethStopTimer, eMethLoadPicture, eMethTest, eMethLast // Mindig utolsó );
Szerkesszük a függvényt GetNpropsígy a „Teszt” metódus paramétereinek számát adja vissza:
long CAddInNative::GetNParams(const long lMethodNum) ( switch(lMethodNum) ( case eMethShowInStatusLine: return 1; case eMethLoadPicture: return 1; case eMethTest: return 2; alapértelmezett: return 0; ) return 0; )
Változtassunk a függvényen:
bool CAddInNative::GetParamDefValue(const long lMethodNum, const long lParamNum, tVariant *pvarParamDefValue) (TV_VT(pvarParamDefValue)= VTYPE_EMPTY; switch(lMethodNum) (L case:eMethIntaMeisth case:caseMethInEnable eMethStartTime r: case eMethStopTimer: case eMethTest: / / Alapértelmezés szerint nincsenek paraméterértékek; alapértelmezett: return false; ) return false; )
A hozzáadott vonalnak köszönhetően
eset eMethTest:
ha egy vagy több argumentum hiányzik, a megfelelő paraméterek értéke üres lesz ( VTYPE_EMPTY). Ha egy paraméterhez alapértelmezett értékre van szüksége, akkor azt a szakaszban kell beállítani eMethTest funkciókapcsoló utasítás CAddInNative::GetParamDefValue.
Mivel a Teszt módszer visszaadhat egy értéket, módosítania kell a függvénykódot HasRetVal:
bool CAddInNative::HasRetVal(const long lMethodNum) ( switch(lMethodNum) ( case eMethLoadPicture: case eMethTest: return true; alapértelmezett: return false; ) return false; )
És add hozzá a metódus végrehajtható kódját a függvényhez CallAsFunc:
bool CAddInNative::CallAsFunc(const long lMethodNum, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray) ( ... std::wstring s1, s2; switch(lMethodNum) ( case eMethLoadT eset: eMethLoadP; if (!lSizeArray || !paParams) false értéket ad vissza; s1 = (paParams) -> pwstrVal; s2 = (paParams+1) -> pwstrVal; wstring_to_p(std::wstring(s1+s2), pvarRetValue); ret = igaz ; break; ) return ret;)
Fordítsuk le a komponenst, és vigyük be a konfigurációs kódot az űrlapba:
változó DemoComp; Eljárás a rendszer indításakor() Csatlakoztassa a külső komponenst ("...", "DemoVK", külső összetevő típusa. Natív); DemoComp = New("AddIn.DemoVK.SomeName"); sáv = DemoComp.Test("Helló", "Világ!"); Jelentés(per); Az eljárás vége
A konfiguráció elindítása után a „Hello, World!” üzenetet kapjuk, ami azt jelzi, hogy a módszer sikeresen működött.

Időzítő

Feladat:
  1. Tanulmányozza az időzítő megvalósítását a demo VK-ban
  2. Módosítsa a „StartTimer” metódust az időzítő válaszintervallumának (ezredmásodpercben) átadásának lehetőségével a paraméterekben.
  3. Győződjön meg arról, hogy az elvégzett változtatások működnek.

A WinAPI-ban az üzenetet az idő függvényében használhatja WM_TIMER. Ez az üzenet az időzítő létrehozásakor beállított időintervallumban kerül elküldésre a programnak.
Időzítő létrehozásához használja a funkciót SetTimer:
UINT SetTimer(HWND hWnd, // ablakleíró UINT nIDevent, // időzítő azonosítója (szám) UINT nElapse, // késleltetés TIMERPROC lpTimerFunc); // mutató a függvényre
Az operációs rendszer üzenetet küld WM_TIMER a programba az argumentumban megadott intervallummal nEltelt(ezredmásodpercben). Az utolsó paraméterben megadhat egy funkciót, amely az időzítő minden egyes indításakor végrehajtódik. A függvény fejlécének így kell kinéznie (a név bármi lehet):
void __stdcall TimerProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
Tekintsük az időzítő megvalósítását a demo VC-ben.
Mivel a Windows OS család külső összetevőjének fejlesztésén gondolkodunk, nem vesszük figyelembe az időzítő megvalósítását más operációs rendszerekben. Különösen GNU/Linux OS esetén a megvalósítás különbözik a függvény szintaxisában SetTimerÉs TimerProc.
A végrehajtható kód meghívja a metódust SetTimer, amelyre a függvényt továbbítják MyTimerProc:
m_uiTimer = ::SetTimer(NULL,0,100,(TIMERPROC)MyTimerProc);
A létrehozott időzítő azonosítója egy változóba kerül m_uiTimer hogy később lehessen tiltani.
Funkció MyTimerProc alábbiak szerint:
VOID CALLBACK MyTimerProc(HWND hwnd, // az időzítő üzenetek ablakának fogantyúja UINT uMsg, // WM_TIMER üzenet UINT idEvent, // időzítő azonosító DWORD dwTime // aktuális rendszeridő) ( if (!pAsyncEvent) return; wchar_t "ComponentNative", *what = L"Timer"; wchar_t *wstime = new wchar_t; if (wstime) ( wmemset(wstime, 0, TIME_LEN); ::_ultow(dwTime, wstime, 10); pAsyncEvent->ExternalEvent(who , what, wstime); wstime törlése; ) )
A függvény lényege, hogy a metódus hívásra kerül Külső esemény, amely üzenetet küld az 1C: Enterprise rendszernek.
A módszer funkcionalitásának bővítése StartTimer Tegyük a következőket:
A módszer kódjának módosítása GetNParams hogy a módszerhez való eMethStartTimer visszaadott értéke 1:
case eMethStartTimer: return 1;
Itt van a módszer kódja CallAsProc az űrlaphoz:
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;
Most nézzük meg a funkcionalitást. Ehhez beírjuk a kódot a konfiguráció menedzselt alkalmazás moduljába:
változó DemoComp; Eljárás a rendszer indításakor() Csatlakoztassa a külső komponenst ("...", "DemoVK", külső összetevő típusa. Natív); DemoComp = New("AddIn.DemoVK.SomeName"); DemoComp.StartTimer(2000); Az eljárás vége
A konfiguráció elindítása után a program 2 másodperces időközönként üzeneteket kap, ami azt jelzi, hogy az időzítő megfelelően működik.

Interakció az 1C: Enterprise rendszerrel

A külső összetevő és az 1C: Enterprise rendszer közötti interakcióhoz a fájlban leírt IAddInDefBase osztály metódusai AddInDefBase.h. Felsoroljuk a leggyakrabban használtakat:
Hibaüzenet generálása
virtual bool ADDIN_API AddError (aláíratlan rövid wcode, const WCHAR_T* forrás, const WCHAR_T* leírás, hosszú kód)
wcode, scode- hibakódok (a hibakódok listája leírásokkal az ITS lemezen található)
forrás- hibaforrás
descr- hiba leírása
Üzenet küldése az 1C: Enterprise rendszernek
virtual bool ADDIN_API ExternalEvent(WCHAR_T* wszSource, WCHAR_T* wszMessage, WCHAR_T* wszData) = 0;
wszForrás- üzenet forrása
wszÜzenet- Üzenet szövege
wszData- továbbított adatok
Az üzenetlehallgatás a külső eseményfeldolgozási eljárással történik
Külső komponens regisztrációja az 1C: Enterprise rendszerben
virtual bool ADDIN_API RegisterProfileAs(WCHAR_T* wszProfileName)
wszProfileName- alkatrész neve.
Ezek a módszerek elegendőek a VK és az 1C közötti teljes interakcióhoz. Ahhoz, hogy egy külső komponens adatokat fogadjon az 1C: Enterprise rendszerből és fordítva, a külső komponens speciális üzenetet küld, amelyet viszont az 1C rendszer elfog, és szükség esetén meghívja a külső komponens metódusait az adatok visszaküldésére. .

tVáltozatos adattípus

A külső komponens és az 1C: Enterprise rendszer közötti adatcserénél a tVariant adattípust használjuk. Ennek leírása a type.h fájlban található, amely az ITS lemezen található:
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 uulllu2; uint16_t uulllu2; ; int32_t errCode; hosszú hRes; float fltVal; dupla dblVal; bool bVal; char chVal; wchar_t wchVal; DÁTUM dátum; IID IDVal; struct _tVariant *pvarVal; struct tm tmVal; _ANONYMOUS_STRUCT struct ( void* pInterfaceVal; IID InterfaceID; ) __VARIANT_NAME_2/p charuTNY*/; int32_t strLen ; //bájtszám /Méret an egydimenziós tömb a pvarVal TYPEVAR vt-ben; );
típus tVáltozat egy olyan szerkezet, amely a következőket tartalmazza:
  • közvetlenül adattárolásra szánt keverék (unió).
  • adattípus azonosító
Általában a típusú változókkal való munkavégzés tVáltozat a következő algoritmus szerint történik:
  1. A változóban pillanatnyilag tárolt adatok típusának meghatározása
  2. Az adatok közvetlen eléréséhez nyissa meg a megfelelő keverékmezőt
A típus használata tVáltozat jelentősen leegyszerűsíti az 1C: Enterprise rendszer és a külső összetevők interakcióját

Alkalmazás

Az „examples” könyvtár példákat tartalmaz a cikkhez
examples/1 - indítsa el a bemutató komponenst
példák/2 - tulajdonságlista bővítésének bemutatása
példák/3 - a módszerek listájának bővítésének bemutatása
Minden könyvtár tartalmaz egy VS 2008 projektet és egy kész 1C konfigurációt.

OLEG FILIPOV, ANT-Inform, fejlesztési osztályvezető-helyettes, [e-mail védett]

Az 1C:Enterprise funkcióinak bővítése
1. rész: Külső COM-komponensek

Minden 1C fejlesztő életében eljön az idő, amikor a rá rendelt feladatok meghaladják az 1C platform képességeit. Nézzük meg, hogyan lehet „leküzdeni a lehetséges határait”

A tapasztalt 1C fejlesztők valószínűleg már a cikk címének első három szava alapján sejtették, hogy az 1C:Enterprise külső összetevőiről lesz szó. Ez a technológia az 1C:Enterprise 7.7 platform (vagy korábban) óta létezik. Történelmileg elsősorban a kereskedelmi berendezésekkel (Vonalkód-leolvasók, elektronikus mérlegek, pénztárgépek) való interakció problémáinak megoldására tűnt fel, amelyek során szükségessé vált, hogy az 1C platform feldolgozzon bizonyos, a berendezés által kezdeményezett eseményeket. Az ilyen események nem mások, mint a COM/LPT portokon érkező bájtok sorozatai. Természetesen nem lenne túl helyes az 1C platformot ilyen alacsony szintű mechanizmusokhoz igazítani, ezért kitaláltuk a külső komponensek technológiáját az 1C: Enterprise számára.

Ebben a cikksorozatban nem csak a külső alkatrészekről fogunk beszélni: a külső alkatrészek meglehetősen erős és összetett mechanizmusok, így bizonyos problémák megoldására való felhasználásukat „ágyúval veréb lövöldözésnek” tekintik. Mindazonáltal a külső összetevők a leguniverzálisabb eszközök mindenféle probléma megoldására, amelyek túlmutatnak az 1C: Enterprise platformon, ezért velük kezdjük. A cikk a legrégebbi, leggyakrabban használt és jól bevált technológiát tárgyalja a külső komponensek létrehozására - COM alapú.

Mik azok a külső alkatrészek

Mint fentebb említettük, a külső összetevők a 7.7-es verzió óta megjelentek az 1C:Enterprise-ben. Kezdetben a hagyomány szerint az 1C platform fejlesztői „nem zavarták”, és a külső összetevők bizonyos kötelező tulajdonságokkal és módszerekkel rendelkező objektumok voltak. A komponensek a mai napig változatlan formában léteznek. Vagyis az 1C:Enterprise 7.7-hez írt komponensek (elméletileg) működni fognak az 1C:Enterprise 8.3-mal.

A gyakorlatban számos finomság létezik: ha egy külső összetevő aktívan kölcsönhatásba lép magával a platformmal, akkor funkciói egy adott verzióra specializálódnak.

A 8.2-es verzió óta az 1C:Enterprise új technológiát vezetett be a külső összetevők fejlesztésére, az úgynevezett NativeAPI-t. Az ezzel a technológiával írt összetevők már nem COM-objektumok. Ez egyrészt plusz - ezek az összetevők nem igényelnek regisztrációt, másrészt az 1C:Enterprise platformon kívül máshol nem használhatók. A külső komponensek fejlesztése némileg bonyolultabbá vált, valamivel több szükséges interfészt kell támogatniuk. De erről a következő cikkben fogunk beszélni.

Külső alkatrészek és COM technológia

Magát a COM technológiát nem írom le részletesen, mert rengeteg szakirodalom létezik a témában. Valószínűleg csak azt kellene mondani, hogy sokan „összetévesztik” egy normál inproc szerver létrehozását az 1C:Enterprise külső összetevőinek létrehozásával, de ez nem áll messze az igazságtól. Nagyon gyakran meg lehet boldogulni ezzel a funkcióval. A COM-objektumok Visual Studióban való létrehozásának módját számos különböző forrás írja le. A személy részletesen leírta, hogyan történik ez, egy példával az 1C:Enterprise hívásáról.

Ha továbbra is teljes értékű COM külső komponenst szeretne készíteni az 1C:Enterprise számára (a lehetőségek közül, miért lehet erre szükség, csak egyet tudok elképzelni - a komponensnek aktívan interakcióba kell lépnie a rendszerrel az 1C platformon, értesítenie kell a felhasználókat, módosítsa az állapotsort, jelenítse meg a párbeszédpaneleket stb.), akkor közvetlenül a külső összetevők technológiáját kell használnia. Tehát kezdjük.