Custom CDF and strings
Custom CDF and strings
Hi there,
I read the blog entry on how to create custom cdf and I am really excited about that. It has been long time since I desired to add some function and cannot do that was kind of frustrating. Anyway, I followed the example and everything worked straight away. I created some simple functions by myself and those work well, unless there are string involved. I am trying to create a trim function and I have a working algorithm, the dll is built without errors, but when I try to use it in dataease it crashes. I am using c so I do not really use strings, but *char. Is there some specific issue I have to take in mind when creating functions dealing with strings?
Regards
Re:Custom CDF and strings
Hi George, and thanks for the interest in extending DfW your self using a custom CDF.
The most common reason for DfW to crash when returning a string from a CDF is simply that the memory used by the string it self is released before DfW can get to it. To fix this, simply make sure you allocate a buffer that stay in memory for as long as the DLL is loaded. The simplest way to achieve this is to create a global variable outside of your function, then use this buffer for your trimming and then return this buffer when returning string data to DfW.
//from examplecdf.h EXAMPLECDF_API LPCSTR returnstring(void); //from examplecdf.cpp char _returnbuffer[256]; // This is an example of an exported function that returns a string buffer EXAMPLECDF_API LPCSTR returnstring(void) { strncpy_s(_returnbuffer, "42",256); return _returnbuffer; }
CDFs are loaded the first time they are used and do not get reloaded or released before you shut down DfW and start i up again. So every time you make a change to your function and need to test a new version, make sure that you shut down DfW and start it again before you do a new test.
Hope this helps. We will come back with a more extensive article on CDFs and some example code to test them later.
Re:Custom CDF and strings
Hi there,
I know this is a one year old thread, but I put this problem aside and now it has been brought again to my attention... any news on this issue (it seems that it is possible to build only functions returning a string)?
Regards
Re:Re:Custom CDF and strings
Pretty much on the same subject about creating CDF.
Windows o/s alone comes with 100s of functions. For us to use the Windows API, we need an add-on into DataEase. As soon as we can use that Add-on. We can open up Windows API and use 1000s of functions already available. There won't be a need for CDF :-)
I think previous DataEase developers did try something like this in a version. I think 7.0 or something, I can't even remember now.
Re:Re:Custom CDF and strings
Thank you G3 development for the clarification. I am still missing something on CDF development, cause a lot of very simple routines give GPF in an apparently random way. I can't help to read more about that topic. I know the development team is really busy on the de 8.5 development, but if you could release just the source code of a working CDF with few functions on it, it would be grate and I would really appreciate that.
Re:Re:Custom CDF and strings
Up, I really need this.
Re:Re:Re:Custom CDF and strings
Arul... Simplistic solutions aren't always the simplest solutions. ;-)
The CDF interface is very much an interface to the Windows API and everything else.
There is no way you can magically create a way to use the WIndows API that is any simpler than the CDF interface in DataEase.
DataEase itself is partly an simplified interfaced to Windows/Windows API development.
If we did what you want, the point of DataEase would have evaporated as you must then have the same programming knowledge/understanding as a C/C++ developer.
Using the Windows API is not as simple as calling a function in DataEase. There is a lot of rules, limitations and environment restrictions that has to be adhered to. I know how you had GPFs and I can promise you that you would get even more closely acquainted with them if you tried to call Windows API stuff directly from DataEase.
Our job is to develop a RAD environment with a "minimum" of complexity for the developer.
With the CDF interface you can make any "add-on" functionality using any and all WIndows API functions and any other functionality or code you can run in Windows.
You can quickly develop the majority of the application and if there is things that you can't do in DataEase you can invest the time/money in a specialist/tailor made CDF to do the rest.
Re:Re:Re:Custom CDF and strings
When you refer to the previous Developers attempting to access the Windows API you are thinking of using ShellExecute to call some Shell functions in Windows. This is not strictly the same thing.
https://msdn.microsoft.com/en-us/library/windows/d....aspx
You can use ShellExecute to execute functions in other programs including Windows, but it is not very easy...
The sole purpose of a product like DataEase is to make something complex understandable, so it would be a big step in the wrong direction if we tried to go in the other direction.
Re:Re:Re:Custom CDF and strings
Hi, to call with parmeters as input is a bit more complicated than the first example in CDF part1. Here is part 2 that tells more of the story of creating CDFs. Added the code example as well for you all to play with.
Re:Re:Re:Custom CDF and strings
This is a developer interface for specially interested parties and not a priority when it comes to development/support.
It has not been updated for 20 years so using a new compiler etc, might in itself be the problem as your DLL might not be compatible with the interface.
DataEase convert automatically between string to number etc, so if String return value works and double doesn't then the solution is to return a string.
Most DataEase functions return string anyway as it is the most practical return value as we can return error messages etc directly if the function fails.
We can't do anything about the API/Interface as it would interfere with the existing CDF libraries.
Re:Re:Re:Re:Custom CDF and strings
No. I am talking about different thing here.
Actually I managed to dig into old documents and found this.
In the following script, addressof is the new function introduced by the DE developer in the past.
Not sure which DE version. In the example below it will use the GetDefaultPrinterA Window API
--------------------------
define "BufName" text 49 .
define "BufLen" text 4 .
define "prtName" number .
define "prtLen" number .
define "retval" number .
BufName := "1234567890123456789012345678901234567890123456789" .
Buflen := "1" . -- Ascii 49
prtName := addressof(BufName) .
prtLen := addressof(BufLen) .
retval := GetDefaultPrinterA( prtName, prtLen ) .
message BufName window .
-------------------------------
Re:Re:Re:Re:Custom CDF and strings
Hi there,
thank you for this tutorial, very useful and informative. I went through it and it seems pretty clear. I have just one question regarding the memory space taken by the input variables. In the CDF form there are five types defined in the drop-down menu: int, long, float, double, string. We have to map those to C variables, so, in my understanding, it should be:
DataEase | C | Bytes |
INT | INT | 4 |
LONG | LONG | 4 |
FLOAT | FLOAT | 4 |
DOUBLE | DOUBLE | 8 |
STRING | LPCSTR | 4 |
We have to take in mind this table when "mangling" parameters in DLL functions.
Are those values correct?
Regards.
Re:Re:Re:Re:Re:Custom CDF and strings
Arul. I think you have just proven my point here.
With DataEase you can get the default printer name and also set a default printer automatically as following:
Step 1:
Create following CDFs:
1- Function Name: GetDefaultPrinterA
CDF Library Name: Winspool.drv
Return Type: Long
Parameters:
1.Name: prtNameBuffer Type: Long
2.Name: prtNameSizeBuffer Type: Long
2- Function Name: SetDefaultPrinterA
CDF Library Name: Winspool.drv
Return Type: Long
Parameters
1.Name: prtName Type: String
3- Function Name: RecordSave
CDF Library Name: dfwacts.dll
Return Type: Int
After creating those CDFs, please close the application and reopen it again (to register the CDFs).
Step 2:
Create a new form with one field.
Form name: GetDefaultPrinter.
Field name: DefaultPrinterName data-type: Text 255, Prevent Entry
Create a button with caption Click Here to Get the Default Printer Name.
Right-click the button and select OML from the pop-up menu and type the
following:
define BufName text 49 .
define BufLen text 4 .
define prtName number .
define prtLen number .
define retval number .
BufName := 1234567890123456789012345678901234567890123456789 .
Buflen := 1 . — Ascii 49
prtName := addressof(BufName) .
prtLen := addressof(BufLen) .
retval := GetDefaultPrinterA( prtName, prtLen ) .
DefaultPrinterName.Value := BufName .
retval := RecordSave () .
Now save the form and return to the User View (or press F4). In the user view click the button to get your default printer name and place it into the field.
Step 3:
Create a new form with one field:
Form name: SetDefaultPrinter
Field name: PrinterName , data-type: text and length 255
Create a button with caption Type the Printer Name You want to set. Click this button to set the Default Printer. Right-click the button and select OML from the pop-up menu and type the following:
define retval number .
retval := RecordSave () .
retval := SetDefaultPrinterA (PrinterName.Value) .
Now save the form and return to the User View (or press F4). In the user view enter the printer name exactly as it appears under control panel >> Printers and Faxes.
SetDefaultPrinterA IS a CDF....
addressof() is one of the very few functions that they found time to make from 5.x to 7.1.... and it doesn't something that is illegible for the majority of our users.
CDF's are "external" methods and they expect real addresses. Moreover, they expect addresses to certain data types, e.g. LPDWORD or LPWORD, which are not the same because they point to datum of different lengths. DataEase 7 contains an addressof function which returns the real address of data to the DataEase process memory.
For example, assume that a CDF named SqCDF is registered with the application. SqCDF accepts a pointer to a number, and it multiplies that number by itself.
Then the script:
define "Arg" Number .define "Res" Number .assign Arg := 10 .assign Res := SqCDF(addressof(Arg)) .message Arg window .
Â?will display the number 100 in a message window. But notice that we are displaying the variable Arg, which has not had any Â?assignÂ? statements made to it. Instead the new value has been directly written into the memory address of Arg.
Note: The example above assumes that the CDF SqCDF treats passed pointer as a pointer to an 8-byte double precision number.
LIke I said, to do this you need a completely different kind of programming skill than DataEase aim for and simply showcase that they people in charge did not understand the target group for the product.
It is/was called DataEase after all.
If you have read the blog you have got that we finally implemented this in 8.5
SetState("DefaultPrinter","PrinterName")
GetSate("DeafultPrinter") -- return default printer name.
I would say that that is a little more DataEasey then the example above.
Re:Re:Re:Re:Re:Custom CDF and strings
Hi there,
I am playing with CDF and I am cleraly missing something. Followed the part 2 tutorial and everything went ok: I can pass parameter to a function in an externel ddl and get a result. So, next step was to write my own function. I began with a small test function to calculate the area of a circle, given the radius. So, in the ExampleCDF.h file I adde the following lines:
EXAMPLECDF_API double mycircle(double radius);
#pragma comment(linker, "/EXPORT:mycircle=_mycircle@8")
And in the ExampleCDF.cpp:
EXAMPLECDF_API double mycircle(double radius)
{
double c_area = 3.14 * radius * radius ;
return c_area ;
}
Finally I registered the function in DataEase in the Custom Functions table.
Then I ran a simple test, I created a procedure as follows:
message mycircle(10) window .
And the result of the function is... 10
After many attempts to get the correct result, I modified the function as follows:
ExampleCDF.h
EXAMPLECDF_API LPCSTR mycircle(double radius);
#pragma comment(linker, "/EXPORT:mycircle=_mycircle@8")
ExampleCDF.cpp
EXAMPLECDF_API LPCSTR mycircle(double radius)
{
double c_area = 3.14 * radius * radius ;
sprintf_s(_returnbuffer, 256, "%f", c_area);
return _returnbuffer;
}
And the result was the correct one, 314.
Please, shed some light on this mistery.
Regards.
Re:Re:Re:Re:Re:Re:Custom CDF and strings
I see what you were saying.
But, some clients already feeling DataEase 8.x developer is way advance than previous versions.
I am ok with this :-)
Re:Re:Re:Re:Re:Re:Custom CDF and strings
up
Re:Re:Re:Re:Re:Re:Re:Custom CDF and strings
Our resident CDF expert has been on holiday but will be back next week.