windows-nt/Source/XPSP1/NT/printscan/fax/samples/faxsiren/faxsiren.cpp
2020-09-26 16:20:57 +08:00

934 lines
25 KiB
C++

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
faxsiren.cpp
Abstract:
sample routing extension. Sets an event when a fax is received, writes routing data into a log file.
--*/
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <faxroute.h>
#include <winfax.h>
//
// macros
//
#define ValidString( String ) ((String) ? (String) : L"" )
#ifdef DBG
#define MYDEBUG( parm ) DebugPrint parm
#else
#define MYDEBUG( parm )
#endif
//
// constants
//
#define ROUTEITGUID L"{5797dee0-e738-11d0-83c0-00c04fb6e984}"
#define FAXSIREN L"FaxSirenEvent"
#define LOGNAME L"%temp%\\ReceiveLog.txt"
#define ININAME L"%temp%\\FaxRoute.ini"
#define SIRENDIR L"FaxSirenFolder"
#define EXTENSIONNAME L"FaxSiren Routing Extension"
#define EXTENSIONFRIENDLYNAME L"Fax Siren"
#define EXTENSIONPATH L"%systemroot%\\system32\\faxsiren.dll"
#define FAXSIRENMETHOD L"Siren"
#define FAXSIRENFRIENDLYNAME L"Routing Siren"
#define FAXSIRENFUNCTION L"RouteIt"
//
// forward declarations
//
BOOL
WriteRoutingInfoIntoIniFile(
LPWSTR TiffFileName,
PFAX_ROUTE FaxRoute
);
BOOL
AppendFileNametoLogFile(
LPWSTR TiffFileName,
PFAX_ROUTE FaxRoute
);
void
DebugPrint(
LPWSTR,
...
);
BOOL WINAPI
ExtensionCallback(
IN HANDLE FaxHandle,
IN LPVOID Context,
IN OUT LPWSTR MethodName,
IN OUT LPWSTR FriendlyName,
IN OUT LPWSTR FunctionName,
IN OUT LPWSTR Guid
);
//
// globals
//
PFAXROUTEADDFILE FaxRouteAddFile;
PFAXROUTEDELETEFILE FaxRouteDeleteFile;
PFAXROUTEGETFILE FaxRouteGetFile;
PFAXROUTEENUMFILES FaxRouteEnumFiles;
PFAXROUTEMODIFYROUTINGDATA FaxRouteModifyRoutingData;
HANDLE hHeap;
HANDLE hReceiveEvent;
CRITICAL_SECTION csRoute;
LPWSTR IniFile = NULL;
LPWSTR LogFile = NULL;
HINSTANCE MyhInstance;
extern "C"
DWORD
DllEntry(
HINSTANCE hInstance,
DWORD Reason,
LPVOID Context
)
/*++
Routine Description:
dll entrypoint
Arguments:
hInstance - Module handle
Reason - Reason for being called
Context - Register context
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
switch (Reason) {
case DLL_PROCESS_ATTACH:
MyhInstance = hInstance;
DisableThreadLibraryCalls(hInstance);
break;
case DLL_PROCESS_DETACH:
//
// cleanup
//
if (hReceiveEvent) CloseHandle(hReceiveEvent);
break;
}
return TRUE;
}
STDAPI
DllRegisterServer(
VOID
)
/*++
Routine Description:
Function for the in-process server to create its registry entries
Return Value:
S_OK on success
Notes:
We leverage the DllRegisterServer entrypoint as an easy way to configure
our routing extension for use on the system. Note that the extension doesn't
have any COM code in it per se, but this makes installation much simpler since
the setup code doesn't have to use custom code to setup the routing extension.
--*/
{
HRESULT RetCode;
HMODULE hWinFax;
PFAXREGISTERROUTINGEXTENSION pFaxRegisterRoutingExtension;
PFAXCONNECTFAXSERVER pFaxConnectFaxServer;
PFAXCLOSE pFaxClose;
HANDLE hFax;
DWORD ExtensionCount = 0;
//
// we assume that the routing extension has already been installed into the
// proper location by the setup code.
//
hWinFax = LoadLibrary( L"winfax.dll" );
if (!hWinFax) {
MYDEBUG(( L"LoadLibrary failed, ec = %d\n", GetLastError() ));
RetCode = E_UNEXPECTED;
goto e0;
}
pFaxRegisterRoutingExtension = (PFAXREGISTERROUTINGEXTENSION) GetProcAddress(
hWinFax,
"FaxRegisterRoutingExtensionW" );
pFaxConnectFaxServer = (PFAXCONNECTFAXSERVER) GetProcAddress(
hWinFax,
"FaxConnectFaxServerW" );
pFaxClose = (PFAXCLOSE) GetProcAddress(
hWinFax,
"FaxClose" );
if (!pFaxRegisterRoutingExtension || !pFaxConnectFaxServer || !pFaxClose) {
MYDEBUG(( L"GetProcAddress failed, ec = %d\n", GetLastError() ));
RetCode = E_UNEXPECTED;
goto e1;
}
if (!pFaxConnectFaxServer( NULL, &hFax )) {
MYDEBUG(( L"FaxConnectFaxServer failed, ec = %d\n", GetLastError() ));
RetCode = HRESULT_FROM_WIN32( GetLastError() );
goto e1;
}
if (!pFaxRegisterRoutingExtension(
hFax,
EXTENSIONNAME,
EXTENSIONFRIENDLYNAME,
EXTENSIONPATH,
ExtensionCallback,
(LPVOID) &ExtensionCount
)) {
MYDEBUG(( L"FaxRegisterRoutingExtension failed, ec = %d\n", GetLastError() ));
RetCode = HRESULT_FROM_WIN32( GetLastError() );
goto e2;
}
RetCode = S_OK;
e2:
pFaxClose( hFax );
e1:
FreeLibrary( hWinFax );
e0:
return RetCode;
}
BOOL WINAPI
ExtensionCallback(
IN HANDLE FaxHandle,
IN LPVOID Context,
IN OUT LPWSTR MethodName,
IN OUT LPWSTR FriendlyName,
IN OUT LPWSTR FunctionName,
IN OUT LPWSTR Guid
)
/*++
Routine Description:
Callback function for adding our routing extensions
Return Value:
TRUE if we added another extension, FALSE if we're done adding extensions
--*/
{
PDWORD ExtensionCount = (PDWORD) Context;
//
// since we only have one extension, this is a really simple function
// --we don't really need the context data above, it's just here to
// illustrate what you might use the context data for.
//
if (ExtensionCount) {
MYDEBUG(( L"ExtensionCallback called for extension %d\n", *ExtensionCount ));
} else {
MYDEBUG(( L"context data is NULL, can't continue\n", *ExtensionCount ));
return FALSE;
}
if (*ExtensionCount != 0) {
//
// we've added all of our methods, return FALSE to signify that we're done
//
return FALSE;
}
wcscpy(MethodName, FAXSIRENMETHOD );
wcscpy(FriendlyName, FAXSIRENFRIENDLYNAME );
wcscpy(FunctionName, FAXSIRENFUNCTION );
wcscpy(Guid, ROUTEITGUID );
*ExtensionCount += 1;
return TRUE;
}
//
// required exports
//
BOOL WINAPI
FaxRouteInitialize(
IN HANDLE HeapHandle,
IN PFAX_ROUTE_CALLBACKROUTINES FaxRouteCallbackRoutines
)
/*++
Routine Description:
This functions is called by the fax service to initialize the routing extension. This function
should only be called once per instantiation of the fax service
Arguments:
HeapHandle - Heap handle for memory all allocations
FaxRouteCallbackRoutines - structure containing callback functions
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
DWORD dwNeeded;
SECURITY_DESCRIPTOR sd;
SECURITY_ATTRIBUTES sa;
//
// make sure we can understand the structure
//
if ( FaxRouteCallbackRoutines->SizeOfStruct < sizeof(FAX_ROUTE_CALLBACKROUTINES) ) {
MYDEBUG ((L"The passed in SizeOfStruct (%d) is smaller than expected (%d) ",
FaxRouteCallbackRoutines->SizeOfStruct,
sizeof(FAX_ROUTE_CALLBACKROUTINES) ));
return FALSE;
}
hHeap = HeapHandle;
FaxRouteAddFile = FaxRouteCallbackRoutines->FaxRouteAddFile;
FaxRouteDeleteFile = FaxRouteCallbackRoutines->FaxRouteDeleteFile;
FaxRouteGetFile = FaxRouteCallbackRoutines->FaxRouteGetFile;
FaxRouteEnumFiles = FaxRouteCallbackRoutines->FaxRouteEnumFiles;
FaxRouteModifyRoutingData = FaxRouteCallbackRoutines->FaxRouteModifyRoutingData;
InitializeCriticalSection( &csRoute );
//
// create a named event
//
// note that we need to create a security descriptor with a NULL DACL (all access) because we want the named
// event to be opened by programs that might not be running in same context as the fax service
//
if ( !InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION) ) {
MYDEBUG(( L"InitializeSecurityDecriptor failed, ec = %d\n", GetLastError() ));
return FALSE;
}
if ( !SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE) ) {
MYDEBUG(( L"SetSecurityDescriptorDacl failed, ec = %d\n", GetLastError() ));
return FALSE;
}
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;
hReceiveEvent = CreateEvent(&sa,FALSE,FALSE,FAXSIREN);
if (!hReceiveEvent) {
if (GetLastError() != ERROR_ALREADY_EXISTS) {
MYDEBUG (( L"CreateEvent failed, ec = %d\n", GetLastError() ));
return FALSE;
}
}
//
// get path to files for logging, etc.
//
dwNeeded = ExpandEnvironmentStrings(ININAME,IniFile,0);
IniFile = (LPWSTR) HeapAlloc(hHeap,HEAP_ZERO_MEMORY,(dwNeeded)*sizeof(WCHAR));
if (!IniFile) {
MYDEBUG((L"HeapAlloc failed, ec = %d\n", GetLastError() ));
return FALSE;
}
DWORD dwSuccess = ExpandEnvironmentStrings(ININAME,IniFile,dwNeeded);
if (dwSuccess == 0) {
return FALSE;
}
dwNeeded = ExpandEnvironmentStrings(LOGNAME,LogFile,0);
LogFile = (LPWSTR) HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(WCHAR)*(dwNeeded));
if (!LogFile) {
MYDEBUG(( L"HeapAlloc failed, ec = %d\n", GetLastError() ));
return FALSE;
}
dwSuccess = ExpandEnvironmentStrings(LOGNAME,LogFile,dwNeeded);
if (dwSuccess == 0) {
return FALSE;
}
MYDEBUG (( L"Logfile : %s\n", LogFile ));
MYDEBUG (( L"Inifile : %s\n", IniFile ));
return TRUE;
}
BOOL WINAPI
FaxRouteGetRoutingInfo(
IN LPWSTR RoutingGuid,
IN DWORD DeviceId,
IN LPBYTE RoutingInfo,
OUT LPDWORD RoutingInfoSize
)
/*++
Routine Description:
This functions is called by the fax service to
get routing configuration data.
Arguments:
RoutingGuid - Unique identifier for the requested routing method
DeviceId - Device that is being configured
RoutingInfo - Routing info buffer
RoutingInfoSize - Size of the buffer (in bytes)
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
//
// the sample doesn't have any per device routing data, so this function is
// just stubbed out -- if you have per device routing data, it would be
// retrieved here.
//
if (RoutingInfoSize) {
*RoutingInfoSize = 0;
}
return TRUE;
}
BOOL WINAPI
FaxRouteSetRoutingInfo(
IN LPWSTR RoutingGuid,
IN DWORD DeviceId,
IN LPBYTE RoutingInfo,
IN DWORD RoutingInfoSize
)
/*++
Routine Description:
This functions is called by the fax service to
set routing configuration data.
Arguments:
RoutingGuid - Unique identifier for the requested routing method
DeviceId - Device that is being configured
RoutingInfo - Routing info buffer
RoutingInfoSize - Size of the buffer (in bytes)
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
//
// the sample doesn't have any per device routing data, so this function is
// just stubbed out -- if you have per device routing data, it would be
// commited to storage here.
//
return TRUE;
}
BOOL WINAPI
FaxRouteDeviceEnable(
IN LPWSTR RoutingGuid,
IN DWORD DeviceId,
IN LONG Enabled
)
/*++
Routine Description:
This functions is called by the fax service to determine if a routing extension is enabled or
to enable a routing extension
Arguments:
RoutingGuid - Unique identifier for the requested routing method
DeviceId - Device that is being configured
Enabled - meaning differs based on context (see FAXROUTE_ENABLE enumerated type)
Return Value:
depends on context.
--*/
{
//
// -note that we make the assumption that there are never more than 4 devices in this sample
// -also note that this isn't thread safe
// -a real routing extension wouldn't make these assumptions, and would probably have some
// persistent store which kept track of the enabled state of an extension per-routing method
//
static long MyEnabled[4] = {STATUS_ENABLE,STATUS_ENABLE,STATUS_ENABLE,STATUS_ENABLE};
static DWORD DeviceIdIndex[4] = {0,0,0,0};
DWORD count;
//
// make sure that we're dealing with our routing method
//
if (wcscmp(RoutingGuid,ROUTEITGUID) != 0) {
MYDEBUG (( L"Passed a GUID (%s) for a method not in this extension!\n", RoutingGuid ));
return FALSE;
}
for (count = 0 ; count <4; count ++) {
if (DeviceIdIndex[count] == DeviceId) {
break;
} else if (DeviceIdIndex[count] == 0) {
DeviceIdIndex[count] = DeviceId;
}
}
if (Enabled == QUERY_STATUS) {
return MyEnabled[count];
}
MYDEBUG (( L"Setting enabled state to %s\n",
(Enabled == STATUS_DISABLE) ? L"STATUS_DISABLE" : L"STATUS_ENABLE"
));
MyEnabled[count] = Enabled;
return TRUE;
}
BOOL WINAPI
FaxRouteDeviceChangeNotification(
IN DWORD DeviceId,
IN BOOL NewDevice
)
/*++
Routine Description:
This functions is called by the fax service to alert the routing extension that a device
has changed
Arguments:
DeviceId - Device that has changed
NewDevice - TRUE means device was added, FALSE means a device was removed
Return Value:
TRUE for success
--*/
{
//
// We don't have any per device routing data, so this is just stubbed out
//
return TRUE;
}
//
// routing method(s)
//
BOOL WINAPI
RouteIt(
PFAX_ROUTE FaxRoute,
PVOID *FailureData,
LPDWORD FailureDataSize
)
/*++
Routine Description:
This functions is called by the fax service to
route a received fax.
Arguments:
FaxRoute - Routing information
FailureData - Failure data buffer
FailureDataSize - Size of failure data buffer
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
WCHAR TiffFileName[MAX_PATH];
WCHAR Dir[MAX_PATH],Drive[10],File[MAX_PATH],Ext[10];
WCHAR CopyOfTiff[MAX_PATH];
DWORD Size = sizeof(TiffFileName);
//
// serialize access to this function so that data is written into the logfile accurately
//
EnterCriticalSection( &csRoute );
if (!FaxRouteGetFile(
FaxRoute->JobId,
0,
TiffFileName,
&Size))
{
MYDEBUG(( L"Couldn't FaxRouteGetFile, ec = %d", GetLastError() ));
LeaveCriticalSection( &csRoute );
return FALSE;
}
MYDEBUG ((L"Received fax %s\n\tCSID :%s\n\t Name : %s\n\t #: %s\n\tDevice: %s\n",
TiffFileName,
ValidString ( FaxRoute->Csid ),
ValidString ( FaxRoute->ReceiverName),
ValidString ( FaxRoute->ReceiverNumber),
ValidString ( FaxRoute->DeviceName )
));
_wsplitpath(TiffFileName, Drive, Dir, File, Ext );
wsprintf(CopyOfTiff,L"%s\\%s\\%s%s",Drive,SIRENDIR,File,Ext);
//
// copy the tiff so it persists after this routine exits
//
CopyFile(TiffFileName,CopyOfTiff,FALSE);
//
// write some logging data
//
WriteRoutingInfoIntoIniFile(CopyOfTiff,FaxRoute);
AppendFileNametoLogFile(TiffFileName, FaxRoute);
//
// signal event -- another application could use this named event to do something
// with the file that was just copied into this directory
// (note that the INI file isn't thread-safe accross applications, we could have the routing data overwritten by
// another fax being received)
//
SetEvent(hReceiveEvent);
//
// service needs to be able to interact with the current desktop for this to work
//
MessageBeep(MB_ICONEXCLAMATION);
LeaveCriticalSection( &csRoute );
return TRUE;
}
//
// utility fcn's
//
BOOL WriteRoutingInfoIntoIniFile(LPWSTR TiffFileName,PFAX_ROUTE FaxRoute)
{
WCHAR Buffer[MAX_PATH*2];
//
// write each routing info member into ini file
//
//filename
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%s",ValidString (TiffFileName) );
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"FileName",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
//jobid
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%u",FaxRoute->JobId);
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"JobId",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
//elapsedtime
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%u",FaxRoute->ElapsedTime);
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"ElapsedTime",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
//receivetime
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%u",FaxRoute->ReceiveTime);
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"ReceiveTime",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
//pagecount
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%u",FaxRoute->PageCount);
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"PageCount",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
//Csid
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%s",ValidString (FaxRoute->Csid ));
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"Csid",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
//CallerId
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%s",ValidString (FaxRoute->CallerId ));
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"CallerId",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
//RoutingInfo
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%s",ValidString (FaxRoute->RoutingInfo ));
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"RoutingInfo",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
//ReceiverName
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%s",ValidString (FaxRoute->ReceiverName ));
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"ReceiverName",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
//ReceiverNumber
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%s",ValidString (FaxRoute->ReceiverNumber ));
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"ReceiverNumber",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
//DeviceName
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%s",ValidString (FaxRoute->DeviceName ));
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"DeviceName",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
//DeviceId
ZeroMemory(Buffer,MAX_PATH*2);
wsprintf(Buffer, L"%u",FaxRoute->DeviceId );
WritePrivateProfileString( L"RoutingInfo",// pointer to section name
L"DeviceId",// pointer to key name
Buffer, // pointer to string to add
IniFile // pointer to initialization filename
);
return TRUE;
}
BOOL AppendFileNametoLogFile (LPWSTR TiffFileName,PFAX_ROUTE FaxRoute)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
WCHAR Buffer[MAX_PATH];
WCHAR szDateTime[104];
WCHAR lpDate[50];
WCHAR lpTime[50];
DWORD dwWrote = 0;
hFile = CreateFile(LogFile,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
MYDEBUG (( L"CreateFile failed, ec = %d\n", GetLastError() ));
return FALSE;
}
if ( !GetDateFormat( LOCALE_SYSTEM_DEFAULT,
DATE_SHORTDATE,
NULL, // use system date
NULL, // use locale format
lpDate,
sizeof(lpDate) ) ) {
MYDEBUG(( L"GetDateFormat failed, ec = %d\n", GetLastError() ));
return FALSE;
}
if ( !GetTimeFormat( LOCALE_SYSTEM_DEFAULT,
TIME_NOSECONDS,
NULL, // use system time
NULL, // use locale format
lpTime,
sizeof(lpTime) ) ) {
MYDEBUG(( L"GetTimeFormat failed, ec = %d\n", GetLastError() ));
return FALSE;
}
wsprintf( szDateTime, TEXT("%-8s %-8s"), lpDate, lpTime);
wsprintf(Buffer, L"%s :Received %s\r\n",ValidString(szDateTime),ValidString(TiffFileName));
SetFilePointer(hFile,0,0,FILE_END);
if (!WriteFile(hFile,Buffer,lstrlen(Buffer)*sizeof(WCHAR),&dwWrote,NULL)) {
MYDEBUG (( L"WriteFile() failed, ec = %d\n", GetLastError() ));
CloseHandle(hFile);
return FALSE;
}
CloseHandle(hFile);
return TRUE;
}
void
DebugPrint(
LPTSTR Format,
...
)
/*++
Routine Description:
Prints a debug string
Arguments:
format - wsprintf() format string
... - Variable data
Return Value:
None.
--*/
{
TCHAR Buffer[1024];
TCHAR AppName[MAX_PATH];
TCHAR ShortName[MAX_PATH];
SYSTEMTIME CurrentTime;
int len=0;
va_list marker;
ZeroMemory(AppName,MAX_PATH);
ZeroMemory(ShortName,MAX_PATH);
if ( GetModuleFileName(NULL, // handle to module to find filename for
AppName,
MAX_PATH) ) {
_tsplitpath(AppName,NULL,NULL,ShortName,NULL);
}
ZeroMemory(&CurrentTime,sizeof(SYSTEMTIME));
GetLocalTime(&CurrentTime);
wsprintf(Buffer, TEXT ("%02d.%02d.%02d.%03d %s: "),CurrentTime.wHour,
CurrentTime.wMinute,
CurrentTime.wSecond,
CurrentTime.wMilliseconds,
ShortName );
// init arg list
va_start(marker,Format);
// point to rest of blank buffer
len = lstrlen(Buffer);
_vsntprintf(&Buffer[len], // don't want to overwrite the start of the string!
sizeof(Buffer)-len, //size of the rest of the buffer
Format,
marker);
len = lstrlen(Buffer);
if (Buffer[len-1] == L'\n' ) {
Buffer[len-1] = L'\r';
Buffer[len] = L'\n';
Buffer[len+1] = 0;
}
else {
Buffer[len] = L'\r';
Buffer[len+1] = L'\n';
Buffer[len+2] = 0;
}
OutputDebugString(Buffer);
va_end(marker);
}