This question already has answers here:
How do I concatenate const/literal strings in C?
(17 answers)
Closed 6 years ago.
I want to add an string to a LPTSTR.
The code is:
hSourceFile = CreateFile(
pszSourceFile,
FILE_READ_DATA,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE != hSourceFile)
{
_tprintf(
TEXT("The source plaintext file, %s, is open. \n"),
pszSourceFile);
}
The pszSourceFile is a type of LPTSTR, but i want to add some extra text.
Like (not working)
pszSourceFile + ".txt"
What is the best way to do this?
Considering the C-style and the use of the Windows API (with TEXT() et. al.); use _tcscat() or _tcsncat() (the later requires a buffer size).
For example;
TCHAR buffer[1024] = {}; // or '\0'
_tcsncat(buffer, pszSourceFile, 1024);
_tcsncat(buffer, TEXT(".txt"), 1024);
Demo.
Warning; watch out for your buffer overruns. Assuming the "normal" Windows 260 character path file and name limits (_MAX_PATH), the buffer needs to cater for that.
For C++ (as originally tagged), an alternative is to use std::basic_string<TCHAR> and then the operator+ (or +=) as per usual. The .c_str() will get you the resultant string;
std::basic_string<TCHAR> buffer(pszSourceFile);
buffer += TEXT(".txt");
auto ptr = buffer.c_str();
Your particular use case is not a simple "append" but rather an insert/format. Along the same lines as Niall you are using the TCHAR macros so I would recommend _stprintf_s (or _sntprintf_s... see MSDN)
TCHAR output[SIZE] = {0};
_stprintf_s(output, _T("The %s directory"), pszSourceFile);
Of course it depends on what exactly pszSourceFile is... if it is a std::string then you'll need to use the c_str() member to get a pointer, and you'll need to be careful about using std::string versus std::wstring.
Related
I have a C app that I need to compile in Windows. And I am really unable to wrap my head around the UNICODE and ANSI concept in Windows
I want to use GetDriveType function and there are 2 variables A and W. There is also a note here saying that GetDriveType is an alias to both and will select either based on some pre-processor.
But how should I call this function ?
This is what I am trying:
const TCHAR* path = "C:\\Users\\";
const TCHAR* trailing_slash = "\\";
size_t requiredSize = mbstowcs(NULL, path, 0);
TCHAR* win_path = (char*)malloc((requiredSize + 2) * sizeof(char));
UINT driveType = 0;
strncpy(win_path, path, requiredSize + 1);
strncat(win_path, trailing_slash, 2);
printf("Checking path: %s\n", win_path);
driveType = GetDriveType(win_path);
wprintf(L"Drive type is: %d\n", driveType);
if (driveType == DRIVE_FIXED)
printf("Success\n");
else
printf("Failure\n");
return 0;
It produces the result
Checking path: C:\Users\
Drive type is: 1
Failure
If I replace GetDriveType with GetDriveTypeA it returns the correct value 3 and succeeds.
I tried another variant too
size_t requiredSize = mbstowcs(NULL, path, 0);
uint32_t drive_type = 0;
const wchar_t *trailing_slash = L"\\";
wchar_t *win_path = (wchar_t*) malloc((requiredSize + 2) * sizeof(wchar_t));
/* Convert char* to wchar* */
size_t converted = mbstowcs(win_path, path, requiredSize+1);
/* Add a trailing backslash */
wcscat(win_path, trailing_slash);
/* Finally, check the path */
drive_type = GetDriveType(win_path);
I see this warning:
'function' : incompatible types - from 'wchar_t *' to 'LPCSTR'
So, which one to use ? How is it generic ? The path I will be reading is from an environment variable on Windows
What is TCHAR and wchar_t etc. ? I found this post, but could not understand much
This Microsoft post says
Depending on your preference, you can call the Unicode functions explicitly, such as SetWindowTextW, or use the macros
So is it Ok to use wchar_t everywhere and call GetDriveTypeW directly ?
Back in the mid-90s you had Windows 95/98/ME that did not support Unicode and NT4/2000/XP that did. You could create source code that could compile with or without Unicode support just by changing the UNICODE define.
This type of code looks like this:
UINT type = GetDriveType(TEXT("c:\\"));
There is no function named GetDriveType, 99% of all functions that take a string parameter in Windows have two versions, in this case GetDriveTypeA and GetDriveTypeW.
Inside the Windows header files you have code that looks like this:
#ifdef UNICODE
#define GetDriveType GetDriveTypeW
#else
#define GetDriveType GetDriveTypeA
#endif
If UNICODE is defined before including windows.h the above code expands to:
UINT type = GetDriveTypeW(L"c:\\");
and if not:
UINT type = GetDriveTypeA("c:\\");
These days most applications should use Unicode. Whether you should use wchar_t/WCHAR and call GetDriveTypeW directly or still rely on the defines is a style question. There might be situations where you need to force the A or W function and that is OK as well.
The same applies to the C library with the _TEXT macro and the _tcs functions except that those are controlled by the _UNICODE define.
If you get a warning about incompatible string types then you are calling the wrong function or you have not added #define UNICODE (and _UNICODE). If you are compiling cross platform code intended for Unix you might have to convert from char* to a wide string in some places.
See also:
TEXT vs. _TEXT vs. _T, and UNICODE vs. _UNICODE
I've got a C project that I'm working on and I'm having a problem.
The program reads a string that is echoed by a .php page. It uses this code
to read the data and appoint it to a variable, which get sent to the Commands() function:
LPSTR szBuffer=(LPSTR)chunk+0x1000;
DWORD dwRead;
if (CWA(_HttpSendRequestA, wininet, hHttpRequest, szHeaders, lpstrlenA(szHeaders), szReq, lpstrlenA(szReq)) != 0)
{
CWA(_InternetReadFileA, wininet, hHttpRequest, szBuffer, 0x400, &dwRead);
if (dwRead)
Commands((LPBYTE)szBuffer, dwRead);
}
As you can see the data is sent to the Commands() function, which receives the LPBYTE szBuffer (named "command" in the function) and the DWORD dwRead (named "size" in the function).
In the Commands() function, it's supposed to read the string that it read from the .php page. However, since the data seems to be stored as LPBYTE, I've done a lot of things trying to get that to a char*. When I thought I had finally got it however, I tried outputting it using a MessageBox (to see if it displays the string it should have read). However, this returns me Chinese characters (while the original string should be this:
"TASKci=0C73CCFD206BBD011E7087CE0806E6F6E69630,job=dlex,ti=AD62A5950B76F3812C542C24040EACE9,pr=https,ur=//test.com/test.txt,cl=".
Screenshot of what it returns me: http://prntscr.com/h0p5iw
How the code inside Commands() looks:
BOOL Commands(LPBYTE command, DWORD size) {
LPTSTR x = (LPTSTR)((char*)command);
{
int msgboxID = MessageBox(
NULL,
x,
(LPCWSTR)L"Woop",
MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2 );
}
CWA(Sleep, kernel32, 100);
return 1; }
I'm new at C (I've only written stuff in C# before) so I am sorry if I am asking any dumb questions, I've really tried my best but I cannot seem to find any solution by myself.
Also, keep in mind that everything except for the stuff inside the Commands() function is not coded by me but by someone who is way more experienced. That code should be fine and I am sure that it is reading the data from the page, it's probably just me screwing up a conversion somewhere.
A narrow string (char*) tends to look like Chinese when you use it somewhere that expects a wide UTF-16 Unicode string.
You cannot just cast the string to change its type, you need to call MultiByteToWideChar.
dirname() is really terrible, because it modifies the argument so that it need another ugly copy of the original string. So no dirname(), please.
Is there any function like that but which is able to use safely?
EDIT: To fix the horrible workaround when I was stupid (two years ago);
std::string_view getDirName(std::string_view filePath) {
return filePath.substr(0, filePath.rfind('/'));
}
Standard C99 or C11 do not know about directories, which is a notion provided by some operating system API (or some external library above it).
On Linux, dirname(3) man page shows examples calling strdup(3):
char *dirc, *basec, *bname, *dname;
char *path = "/etc/passwd";
dirc = strdup(path);
basec = strdup(path);
dname = dirname(dirc);
bname = basename(basec);
printf("dirname=%s, basename=%s\n", dname, bname);
(of course you should free both dirc and basec, and the code above don't check for failure of strdup)
You might also want the canonical directory of a path, using realpath(3). For example, you would code:
char* filepath = something();
char* canpath = realpath(filepath, NULL);
if (!canpath) { perror("realpath"); exit(EXIT_FAILURE); };
// so canpath is malloc-ed
char *candir = dirname(canpath); // would modify the string in canpath
/// use candir here, e.g.
printf("canonical directory for %s is %s\n", filepath, candir);
free (canpath);
BTW, glib offers g_path_get_dirname (whose result should be freed).
The freebsd man page of dirname(3) says that The dirname() function returns a pointer to internal storage space allocated on the first call that will be overwritten by subsequent calls. so check your documentation. Anyway, you can get a safe call if your implementation modifies directly the input string with:
char *aux = dirname(strdup(the_path));
...
free(aux);
I want to hook NtReadFile so that it can change text that is read from the file. But when I try to read a file, I get the message "This application has failed to start because the application configuration is incorrect".
Here's my code. What's wrong?
NTSTATUS HookNtReadFile (
IN HANDLE FileHandle,
IN HANDLE Event,
IN PIO_APC_ROUTINE ApcRoutine,
IN PVOID ApcContext,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER ByteOffset,
IN PULONG Key)
{
NTSTATUS retstatus;
retstatus = glRealNtReadFile (FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, Buffer, Length, ByteOffset, Key);
IoStatusBlock->Information = 3;
Length = 3;
Buffer = ExAllocatePool(PagedPool, Length);
Buffer = "hi";
return retstatus;
}
This is clearly not going to work:
Buffer = ExAllocatePool(PagedPool, Length);
Buffer = "hi";
You're allocating memory, then immediately discarding that address. This is not how you copy strings in C. You need to use strcpy, or preferably one of the safer alternatives.
It's also worth pointing out that the Native API doesn't use ASCII characters. In general all strings are expected to be wide strings.
Lastly, you should only be changing the values if the return code indicates success, and (as others have pointed out in the comments) when the file handle is associated with the specific file you're trying to change.
http://www.rohitab.com/discuss/topic/40492-my-first-kernel-mode-rootkit/
I know it looks like a dodgy link. But the answer you seek can be found at a click.
I am writing a program in C and Windows API. I am using Visual Studio 2010 Express and Character Set is set to "Not Set". I have made an edit control to accept username. Here's declaration:
hwnduser = CreateWindow (TEXT("EDIT"), NULL,
WS_VISIBLE | WS_CHILD | WS_BORDER,
220, 70, 80, 20,
hwnd, (HMENU) 3, NULL, NULL);
I am fetching its value into a string named username.
len = GetWindowTextLength(hwnduser) + 1;
GetWindowText(hwnduser, username, len);
Now, the valid username is in a string called c_user:
char c_user[] = "foo";
When I compare them to check if the user has entered valid username using following code,
if (username == c_user)
{
MessageBox(hwnd, "Foo", "Bar", MB_OK);
}
else
{
MessageBox(hwnd, "Bar", "Foo", MB_OK);
}
It never validates. Instead, the else condition is always executed! Where am I making a mistake?
How to correct this?
I have tried strcmp! But still, output does not change. See the output(and comparison in code):
C and C++ have no built-in string type and so you cannot compare strings this way. C and C++ instead use an array of chars and this syntax simply compares the address of each array (which won't match).
Instead use strcmp() or _tcscmp().
I believe you'll actually need to use wchar_t's (wide characters). it's been a while since I've looked at the syntax but i think it'll be something like this:
wchar_t* c_user = L"foo";
if (wcscmp(username, c_user) == 0)
...
make sure username is also defined as the correct type.
you might also look into TCHAR which is a more generic representatic of a character type (it changes based off of the compiler settings). depending on settings, itll either be a char or wchar_t i think.
Writing username == c_user checks whether they both point to the same memory location.
You need to call strcmp to compare the strings' values.
I'd use strcmp (or any synonym)
if ( strcmp( username, c_user) == 0 )
{
// 0 indicate there is no difference, thus equal
}
else
{
}
You should use strcmp for this , or strcmpi if you want to ignore the case.
if (strcmp(username, c_user) == 0)
{
...
}
Use the functions GetWindowTextA() and MessageBoxA(), it works for me.