Simple C++ Logging - c++

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;
}

Related

Convert std::string to STRSAFE_LPCWSTR in an utility method

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));

Passing std::string to C++ DLL for use with C# dll

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.

How to set HTML Unicode text to clipboard in VC++?

I am a newbie to C++. I want to get the content of the clipboard, which might contain Unicode chars, append a div tag with some content formatted in HTML and set that back to clipboard.
I have achieved successfully in getting the content and appending it. But could not set it back to the clipboard as an HTML text. I have achieved setting as simple text. Here is my code:
#include <shlwapi.h>
#include <iostream>
#include <conio.h>
#include <stdio.h>
using namespace std;
wstring getClipboard(){
if (OpenClipboard(NULL)){
HANDLE clip = GetClipboardData(CF_UNICODETEXT);
WCHAR * c;
c = (WCHAR *)clip;
CloseClipboard();
return (WCHAR *)clip;
}
return L"";
}
bool setClipboard(wstring textToclipboard)
{
if (OpenClipboard(NULL)){
EmptyClipboard();
HGLOBAL hClipboardData;
size_t size = (textToclipboard.length()+1) * sizeof(WCHAR);
hClipboardData = GlobalAlloc(NULL, size);
WCHAR* pchData = (WCHAR*)GlobalLock(hClipboardData);
memcpy(pchData, textToclipboard.c_str(), size);
SetClipboardData(CF_UNICODETEXT, hClipboardData);
GlobalUnlock(hClipboardData);
CloseClipboard();
return true;
}
return false;
}
int main (int argc, char * argv[])
{
wstring s = getClipboard();
s += std::wstring(L"some extra text <b>hello</b>");
setClipboard(s);
getch();
return 0;
}
I did try using the code described here and read the doc here. But I couldn't make it work. What I tried could be way off track or completely wrong.
Update: The code below is what I tried after the modifications suggested by Cody Gray to the original code presented here:
bool CopyHTML2(WCHAR *html ){
wchar_t *buf = new wchar_t [400 + wcslen(html)];
if(!buf) return false;
static int cfid = 0;
if(!cfid) cfid = RegisterClipboardFormat("HTML Format");
// Create a template string for the HTML header...
wcscpy(buf,
L"Version:0.9\r\n"
L"StartHTML:00000000\r\n"
L"EndHTML:00000000\r\n"
L"StartFragment:00000000\r\n"
L"EndFragment:00000000\r\n"
L"<html><body>\r\n"
L"<!--StartFragment -->\r\n");
// Append the HTML...
wcscat(buf, html);
wcscat(buf, L"\r\n");
// Finish up the HTML format...
wcscat(buf,
L"<!--EndFragment-->\r\n"
L"</body>\r\n"
L"</html>");
wchar_t *ptr = wcsstr(buf, L"StartHTML");
wsprintfW(ptr+10, L"%08u", wcsstr(buf, L"<html>") - buf);
*(ptr+10+8) = L'\r';
ptr = wcsstr(buf, L"EndHTML");
wsprintfW(ptr+8, L"%08u", wcslen(buf));
*(ptr+8+8) = '\r';
ptr = wcsstr(buf, L"StartFragment");
wsprintfW(ptr+14, L"%08u", wcsstr(buf, L"<!--StartFrag") - buf);
*(ptr+14+8) = '\r';
ptr = wcsstr(buf, L"EndFragment");
wsprintfW(ptr+12, L"%08u", wcsstr(buf, L"<!--EndFrag") - buf);
*(ptr+12+8) = '\r';
// Open the clipboard...
if(OpenClipboard(0)) {
EmptyClipboard();
HGLOBAL hText = GlobalAlloc(GMEM_MOVEABLE |GMEM_DDESHARE, wcslen(buf)+4);
wchar_t *ptr = (wchar_t *)GlobalLock(hText);
wcscpy(ptr, buf);
GlobalUnlock(hText);
SetClipboardData(cfid, hText);
CloseClipboard();
GlobalFree(hText);
}
// Clean up...
delete [] buf;
return true;
}
This code compiles successfully, But I get the following error at SetClipboardData : HEAP[Project1.exe]: Heap block at 007A8530 modified at 007A860A past requested size of d2
Project1.exe has triggered a breakpoint.
Please guide me on how to proceed. I am using Visual Studio Express 2012 on Windows 8. Thanks.
You're mismatching ANSI (narrow) and Unicode (wide) strings.
Unlike the wcscpy function, the w in the wsprintf function doesn't stand for "wide", it stands for "Windows". It is part of the Win32 API, rather than the C runtime library. All of the Win32 API functions that work with strings have two versions, one suffixed with an A that deals with ANSI strings and another suffixed with a W that deals with wide strings. The headers hide all of this from you with macros. I explain all of this in more detail here—recommended reading.
Anyway, the simple fix here is to explicitly call the wide variant of that function, since you're correctly using wide strings everywhere else. Make all the calls to wsprintf look like this:
wchar_t *ptr = wcsstr(buf, L"StartHTML");
wsprintfW(ptr+10, L"%08u", wcsstr(buf, L"<html>") - buf);
*(ptr+10+8) = L'\r';
Alternatively, you could use the swprintf function provided by the C runtime library instead of the Win32 version. This one works just like the wcsstr and wcscpy functions you're using elsewhere. The w in the name means "wide". The documentation for this series of functions is here.
Note also that when you use character or string literals, they also need to be wide characters. You accomplish that by prepending them with an L. You do that some places, but miss doing it others. Make sure that you do it consistently.
The compiler should warn you about all this, though. You just need to make sure you turn your warning level up and don't ignore any of the warnings. Also make sure that both the UNICODE and _UNICODE preprocessor symbols are defined globally for your project. That will ensure that you are always calling the Unicode/wide versions of functions. Although that should be the default for all new projects.
This is the function I came up with the help of Jochen Arndt at codeproject.com. Hope this helps somebody. Here is a complete working code, if you are interested in checking this out.
It still has one problem. That is when pasted to onenote alone, it pastes gibberish after a anchor tag. It does not happen with Word, PowerPoint or Excel. And it does not have this problem for normal English language texts. If you have a solution for this, please do let me know. The problem seems to be with OneNote. Not with the code.
bool setClipboard(LPCWSTR lpszWide){
int nUtf8Size = ::WideCharToMultiByte(CP_UTF8, 0, lpszWide, -1, NULL, 0, NULL, NULL);
if (nUtf8Size < 1) return false;
const int nDescLen = 105;
HGLOBAL hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, nDescLen + nUtf8Size);
if (NULL != hGlobal)
{
bool bErr = false;
LPSTR lpszBuf = static_cast<LPSTR>(::GlobalLock(hGlobal));
LPSTR lpszUtf8 = lpszBuf + nDescLen;
if (::WideCharToMultiByte(CP_UTF8, 0, lpszWide, -1, lpszUtf8, nUtf8Size, NULL, NULL) <= 0)
{
bErr = true;
}
else
{
LPCSTR lpszStartFrag = strstr(lpszUtf8, "<!--StartFragment-->");
LPCSTR lpszEndFrag = strstr(lpszUtf8, "<!--EndFragment-->");
lpszStartFrag += strlen("<!--StartFragment-->") + 2;
int i = _snprintf(
lpszBuf, nDescLen,
"Version:1.0\r\nStartHTML:%010d\r\nEndHTML:%010d\r\nStartFragment:%010d\r\nEndFragment:%010d\r\n",
nDescLen,
nDescLen + nUtf8Size - 1, // offset to next char behind string
nDescLen + static_cast<int>(lpszStartFrag - lpszUtf8),
nDescLen + static_cast<int>(lpszEndFrag - lpszUtf8));
}
::GlobalUnlock(hGlobal);
if (bErr)
{
::GlobalFree(hGlobal);
hGlobal = NULL;
}
// Get clipboard id for HTML format...
static int cfid = 0;
cfid = RegisterClipboardFormat("HTML Format");
// Open the clipboard...
if(OpenClipboard(0)) {
EmptyClipboard();
HGLOBAL hText = GlobalAlloc(GMEM_MOVEABLE |GMEM_DDESHARE, strlen(lpszBuf)+4);
char *ptr = (char *)GlobalLock(hText);
strcpy(ptr, lpszBuf);
GlobalUnlock(hText);
::SetClipboardData(cfid, hText);
CloseClipboard();
GlobalFree(hText);
}
}
return NULL != hGlobal;
}
Your problem comes from the use of wchar_t instead of char in the cited example which makes you wrong on the offset computations.
I would however recommend you avoiding the use of wchar_t for transfering UNICODE text to the clipboard. Indeed, UTF-8 char could coded with a sequence of bytes comprised between 1 and 4 bytes, while wchar_t on Windows is a fixed 2 bytes type.
As explained in the Microsoft doc refered in your email, the content of the clipboard shall be UNICODE, which happens to be the same as ASCII for the characters contained in the header of the clipboard memory.
To transfert UNICODE in the clipboard, you can do it using the standard char C++ functions to prepare the content sent to clipboard (std::string for eg.)
While the cited example works, please find here another code sample using C++ framework that can actually copy UTF-8 chars to the clipboard in HTML format:
void copyHTMLtoClipboard(const std::string& html) {
std::string contextStart("Version:0.9\r\nStartHTML:0000000000\r\nEndHTML:0000000000\r\nStartFragment:0000000000\r\nEndFragment:0000000000\r\n<html><body>\r\n<!--StartFragment -->\r\n");
std::string contextEnd("\r\n<!--EndFragment -->\r\n</body></html>");
std::stringstream aux;
aux << contextStart << html << contextEnd;
std::string res = aux.str();
size_t htmlStart = 105 * sizeof(char);
size_t fragmentStart = 119 * sizeof(char);
size_t htmlEnd = res.size() * sizeof(char);
size_t fragmentEnd = htmlEnd - 35 * sizeof(char);
aux.fill('0');
aux.width(10);
aux.seekp(23);
aux << htmlStart;
aux.seekp(43);
aux.fill('0');
aux.width(10);
aux << htmlEnd;
aux.seekp(69);
aux.fill('0');
aux.width(10);
aux << fragmentStart;
aux.seekp(93);
aux.fill('0');
aux.width(10);
aux << fragmentEnd;
res = aux.str();
HGLOBAL hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, htmlEnd + sizeof(char));
LPSTR dst = (LPSTR)GlobalLock(hdst);
memcpy(dst, res.c_str(), htmlEnd);
dst[htmlEnd] = 0;
GlobalUnlock(hdst);
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(RegisterClipboardFormat(L"HTML Format"), hdst);
CloseClipboard();
GlobalFree(hdst);
}
Note that this code was compiled defining the macros _UNICODE and UNICODE.

C++/CLI UTF-8 & JNI Not Converting Unicode String Properly

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.

How can I refactor this to use an inline function or template instead of a macro?

I have a useful macro here:
#include <algorithm>
#include <vector>
#include <string>
#include <boost/algorithm/string.hpp>
#include <Windows.h>
namespace Path {
bool Exists(const std::wstring& path)
{
DWORD result = GetFileAttributesW(path.c_str());
return result != INVALID_FILE_ATTRIBUTES;
}
// THIS IS THE MACRO IN QUESTION!
#define PATH_PREFIX_RESOLVE(path, prefix, environment) \
if (boost::algorithm::istarts_with(path, prefix)) { \
ExpandEnvironmentStringsW(environment, buffer, MAX_PATH); \
path.replace(0, (sizeof(prefix)/sizeof(wchar_t)) - 1, buffer); \
if (Exists(path)) return path; \
}
std::wstring Resolve(std::wstring path)
{
using namespace boost::algorithm;
wchar_t buffer[MAX_PATH];
trim(path);
if (path.empty() || Exists(path)) return path;
//Start by trying to see if we have a quoted path
if (path[0] == L'"') {
return std::wstring(path.begin() + 1, std::find(path.begin() + 1, path.end(), L'"'));
}
//Check for those nasty cases where the beginning of the path has no root
PATH_PREFIX_RESOLVE(path, L"\\", L"");
PATH_PREFIX_RESOLVE(path, L"?\?\\", L"");
PATH_PREFIX_RESOLVE(path, L"\\?\\", L"");
PATH_PREFIX_RESOLVE(path, L"globalroot\\", L"");
PATH_PREFIX_RESOLVE(path, L"system32\\", L"%systemroot%\\System32\\");
PATH_PREFIX_RESOLVE(path, L"systemroot\\", L"%systemroot%\\");
static std::vector<std::wstring> pathExts;
if (pathExts.empty()) {
#define MAX_ENVVAR 32767
wchar_t pathext[MAX_ENVVAR];
DWORD length = GetEnvironmentVariableW(L"PATHEXT", pathext, MAX_ENVVAR);
if (!length) WindowsApiException::ThrowFromLastError();
split(pathExts, pathext, std::bind2nd(std::equal_to<wchar_t>(), L';'));
pathExts.insert(pathExts.begin(), std::wstring());
}
std::wstring::iterator currentSpace = path.begin();
do {
currentSpace = std::find(currentSpace, path.end(), L' ');
std::wstring currentPath(path.begin(), currentSpace);
std::wstring::size_type currentPathLength = currentPath.size();
typedef std::vector<std::wstring>::const_iterator ExtIteratorType;
for(ExtIteratorType it = pathExts.begin(); it != pathExts.end(); it++) {
currentPath.replace(currentPathLength, currentPath.size() - currentPathLength, *it);
if (Exists(currentPath)) return currentPath;
}
if (currentSpace != path.end())
currentSpace++;
} while (currentSpace != path.end());
return path;
}
}
It's used about 6 times within the scope of a single function (that's it), but macros seem to have "bad karma" :P
Anyway, the problem here is the sizeof(prefix) part of the macro. If I just replace this with a function taking a const wchar_t[], then the sizeof() will fail to deliver expected results.
Simply adding a size member doesn't really solve the problem either. Making the user supply the size of the constant literal also results in a mess of duplicated constants at the call site.
Any ideas on this one?
Pass the array by reference, using a template to infer the length. I'll go looking for an example, but basically:
template<size_t N>
bool func(const char (&a)[N], blah, blah) { ... }
EDIT: Someone explained it here:
http://heifner.blogspot.com/2008/04/c-array-size-determination.html
Why not just make it a regular function that uses wcslen() to get the length of the parameter you're interested in. There's enough stuff going on in that macro/function that I imagine there's little value trying to force it to be inlined. The 'overhead; of the wcslen() call and processing is almost certainly not going to be a bottleneck.
The only trick in the macro that you really should be concerned with is (as GMan pointed out) the return from the within the macro that's hidden when the macro is invoked.
Just have the thing be a function that returns a success/fail, and you can return if the function succeeds:
bool PathPrefixResolve( std::wstring& path, wchar_t const* prefix, wchar_t const* environment)
{
wchar_t buffer[MAX_PATH];
if (boost::algorithm::istarts_with(path, prefix)) {
ExpandEnvironmentStringsW( environment, buffer, MAX_PATH);
std::wstring tmp( path);
tmp.replace(0, wcslen( prefix), buffer);
if (Exists(tmp)) {
path = tmp;
return true;
}
}
return false;
}
to use the function:
//Check for those nasty cases where the beginning of the path has no root
if (PathPrefixResolve2(path, L"\\", L"")) return path;
if (PathPrefixResolve2(path, L"?\?\\", L"")) return path;
if (PathPrefixResolve2(path, L"\\?\\", L"")) return path;
if (PathPrefixResolve2(path, L"globalroot\\", L"")) return path;
if (PathPrefixResolve2(path, L"system32\\", L"%systemroot%\\System32\\")) return path;
if (PathPrefixResolve2(path, L"systemroot\\", L"%systemroot%\\")) return path;
Given what the processing that's occurring in the macro, I don't think you need to be worried about function call overhead.
Also, your macro implementation has some behavior which I think is probably a bug - if the path starts with L"\\?\\" that means it also starts with L"\\" and your first invocation of the macro:
PATH_PREFIX_RESOLVE(path, L"\\", L"");
will change the path variable. As the program gets maintained and additional prefixes get added, the problem could be seen with other path prefixes. This bug isn't in the function version, since the function changes the path parameter only when there's a verified match.
However, there's still possibly an issue when dealing with the L"\\?\\" and L"\\" prefixes in that both might be a match - you need to make sure you pass in the prefixes that might match more than once in 'priority' order.
Have you tried replacing sizeof(prefix)/sizeof(wchar_t) with wcslen(prefix)?