I have semi-successfully translated reinit.pas to C++ to use it in my project. The part where int __fastcall LoadNewResourceModule(LCID locale); is called works fine, in fact I can even call it prior to Application->Initialize() and it will load the proper language at startup. However, the part that calls void __fastcall ReinitializeForms(void); does not work, and gives a runtime error:
Resource TControl not found
Here is the dirty version of .cpp, and .h, I've yet to clean it up and comment it properly, at this point the thing just has to work fully. Please help me sort this out.
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <SysInit.hpp>
#include <Vcl.Forms.hpp>
#pragma hdrstop
#include "reinit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
class TAsInheritedReader : public TReader
{
public:
void __fastcall ReadPrefix(TFilerFlags &_flags, int &_aChildPos);
inline __fastcall TAsInheritedReader(TStream* Stream, int BufSize) : TReader(Stream, BufSize) {}
};
//---------------------------------------------------------------------------
void __fastcall TAsInheritedReader::ReadPrefix(TFilerFlags &_flags, int &_aChildPos)
{
TReader::ReadPrefix(_flags, _aChildPos);
_flags = _flags << ffInherited;
}
//---------------------------------------------------------------------------
int __fastcall SetResourceHInstance(int _newInstance)
{
PLibModule CurModule = LibModuleList;
while(CurModule != NULL) {
if (reinterpret_cast<void*>(CurModule->Instance) == HInstance) {
if (CurModule->ResInstance != CurModule->Instance) {
FreeLibrary(reinterpret_cast<HMODULE>(CurModule->ResInstance));
CurModule->ResInstance = _newInstance;
return _newInstance;
}
CurModule = CurModule->Next;
}
}
return 0;
}
//---------------------------------------------------------------------------
int __fastcall LoadNewResourceModule(LCID locale)
{
wchar_t FileName[260];
PChar P;
wchar_t LocaleName[4];
int NewInst = 0;
GetModuleFileName(HInstance, FileName, sizeof(FileName));
GetLocaleInfo(locale, LOCALE_SABBREVLANGNAME, LocaleName, sizeof(LocaleName));
P = PChar(&FileName) + lstrlen(FileName);
while((*P != L'.') && (P != reinterpret_cast<PChar>(&FileName))) {
--P;
}
if (P != reinterpret_cast<PChar>(&FileName)) {
++P;
if (LocaleName[0] != L'\0') {
NewInst = reinterpret_cast<int>(LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE));
if (NewInst == 0) {
LocaleName[2] = L'\0';
lstrcpy(P, LocaleName);
NewInst = reinterpret_cast<int>(LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE));
}
}
}
if (NewInst != 0) {
return SetResourceHInstance(NewInst);
}
return 0;
}
//---------------------------------------------------------------------------
bool __fastcall InternalReloadComponentRes(const String ResName, THandle HInst, TComponent* Instance)
{
//TResourceStream* ResStream = new TResourceStream;
//TAsInheritedReader* AsInheritedReader = new TAsInheritedReader;
if (HInst == 0) {
HInst = reinterpret_cast<THandle>(HInstance);
}
THandle HRsrc = reinterpret_cast<THandle>(FindResource((HMODULE)HInst, (LPCWSTR)ResName.w_str(), (LPCWSTR)RT_RCDATA));
if(HRsrc != 0) {
return false;
}
/* THIS IS THE OFFENDING LINE OF CODE THAT THROWS EXCEPTION
I checked HInst, it is not 0...
ResName = "TControl"
and it throws exception here for some reason
saying resource tcontrol not found
*/
TResourceStream* ResStream = new TResourceStream(HInst, ResName, RT_RCDATA);
try {
TAsInheritedReader* AsInheritedReader = new TAsInheritedReader(ResStream, 4096);
try {
Instance = AsInheritedReader->ReadRootComponent(Instance);
} __finally {
delete AsInheritedReader;
}
}
__finally {
delete ResStream;
}
return true;
}
//---------------------------------------------------------------------------
bool __fastcall InitComponent(TClass ClassType)
{
if ((ClassType->ClassName() == "TComponent") || (ClassType->ClassName() == "RootAncestor")) {
return false;
}
InitComponent(ClassType->ClassParent());
return InternalReloadComponentRes(ClassType->ClassName(), FindResourceHInstance(FindClassHInstance(ClassType)), (TComponent*)&ClassType);
}
//---------------------------------------------------------------------------
bool __fastcall ReloadInheritedComponent(TComponent* Instance)
{
return InitComponent(Instance->ClassType());
}
//---------------------------------------------------------------------------
void __fastcall ReinitializeForms(void)
{
for(int i=0; i<Screen->FormCount-1; i++) {
ReloadInheritedComponent(Screen->Forms[i]);
}
}
#ifndef _reinit_h
#define _reinit_h
#include <windows.h>
extern int __fastcall LoadNewResourceModule(LCID locale);
extern void __fastcall ReinitializeForms(void);
#endif
You don't really need to translate the code at all. You can use Delphi .pas units as-is in C++Builder projects. Simply add the .pas file to your project and compile it, the IDE will generate a .hpp file that you can then #include in your C++ code.
In any case, your translation is not correct in many places.
For instance, the original code wasn't written with Unicode in mind, but you are using Unicode strings in your code. Expressions like sizeof(FileName) and sizeof(LocaleName) are the wrong buffer sizes to pass to the Win32 APIs being used, which can potentially allow buffer overflows. The code was clearly expecting BYTE-sized narrow characters, not WORD-sized wide characters.
It also looks like the original code was not written with 64-bit in mind, either. It is using 32-bit integers in places where 64-bit integers would be needed (for resource handles, etc).
So, the original code needs some updating to support modern systems properly.
But also, your translation of InitComponent() is wrong. It is using strings where the original code is using metaclass references instead, and it is passing the wrong value in the last parameter of InternalReloadComponentRes(), which you have not even declared correctly.
And also, your loop in ReinitializeForms() is skipping the last TForm in the Screen->Forms[] array.
Now, that all being said, try something more like this:
ReInit.h
#ifndef REINIT_H
#define REINIT_H
void __fastcall ReinitializeForms();
NativeUInt __fastcall LoadNewResourceModule(unsigned long Locale);
#endif
ReInit.cpp
#include "ReInit.h"
#include <Windows.hpp>
#include <SysInit.hpp>
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Forms.hpp>
#include <memory>
class TAsInheritedReader : public TReader
{
public:
void __fastcall ReadPrefix(TFilerFlags &Flags, int &AChildPos) override;
};
void __fastcall TAsInheritedReader::ReadPrefix(TFilerFlags &Flags, int &AChildPos)
{
TReader::ReadPrefix(Flags, AChildPos);
Flags << ffInherited;
}
NativeUInt __fastcall SetResourceHInstance(NativeUInt NewInstance)
{
PLibModule CurModule = LibModuleList;
while (CurModule)
{
if (CurModule->Instance == HInstance)
{
if (CurModule->ResInstance != CurModule->Instance)
::FreeLibrary(reinterpret_cast<HMODULE>(CurModule->ResInstance));
CurModule->ResInstance = NewInstance;
return NewInstance;
}
CurModule = CurModule->Next;
}
return 0;
}
NativeUInt __fastcall LoadNewResourceModule(unsigned long Locale)
{
WCHAR FileName[MAX_PATH+1] = {};
::GetModuleFileNameW(reinterpret_cast<HMODULE>(HInstance), FileName, MAX_PATH+1);
WCHAR LocaleName[5] = {};
::GetLocaleInfoW(Locale, LOCALE_SABBREVLANGNAME, LocaleName, 5);
LPWSTR P = &FileName[lstrlenW(FileName)];
while ((*P != L'.') && (P != FileName)) --P;
HMODULE NewInst = nullptr;
if (P != FileName)
{
++P;
if (LocaleName[0] != L'\0')
{
// Then look for a potential language/country translation
lstrcpyW(P, LocaleName);
NewInst = LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
if (!NewInst)
{
// Finally look for a language only translation
LocaleName[2] = L'\0';
lstrcpyW(P, LocaleName);
NewInst = LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
}
}
}
if (NewInst)
return SetResourceHInstance(reinterpret_cast<NativeUInt>(NewInst));
return 0;
}
bool __fastcall InternalReloadComponentRes(const UnicodeString &ResName, NativeUInt HInst, TComponent* &Instance)
{
// avoid possible EResNotFound exception
if (HInst == 0) HInst = HInstance;
HRSRC HRsrc = FindResourceW(reinterpret_cast<HMODULE>(HInst), ResName.c_str(), MAKEINTRESOURCEW(10)/*RT_RCDATA*/);
if (!HRsrc) return false;
auto ResStream = std::make_unique<TResourceStream>(HInst, ResName, MAKEINTRESOURCEW(10)/*RT_RCDATA*/);
auto AsInheritedReader = std::make_unique<TAsInheritedReader>(ResStream.get(), 4096);
Instance = AsInheritedReader->ReadRootComponent(Instance);
return true;
}
bool __fastcall ReloadInheritedComponent(TComponent *Instance, TClass RootAncestor)
{
const auto InitComponent = [&Instance,RootAncestor](TClass ClassType) -> bool
{
auto InitComponent_impl = [&Instance,RootAncestor](TClass ClassType, auto& InitComponent_ref) -> bool
{
if ((ClassType == __classid(TComponent)) || (ClassType == RootAncestor)) return false;
bool Result = InitComponent_ref(ClassType->ClassParent(), InitComponent_ref);
return InternalReloadComponentRes(ClassType->ClassName(), FindResourceHInstance(FindClassHInstance(ClassType)), Instance) || Result;
}
return InitComponent_impl(ClassType, InitComponent_impl);
}
return InitComponent(Instance->ClassType());
}
void __fastcall ReinitializeForms()
{
int Count = Screen->FormCount;
for(int i = 0; i < Count; ++i)
{
ReloadInheritedComponent(Screen->Forms[I], __classid(TForm));
}
}
Related
I am trying to programmatically remove a selected component from its parent container using the code below (I may be using found incorrectly, but that is not the problem, suggestions are welcome):
void __fastcall TScrollControlsListContainer::RemoveItem(TWinControl* ctrl)
{
// find control in vector containing TWinControl(s)
std::vector<FWinControl*>::iterator found = std::find_if(FWinControls.begin(), FWinControls.end(), IsCtrl(ctrl));
if(found != FWinControls.end())
{
Components->RemoveComponent(dynamic_cast<TComponent*>(found));
//[bcc32 Error] ScrollControlsListContainer.cpp(208): E2193 Too few parameters in call to '_fastcall TComponent::GetComponent(int)' Full parser context
FWinControls.erase(found);
}
}
I am confused about the error, as only one parameter is required according to the help file example below:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int I;
TComponent *Temp;
Memo1->Lines->Add("Components removed: ");
Form2->Memo1->Lines->Add("Components added: ");
for (I = ComponentCount - 1; I >= 0; I--)
{
Temp = Components[I];
// Move only the components that are not controls.
if (dynamic_cast<TControl *>(Temp) == NULL)
{
RemoveComponent(Temp);
Memo1->Lines->Add(Temp->Name);
Form2->InsertComponent(Temp);
Form2->Memo1->Lines->Add(Temp->Name);
}
}
}
What am I missing here?
My environment:
RadStudio 10.2 Tokyo (and also XE4)
I was implementing a copy property method to copy TShape properties.
Following is what I implemented:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// set Shape1 color to [clWhite]
Shape1->Brush->Color = clRed; // clWhite
Shape2->Brush->Color = clAqua;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::copyProperties(TControl *srcCtrl, TControl *dstCtrl)
{
// to Keep original names
String orgName_src = srcCtrl->Name;
String orgName_dst = dstCtrl->Name;
// copy properties
TMemoryStream *strm = new TMemoryStream;
Shape1->Name = L""; // to avoid source collision
try {
strm->WriteComponent(srcCtrl);
strm->Position = 0;
strm->ReadComponent(dstCtrl);
}
__finally
{
delete strm;
}
srcCtrl->Name = orgName_src;
dstCtrl->Name = orgName_dst;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
copyProperties((TControl *)Shape1, (TControl *)Shape2);
// shift to avoid position-overlapping
Shape2->Left = Shape1->Left + 150;
}
//---------------------------------------------------------------------------
The code seems work fine.
But there is a single case, in which the code does not work.
i.e. when the Brush->Color = clWhite for Shape1.
This bug? can be reproduced also for XE4.
I wonder why only the clWhite has this kind of bug? Other colors does not have this kind of bug.
There is no bug in the streaming. It is operating as designed. You are simply using it in a way that it is not intended for.
clWhite is the declared default value of the TBrush.Color property. The DFM streaming system does not stream properties that are currently set to their default values, unless those properties are declared as nodefault or stored=true. TBrush.Color is neither. So the current Brush.Color value will not be streamed when it is set to clWhite.
Consider using the RTTI system directly instead of using the DFM system to copy properties from one object to another. Then you can copy property values regardless of defaults, if you choose to do so. And you can opt to ignore the Name property without having to (re)store it each time.
For example:
#include <System.TypInfo.hpp>
void __fastcall TForm1::copyProperties(TControl *srcCtrl, TControl *dstCtrl)
{
PTypeInfo pDstTypeInfo = static_cast<PTypeInfo>(dstCtrl->ClassInfo());
PPropList srcPropList;
int srcPropCount = GetPropList(srcCtrl, srcPropList);
try
{
for (int i = 0; i < srcPropCount; ++i)
{
PPropInfo pSrcPropInfo = (*srcPropList)[i];
if (pSrcPropInfo->Name == "Name") continue;
PTypeInfo pSrcPropTypeInfo = *(pSrcPropInfo->PropType);
if (pSrcPropTypeInfo->Kind == tkClass)
{
PPropInfo pDstPropInfo = GetPropInfo(pDstTypeInfo, pSrcPropInfo->Name, TTypeKinds() << tkClass);
if (pDstPropInfo)
{
TPersistent *pDstObj = static_cast<TPersistent*>(GetObjectProp(dstCtrl, pDstPropInfo, __classid(TPersistent)));
if (pDstObj)
{
TPersistent *pSrcObj = static_cast<TPersistent*>(GetObjectProp(srcCtrl, pSrcPropInfo, __classid(TPersistent)));
pDstObj->Assign(pSrcObj);
}
}
}
else
{
PPropInfo pDstPropInfo = GetPropInfo(pDstTypeInfo, pSrcPropInfo->Name);
if (pDstPropInfo)
{
Variant value = GetPropValue(srcCtrl, pSrcPropInfo);
SetPropValue(dstCtrl, pDstPropInfo, value);
}
}
}
}
__finally
{
FreeMem(srcPropList);
}
}
Alternatively:
#include <System.Rtti.hpp>
void __fastcall TForm1::copyProperties(TControl *srcCtrl, TControl *dstCtrl)
{
TRttiContext ctx;
TRttiType *pSrcType = ctx.GetType(srcCtrl->ClassInfo());
TRttiType *pDstType = ctx.GetType(dstCtrl->ClassInfo());
DynamicArray<TRttiProperty*> srcProps = pSrcType->GetProperties();
for (int i = 0; i < srcProps.Length; ++i)
{
TRttiProperty *pSrcProp = srcProps[i];
if (pSrcProp->Name == L"Name") continue;
if (pSrcProp->PropertyType->TypeKind == tkClass)
{
TRttiProperty *pDstProp = pDstType->GetProperty(pSrcPropInfo->Name);
if ((pDstProp) && (pDstProp->PropertyType->TypeKind == tkClass))
{
TPersistent *pDstObj = dynamic_cast<TPersistent*>(pDstProp->GetValue(dstCtrl).AsObject());
if (pDstObj)
{
TPersistent *pSrcObj = dynamic_cast<TPersistent*>(pSrcProp->GetValue(srcCtrl).AsObject());
pDstObj->Assign(pSrcObj);
}
}
}
else
{
TRttiProperty *pDstProp = pDstType->GetProperty(pSrcPropInfo->Name);
if (pDstProp)
{
TValue value = pSrcProp->GetValue(srcCtrl);
pDstProp->SetValue(dstCtrl, value);
}
}
}
}
The thread at How to get version info shows the code to get FileVersion, I need to get other values, includuing some I myself have added to VersionInfo table.
How can I get them using C++Builder 10.2 (Tokyo) ?
I used to get them in C++Builder 6.0 using the VerQueryValue method but it is raising too many exception on types.
I do not know how to change the code to C++Builder 10.2.
Bellow is the actual code I am using:
class.h
struct TransArray
{
WORD LanguageID, CharacterSet;
};
DWORD VerInfo, VerSize;
HANDLE MemHandle;
LPVOID MemPtr, BufferPtr;
UINT BufferLength;
TransArray *Array;
char QueryBlock[255];
String FFileVersion ;
class.cpp
// this one of the methods which have errors
String __fastcall TAppVersion::GetFileVersion(void)
{
String Result ;
BufferPtr = NULL ;
// Get the product version.
wsprintf(QueryBlock, "\\StringFileInfo\\%04x%04x\\FileVersion",
Array[0].LanguageID, Array[0].CharacterSet);
VerQueryValue(MemPtr, QueryBlock, &BufferPtr, &BufferLength);
if(BufferPtr) Result = (char *)BufferPtr;
return(Result);
}
//---------------------------------------------------
__fastcall TAppVersion::TAppVersion()
{
FFileName = Application->ExeName ;
VerSize = GetFileVersionInfoSize(FFileName.c_str(), &VerInfo);
if (VerSize > 0) {
MemHandle = GlobalAlloc(GMEM_MOVEABLE, VerSize);
MemPtr = GlobalLock(MemHandle);
GetFileVersionInfo(FFileName.c_str(), VerInfo, VerSize, MemPtr);
VerQueryValue(MemPtr, "\\VarFileInfo\\Translation", &BufferPtr,
&BufferLength);
Array = (TransArray *)BufferPtr;
FFileVersion = GetFileVersion();
}
}
//-----------------------------------------------
Your code is mixing ANSI and UNICODE logic incorrectly. VerQueryValue() is a preprocessor macro that maps to either VerQueryValueW() or VerQueryValueA() depending on whether UNICODE is defined or not, respectively. Your code is assuming VerQueryValueA() is being used, but that is not always the case. In modern versions of C++Builder, VerQueryValueW() would be used instead, by default.
Try something more like this instead:
struct TransArray
{
WORD LanguageID, CharacterSet;
};
DWORD VerInfo, VerSize;
LPVOID MemPtr, BufferPtr;
UINT BufferLength;
TransArray *Array;
String FFileName, FFileVersion;
...
#include <tchar.h>
String __fastcall TAppVersion::GetFileVersion(void)
{
String Result;
if (MemPtr && Array)
{
// Get the product version.
TCHAR QueryBlock[40];
_stprintf(QueryBlock, _T("\\StringFileInfo\\%04x%04x\\FileVersion"), Array[0].LanguageID, Array[0].CharacterSet);
if (VerQueryValue(MemPtr, QueryBlock, &BufferPtr, &BufferLength)) {
Result = String(static_cast<TCHAR*>(BufferPtr), BufferLength).Trim();
}
}
return Result;
}
//---------------------------------------------------
__fastcall TAppVersion::TAppVersion()
{
MemPtr = NULL;
Array = NULL;
FFileName = Application->ExeName;
DWORD Unused;
VerSize = GetFileVersionInfoSize(FFileName.c_str(), &Unused);
if (VerSize == 0) return;
MemPtr = new BYTE[VerSize];
if (GetFileVersionInfo(FFileName.c_str(), Unused, VerSize, MemPtr)) {
if (VerQueryValue(MemPtr, TEXT("\\VarFileInfo\\Translation"), &BufferPtr, &BufferLength) {
Array = (TransArray *) BufferPtr;
FFileVersion = GetFileVersion();
}
}
}
//-----------------------------------------------
__fastcall TAppVersion::~TAppVersion()
{
delete[] static_cast<BYTE*>(MemPtr);
}
//-----------------------------------------------
Though really, you shouldn't rely on TCHAR at all in modern code:
struct TransArray
{
WORD LanguageID, CharacterSet;
};
DWORD VerInfo, VerSize;
LPVOID MemPtr, BufferPtr;
UINT BufferLength;
TransArray *Array;
UnicodeString FFileName, FFileVersion;
...
UnicodeString __fastcall TAppVersion::GetFileVersion(void)
{
UnicodeString Result;
if (MemPtr && Array)
{
// Get the product version.
WCHAR QueryBlock[40];
swprintf(QueryBlock, L"\\StringFileInfo\\%04x%04x\\FileVersion", Array[0].LanguageID, Array[0].CharacterSet);
if (VerQueryValueW(MemPtr, QueryBlock, &BufferPtr, &BufferLength)) {
Result = UnicodeString(static_cast<WCHAR*>(BufferPtr), BufferLength).Trim();
}
}
return Result;
}
//---------------------------------------------------
__fastcall TAppVersion::TAppVersion()
{
MemPtr = NULL;
Array = NULL;
FFileName = Application->ExeName;
DWORD Unused;
VerSize = GetFileVersionInfoSizeW(FFileName.c_str(), &Unused);
if (VerSize == 0) return;
MemPtr = new BYTE[VerSize];
if (GetFileVersionInfoW(FFileName.c_str(), Unused, VerSize, MemPtr)) {
if (VerQueryValueW(MemPtr, L"\\VarFileInfo\\Translation", &BufferPtr, &BufferLength) {
Array = (TransArray *) BufferPtr;
FFileVersion = GetFileVersion();
}
}
}
//-----------------------------------------------
__fastcall TAppVersion::~TAppVersion()
{
delete[] static_cast<BYTE*>(MemPtr);
}
//-----------------------------------------------
A while ago I did some playing with this. It is not complete and I would call it beta 1. But here it goes.
The header:
class TAppVerInfo
{
public:
__fastcall TAppVerInfo(const wchar_t* pModPath);
__fastcall virtual ~TAppVerInfo(void);
__property System::String LanguagesCodePage = {read = GetCodePage};
__property System::String Comments[System::String LanId] = {read = GetComments};
__property System::String InternalName[System::String LanId] = {read = GetInternalName};
__property System::String ProductName[System::String LanId] = {read = GetProductName};
__property System::String CompanyName[System::String LanId] = {read = GetCompanyName};
__property System::String LegalCopyright[System::String LanId] = {read = GetLegalCopyright};
__property System::String ProductVersion[System::String LanId] = {read = GetProductVersion};
__property System::String FileDescription[System::String LanId] = {read = GetFileDescription};
__property System::String LegalTrademarks[System::String LanId] = {read = GetLegalTrademarks};
__property System::String PrivateBuild[System::String LanId] = {read = GetPrivateBuild};
__property System::String FileVersion[System::String LanId] = {read = GetFileVersion};
__property System::String OriginalFilename[System::String LanId] = {read = GetOriginalFilename};
__property System::String SpecialBuild[System::String LanId] = {read = GetSpecialBuild};
protected:
void *VerInfo;
System::String __fastcall GetCodePage(void);
System::String __fastcall GetComments(System::String LanId);
System::String __fastcall GetInternalName(System::String LanId);
System::String __fastcall GetProductName(System::String LanId);
System::String __fastcall GetCompanyName(System::String LanId);
System::String __fastcall GetLegalCopyright(System::String LanId);
System::String __fastcall GetProductVersion(System::String LanId);
System::String __fastcall GetFileDescription(System::String LanId);
System::String __fastcall GetLegalTrademarks(System::String LanId);
System::String __fastcall GetPrivateBuild(System::String LanId);
System::String __fastcall GetFileVersion(System::String LanId);
System::String __fastcall GetOriginalFilename(System::String LanId);
System::String __fastcall GetSpecialBuild(System::String LanId);
System::String __fastcall GetValue(const System::String& LanId, const wchar_t* pName);
};
and the CPP:
__fastcall TAppVerInfo::TAppVerInfo(const wchar_t* pModPath)
{
DWORD dwUnused;
DWORD VerInfoSize = GetFileVersionInfoSize(pModPath, &dwUnused);
VerInfo = malloc(VerInfoSize);
if(VerInfo)
{
if(0 == GetFileVersionInfo(pModPath, 0, VerInfoSize, VerInfo))
{
free(VerInfo);
VerInfo = NULL;
throw Exception(SysErrorMessage(GetLastError()));
}
} //040904E4
}
//---------------------------------------------------------------------------
__fastcall TAppVerInfo::~TAppVerInfo(void)
{
if(VerInfo)
free(VerInfo);
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetCodePage(void)
{
System::String retVal;
if(VerInfo)
{
struct LANGANDCODEPAGE
{
WORD wLanguage;
WORD wCodePage;
} *lpTranslate;
UINT cbTranslate;
TAutoStringList tStr(new TStringList);
UINT i;
VerQueryValue(VerInfo,L"\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &cbTranslate);
for(i = 0; i < (cbTranslate/sizeof(LANGANDCODEPAGE)); i++)
tStr->Add(System::String().sprintf(L"%04x%04x", lpTranslate[i].wLanguage, lpTranslate[i].wCodePage));
retVal = tStr->CommaText;
}
return retVal;
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetComments(System::String LanId)
{
return GetValue(LanId, L"Comments");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetInternalName(System::String LanId)
{
return GetValue(LanId, L"InternalName");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetProductName(System::String LanId)
{
return GetValue(LanId, L"ProductName");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetCompanyName(System::String LanId)
{
return GetValue(LanId, L"CompanyName");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetLegalCopyright(System::String LanId)
{
return GetValue(LanId, L"LegalCopyright");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetProductVersion(System::String LanId)
{
return GetValue(LanId, L"ProductVersion");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetFileDescription(System::String LanId)
{
return GetValue(LanId, L"FileDescription");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetLegalTrademarks(System::String LanId)
{
return GetValue(LanId, L"LegalTrademarks");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetPrivateBuild(System::String LanId)
{
return GetValue(LanId, L"PrivateBuild");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetFileVersion(System::String LanId)
{
return GetValue(LanId, L"FileVersion");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetOriginalFilename(System::String LanId)
{
return GetValue(LanId, L"OriginalFilename");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetSpecialBuild(System::String LanId)
{
return GetValue(LanId, L"SpecialBuild");
}
//---------------------------------------------------------------------------
System::String __fastcall TAppVerInfo::GetValue(const System::String& LanId, const wchar_t* pName)
{
System::String retVal;
if(VerInfo)
{
System::String aPath(System::String().sprintf(L"\\StringFileInfo\\%s\\%s", LanId.c_str(), pName));
wchar_t *pBuf;
UINT uLen;
if(VerQueryValue(VerInfo, aPath.c_str(), (void**)&pBuf, &uLen))
retVal = System::String(pBuf);
}
return retVal;
}
I need your help.
I have this Xamarin app, that is sending a multicast on the network using System.Net.UdpClient, but it seems pretty unstable and crashes a lot in background threads that I don't control. So I though why not go low-level.
Everything seems good except for the part of enabling the broadcast flag on the socket. In Objective-C you can do this:
setsockopt(CFSocketGetNative(cfSocket), SOL_SOCKET, SO_BROADCAST, (void *)&yes, sizeof(yes));
By looking at the mono source you'll see that the Socket class has an EnableBroadcast: https://github.com/mono/mono/blob/463cf3b5c1590df58fef43577b9a3273d5eece3d/mcs/class/System/System.Net.Sockets/Socket.cs#L195
Which inspired to this (very experimental) code:
public class NetworkHelper
{
[DllImport("libc", SetLastError = true)]
protected unsafe static extern int setsockopt(int s, int level, int optname, void* optval, uint optlen);
public unsafe static void DoMulticast()
{
var socket = new CFSocket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
var optval = 1;
var res = setsockopt(socket.Handle.ToInt32(), (int)SocketOptionLevel.Socket, (int)SocketOptionName.Broadcast, &optval, sizeof(int));
if (res < 0)
{
return;
}
}
}
It runs, but no matter what I change the setsockopt signature to, it returns -1.
TL;DR Do you think it's possible to enable the broadcast flag on a CFSocket (though Xamarin.iOS) done in the old CFNetwork.framework?
I've nailed it. I was missing the CFSocketNativeHandle like in the "native" Obj-C code, which I guess converts a CFNetwork->CFSocket into a native file descriptor pointer.
This is my full implementation (C# Extension):
public static class CFSocketExtensions
{
[DllImport(Constants.CoreFoundationLibrary)]
extern static nint CFSocketSendData(IntPtr handle, IntPtr address, IntPtr data, double timeout);
[DllImport("libc", SetLastError = true)]
extern static int setsockopt(CFSocketNativeHandle s, int level, int optname, IntPtr optval, int optlen);
[DllImport(Constants.CoreFoundationLibrary)]
extern static CFSocketNativeHandle CFSocketGetNative(IntPtr handle);
public static void SendData(this CFSocket socket, CFSocketAddress address, byte[] data, double timeout)
{
using (var buffer = new CFDataBuffer(data))
{
var error = (CFSocketError)(long)CFSocketSendData(socket.Handle, address.Handle, buffer.Handle, timeout);
if (error != CFSocketError.Success)
throw new CFSocketException(error);
}
}
public static bool EnableBroadcast(this CFSocket socket, bool enable = true)
{
int size = Marshal.SizeOf<int>();
IntPtr pBool = Marshal.AllocHGlobal(size);
Marshal.WriteInt32(pBool, 0, enable ? 1 : 0); // last parameter 0 (FALSE), 1 (TRUE)
var res = setsockopt(CFSocketGetNative(socket.Handle), (int)SocketOptionLevel.Socket, (int)SocketOptionName.Broadcast, pBool, size);
Marshal.FreeHGlobal(pBool);
return res > -1;
}
}
public class CFSocketAddress : CFDataBuffer
{
public CFSocketAddress(IPEndPoint endpoint)
: base(CreateData(endpoint))
{
}
internal static IPEndPoint EndPointFromAddressPtr(IntPtr address)
{
using (var buffer = new CFDataBuffer(address))
{
if (buffer[1] == 30)
{ // AF_INET6
int port = (buffer[2] << 8) + buffer[3];
var bytes = new byte[16];
Buffer.BlockCopy(buffer.Data, 8, bytes, 0, 16);
return new IPEndPoint(new IPAddress(bytes), port);
}
else if (buffer[1] == 2)
{ // AF_INET
int port = (buffer[2] << 8) + buffer[3];
var bytes = new byte[4];
Buffer.BlockCopy(buffer.Data, 4, bytes, 0, 4);
return new IPEndPoint(new IPAddress(bytes), port);
}
else
{
throw new ArgumentException();
}
}
}
static byte[] CreateData(IPEndPoint endpoint)
{
if (endpoint == null)
throw new ArgumentNullException("endpoint");
if (endpoint.AddressFamily == AddressFamily.InterNetwork)
{
var buffer = new byte[16];
buffer[0] = 16;
buffer[1] = 2; // AF_INET
buffer[2] = (byte)(endpoint.Port >> 8);
buffer[3] = (byte)(endpoint.Port & 0xff);
Buffer.BlockCopy(endpoint.Address.GetAddressBytes(), 0, buffer, 4, 4);
return buffer;
}
else if (endpoint.AddressFamily == AddressFamily.InterNetworkV6)
{
var buffer = new byte[28];
buffer[0] = 32;
buffer[1] = 30; // AF_INET6
buffer[2] = (byte)(endpoint.Port >> 8);
buffer[3] = (byte)(endpoint.Port & 0xff);
Buffer.BlockCopy(endpoint.Address.GetAddressBytes(), 0, buffer, 8, 16);
return buffer;
}
else
{
throw new ArgumentException();
}
}
}
public static class CFObject
{
[DllImport(Constants.CoreFoundationLibrary)]
internal extern static void CFRelease(IntPtr obj);
[DllImport(Constants.CoreFoundationLibrary)]
internal extern static IntPtr CFRetain(IntPtr obj);
}
public class CFData : INativeObject, IDisposable
{
internal IntPtr handle;
public CFData(IntPtr handle)
: this(handle, false)
{
}
public CFData(IntPtr handle, bool owns)
{
if (!owns)
CFObject.CFRetain(handle);
this.handle = handle;
}
~CFData()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public IntPtr Handle
{
get { return handle; }
}
[DllImport(Constants.CoreFoundationLibrary, EntryPoint = "CFDataGetTypeID")]
public extern static /* CFTypeID */ nint GetTypeID();
protected virtual void Dispose(bool disposing)
{
if (handle != IntPtr.Zero)
{
CFObject.CFRelease(handle);
handle = IntPtr.Zero;
}
}
public nint Length
{
get { return CFDataGetLength(handle); }
}
[DllImport(Constants.CoreFoundationLibrary)]
extern static /* CFIndex */ nint CFDataGetLength(/* CFDataRef */ IntPtr theData);
public byte[] GetBuffer()
{
var buffer = new byte[Length];
var ptr = CFDataGetBytePtr(handle);
Marshal.Copy(ptr, buffer, 0, buffer.Length);
return buffer;
}
[DllImport(Constants.CoreFoundationLibrary)]
extern static /* UInt8* */ IntPtr CFDataGetBytePtr(/* CFDataRef */ IntPtr theData);
/*
* Exposes a read-only pointer to the underlying storage.
*/
public IntPtr Bytes
{
get { return CFDataGetBytePtr(handle); }
}
[DllImport(Constants.CoreFoundationLibrary)]
extern static /* CFDataRef */ IntPtr CFDataCreate(/* CFAllocatorRef */ IntPtr allocator, /* UInt8* */ IntPtr bytes, /* CFIndex */ nint length);
public static CFData FromData(IntPtr buffer, nint length)
{
return new CFData(CFDataCreate(IntPtr.Zero, buffer, length), true);
}
[DllImport(Constants.CoreFoundationLibrary)]
extern static /* CFDataRef */ IntPtr CFDataCreateCopy(/* CFAllocatorRef */ IntPtr allocator, /* CFDataRef */ IntPtr theData);
public CFData Copy()
{
return new CFData(CFDataCreateCopy(IntPtr.Zero, Handle), true);
}
}
public class CFDataBuffer : IDisposable
{
byte[] buffer;
CFData data;
public CFDataBuffer(byte[] buffer)
{
this.buffer = buffer;
/*
* Copy the buffer to allow the native side to take ownership.
*/
//fixed (byte* ptr = buffer)
// data = CFData.FromData((IntPtr)ptr, buffer.Length);
GCHandle pinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
data = CFData.FromData(pinnedBuffer.AddrOfPinnedObject(), buffer.Length);
pinnedBuffer.Free();
}
public CFDataBuffer(IntPtr ptr)
{
data = new CFData(ptr, false);
buffer = data.GetBuffer();
}
~CFDataBuffer()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public IntPtr Handle
{
get { return data.Handle; }
}
public byte[] Data
{
get { return buffer; }
}
public byte this[int idx]
{
get { return buffer[idx]; }
}
protected virtual void Dispose(bool disposing)
{
if (data != null)
{
data.Dispose();
data = null;
}
}
}
The only downside with this implementation is the CFSocketAddress, CFDataBuffer etc. which are defined as internal classes in the source.
I am new in programming. Actually I am in 2nd year of college and starting an internship. They want me to do a program in C++ builder but I only know C. What I studied. And I don't have any knowledge about OOP.
So my question is.
I have TEdit1 and I want to verify if the data introduced in that textbox is a number. I know to verify if it is a number but I don't know how to put data from TEdit into string.
I wrote some code but it doesn't work.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int Size = Edit1->GetTextLen(); //Get length of string in Edit1
Size++; //Add room for null character
Char *Buffer = new Char[Size]; //Creates Buffer dynamic variable
std::auto_ptr<Char> Buffer(new Char[Size]);
Edit1->GetTextBuf(Buffer.get(),Size); //Puts Edit1->Text into Buffer
ShowMessage(Buffer);
}
And I get these errors:
E2034 Cannot convert 'std::auto_ptr<wchar_t>' to 'UnicodeString'
E2342 Type mismatch in parameter 'Msg' (wanted 'const UnicodeString', got std::auto_ptr<wchar_t>')
Can you please explain what i did wrong, or where i can found Embarcadero C++ Builder tutorials? I searched all google and didn't find something to help me.
Your code has several mistakes in it:
you are declaring two Buffer variables in the same scope. That is not allowed. You need to remove one of them.
you are passing the std::auto_ptr itself to ShowMessage(), but it expects a System::UnicodeString instead, thus the compiler error message. You can use the std::auto_ptr::get() method to get the wchar_t* pointer and pass it to ShowMessage(), as UnicodeString has a constructor that accepts wchar_t* as input:
ShowMessage(Buffer.get());
despite the above, you actually cannot use a pointer from new[] with std::auto_ptr to begin with. std::auto_ptr uses delete instead of delete[] to free the memory being pointed at. You must always use delete with new, and delete[] with new[]. So, while the code will compile, it will not free the memory correctly at runtime. C++11 introduced a new std::unique_ptr class to replace std::auto_ptr, and std::unique_ptr supports new[] and delete[] (however, C++Builder's 32bit compiler does not support C++11 yet - that is in the works - but its 64bit compiler does):
#include <memory>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int Size = Edit1->GetTextLen(); //Get length of string in Edit1
Size++; //Add room for null character
std::unique_ptr<Char[]> Buffer(new Char[Size]); //Creates Buffer dynamic variable
Edit1->GetTextBuf(Buffer.get(), Size); //Puts Edit1->Text into Buffer
ShowMessage(Buffer.get());
}
Now, with that said, if you are going to continue using C++Builder, you should learn how to use its built-in functionalities, like UnicodeString, which the RTL/VCL relies heavily on (use the System::String alias for most code, use UnicodeString directly only when absolutely necessary).
Your example can be vastly simplified using the TEdit::Text property:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
String s = Edit1->Text; //Get string in Edit1
ShowMessage(s);
}
The simplest solution to your problem would be to use the TCSpinEdit component instead of TEdit, as TCSpinEdit only allows numeric input in the first place, and has a Value property that returns an int:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number = CSpinEdit1->Value;
ShowMessage("It is a number");
// use number as needed...
}
But, if you have to stick with TEdit, there are many ways to check a UnicodeString for numeric content.
You can set the TEdit::NumbersOnly property to true so the user cannot enter a non-numeric value (unless they use copy/paste, but let's ignore that for the moment), and then use the RTL's StrToInt() function, or System::UnicodeString::ToInt() method, to convert it as-is:
#include <System.SysUtils.hpp>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number;
try
{
number = StrToInt(Edit1->Text);
// or: number = Edit1->Text.ToInt();
}
catch (const EConvertError&)
{
// not a number, do something else...
ShowMessage("It is not a number");
return;
}
ShowMessage("It is a number");
// use number as needed...
}
Or you can use the RTL's System::Sysutils::TryStrToInt() function:
#include <System.SysUtils.hpp>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number;
if (TryStrToInt(Edit1->Text, number))
{
// use number as needed...
ShowMessage("It is a number");
}
else
{
// not a number, do something else...
ShowMessage("It is not a number");
}
}
Or you can use the STL's std::wistringstream class:
#include <sstream>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number;
std::wistringstream iss(Edit1->Text.c_str());
if (iss >> number)
{
// use number as needed...
ShowMessage("It is a number");
}
else
{
// not a number, do something else...
ShowMessage("It is not a number");
}
}
Or, since you have a C background, you can use the C _wtoi() function (which doesn't offer much in the way of error checking):
#include <cstdlib>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number = std::_wtoi(Edit1->Text.c_str());
if (number != 0)
{
// use number as needed...
ShowMessage("It is a valid number");
}
else
{
// not a number, do something else...
ShowMessage("It is not a valid number");
}
}
Or you can use the C wcstol() function:
#include <cstdlib>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
String s = Edit1->Text;
Char *p = s.c_str(), *end;
int number = std::wcstol(p, &end, 10);
if (end != p)
{
// use number as needed...
ShowMessage("It is a number");
}
else
{
// not a number, do something else...
ShowMessage("It is not a number");
}
}
Or you can use the C swscanf() function:
#include <cstdio>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number;
if (std::swscanf(Edit1->Text.c_str(), L"%d", &number) == 1)
{
// use number as needed...
ShowMessage("It is a number");
}
else
{
// not a number, do something else...
ShowMessage("It is not a number");
}
}