Jump to content
JackT

I'm trying to call Application->Messagebox in a C++ Builder BPL

Recommended Posts

I'm trying to follow Dave Millington's code rage example from 2016  on how to use a Delphi abstract class to make an abstract class for use in a C++ Builder bpl library where you need to link to a lib file that is only available in C.

 

https://learndelphi.org/this-is-how-to-use-c-builder-to-extend-the-reach-of-delphi/

 

https://github.com/Embarcadero/CodeRage2016

 

I made a function called about in my concrete class called TBridge::About derived for a pure abstract delphi class which I can call sucessfully.

 

However when I do so Application->MessageBox falls over as it doesn't seem able to create a font resource or lacks resources.

 

I am assuming in am not linking some sort of necessary resource file into my C++ BPL ?

The question is I don't know which files I should be linking into the BPL to get it to display standard VCL dialogs. MessageDlg doesn't work either.

 

//---------------------------------------------------------------------------

#pragma hdrstop

#include "SolidBridge.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#include <windows.h>
#include <vcl.h>
#include <Vcl.Controls.hpp>
#include <Vcl.stdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Dialogs.hpp>
#include <Vcl.Dialogs.hpp>



void __fastcall TBridge::About()
{
	//Application->MessageBox('Hello world!','About',MB_OK);
	UnicodeString txt = "Hello world";
	Application->MessageBox(L"Hello world",L"About", MB_OKCANCEL);
	//MessageDlg(txt,mtInformation,TMsgDlgButtons() << mbOK,0);
}

TAbstractBridge* __stdcall SolidBridgeFactory()
{
  return (TAbstractBridge*)new TBridge();
}

 

 

 

ResData is passed in a zero so the first line

 

ResHash := VCL.Graphics.GetHashCode(ResData, ResDataSize); 

 

throws an exception

 

in VCL.Graphics

{$IF NOT DEFINED(CLR)}
function TResourceManager.AllocResource(const ResData): PResource;
var
  ResHash: Word;
  LOwner: TThreadID;
begin
  ResHash := Vcl.Graphics.GetHashCode(ResData, ResDataSize);
  Lock;
  try
    LOwner := TThread.CurrentThread.ThreadID;
    Result := ResList;
    while (Result <> nil) and ((Result^.Owner <> LOwner) or (Result^.HashCode <> ResHash) or
      not CompareMem(@Result^.Data, @ResData, ResDataSize)) do
      Result := Result^.Next;
    if Result = nil then
    begin
      GetMem(Result, ResDataSize + ResInfoSize);
      with Result^ do
      begin
        Next := ResList;
        RefCount := 0;
        Handle := TResData(ResData).Handle;
        HashCode := ResHash;
        Owner := LOwner;
        Move(ResData, Data, ResDataSize);
      end;
      ResList := Result;
    end;
    Inc(Result^.RefCount);
  finally
    Unlock;
  end;
end;
{$ENDIF}

 

 

 

 

Share this post


Link to post
On 6/23/2023 at 4:58 AM, JackT said:

I made a function called about in my concrete class called TBridge::About derived for a pure abstract delphi class which I can call sucessfully.

Can you please show the code in SolidBridge.h, as well as the Delphi code it is based on?

On 6/23/2023 at 4:58 AM, JackT said:

However when I do so Application->MessageBox falls over as it doesn't seem able to create a font resource or lacks resources.

What do you mean, exactly?  VCL's TApplication::MessageBox() method simply calls the Win32 API MessageBox() function, using either TApplication::ActiveFormHandle or TApplication::Handle as the owner window for the dialog.  So, it does not use any VCL font resources.

Share this post


Link to post
//---------------------------------------------------------------------------

#ifndef SolidBridgeH
#define SolidBridgeH
//---------------------------------------------------------------------------
#endif

#include <LoftyBridge.hpp>
#include <System.Classes.hpp>
#include <System.Generics.Collections.hpp>
#include <System.SysUtils.hpp>

class TBridgeException:Exception
{
private:
protected:
public:
};


class TBridge:TAbstractBridge
{
private:
TList *ModuleList;
void CleanUpModuleList();
protected:

public:

TBridge();
//~TBridge();
void  __fastcall About();
void  __fastcall ReQuery();
int __fastcall ModuleCount();
bool __fastcall GetModuleData(int Index, RModuleData &pModuleData);
bool __fastcall GetModuleChannels(int Index, System::Classes::TStringList* &pSL);
bool __fastcall GetSafeModuleChannelList(char * pBuffer,int Index, int MaxLen);
void __fastcall Finalize();
};

typedef TBridge *pTBridge;
typedef TList__1<TBridge> TBridgeList;
typedef TBridgeList *pTBridgeList;

extern "C" __declspec(dllexport) TAbstractBridge* __stdcall SolidBridgeFactory();
// CodeGear C++Builder
// Copyright (c) 1995, 2022 by Embarcadero Technologies, Inc.
// All rights reserved

// (DO NOT EDIT: machine generated header) 'LoftyBridge.pas' rev: 35.00 (Windows)

#ifndef LoftybridgeHPP
#define LoftybridgeHPP

#pragma delphiheader begin
#pragma option push
#pragma option -w-      // All warnings off
#pragma option -Vx      // Zero-length empty class member 
#pragma pack(push,8)
#include <System.hpp>
#include <SysInit.hpp>
#include <System.Classes.hpp>

//-- user supplied -----------------------------------------------------------

namespace Loftybridge
{
//-- forward type declarations -----------------------------------------------
struct RModuleData;
class DELPHICLASS TCppBuilderObject;
class DELPHICLASS TAbstractBridge;
//-- type declarations -------------------------------------------------------
typedef System::StaticArray<char, 256> ShortAnsiString;

struct DECLSPEC_DRECORD RModuleData
{
public:
	ShortAnsiString name;
	ShortAnsiString product_type;
	bool not_chassied;
	unsigned SerialNo;
	unsigned SlotNo;
};


typedef RModuleData *pModuleData;

typedef System::Classes::TStringList* *pTStringList;

#pragma pack(push,4)
class PASCALIMPLEMENTATION TCppBuilderObject : public System::TObject
{
	typedef System::TObject inherited;
	
public:
	virtual void __fastcall Finalize() = 0 ;
	HIDESBASE void __fastcall Free();
public:
	/* TObject.Create */ inline __fastcall TCppBuilderObject() : System::TObject() { }
	/* TObject.Destroy */ inline __fastcall virtual ~TCppBuilderObject() { }
	
};

#pragma pack(pop)

#pragma pack(push,4)
class PASCALIMPLEMENTATION TAbstractBridge : public TCppBuilderObject
{
	typedef TCppBuilderObject inherited;
	
public:
	virtual void __fastcall About() = 0 ;
	virtual void __fastcall ReQuery() = 0 ;
	virtual int __fastcall ModuleCount() = 0 ;
	virtual bool __fastcall GetModuleData(int Index, RModuleData &pModuleData) = 0 ;
	virtual bool __fastcall GetModuleChannels(int Index, System::Classes::TStringList* &pSL) = 0 ;
	virtual bool __fastcall GetSafeModuleChannelList(char * pBuffer, int Index, int MaxLen) = 0 ;
public:
	/* TObject.Create */ inline __fastcall TAbstractBridge() : TCppBuilderObject() { }
	/* TObject.Destroy */ inline __fastcall virtual ~TAbstractBridge() { }
	
};

#pragma pack(pop)

//-- var, const, procedure ---------------------------------------------------
static const System::Int8 NI_MODULE = System::Int8(0x0);
static const System::Int8 NI_CHANNEL = System::Int8(0x1);
static const System::Word STRING_SIZE = System::Word(0x100);
extern "C" TAbstractBridge* __stdcall SolidBridgeFactory();
extern DELPHI_PACKAGE TAbstractBridge* __stdcall BridgeFactory();
}	/* namespace Loftybridge */
#if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_LOFTYBRIDGE)
using namespace Loftybridge;
#endif
#pragma pack(pop)
#pragma option pop

#pragma delphiheader end.
//-- end unit ----------------------------------------------------------------
#endif	// LoftybridgeHPP
unit LoftyBridge;

{WARNING THIS UNIT MAY CAUSE MEMORY ISSUES
IT IS AN INTERFACE FOR NIBridge writen in C++ Builder
Try ShareMem in the uses clause
USE SAFE FUNCTIONS WITH NO ShareMem
}

interface

uses Classes;

const
NI_MODULE = 0;
NI_CHANNEL = 1;

const STRING_SIZE= 256;
type ShortAnsiString = array[1..256] of ansichar;

type RModuleData = record
  name:ShortAnsiString;
  product_type:ShortAnsiString;
  not_chassied:boolean;
  SerialNo:uInt32;
  SlotNo:uInt32;
end;

type pModuleData = ^RModuleData;
type pTStringList = ^TStringList;

Type TCppBuilderObject = class
private
protected

public
procedure Finalize();virtual;abstract;
procedure Free();
end;

type TAbstractBridge = class(TCppBuilderObject)
private
protected
//procedure Finalize();virtual;abstract;
public
procedure About();virtual;abstract;
procedure ReQuery();virtual;abstract;
function ModuleCount():integer;virtual;abstract;
function GetModuleData(Index:integer; var pModuleData:RModuleData):boolean;virtual;abstract;
function GetModuleChannels(Index:integer;var pSL:TStringList):boolean;virtual;abstract;
function GetSafeModuleChannelList(pBuffer:PAnsiChar;Index:Integer;MaxLen:Integer):boolean;virtual;abstract;
end;
{$IFDEF TEST_BRIDGE}
function SolidBridgeFactory():TAbstractBridge;stdCall;external 'C:\CBuild\NIBridge.bpl';
{$ELSE}
{$IFDEF WIN32}
function SolidBridgeFactory():TAbstractBridge;stdCall;external 'NIBridge.bpl';
{$ENDIF}
{$IFDEF WIN64}
function SolidBridgeFactory():TAbstractBridge;stdCall;external 'NIBridge.bpl';
{$ENDIF}
{$ENDIF}
function BridgeFactory():TAbstractBridge;stdCall
implementation

function BridgeFactory():TAbstractBridge;stdCall
begin
  result := nil;
end;
{ TAbstractBridge }


{ TCppBuilderObject }

procedure TCppBuilderObject.Free;
begin
Finalize();
inherited;
end;

{ TAbstractBridge }


end.

 

Share this post


Link to post

I'm generally having problems using VCL objects in a Win32 C++ Builder command line programs. I am also having trouble with TXMLDocument and IXMLNODE. I can use TXMLDocument in a VCL builder application and Delphi without a problem but as soon as I try to use it in a C++ Builder command line program I get silent failures

 

THIS CODE IN IT'S OWN THREAD CRASHES

 

 TXMLDocument * pMEGConfig = NULL;

    TDOMVendor *vendor;

    if(CoInitializeEx(0,COINIT_SPEED_OVER_MEMORY) != S_OK)
    {
        return;
    }

  pMEGConfig = new TXMLDocument(NULL);

 

            vendor = DOMVendors->Find("MSXML");

            if( vendor == NULL) {
                throw new Exception("vendor MSXML XML wasn't found'");
            }
            if(pMEGConfig->Active)
            {
                pMEGConfig->Active = false;
            }

            pMEGConfig->DOMVendor = vendor;
            pMEGConfig->LoadFromFile("An XML FILE");
            pMEGConfig->Active = true;

            IXMLNode * ROOT = pMEGConfig->DocumentElement;  //THIS LINE CAUSE THE THREAD AND  DEBUGGER TO EXIT IMMEDIAETLY AND SILENTLY

 

I am assuming I am some how not linking to the correct lib files and or my compiler / linker settings are wrong

 

 

Share this post


Link to post
21 hours ago, JackT said:

I am also having trouble with TXMLDocument and IXMLNODE. I can use TXMLDocument in a VCL builder application and Delphi without a problem but as soon as I try to use it in a C++ Builder command line program I get silent failures

Did you read the documentation?

https://docwiki.embarcadero.com/Libraries/en/Xml.XMLDoc.TXMLDocument

Quote

When TXMLDocument is created without an Owner, it behaves like an interfaced object. That is, when all references to its interface are released, the TXMLDocument instance is automatically freed. When TXMLDocument is created with an Owner, however, it behaves like any other component, and is freed by its Owner. When you add a TXMLDocument component from the component palette to a form or data module, it is automatically created with an Owner. When the TXMLDocument component is created using the global LoadXMLDocument function (or by a function that the XML Data Binding wizard generates to return the root node of the document), the function creates a TXMLDocument instance without an Owner.

You are creating the TXMLDocument without an Owner, so it behaves like an interfaced object, but you are not treating it as an interfaced object.  You need to change your pMEGConfig variable from being a TXMLDocument* raw pointer to being a _di_IXMLDocument smart pointer that manages the interface's reference count.

 

You need to do the same for your IXMLNode* raw pointers, too.  Use _di_IXMLNode instead.

 

Also, because they are reference counted objects, make sure they go out of scope (or are explicitly Release() 'ed) before you call CoUninitialize().

 

Also, do not 'new' an Exception that you are going to 'throw'.  Throw it by value rather than by pointer.  This is the one and only place where Delphi-style classes (ie, classes derived from TObject) are allowed to be created without 'new' in C++Builder.

 

Try this:

if (FAILED(CoInitializeEx(0, COINIT_SPEED_OVER_MEMORY))) {
    return;
}
try {
    TDOMVendor *vendor = DOMVendors->Find(_D("MSXML"));
    if (vendor == NULL) {
        throw Exception(_D("vendor MSXML XML wasn't found'"));
    }

    _di_IXMLDocument pMEGConfig = new TXMLDocument(NULL);
    // or: _di_IXMLDocument pMEGConfig = NewXMLDocument();

    pMEGConfig->DOMVendor = vendor;
    pMEGConfig->LoadFromFile(_D("An XML FILE"));
    pMEGConfig->Active = true;

    _di_IXMLNode ROOT = pMEGConfig->DocumentElement;
    ...
}
__finally {
    CoUninitialize();
}

Alternatively:

if (FAILED(CoInitializeEx(0, COINIT_SPEED_OVER_MEMORY))) {
    return;
}
try {
    DefaultDOMVendor = _D("MSXML");
    _di_IXMLDocument pMEGConfig = LoadXMLDocument(_D("An XML FILE"));
    _di_IXMLNode ROOT = pMEGConfig->DocumentElement;
    ...
}
__finally {
    CoUninitialize();
}

 

Edited by Remy Lebeau

Share this post


Link to post

Thanks Remy,

                       Thanks for taking the time to reply to my questions.

                      I haven't done a lot of C++ Builder programming so I didn't realise I had to use smart pointers for interfaces and I didn't know I wasn't supposed to throw New exception objects. I actually switched out my code to use the microsoft xmllite library in the mean time, but at least in the future I will know how to properly handle interfaces in C++ builder. I still have a few grey areas how memory clean up happens in C++ Builder such as should I be setting dynamic arrays to NULL to destroy them or does the C++ Compiler handle it. For example will WSA get destroyed correctly when it goes out of scope or do I need to do something else ?

 

         typedef DynamicArray<WideString> DynArrayWideString;

        DynArrayWideString WSA;
            WSA.Length = 4;
            WSA[0] = "A STRING";
            WSA[1] = "ANOTHER STRING";
            WSA[2] = "STRING 3";
            WSA[3] = "END OF LIST";

Share this post


Link to post
11 hours ago, JackT said:

I didn't realise I had to use smart pointers for interfaces

Next time, read the documentation, and also just look at the declarations (ie, the DocumentElement property returns an _di_IXMLNode, not an IXMLNode*).

11 hours ago, JackT said:

should I be setting dynamic arrays to NULL to destroy them

Typically no, although you can do so manually if you want to, ie, to free the array's memory when you want to reuse the variable for a new array.

11 hours ago, JackT said:

or does the C++ Compiler handle it

Yes.  The array's memory is freed when the object goes out of scope, or is reassigned.  Just any any other good RAII-style class does.

11 hours ago, JackT said:

For example will WSA get destroyed correctly when it goes out of scope

Yes

11 hours ago, JackT said:

or do I need to do something else ?

No

Share this post


Link to post

Thanks for the advice - much appreciated. I think I just assumed that the pointer for interfaces were going to be the same sorts in Delphi.

Share this post


Link to post
1 hour ago, JackT said:

I think I just assumed that the pointer for interfaces were going to be the same sorts in Delphi.

They are.  But Delphi automatically handles reference counting for interfaces, whereas C++ does not, hence the use of _di_... smart pointers on the C++ side to mimic the Delphi behavior.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×