I am trying to pass strings from a C++ console app to a C++ dll, which i then convert to System::string to be sent to a C# dll. But the string comes through as random characters. Note that lShutdown actually comes through correct as it is defined in the dll so the problem must be between the testapp and the C++ dll. I had this working before, but i cant figure out for the life of me how i did it
Here is the code for passing to the dll:
void GMSTestAppC::GMSSetup()
{
string processName = "GMSCTestApp";
string progName = "TestApp";
//Directory where program is installed
string progDir = "C:/";
string version = "4.0.0.1";
//Directory for config file
string configDir = "C:/";
//Directory for log files
string logDir = "C:/";
string user = "Smurf";
string shutdownby = "";
Initialize(processName, GetCurrentProcessId(), progName, progDir, version, configDir, logDir, user, shutdownby);
}
Here is the dll code:
bool Initialize(std::string& processName, int processID, std::string& programName, std::string& programDir, std::string& version, std::string& configDir, std::string& logDir, std::string& currentUser, std::string& lastShutDownBy)
{
String^ pName = gcnew String(processName.c_str());
String^ progName = gcnew String(programName.c_str());
String^ progDir = gcnew String(programDir.c_str());
String^ cDir = gcnew String(configDir.c_str());
String^ lDir = gcnew String(logDir.c_str());
String^ cUser = gcnew String(currentUser.c_str());
String^ lShutdown = gcnew String("test");
GMS::GMSVersion fv;
sscanf_s(version.c_str(), "%d.%d.%d.%d", &fv.Major, &fv.Minor, &fv.Revision, &fv.Build);
int pID = int(processID);
Return GMS::Initialize(pName, pID, progName, progDir, fv, cDir, lDir, cUser, lShutdown);
}
Here is what i actually get through:
Sent: <InitializeProgram>
<TimeStamp>2018-01-22T14:34:37.6152709+00:00</TimeStamp>
<PID>13504</PID>
<ProcessName>(÷'</ProcessName>
<ProgramName>Ðð'</ProgramName>
<ProgramDir>°ñ'</ProgramDir>
<Version>0.0.0.0</Version>
<ConfigDir>^ô'</ConfigDir>
<LogDir>èñ'</LogDir>
<CurrentUser>÷'</CurrentUser>
<LastShutDownBy>test</LastShutDownBy>
</InitializeProgram>
The characters that come through are different each time the program is run
Any help would be greatly appreciated
If you are using C++11 (which you most probably be using), you can try using std::u32string. It removes the need to use std::wstring and is standard across platforms.
Related
I'm relatively new to C++ and I'm trying out Windows Notification using Win32 API.
This is the method I have:
BOOL Notification::ShowNotification(std::string title, std::string info) {
NOTIFYICONDATA nid = {
sizeof(nid)
};
nid.uFlags = NIF_INFO | NIF_GUID;
nid.guidItem = __uuidof(AppIcon);
nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
std::wstring wtitle = std::wstring(title.begin(), title.end());
const wchar_t * wchar_title = (STRSAFE_LPCWSTR) wtitle.c_str();
StringCchCopy(nid.szInfoTitle, sizeof(nid.szInfoTitle), wchar_title);
std::wstring wInfo = std::wstring(info.begin(), info.end());
const wchar_t * wchar_Info = (STRSAFE_LPCWSTR) wInfo.c_str();
StringCchCopy(nid.szInfo, sizeof(nid.szInfo), wchar_Info);
LoadIconMetric(g_hInst, MAKEINTRESOURCE(IDI_NOTIFICATIONICON), LIM_LARGE, & nid.hBalloonIcon);
return Shell_NotifyIcon(NIM_MODIFY, & nid);
}
As you can see, there is duplicate code for converting the string type to STRSAFE_LPCWSTR for the variables title and info. I was thinking of a small utility method that would replace the duplicate code.
Something like this:
void Notification::ConvertToLPCWSTR(std::string input, STRSAFE_LPCWSTR &result)
{
std::wstring wide_string = std::wstring(input.begin(), input.end());
result = (STRSAFE_LPCWSTR)wide_string.c_str();
}
And then use it from the ShowNotification method like this, where wchar_title is passed by reference:
STRSAFE_LPCWSTR wchar_title;
ConvertToLPCWSTR(title, wchar_title);
But it is failing because wide_string variable is stack allocated and it goes out of scope when ConvertToLPCWSTR execution is finished, because of which wchar_title is pointing at deallocated memory.
Anyone know of a good way to fix this ?
You need to move all three lines of the repeated code into a small utility function.
static void Notification::ConvertToLPCWSTR(const std::string& input, LPWSTR result, size_t result_max_size) {
std::wstring wInfo = std::wstring(input.begin(), input.end());
const wchar_t * wchar_Info = (STRSAFE_LPCWSTR) wInfo.c_str();
StringCchCopy(result, result_max_size, wchar_Info);
}
And call like
ConvertToLPCWSTR(info, nid.szInfo, sizeof(nid.szInfo));
I have been set the task of:
To evolve a Logger class that can be integrated into your
projects/developments, most probably by declaring a single global instance; this will be used to capture and save log information.
There are 2 levels that are acceptable:
Logger - essentially just debug code in the
source, no class.
Logger - packaged in a class, with default behavior (not configurable). Controlled by _DEBUG and writes to std::clog (can be re-directed).
I really do not know where to start with this, and have spent hours trying to find help somewhere.
Your task may be to use a more complex Logger class which makes logging throughout your entire project easier due to functionalities such as
being able to break a string on multiple lines of code when debugging it
separate methods between error logging and debug logging
being able to turn logging on/off from that Logger instance, so that all the debug messages throughout your project become obsolete (instead of needing to comment all of them for instance)
adding other methods specific to your case (you will see below)
This is a Logger class I used for a windows project. It supports all the features previously mentioned. Feel free to use it.
#include "Logger.h"
Logger::Logger(
wstring wstrComponentName,
wstring wstrFunctionName) :
_wstrApplicationName(APPLICATION_NAME),
_wstrComponentName(StripFileName(wstrComponentName)),
_wstrFunctionName(wstrFunctionName)
{}
// This function doesn't support string, but wstring for it's arguments
VOID
Logger::Debug(
LPCWSTR format,
...
)
{
wstring wstrBase =
_wstrApplicationName + wstring(L": ") +
_wstrComponentName + wstring(L": ") +
_wstrFunctionName + wstring(L": ");
va_list args;
va_start(args, format);
wchar_t* msg = new wchar_t[_1kB];
wvsprintf(msg, format, args);
va_end(args);
wstring wstrOutput = wstrBase + msg;
delete[] msg;
OutputDebugString(wstrOutput.c_str());
}
// This function doesn't support string, but wstring for it's arguments
VOID
Logger::Error(
LPCWSTR format,
...
)
{
wstring wstrBase = wstring(L"[ERROR] ") +
_wstrApplicationName + wstring(L": ") +
_wstrComponentName + wstring(L": ") +
_wstrFunctionName + wstring(L": ");
va_list args;
va_start(args, format);
wchar_t* msg = new wchar_t[_1kB];
wvsprintf(msg, format, args);
va_end(args);
wstring wstrOutput = wstrBase + msg;
delete[] msg;
OutputDebugString(wstrOutput.c_str());
}
wstring
Logger::StripFileName(
__in wstring wstrFileName
)
{
DWORD dwLeftLimit = 0;
DWORD dwRightLimit = wstrFileName.length();
wstring wstrResult(L"FileName unavailable");
if (wstrFileName.rfind(L"\\") != wstring::npos)
{
dwLeftLimit = wstrFileName.rfind(L"\\") + 1;
}
if (wstrFileName.rfind(L".") != wstring::npos)
{
dwRightLimit = wstrFileName.rfind(L".");
}
if (dwRightLimit > dwLeftLimit)
{
wstrResult = wstrFileName.substr(
dwLeftLimit,
dwRightLimit - dwLeftLimit
);
}
return wstrResult;
}
My current code looks something like this:
String^ callProcess(String^ cmd, String^ args)
{
Process^ p = gcnew Process();
p->StartInfo->UseShellExecute = false;
p->StartInfo->RedirectStandardOutput = true;
p->StartInfo->CreateNoWindow = true;
p->StartInfo->Arguments = args;
p->StartInfo->FileName = cmd;
p->Start();
p->WaitForExit();
String^ output = p->StandardOutput->ReadToEnd();
p->Close();
return output;
}
int main()
{
String^ output1 = callProcess("handle.exe","-arg1");
String^ output2 = callProcess("handle.exe","-arg2");
}
handle.exe opens a handle to a DLL let's call it HANDLE.DLL. After trying to look at output2 it has something like "Error during SomeClass Call.... Can't get a handle to HANDLE.DLL".
It looks like the first call to CallProcess isn't properly releasing the handle to the DLL. Is there a way I can force it to release the DLL handle?
I have a Java class that returns a unicode string... Java has the correct version of the string but when it comes through a JNI wrapper in the form of a jstring it must be converted over to a C++ or C++/CLI string. Here is some test code I have which actually works on most languages except for the asian char sets. Chinese Simplified & Japanese characters are garbled and I can't figure out why. Here is the code snippet, I don't see anything wrong with either methods of conversion (the if statement checks os as I have two VMs with diff OS's and runs the appropriate conversion method).
String^ JStringToCliString(const jstring string){
String^ converted = gcnew String("");
JNIEnv* envLoc = GetJniEnvHandle();
std::wstring value;
jboolean isCopy;
if(string){
try{
jsize len = env->GetStringLength(string);
if(Environment::OSVersion->Version->Major >= 6) // 6 is post XP/2003
{
TraceLog::Log("Using GetStringChars() for string conversion");
const jchar* raw = envLoc->GetStringChars(string, &isCopy);
// todo add exception handling here for jvm
if (raw != NULL) {
value.assign(raw, raw + len);
converted = gcnew String(value.c_str());
env->ReleaseStringChars(string, raw);
}
}else{
TraceLog::Log("Using GetStringUTFChars() for string conversion.");
const char* raw = envLoc->GetStringUTFChars(string, &isCopy);
if(raw) {
int bufSize = MultiByteToWideChar(CP_UTF8, 0 , raw , -1, NULL , 0 );
wchar_t* wstr = new wchar_t[bufSize];
MultiByteToWideChar( CP_UTF8 , 0 , raw , -1, wstr , bufSize );
String^ val = gcnew String(wstr);
delete[] wstr;
converted = val; // partially working
envLoc->ReleaseStringUTFChars(string, raw);
}
}
}catch(Exception^ ex){
TraceLog::Log(ex->Message);
}
}
return converted;
}
Answer was to enable east asian languages in Windows XP as Win7 + Later work fine. Super easy.... waste of a entire day lol.
I want to extract a const char* filename from a const char* filepath. I tried with regex but failed:
const char* currentLoadedFile = "D:\files\file.lua";
char fileName[256];
if (sscanf(currentLoadedFile, "%*[^\\]\\%[^.].lua", fileName)) {
return (const char*)fileName; // WILL RETURN "D:\files\file!!
}
The issue is that "D:\files\file" will be returned and not the wanted "file"(note: without ".lua")
What about using std::string?
e.g.
std::string path("d:\\dir\\subdir\\file.ext");
std::string filename;
size_t pos = path.find_last_of("\\");
if(pos != std::string::npos)
filename.assign(path.begin() + pos + 1, path.end());
else
filename = path;
Just use boost::filesystem.
#include <boost/filesystem.hpp>
std::string filename_noext;
filename_noext = boost::filesystem::path("D:\\files\\file.lua").stem().string().
const char* result_as_const_char = filename_noext.c_str();
or alternatively, if you want to introduce bugs yourself :
// have fun defining that to the separator of the target OS.
#define PLATFORM_DIRECTORY_SEPARATOR '\\'
// the following code is guaranteed to have bugs.
std::string input = "D:\\files\\file.lua";
std::string::size_type filename_begin = input.find_last_of(PLATFORM_DIRECTORY_SEPERATOR);
if (filename_begin == std::string::npos)
filename_begin = 0;
else
filename_begin++;
std::string::size_type filename_length = input.find_last_of('.');
if (filename_length != std::string::npos)
filename_length = filename_length - filename_begin;
std::string result = input.substr(filename_begin, filename_length);
const char* bugy_result_as_const_char = result.c_str();
You can do this portably and easily using the new filesystem library in C++17.
#include <cstdint>
#include <cstdio>
#include <filesystem>
int main()
{
std::filesystem::path my_path("D:/files/file.lua");
std::printf("filename: %s\n", my_path.filename().u8string().c_str());
std::printf("stem: %s\n", my_path.stem().u8string().c_str());
std::printf("extension: %s\n", my_path.extension().u8string().c_str());
}
Output:
filename: file.lua
stem: file
extension: .lua
Do note that for the time being you may need to use #include <experimental/fileystem> along with std::experimental::filesystem instead until standard libraries are fully conforming.
For more documentation on std::filesystem check out the filesystem library reference.
You can easily extract the file:
int main()
{
char pscL_Dir[]="/home/srfuser/kush/folder/kushvendra.txt";
char pscL_FileName[50];
char pscL_FilePath[100];
char *pscL;
pscL=strrchr(pscL_Dir,'/');
if(pscL==NULL)
printf("\n ERROR :INvalid DIr");
else
{
strncpy(pscL_FilePath,pscL_Dir,(pscL-pscL_Dir));
strcpy(pscL_FileName,pscL+1);
printf("LENTH [%d}\n pscL_FilePath[%s]\n pscL_FileName[%s]",(pscL-pscL_Dir),pscL_FilePath,pscL_FileName);
}
return 0;
}
output:
LENTH [25}
pscL_FilePath[/home/srfuser/kush/folder]
pscL_FileName[kushvendra.txt
Here you can find an example. I'm not saying it's the best and I'm sure you could improve on that but it uses only standard C++ (anyway at least what's now considered standard).
Of course you won't have the features of the boost::filesystem (those functions in the example play along with plain strings and do not guarantee/check you'll actually working with a real filesystem path).
// Set short name:
char *Filename;
Filename = strrchr(svFilename, '\\');
if ( Filename == NULL )
Filename = svFilename;
if ( Filename[0] == '\\')
++Filename;
if ( !lstrlen(Filename) )
{
Filename = svFilename;
}
fprintf( m_FileOutput, ";\n; %s\n;\n", Filename );
You could use the _splitpath_s function to break a path name into its components. I don't know if this is standard C or is Windows specific. Anyway this is the function:
#include <stdlib.h>
#include <string>
using std::string;
bool splitPath(string const &path, string &drive, string &directory, string &filename, string &extension) {
// validate path
drive.resize(_MAX_DRIVE);
directory.resize(_MAX_DIR);
filename.resize(_MAX_FNAME);
extension.resize(_MAX_EXT);
errno_t result;
result = _splitpath_s(path.c_str(), &drive[0], drive.size(), &directory[0], directory.size(), &filename[0], filename.size(), &extension[0], extension.size());
//_splitpath(path.c_str(), &drive[0], &directory[0], &filename[0], &extension[0]); //WindowsXp compatibility
_get_errno(&result);
if (result != 0) {
return false;
} else {
//delete the blank spaces at the end
drive = drive.c_str();
directory = directory.c_str();
filename = filename.c_str();
extension = extension.c_str();
return true;
}
}
It is a lot easier and safe to use std::string but you could modify this to use TCHAR* (wchar, char)...
For your specific case:
int main(int argc, char *argv[]) {
string path = argv[0];
string drive, directory, filename, extension;
splitPath(path, drive, directory, filename, extension);
printf("FILE = %s%s", filename.c_str(), extension.c_str());
return 0;
}
If you are going to display a filename to the user on Windows you should respect their shell settings (show/hide extension etc).
You can get a filename in the correct format by calling SHGetFileInfo with the SHGFI_DISPLAYNAME flag.