Link to home
Start Free TrialLog in
Avatar of Jako PH
Jako PH

asked on

C++/CLR GetSystemMetrics Doesn't work

Hi i was having some problems in finding the screen width and height how ever i am working on an project and i finally found
GetSystemMetric(SM_CXSCREEN)

Open in new window

but every time i use this function in CLR i am getting this problem :
1>Width.obj : error LNK2028: unresolved token (0A00002E) "extern "C" int __stdcall GetSystemMetrics(int)" (?GetSystemMetrics@@$$J14YGHH@Z) referenced in function "int __clrcall main(cli::array<class System::String ^ >^)" (?main@@$$HYMHP$01AP$AAVString@System@@@Z)
1>Width.obj : error LNK2019: unresolved external symbol "extern "C" int __stdcall GetSystemMetrics(int)" (?GetSystemMetrics@@$$J14YGHH@Z) referenced in function "int __clrcall main(cli::array<class System::String ^ >^)" (?main@@$$HYMHP$01AP$AAVString@System@@@Z)

Open in new window


Isn't it supported in Clr ? if yes what i can use instead.
Avatar of Jako PH
Jako PH

ASKER

Why there's no any answers ?
ASKER CERTIFIED SOLUTION
Avatar of sarabande
sarabande
Flag of Luxembourg image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Jako PH

ASKER

Thanks a lot Sara you always help me <3
anyway i have another question ...
I have a list view and i am getting the selected text with :
String ^Selected = listView1->SelectedItems[0]->SubItems[0]->Text;
But i wanted to write the result to an ini file :
WritePrivateProfileSection(Selected,L"Key",L".//Test.txt");
But the writeprivateprofilesection needs a parameter of type LPCWSTR so this string cannot be used in it
any ideas ?
L".//Test.txt" is already a Long Pointer to Constant Wide String (LPCWSTR). if Test.txt wasn't found you may Change to L".\\test.txt" or L"test.txt" what is all the same (if your current folder is the right one).

a System::String is a gc object. you can get a pointer from it by code like
(code from https://msdn.microsoft.com/de-de/library/ms235631.aspx)

    // Set up a System::String and display the result.  
    String ^orig = gcnew String("Hello, World!");  
    Console::WriteLine("{0} (System::String)", orig);  
  
    // Obtain a pointer to the System::String in order to  
    // first lock memory into place, so that the  
    // Garbage Collector (GC) cannot move that object  
    // while we call native functions.  
    pin_ptr<const wchar_t> wch = PtrToStringChars(orig);  

Open in new window


if you have atl or mfc with your native code (or include atlbase.h and atlconv.h) you simply could do

CString wstr(selected.Text());

Open in new window



the CString automatically handles wide strings if character set is not set to multi-byte but to UNICODE (what is default) and has a constructor that takes a String. it also has a operator const wchar_t * which casts a CString to LPCWSTR.

so

you can do

String ^Selected = listView1->SelectedItems[0]->SubItems[0]->Text;
WritePrivateProfileSection(CString(Selected), CString(L"Key=Value\0\0", 11), L"Test.txt");

Open in new window


note, the above would only write one entry Key=Value to the new section.

if you want to write more entries you have to add binary zero wide characters (=16 bit integer 0) or L'\0'. to the string and then append the next key=value pair after the zero. the string ends with a double-zero what is also required for your single pair (see code sample where i used a CString constructor that takes a LPCWSTR and a length in characters) . it probably is more easy to use CString or std::wstring for this kind of concatenation.

Sara
Avatar of Jako PH

ASKER

So CString converts from String ^Selected to std::string ?
no, CString has a constructor that takes a System::String.

CString also has an operator LPCTSTR (a so-called cast operator) which returns a const pointer to TCHAR. TCHAR is wchar_t if the characterset in the project properties is UNICODE (more precisely it is not UNICODE but UTF16 wide characters). TCHAR is char if the characterset is 'multi-byte' what actually means ANSI characterset.

with the cast operator of CString you always can use a CString variable where a const wchar_t * (LPCWSTR) was required (UNICODE) or const char * (LPCSTR) in case of ANSI character set. in mixed managed and unmanaged project you better use wide characters for CString to avoid errors. System::String is always wide characters.

std::string now is std::basic_string<char> and would take a const char * in one of ist constructors. so if you have a CString with wide characters you firstly have to convert the const wchar_t * to const char * (what could be done by wcstombs function). then you could feed the std::string with that const char * pointer. much simpler is to use std::wstring what is std::basic_string<wchar_t>. with that you can do:

System::String^ sysstr = L"Hello World";
std::wstring wstr = CString(sysstr);

Open in new window


if the project uses wide strings.

sara
Avatar of Jako PH

ASKER

Thanks sara my problem now is the file is always created as ANSI which means i can't write unicode (Arabic) to the file and if i wrote the result would be ???????
So i need a way to create the file in unicode settings is that possible while using the ini file ?
	String ^Shi = listView1->SelectedItems[0]->SubItems[0]->Text;
	WritePrivateProfileSection(CString(Shi),L"Name = "+CString(Shi), L".//"+CString(Shi)+".txt");

Open in new window

Avatar of Jako PH

ASKER

I mean that when i write unicode to a file the must be in unicode settings but i want to make this settings from the code automatic since my project will create a lot of file so i can't change the settings of every file
Avatar of Jako PH

ASKER

Didn't find any solution for that on google !
Avatar of Jako PH

ASKER

I guess i have to use xml instead ?
do you need arabic characters in the ini file? or only in the files where the file names are normal ASCII and the filename is in the ini file?

to write UNICODE into a text file i would suggest to use UTF-8 rather than UTF-16 (what is the MS UNICODE). in UTF-8 all ASCII characters are 1 byte. so if the text is mostly ASCII you can it read without problems. the disadvantage is that you have to care for multibyte utf-8 characters and reconvert them to utf-16 if needed. in c++ you would use the function MultiByteToWideChar function which takes a char array (or pointer to char) as Input and CP_UTF8 as 'codepage' argument, and then produces a wide char buffer in UTF-16. the other way is WideCharToMultiByte which takes the wide string (wchar_t* as explained above) and has an UTF-8 coded char array as output (which you could write in the value part of an inifile entry)

Sara
Avatar of Jako PH

ASKER

You didn't answer me about XML ...
i don't see where XML should help. of course you can write different encodings to XML which are defined in the XML Header. nevertheless you need conversion if you write or read the XML and have or need a different encoding to that which is defined in the XML. inifiles more easily could be read or written than XML and have much less overhead.

can you answer my question whether you want to write UTF-16 to the inifile or to other files which are only referenced in the inifile?

Sara
Avatar of Jako PH

ASKER

that's what i mean
[محمد]
name = محمد
Avatar of Jako PH

ASKER

could you give me an example of how to make the file settings utf-8 like u said ?
String ^Selected = listView1->SelectedItems[0]->SubItems[0]->Text;

char szName[256] = { '\0' };  // create a char buffer which is big enough for section name
char szBuf[1024] = { '\0' }; // create a char buffer which is big enough for entries

CString strW = Selected; 
WideStringToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, strW, strW.GetLength(), szName, sizeof(szName), NULL, NULL);

//the szBuf has all zero characters. we easily can add key=value pairs:

wchar_t wpair[256] = { 0 };
wcscpy(wpair, L"key1=value1");   // here you can use Unicode characters
strW = wpair;
char pair[512] = { '\0' };
int nlen = WideStringToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, strW, strW.GetLength(), pair, sizeof(pair), NULL, NULL);

int noff = 0;
strcpy(&szBuf[noff], pair);
noff += nlen + 1;   // add the length + 1 for zero separator 
wcscpy(wpair, L"key1=value1");   // here you can use Unicode characters
strW = wpair;
nlen = WideStringToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, strW, strW.GetLength(), pair, sizeof(pair), NULL, NULL);
strcpy(&szBuf[noff], pair);
noff += nlen + 1;
// ... and so on 
// you also could add entries in a loop 
// finally increment noff to have two zeros at end of buffer

// we now use the single char version of writeprivateprofilesection
WritePrivateProfileSectionA(szName, szBuf,  noff, "Test.txt");

Open in new window


to get the utf-8 data back you do the reverse

// convert section name to utf-8 (if necessary)
String ^Selected = listView1->SelectedItems[0]->SubItems[0]->Text;
char szName[256] = { '\0' };  // create a char buffer which is big enough for section name
CString strW = Selected; 
WideStringToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, strW, strW.GetLength(), szName, sizeof(szName), NULL, NULL);

// convert key to get to utf-8 (if necessary)
wchar_t wkey[256] = { 0 };
wcscpy(wkey, L"key");   // here you can use Unicode characters
strW = wkey;
char szKey[512] = { '\0' };
int nlen = WideStringToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, strW, strW.GetLength(), szKey, sizeof(szKey), NULL, NULL);

char szValue[512] = { '\0' };
int len = GetPrivateProfileStringA(szName, szKey, "", szValue, sizeof(szValue), "Test.txt");

if (len > 0)
{
     MultiByteToWideChars(CP_UTF8, WC_ERR_INVALID_CHARS, szValue, len, strW.GetBuffer(256), 256, NULL, NULL);
     strW.ReleaseBuffer(-1);

     SystemString^ value = (LPCWSTR)strW;  // hope that works. i am not experienced in managed c++
}

Open in new window


Sara
name = محمد

note, don't use space character left or right of the = between key and value.

if section names and key names would be ASCII you much easier could handle utf-8 values.

if there is any problem converting chars between UTF-16 and UTF-8, the flag WC_ERR_INVALID_CHARS makes that the conversion function fails.

if you don't want that, replace WC_ERR_INVALID_CHARS by 0.

Sara
Avatar of Jako PH

ASKER

Sorry but what's the header file of WideStringToMultiByte ?
Avatar of Jako PH

ASKER

Is there a definition for widestringtomultibyte or what I cannot even find it on msdn or specially on google.
it was my fault. the function was named WideCharsToMultiByte.

Sara
The question was solved.

Sara