1. jorge gonzalez
  2. PowerBuilder
  3. Sunday, 15 October 2023 14:32 PM UTC

The error message:

error r0042

Error: specifield argument type differs from required argument type at runtime in dll <dll name>

(invalid stack pointer on return from function call) at line 20
in open event of object w_genapp1_sheet1.

Environment:

PB 2022 R2 (Build 2819)

Windows 11 running in Parallels on a Apple Mac M1 processor.

Visual studio 2022 (arm 64).

 

Note. I build the dll as an x86 application

 

Problem:

I finally narrowed the problem down to me passing any parameter from a simple PB local external function call:

function long rettext( int text) library "testdllforpb.dll" alias for "rettext"

 

To this simple c++ dll:

extern "C" __declspec(dllexport) int rettext(int text);  // This comes from the header (h) file

// Here's the c++ dll code (cpp)

extern "C" int rettext(int text)

{

    return 3;

}

// Local external PB call:

function long rettext( int text) library "testdllforpb.dll" alias for "rettext"

 

I've tried __stdcall in the dll and PB does not like that at all. 

If I change the dll so it doesn't have any parameters it works in PB.

Note. I found this old article , which is the exact problem I am having:

"https://codeverge.com/forum/sybase.powerbuilder.general_calling-custom-c++-dll-s-in-power_1037457"

Unfortunately the links mentioned in the article no longer exist.

 

 

 

 

John Fauss Accepted Answer Pending Moderation
  1. Sunday, 15 October 2023 19:21 PM UTC
  2. PowerBuilder
  3. # 1

Hi, Jorge - 

An "int" data type in PB is a two-byte signed integer. An "int" in C/C++ is a four-byte signed integer.

Either change the PB data type to Long, or change the C/C++ data type to short.

Oh, and PB requires the use of the _stdcall calling convention.

    https://docs.appeon.com/pb2022/application_techniques/Using_external_functions.html

Best regards, John

Comment
  1. jorge gonzalez
  2. Monday, 16 October 2023 12:56 PM UTC
John I figured out how to pass a string from PB to the c++ app:



// Local external call function

function string rettext( string text) library "testdllforpb.dll" alias for "_rettext@4"



// C++ function



extern "C" char * __stdcall rettext(char* text)

{

//text = text * 199;

return (char*)text;

}
  1. Helpful
  1. Roland Smith
  2. Monday, 16 October 2023 13:36 PM UTC
In C++ a char variable is Ansi and PowerBuilder strings are Unicode. You'll have to add ;Ansi to the alias.
  1. Helpful
  1. Roland Smith
  2. Monday, 16 October 2023 13:49 PM UTC
I always declare my C++ functions like 'datatype WINAPI function_name ( args )' and then put the function names in a DEF file.

WINAPI is a macro that expands to __stdcall.
  1. Helpful 2
There are no comments made yet.
Bruce Armstrong Accepted Answer Pending Moderation
  1. Monday, 16 October 2023 09:16 AM UTC
  2. PowerBuilder
  3. # 2

You can use a DEF file to prevent the name mangling on the exported functions

 

https://learn.microsoft.com/en-us/cpp/build/exporting-from-a-dll-using-def-files?view=msvc-170

Comment
  1. Miguel Leeuwe
  2. Monday, 16 October 2023 13:56 PM UTC
lol,

Hi John,

Yes, that's exactly the problem I have with C++.

I'm not sure, but I think that C# for anyone who knows C++ would be a piece of cake?

regards
  1. Helpful
  1. jorge gonzalez
  2. Monday, 16 October 2023 16:06 PM UTC
I found c++ app that will allow me to interface with the built in Windows speech api. My c++ skills are very rusty and I never called a c++ dll from Powerbuilder. That's what most of this was about. Now that my PB app can interface with the c++ dll I will create a new dll calling the speech api code. I need this code to run fast so will stick with the c++ dll. I actually found a c# app but it was too slow. The c++ app was almost instantaneous.
  1. Helpful
  1. Chris Pollach @Appeon
  2. Monday, 16 October 2023 16:21 PM UTC
Hi Jorge;

Kool!

That's the problem with C# ... It's a more bloated language that carries a significant DotNet overhead. This can also be a PIA to deploy as your client machines need the correct .Net runtime. Whereas C++ is super fast with no bloat. Just my $02. ;-)

Regards ... Chris
  1. Helpful 1
There are no comments made yet.
Ronnie Po Accepted Answer Pending Moderation
  1. Monday, 16 October 2023 16:34 PM UTC
  2. PowerBuilder
  3. # 3

Hi Jorge,

I have an unrelated question regarding your development environment. Are you using the PB IDE successfully on Windows 11 ARM? (I assume yes, since you are using an M1 Mac.) I didn't think that was possible...

Environment:

PB 2022 R2 (Build 2819)

Windows 11 running in Parallels on a Apple Mac M1 processor.

Visual studio 2022 (arm 64)

 

Comment
  1. Ronnie Po
  2. Monday, 16 October 2023 17:34 PM UTC
Good to know. Thanks...
  1. Helpful
  1. jorge gonzalez
  2. Monday, 16 October 2023 17:41 PM UTC
The new Apple Mac M3 processors are coming out soon. I'd hold off on buying a new Mac until this processor comes out. Bye
  1. Helpful
  1. Ronnie Po
  2. Monday, 16 October 2023 18:16 PM UTC
Agreed. Thank you, Jorge.
  1. Helpful
There are no comments made yet.
jorge gonzalez Accepted Answer Pending Moderation
  1. Monday, 16 October 2023 22:34 PM UTC
  2. PowerBuilder
  3. # 4

Here's my final solution to pass a string to a c++ dll from a PB app via a local external function call. If twcspeechdll's parm was defined as char * argv instead of LPCWSTR argv  I'd only get the first letter of the text I passed in when calling from the PB app.

Note. This dll calls the Windows speech functionality api so text passed into it is translated into speech on a computers speaker. The text passed in comes from a PB app.

 

PB local external function call:

function int twcspeechdll( string text) library "twcappealsspeechdll.dll" alias for "_twcspeechdll@4"

 

c++ Header file (H):

extern "C" __declspec(dllexport) int __stdcall twcspeechdll(LPCWSTR argv);

 

C++ Code file (cpp):

extern "C" int __stdcall twcspeechdll(LPCWSTR argv)

{

    ISpVoice* pVoice = NULL;

    string temp;

    string space = " ";

    LPCWSTR text;

    std::wstring stemp;

 

//======================

 

    if (FAILED(::CoInitialize(NULL)))

        return -1;

 

    stemp = argv;

   string lstr(stemp.begin(), stemp.end() );

 

// trimstring is a function I wrote to trim the passed in string

   trimString(lstr, space);

 

   if (lstr.length() <= 0)

       return -2;

 

   text = stemp.c_str() ;

 

   HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&pVoice);

   if (SUCCEEDED(hr))

   {

       hr = pVoice->Speak(text, 0, NULL);

...

Comment
  1. jorge gonzalez
  2. Tuesday, 17 October 2023 12:49 PM UTC
This article would have really helped me but none of my searches turned it up. Thanks Arnd
  1. Helpful
  1. Roland Smith
  2. Tuesday, 17 October 2023 14:39 PM UTC
The W in LPCWSTR indicates Unicode strings. You have to remember that in PowerBuilder, string and char variables are Unicode (two bytes per character). In C++ a char is Ansi (one byte per character). An encoding mismatch is why you only got the first character. If your C++ DLL is Ansi, you must add ;Ansi to the Alias clause so that the PB Runtime will convert the between the two encodings.
  1. Helpful 1
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.