On my way into the "C++ heaven" (oh god) I ran into a problem:
I tried to send a PB Type from a PB-program as an argument to an export-function inside a C++ DLL.
So far I got it working, but access to the members inside the C++ Dll was always postponed.
I could overcome this by doing the following:
Normal practice in PB:
TYPE MyNewType
Var1 AS LONG
Var2 AS WORD
Var3 AS BYTE
END TYPE
Adding a dummy-member in C++
struct MyNewStruct
{
signed int dummy; //added !!!!
signed int Var1;
short int Var2;
unsigned _int8 Var3;
};
This solves the problem for the moment, but it is not a correct solution???
Thanks for help,
Heinz Grandjean
What about this
struct MyNewType {
LONG var1;
WORD var2;
BYTE var3;
};
You need to set the structure packing value. PowerBASIC uses 1-byte packing by default and the C dialects can vary (4 bytes for SDK and MS VC++).
If using Microsoft VC++, use the following code:
#pragma pack (push)
#pragma pack(1)
// Structure defines
#pragma pack (pop)
That will force it to be identical to PB's TYPE/UNIONs
Because of this, it has become my practice to attempt, when I create my PowerBASIC UDTs, to create them in multiples of 8 or 16 bytes. To achieve this I'll often put dummy fillers at the end, just as shown above.
Note that a type such as this will work without problems in both C and PowerBASIC ...
struct MyStruct
{
short int iSp; // 2 bytes
short int iDbh; // 2 bytes
short int iHt; // 2 bytes
short int iCull; // 2 bytes
};
Type Tree
iSp As Integer
iDbh As Integer
iHt As Integer
iCull As Integer
End Type
... because both are 8 bytes in size, which is some sort of memory granulation boundary.
I believe there are some obscure compiler command line switches in C/C++ that can control this, but I've personally never fooled with them.
To be perfectly honest, padding structs or types with extra dummy variables can become useful for when application requirements change and extra variables are needed. Especially when the UDTs are used for templates of file storage into records.
Also, if you use C's sizeof operator on a struct which, for example, contains a four byte int, a 2 byte word, and a one byte char, the sizeof operator will return 8 bytes - not 7. I'm going to check that out quick .../ hold on ...
Yep.
#include <cstdio>
struct MyStruct
{
int iInteger; // 4
short int iShortInt; // 2
char c; // 1
};
int main()
{
printf("sizeof(MyStruct) = %d\n",sizeof(MyStruct));
getchar();
return 0;
}
//sizeof(MyStruct) = 8
PB did choose the VB path regarding padding. This is inconvenient when working with C++ libraries. Many of the TYPEs in the official PB includes are misaligned. I checked all the ones in my headers with the size given by Visual C and added filler members to keep the correct alignment. The problem is becoming more noticeable since the coming of 64-bit. With 32-bit, the norm was to use 4 bytes alignment, but many of the new APIs use QUADs and 8 bytes alignment.
Thank you for the replies.
Like Frederick Harris I tested the sizeof in C++.
When I use Kev Peels advice: #pragma pack(1) , I get 7!
Without that I get 8, like Frederick Harris has demonstrated.
But there is no progress to the problem, sorry.
When I want to see Var1 (set in PB to 15, I get a value of 1637928).
This seems to me like a sort of internal header or a pointer or anything in that direction.
When I want to see Var2 then I get Var1 and so on.
The complete PB test routine:
#COMPILE EXE
#DIM ALL
TYPE MyNewType
Var1 AS LONG
Var2 AS WORD
Var3 AS BYTE
END TYPE
DECLARE FUNCTION myTestFunction CDECL LIB "D:\ESTWGJ_Quell\C++\Betrieb_DLL\Debug\Betrieb_Dll.dll" ALIAS "Testfunction" (MyNewType) AS LONG
FUNCTION PBMAIN () AS LONG
'**************************
LOCAL MyNewT AS MyNewType
MyNewT.Var1 = 15
MyNewT.Var2 = 19
MyNewT.Var3 = 255
?STR$( myTestFunction(MyNewT))
END FUNCTION
The Counterpart in C++8; I use MS Visual Studio Express 2010
Definition of struct:
#pragma pack (push)
#pragma pack(1)
struct MyNewStruct
{
signed int Var1;
short int Var2;
unsigned _int8 Var3;
};
#pragma pack (pop)
declare:
extern "C"
{
__declspec(dllexport) long Testfunction(MyNewStruct);
}
cpp:
//Betrieb_DLL_Main.cpp
#include <stdio.h>
#include <windows.h>
#include "D:\ESTWGJ_Quell\C++\Header\Dat_Datenheader.h"
#include "Betrieb_Header.h"
long Testfunction (MyNewStruct newT)
{
return newT.Var1;
}
By the way, the C++ compiler doesn't allow "word or byte".
Thanks again
Heinz Grandjean
It looks correct to me the correct size is 7 (4+2+1).
IMO the full code was not provided so I am thinking the struct is not being initialized to zero and you are seeing a random value. Try memset() after declaring the struct..
How does C++ handle arrays of structs?
For instance the 24 bit RGB pixels in a BMP.
Quote
How does C++ handle arrays of structs?
Just off the top of my head without testing a specific instance, my guess Charles would be that an odd sized struct, e.g., the seven byte one or your three byte example, would get bumped up to the nearest 4 byte boundary. And the offset of each instance of the struct in memory would be sizeof(struct) bytes from the initial allocation address. So I'm guessing if you had a three byte struct, i.e., three chars, each struct instance would still occupy four bytes. Should be easy to test, but that's where I'd put my money.
Well, I maybe found it.
You have to use pointers in the C++ DLL.
__declspec(dllexport) long Testfunction(MyNewStruct*);
long Testfunction (MyNewStruct* newT)
{
MyNewStruct newT1 = *newT;
return newT1.Var2;
}
I don't know this to be the ultimate solution, but now it does the job!
I even can remove the #pragma pack...
Thank you for your participation.
Heinz Grandjean
Well, if this is your PowerBASIC declare ...
DECLARE FUNCTION myTestFunction CDECL LIB "D:\ESTWGJ_Quell\C++\Betrieb_DLL\Debug\Betrieb_Dll.dll" ALIAS "Testfunction" (MyNewType) AS LONG
... then you are passing the address of an instance of MyNewType.
It also works with:
DECLARE FUNCTION myTestFunction CDECL LIB "D:\ESTWGJ_Quell\C++\Betrieb_DLL\Debug\Betrieb_Dll.dll" ALIAS "Testfunction" (BYVAL MyNewType PTR) AS LONG
No difference as far as I can oversee it.
Heinz Grandjean
Here's my attempt to do it ...
In C++
#include <cstdio>
struct MyNewType
{
int var1;
short var2;
unsigned char var3;
};
extern "C" int __declspec(dllexport) MyTestFunction(MyNewType* mnt)
{
printf("mnt->var1=%d\n",mnt->var1);
printf("mnt->var2=%d\n",mnt->var2);
printf("mnt->var3=%d\n",mnt->var3);
return 1;
}
And PB CC Client ...
#Compile Exe
#Dim All
Type MyNewType
Var1 AS LONG
Var2 AS WORD
Var3 AS BYTE
End Type
DECLARE FUNCTION MyTestFunction CDECL LIB "Heinz1.dll" ALIAS "MyTestFunction" (MyNewType) AS LONG
Function PBMain() As Long
Local MyType As MyNewType
MyType.var1=15
MyType.var2=19
MyType.var3=255
Call MyTestFunction(MyType)
Waitkey$
PBMain=0
End Function
' Output
' ===========
' mnt->var1=15
' mnt->var2=19
' mnt->var3=255
So what is going on here in your last example Heinz is that within TestFunction the passed in pointer to a MyNewStruct is being used to initialize a new 2nd local instance of a MyNewStruct with the one passed in through the parameter...
__declspec(dllexport) long Testfunction(MyNewStruct*);
long Testfunction (MyNewStruct* newT)
{
MyNewStruct newT1 = *newT; // initialize newT1 with what is stored at newT, i.e., *newT
return newT1.Var2;
}
And being that newT1 is a local instance rather than a pointer to a local instance, one can use the dot operator to return the MyNewStruct::Var2 member.
However, by using the '->' pointer notation as seen in my example above, you can directly reference the MyNewStruct::Var2 member, i.e.,
return newT->Var2;
Yes, Frederick, you are right
I put the local instance in to overcome conflicts with "return"., I got because I'm learning.
Beside the "->", I found another way:
(*newT).Var1. This does the job too.
And it demonstrates how simple and elegant our PB is ( hopefully not "was").
But now it's time to by a tutorial book to learn C++ systematical.
Thanks Frederick,
Heinz Grandjean
Quote
But now it's time to by a tutorial book to learn C++ systematical.
That's a tough one. To my way of thinking C++ is about 100 times more difficult than C. I think there are a lot of folks who work with C that after a few years would feel fairly comfortable with saying, "I'm pretty much an expert at C". But I'm not sure it works that way with C++. I think a lot of folks who have worked with it for a lot of years would feel rather uncomfortable with proclaiming themselves to be "C++ experts". At least the honest ones.
In your example I was tempted to say you could specify the C++ export function as passing a reference to a MyStructType, that is MyStructType&, as opposed to a pointer to a MyStructType, that is, MyStructType*. By doing that you could use the dot operator to return any of the struct members, rather than the '->'. However, I'm thinking that would only work for C++ clients using the same development product, due to the fact without the extern_c C++ would mangle the function name, making it unrecognizable to a PowerBASIC client. I could be wrong about that; I'd have to test it. C syntax doesn't recognize the '&' symbol as a pass by reference mechanism. C only knows passing pointers. I guess one could say that's just another example of something C++ stole from basic syntax, along with string objects, etc., etc! :)
The reason going to MS C++ is the ability to have a visual designer.
But there is another baby called C#.
Am I right assuming the inoperability with PB because it is completely OOP?
Heinz Grandjean
I found that when you have to pass structures between different languages, the easiest way is to use pointer, then ressort on the low level API MoveMemory to copy from one memory address to another.
Also memset could be handy to first clear the memory bloc you want to copy in.
The way memory was handled in C still works well with any language i know.
And when i write a DLL that will be used with PB or WinDev i am always using _stdcall to avoid the use of the cdecl close.
I fear, I' m not lucky( to inexperienced) using the _stdcall...
BP declare:
DECLARE FUNCTION myTestFunction STDCALL LIB "D:\ESTWGJ_Quell\C++\Betrieb_DLL\Release\Betrieb_Dll.dll" ALIAS "Testfunction" (BYVAL MyNewType PTR, BYVAL LONG PTR) AS LONG
C++ declare
__declspec(dllexport) long _stdcall Testfunction(MyNewStruct*, int*);
C++ Function
long __stdcall Testfunction (
MyNewStruct* newT,
int* count
)
{
long msg = 0;
switch (*count)
{
case 1:
msg = (*newT).Var1;
break;
case 2:
msg = newT-> Var2;
break;
case 3:
msg = newT-> Var3;
break;
};
return msg;
}
When I put the _stdcall out and set in PB CDECL then it works.
With the _stdcall in I get a "entry point not found" - message
Heinz Grandjean
This is because you are using 32-bit, then
the function names are decorated, to solve this problem
you have to use a .def file.
And you must setup your project properties like these:
(http://www.zapsolution.com/pictures/Heinz1.jpg)
(http://www.zapsolution.com/pictures/Heinz2.jpg)
And here is an example of the .def file i am using with
WinLIFT32.def.
QuoteLIBRARY WinLIFT32.dll
EXPORTS
skPopupOwner
skCaptionFont
skFont
skFontBold
skFontDlg
skCaptionFontPlus
skFontPlus
skFontBoldPlus
skFontDlgPlus
skSkinDisable
skSetZorder
skGetHdcMemBmp
skARGB
ComputeAspect
skChildOffset
skSetLabelFont
MinTrackSizeX
MinTrackSizeY
skVersion
skInitEngine
skSetAnchorCtrl
skUsingDWM
skSkinFolder
skButImage
skAuthor
skBorder
skSkinChildCtrl
skSkinEnable
skRedrawMenuBar
skGetMenu
skMenuContainer
skSkinWindowUpdate
skSkinWindow
skCreateDW
skDestroyDW
skDialogAlert
skDialogError
skDialogInfo
skDialogYesNo
skDialogInput
skButtonImage
skPushButtonImage
skSetToolTipText
skGetToolTipText
skCreateToolTip
skRemoveToolTip
skClockCtrl
skComputeAngleFromPoint
skGaugeSetMinMax
skGaugeGetMinMax
skGaugeSetPos
skGaugeGetPos
skKnobGauge
skStaticImage
skGetDWMregion
If you had used the extern "C" in your C++ exported function, that should have prevented the C++ compiler from decorating/mangling the function name, which then makes it unrecognizable to other languages.
No, that doesn't work by me with 32-bit (VS2010), however it works in 64-bit (without using a .def file).
Here is how my extern functions are declared in the WinLIFT project:
Quote#include <windows.h>
//using namespace std;
#define C_IMPORT extern "C" __declspec(dllimport)
#define C_EXPORT extern "C" __declspec(dllexport)
const int ANCHOR_NONE = 0;
const int ANCHOR_LEFT = ANCHOR_NONE;
const int ANCHOR_WIDTH = 1;
const int ANCHOR_RIGHT = 2;
const int ANCHOR_CENTER_HORZ = 3;
const int ANCHOR_HEIGHT = 4;
const int ANCHOR_HEIGHT_WIDTH = 5;
const int ANCHOR_HEIGHT_RIGHT = 6;
const int ANCHOR_BOTTOM = 7;
const int ANCHOR_BOTTOM_WIDTH = 8;
const int ANCHOR_BOTTOM_RIGHT = 9;
const int ANCHOR_CENTER_HORZ_BOTTOM = 10;
const int ANCHOR_CENTER_VERT = 11;
const int ANCHOR_CENTER_VERT_RIGHT = 12;
const int ANCHOR_CENTER = 13;
C_EXPORT HWND skPopupOwner (IN HWND hWnd);
C_EXPORT HFONT skCaptionFont ();
C_EXPORT HFONT skFont ();
C_EXPORT HFONT skFontBold ();
C_EXPORT HFONT skFontDlg ();
C_EXPORT LONG_PTR skCaptionFontPlus ();
C_EXPORT LONG_PTR skFontPlus ();
C_EXPORT LONG_PTR skFontBoldPlus ();
C_EXPORT LONG_PTR skFontDlgPlus ();
C_EXPORT void skSkinDisable (IN HWND hWnd);
C_EXPORT void skSetZorder (IN HWND hWnd, IN HWND UseOrder);
C_EXPORT HDC skGetHdcMemBmp (IN HWND hOwner);
C_EXPORT long skARGB (IN BYTE A, IN BYTE R, IN BYTE G, IN BYTE B);
C_EXPORT void ComputeAspect (IN long xPic, IN long yPic, IN long xCell, IN long yCell, OUT long &xP, OUT long &yP, OUT long &xS, OUT long &yS);
C_EXPORT void skChildOffset (IN HWND hWnd, OUT long &ofX, OUT long &ofY);
C_EXPORT void skSetLabelFont (IN HWND hCtrl, IN WCHAR* zFontName, IN WORD nFontSize, IN long ARGBcolor, IN WORD nFontStyle);
C_EXPORT long MinTrackSizeX (IN long SizeX);
C_EXPORT long MinTrackSizeY (IN long SizeY);
C_EXPORT WCHAR* skVersion ();
C_EXPORT long skInitEngine (IN WCHAR* zSkinFile, IN WCHAR* zUserKey);
C_EXPORT long skSetAnchorCtrl (IN HWND hWnd, IN long AnchorMode);
C_EXPORT long skUsingDWM ();
C_EXPORT WCHAR* skSkinFolder ();
C_EXPORT HWND skButImage (IN HWND hOwner, OUT HBITMAP hBitmap, IN long xLeft, IN long yLeft, IN long ButID);
C_EXPORT WCHAR* skAuthor ();
C_EXPORT HWND skBorder (IN HWND hWnd, IN long x, IN long y, IN long xW, IN long yH, IN long nID, IN long nBorder);
C_EXPORT void skSkinChildCtrl (IN HWND hWnd, IN long RedrawFlag);
C_EXPORT void skSkinEnable (IN HWND hWnd);
C_EXPORT void skRedrawMenuBar(IN HWND hWnd);
C_EXPORT HMENU skGetMenu (IN HWND hWnd);
C_EXPORT HWND skMenuContainer (IN HWND hWnd);
C_EXPORT void skSkinWindowUpdate (IN HWND hWnd, IN long RedrawFlag);
C_EXPORT long skSkinWindow (IN HWND hWnd, WCHAR* zSysButTip);
C_EXPORT HWND skCreateDW (IN HWND hParent);
C_EXPORT void skDestroyDW (OUT HWND &hDW);
C_EXPORT long skDialogAlert (IN WCHAR* zCaption, IN WCHAR* zMessage, IN WCHAR* zButton);
C_EXPORT long skDialogError (IN WCHAR* zCaption, IN WCHAR* zMessage, IN WCHAR* zButton);
C_EXPORT long skDialogInfo (IN WCHAR* zCaption, IN WCHAR* zMessage, IN WCHAR* zButton);
C_EXPORT long skDialogYesNo (IN WCHAR* zCaption, IN WCHAR* zMessage, IN WCHAR* zButton);
C_EXPORT WCHAR* skDialogInput (IN WCHAR* zCaption, IN WCHAR* zMessage, IN WCHAR* zButton);
C_EXPORT HWND skButtonImage (IN HWND hOwner, IN WCHAR* zFullpathImageName, IN long x, IN long y, IN long ButID, IN long StateMax);
C_EXPORT HWND skPushButtonImage (IN HWND hOwner, IN WCHAR* zFullpathImageName, IN WCHAR* zLabel, IN long x, IN long y, IN long xW, IN long yH, IN long ButID, IN long Alignment);
C_EXPORT void skSetToolTipText (IN HWND hObj, IN WCHAR* zText);
C_EXPORT void skGetToolTipText (IN HWND hObj, IN WCHAR* zLabel);
C_EXPORT HWND skCreateToolTip (IN HWND hObj, IN WCHAR* zText);
C_EXPORT void skRemoveToolTip (IN HWND hObj);
C_EXPORT HWND skClockCtrl (IN HWND hOwner, IN WCHAR* zFullpathImageName, IN long x, IN long y, IN long w, IN long h, IN long nID, IN long ARGB1, IN long ARGB2, IN long GMT);
C_EXPORT long skComputeAngleFromPoint(IN HWND hWnd, IN long mX, IN long mY);
C_EXPORT void skGaugeSetMinMax (IN HWND hCtrl, IN long nMin, IN long nMax);
C_EXPORT void skGaugeGetMinMax (IN HWND hCtrl, OUT long &nMin, OUT long &nMax);
C_EXPORT void skGaugeSetPos (IN HWND hCtrl, IN long nPos, IN long RedrawFlag);
C_EXPORT long skGaugeGetPos (IN HWND hCtrl);
C_EXPORT HWND skKnobGauge (IN HWND hOwner, IN WCHAR* zFullpathImageName, IN long x, IN long y, IN long w, IN long h, IN long ButID, IN long MinValue, IN long MaxValue, IN long UsePos, IN long StateMax);
C_EXPORT HWND skStaticImage (IN HWND hOwner, WCHAR* zFullpathImageName, IN long x, IN long y, IN long w, IN long h, IN long nID);
C_EXPORT HWND skGetDWMregion (IN HWND hWnd);
Without using the .def file, the function name is always mangled by me in 32-bit mode using the __stdcall flag.
Thanks for all that information.
I have done this (extern "C") ; it does not help...
I have set all the settings Patrice has shown, but I have Problems to set the *.def-file in the right place.
Am I right: It is not an include file with #Include?
Where can I find it in MS C++ Express?
How is it linked to the other files?
Trying to fill it as an include -file inside the Ide I get error-messages, starting with the key-word.
Heinz Grandjean
Feeling a little bit guilty of making such a rumor...
But maybe it is interesting for someone else??
You can put the .def file inside of a folder that is within the Windows PATH.
On my computer i have added my folder D:\VS2010 into my path and all my .def files are in there.
Thank you, Patrice!
Could handle the global __stdcall(/Gz) entry and the *.def - file.
Now it does the job.
Interesting to see there is no longer need for these declares??
define C_EXPORT extern "C" __declspec(dllexport)
C_EXPORT long Testfunction( MyNewStruct*, int* );
C_EXPORT long Test2 (int*);
It works with or without them.
Heinz Grandjean
I use VC++ from the Win7 SDK and compile using batch files.
To get undecorated __stdcall in 32bit I use two calls.
the first compile/link creates a .def file which we use in a
the second call to add undecorated function names to the dll.
Where %F% is the file to compile and %VRES% is the RES file.
cl.exe %F%.cpp %VRES% /EHsc /MT /link /DLL /OUT:"%F%.dll"
cl.exe /LD "%F%.obj" %VRES% "%F%.def"
James
Heinz
QuoteInteresting to see there is no longer need for these declares??
You will need it, to create a 64-bit version from the same source code.
...