Call out to 3Gl: struct includes char*
Author: i2stiller@gmx.de (istiller)
Hi freaks Just programming the interface to ELSTER (german tax office application) All going well until it came to the point to exchange a struct Okay, create a dummy entity and add all members of the (C-)struct as fields. The last two members are defined as "char*" Now is the question: how to say UnifAce that this are "char*" and not "char[n]" with a fix number n If I define this as C40 or C*, UnifAce is going into the nirvana. Any clue how to define? BTW: This is a simple task UnifAce should handle! I don't want to implement a wrapper, as then there is a need for an extra environment, maintance overhead and man power. TIA Ingo PS:The structure in question: typedef struct{ uint32_t version; uint32_t vorschau; uint32_t ersteSeite; uint32_t duplexDruck; const char* pdfName; const char* fussText; } eric_druck_parameter_t;
20 Comments
Local Administrator
Author: Stijn Courtheyn (stijn.courtheyn@xperthis.be)
Local Administrator
Hi Stijn Yes, I did create a signature for the DLL. And all all but this function with this parameter are callable This is the function: ERICAPI_DECL int STDCALL EricBearbeiteVorgang( const char* datenpuffer, const char* datenartVersion, uint32_t bearbeitungsFlags, const eric_druck_parameter_t* druckParameter, const eric_verschluesselungs_parameter_t* cryptoParameter, EricTransferHandle* transferHandle, EricRueckgabepufferHandle rueckgabeXmlPuffer, EricRueckgabepufferHandle serverantwortXmlPuffer); Problem is this parameter const eric_druck_parameter_t* druckParameter (and of cource the next one with also a struct* as type) And I dodn't want to start a new overhead (wrapper) for just this case. As UnifAce tell us everytime, UnifAce is the best language in world for "business crtical applications", a normal struct parameter should be no challenge for UnifAce Ingo
Author: istiller (i2stiller@gmx.de)
Local Administrator
Hi, I don't know if I understand your problem correctly.... But if this is the main problem or ar least the first part of the problem "Now is the question: how to say UnifAce that this are “char*” and not “char[n]” with a fix number n" Define parameter: Basic String Interface char* (length 2 - 10240000) Length 10240000 Hmmm, having thought about it, I suppose your problem is not to define the parameter on the level described by me.... Regards RogerW.
Author: rogerw (roger.wallin@abilita.fi)
Local Administrator
Uniface has a run time fit if you define more than one C*, or if you put a fixed length variable after the C*. We have found that, for database purposes anyway, you can define them as SC* and the uniface runtime lockup goes away.
Author: Iain Sharp (i.sharp@pcisystems.co.uk)
Local Administrator
Nope SC* don't solve the problem If I defined the struc typedef struct{ uint32_t version; uint32_t vorschau; uint32_t ersteSeite; uint32_t duplexDruck; const char* pdfName; const char* fussText; } eric_druck_parameter_t; as UnifAce table ERIC_DRUCK_PARA VERSION I4 VORSCHAU I4 ERSTESEITE I4 DUPLEXDRUCK I4 PDFNAME I4 FUSSTEXT I4 and fill the fields VERSION.ERIC_DRUCK_PARA =2 VORSCHAU.ERIC_DRUCK_PARA =0 ERSTESEITE.ERIC_DRUCK_PARA =0 DUPLEXDRUCK.ERIC_DRUCK_PARA =0 PDFNAME.ERIC_DRUCK_PARA =0 FUSSTEXT.ERIC_DRUCK_PARA =0 the active will work, but with NULL-pointers for the strings. This is the resilt expected, as the layout of the C-struct equals the UnifAce-Table. But this does not solve my problem. I need to transfer string (inside a struct) to the DLL. @UnifAce: How to solve this? I need an answer as soon as possible as this is the base of our implementation of ELSTER TIA Ingo
Author: istiller (i2stiller@gmx.de)
Local Administrator
I looked into this problem and I hope I understand the problem correctly. This is what I did in a DLL project VS2015: 1 - declared the structure as described typedef struct { unsigned int version; unsigned int vorschau; unsigned int ersteSeite; unsigned int duplexDruck; const char * pdfName; const char * fussText; } eric_druck_parameter_t; 2 - I created a dll with two functions extern "C" __declspec(dllexport) int unidoCall(unsigned int l_version, unsigned int l_vorschau, unsigned int l_ersteSeite, unsigned int l_duplexDruc, char *l_pdfName, char * l_fussText); extern "C" __declspec(dllexport) int unidoCallEnt(eric_druck_parameter_t *p_entity); The function are empty and only do an outputdebugstring. int unidoCall(unsigned int l_version, unsigned int l_vorschau, unsigned int l_ersteSeite, unsigned int l_duplexDruc, char *l_pdfName, char * l_fussText) { OutputDebugStringA("unidoCall "); return 0; } int unidoCallEnt(eric_druck_parameter_t *p_entity) { OutputDebugStringA("unidoCallEnt "); return 0; } In UNIFACE a callouttest signature is created and an entity with the same fields as the C-structure. In the database entity I have 1 2 3 Test123 FussText Calling the unidoCall with all separate parameters works fine. Calling the unidoCallEnt fails since all data is in one contiguous block of memory. Hence refering to p_entity->fusstext causes a crash Looking into memory shows me: 0x0731A7B0 00000000000000000001....00000000000000000002....00000000 0x0731A7E8 30 30 30 30 30 30 30 30 30 30 30 33 00 00 00 00 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 34 54 65 73 74 31 32 33 20 20 20 20 20 20 20 20 20 20 20 20 20 000000000003....00000000000000000004Test123 0x0731A820 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 46 75 73 73 54 65 78 74 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 FussText All data as one block and the test params are filled out with spaces and zeros (0x30). Expected was a reference to the separate members of the entity. In the current situation I would create an interface with the fields as separate parameter. The documentation also shows the occurrence as a structure in 3GL with members in the struct with an equal size of the fields in the db-table. There are no references in the sample like the char *Fusstext member. Other option is to call a 3gL function without parameter and call back into uniface to get the data per field, for instance with
short ufget (unsigned char* field, unsigned char* buffer, short max); void newData(){
do here your calls } Hope this helps Kind regards Jasper de Keijzer
Author: Jasper (jasper.de.keijzer@compuware.com)
Local Administrator
Moin Jasper Thanks for answering. You did describe the problem well :-) The layout of the entity-"struct" UnifAce will use on calling the C-funcktion looks like typedef struct{ uint32_t version; uint32_t vorschau; uint32_t ersteSeite; uint32_t duplexDruck; char[40] pdfName; // if defined in UnifAce as C40 char[40] fussText; } A_eric_druck_parameter_t; If I define the last two member as I4 I got typedef struct{ uint32_t version; uint32_t vorschau; uint32_t ersteSeite; uint32_t duplexDruck; uint32_t pdfName; // if defined in UnifAce as I4 uint32_t fussText; } B_eric_druck_parameter_t; Everybody knows that "char*" do have the same size in bytes then "uint32_t" char* is an address (pointer) into memory, nothing else the an integer So, "B_eric_druck_parameter_t" is equal by layout to "eric_druck_parameter_t" and I can call the function in question. Okay, if I know the address in memory or provide a NULL-pointer == 0 This thoughts are already done, BUT ... But I need "eric_druck_parameter_t" with two pointers to a "string" of chars in memory The idea by modifing the C-function, nice but not pratical :-) We are not the owner of this software we have to call. ELSTER is a peace of software if the german tax office (https://www.elster.de/eportal/start) I don't believe, if a call Berlin "Hey guys, we are using UnifAce. Please change your function, elsewhere we could not send you the tax values" that Berlin we change anything. So - as UnifAce is a modern and flexible program language - UnifAce do have a solution @UnifAce: So what the solution for this problem? And don't came around with: You have to write a wrapper. This are elementary things, every modern language should handle Regards Ingo
Author: istiller (i2stiller@gmx.de)
Local Administrator
BTW Just got an answer from Berlin: "There is no workaround for the char* member" So before I start to program the wrapper "Hello UnifAce: any workaround within UnifAce?"
Author: istiller (i2stiller@gmx.de)
Local Administrator
Ingo The question is, how would you let UNIFACE know the layout of your C-structure? I do not see any sensible way in the current signature form to explain the 4GL the format of this C-struct. And what if these pointers become 64bit? Can you call support for this? Success Jasper
Author: Jasper (jasper.de.keijzer@compuware.com)
Local Administrator
Hi Jasper, I understand your question but in the previous post on this same subject Mike Taylor answered "using a Uniface entity". Isn't an enriched application model the answer to your question? As Roger already, I am really interested to understand which will be the long term solution to this issue. Regards, Gianni
Author: gianni (gianni.sandigliano@unifacesolutions.com)
Local Administrator
Hi Jasper Uniface already supports struct: You define the (C-)struct as an entity in UnifAce. On calling a C-procedure with this entity, UnifAce does some marshalling All numeric members will be (according to there Interface) convert to a integer type in C Strings with fix a fixed number n of character will convert to char[n] What about strings with interface "C*" ? How will UnifAce convert them? And this is my idea/wish: Convert "C*" at runtime in a char* struct struct_t { char* a_char_star;); struct_t a_struct; On input, this is very easy: a_struct.a_char_star= C_parameter = UnifAce_string.c_str(); function(&a_struct); If this parameter is an output one: a_struct.a_char_star = 0; function(&a_struct); UnifAce_string.assign(a_struct.a_char_star); free( a_struct.a_char_star); If this parameter is an in/output one: a_struct.a_char_star = malloc(MAX_BUFFER_SIZE); MAX_BUFFER_SIZE could be defined by UnifAce and/or in ASN. function(&a_struct); UnifAce_string.assign(a_struct.a_char_star); free( a_struct.a_char_star); So it would be easy to transfer a string as char* into a 3GL and back. BTW: a marshalling like this must already exists, as UnifAce can use "char*" as an parameter on a 3GL-call No witchcreaft needed :-) Ingo
Author: istiller (i2stiller@gmx.de)
Local Administrator
Hi, We have built quite extensive web-service API:s in Visual Studio as Middleware between Uniface and national program-services, ie. being able to by ourselves define the api for Uniface to use. I'm a bit hesitant about what to demand from Uniface. I see Uniface as a programming-module mainly unifying the UI and the database access. Uniface has enough to do, helping us to build nice UI:s, where by the way, they haven't been enough progressive the last decade. Where is the product that cross compiles for both desktop and web (and mobile). Regards RogerW. PS. Sorry Ingo for this :-(. Just build a small C++ program that is able to handle your C++ struct and define the api to fit Uniface yourself. On the other hand I'm interested in hearing about a solution to your problem....
Author: rogerw (roger.wallin@abilita.fi)
Local Administrator
Hi Roger I remember the first step we did with UnifAce: There were no loops at all: Reason (by UnifAce) "loops a 3GL, we are a 4GL!" Luckily UnifAce changed there mind and we do have all this 3GL stuff :-) On the other hand, you have to provide many function with global registers and use $perform Also there where cryptic function, one have to search for in the net (e.g. filesystem operation) This was more then 3GL, this was sometime magic ... So as a moderen language, it should be possible in UnifAce to define a struct layout. And yes, UnifAce have a lot advantages over othe languages! If this ist not true, we would be already on another language. But there is also a real world outside of the UnifAce universe. And we have to connect to this real world! If I have to tell my boss on every new application we will connect to out application:"Sorry, I have to write a wrapper first", I got the answer:"Will it not be better to write all in C#. UnifAce is old-fashioned and out of date." BTW: Announcing staff shake-out will do the rest to look for alternatives :-( So if I could tell my coleagues "no problem for UnifAce", this will help surviving of UnifAce in our company. Else I have to invest a few days to implement a proper, stable and secure wrapper. And time is money :-( Just my two censt to this problem Ingo
Author: istiller (i2stiller@gmx.de)
Local Administrator
Here is an explanation how VB will do the convert from 4GL to 3GL >https://activevb.de/rubriken/kolumne/kol_4/cpptovbtypen.html (okay in german but the tables and examples should be easy to read) Ingo
Author: istiller (i2stiller@gmx.de)
Local Administrator
Hi Ingo, I respect your opinion in this case and I hope you get a solution. However, I have heard so many young programmers shout about how good and comprehensive C# and C++ is compared to Uniface. It's very hard to explain to them that Uniface is built with VS and it's not meant to be compared to 3gl languages. It wouldn't be practical for Uniface to support all kind of concepts supported by a 3gl, then I promise that Uniface will lose. I've seen young programmers doing real balls of thread with C#, ie. code that is very hard to maintain and code that could have been made much simpler. This code is usually done by mistake (the programmer hasn't known better), but I'm sure that sometimes it's done deliberately to get some undeserved advantages. Thats why we need Uniface, ie. unifying the programming process for the most used concepts (UI, database access etc.). And as I said, I'm not content with what Uniface has done during the last decade. For sure Uniface has to find a golden mean, to concentrate on the right things. Regards RogerW.
Author: rogerw (roger.wallin@abilita.fi)
Local Administrator
Hi Roger I'm also like UnifAce :-) It (UnifAce) is a 4GL that hides most of 3GL for us, but sometimes is necassary to call 3GL. I don't want to programm in C/C++/C# but a way to define C-like structurs and arrays for calling DLLs would be nice to have. BTW: I did start programming with "Origin" on a MAI, then there was Synon on AS400 and now UnifAce on Max/Windows. So we always prefer 4GL lagunages to do our job :-) Ingo
Author: istiller (i2stiller@gmx.de)
Local Administrator
Roger, my gotfeel is: as of today integration with other (external) subsystems is very very important, at least on same evel as UI and database access. Gianni
Author: gianni (gianni.sandigliano@unifacesolutions.com)
Local Administrator
Gianni, You are right, and using web-services Uniface has done a good job introducing the Uniface struct, with which you can call almost all eg. Rest APIs (kind of modern web-service without soap). But trying to support directly all kind of native concepts in different kind of languages, that's hard. That's why you usually need some kind of wrapper. Regards RogerW.
Author: rogerw (roger.wallin@abilita.fi)
Local Administrator
Roger, this same discussion on DBMS interface level was solved many years ago differentiating datatypes from storageFormats; AFAIK the problem here is probably of same type...one of the many possible examples: - C* = 32bit char pointer - SC* = 64bit char pointer (or default pointer size) Complex: YES... Too complex: probably NOT! If approached with 20/80 method, many cases could be directly supported leaving wrappers aside or eventually to be used for fewer cases. My 2 cents... Gianni
Author: gianni (gianni.sandigliano@unifacesolutions.com)
Local Administrator
Hi Freaks For char* in a struct I got a solution, thanks to Arthur at the u.B.G at Hamburg/Germany ! Windows allready do have a "wrapper"-DLL, called shlwapi.dll In the DLL there many, many function. One of them is "StrDup" (StrDupA and StrDupW) PSTR StrDupA( PCSTR pszSrch ); Input is a (const) string, i.e a normal "basis string" in UnifAce signature the function will copy the string onto a static heap and return the address This address can the pass to any DLL, direct or in a struct. Don't forget to free the memory after the job is done: LocalFree( HLOCAL hMem ); Ingo
Author: istiller (i2stiller@gmx.de)