I finally got some time to upgrade my video capture class. I wanted to compare VFW (what I have used until now) and DirectShow. As expected, DirectShow is faster, but when I added info texts, suddenly AnsiString::sprint()
is no longer a member of AnsiString
.
After some struggle, I found a workaround as AnsiString::printf()
still works, but I am curious how to fix this. Maybe some define from dshow.h
and dstring.h
are conflicting?
I cut down all the unnecessary code to show this problem:
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <dshow.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
static int i=0;
Caption=AnsiString().sprintf("%i",i); // this does not work
AnsiString s; s.printf("%i",i); Caption=s; // this does work
i++;
}
//---------------------------------------------------------------------------
It is just a simple VCL Form app with a single TTimer
on it. The TTimer
is incrementing the counter i
and outputting it in the Form's Caption
. The DirectX libs are not even linked, just headers included!
The Linker outputs error:
[C++ Error] Unit1.cpp(20): E2316 'sprintf_instead_use_StringCbPrintfA_or_StringCchPrintfA' is not a member of 'AnsiString'
If I swap the vcl.h
and dshow.hincludes, the compiler stops in
dstring.h` on this line:
AnsiString& __cdecl sprintf(const char* format, ...); // Returns *this
With this error message:
[C++ Error] dstring.h(59): E2040 Declaration terminated incorrectly
So, there is clearly some conflict (the AnsiString
keyword is the problem). Putting dshow.h
into a namespace
does not help, either.
Does anyone have any clues?
Q1. How to fix this?
Q2. What/where exactly is causing this?
The only solution that I can think of, and should work (but I want to avoid it if I can), is to create an OBJ (or DLL) with the DirectShow stuff, and then link that into a standard VCL project without including dshow.h
in it, and of course the exports must be without any funny stuff, too.
The problem is not with dshow.h
itself, but is actually with strsafe.h
instead, which dshow.h
includes by default.
strsafe.h
contains the following code 1:
#ifndef STRSAFE_NO_DEPRECATE
// Deprecate all of the unsafe functions to generate compiletime errors. If you do not want
// this then you can #define STRSAFE_NO_DEPRECATE before including this file
#ifdef DEPRECATE_SUPPORTED
...
#pragma deprecated(sprintf)
...
#else // DEPRECATE_SUPPORTED
...
#undef sprintf
#define sprintf sprintf_instead_use_StringCchPrintfA_or_StringCbPrintfA;
...
#endif // DEPRECATE_SUPPORTED
#endif // !STRSAFE_NO_DEPRECATE
1 There are similar #pragma
and #define
statements for many other deprecated "unsafe" C functions.
If both STRSAFE_NO_DEPRECATE
and DEPRECATE_SUPPORTED
are not defined (which is the case in this situation), the use of #define sprintf
causes all subsequent references to any kind of sprintf
symbol to be seen as sprintf_instead_use_StringCchPrintfA_or_StringCbPrintfA;
during compiling.
That is why you are getting the compiler error. When vcl.h
is included before strsafe.h
, dstring.h
gets included first, so the compiler sees the correct declaration for the AnsiString::sprintf()
method, and then strsafe.h
gets included (presumably by Unit1.h
) before the compiler sees your Timer1Timer()
code, so your calls to AnsiString().sprint("%i",i)
are actually trying to call AnsiString().sprintf_instead_use_StringCchPrintfA_or_StringCbPrintfA;("%i",i)
, which fail.
When you swap the vcl.h
and dshow.h
includes, the #define sprintf
statement in strsafe.h
gets processed before dstring.h
is included, so the compiler sees the following declaration for the AnsiString::sprintf()
method in dstring.h
and fails:
AnsiString& __cdecl sprintf_instead_use_StringCchPrintfA_or_StringCbPrintfA;(const char* format, ...); // Returns *this
To prevent this behavior, you could use an #undef sprintf
statement after #include <dshow.h>
, like JeffRSon suggested. However the correct solution is to define STRSAFE_NO_DEPRECATE
before #include <strsafe.h>
. You can do that by either:
adding #define STRSAFE_NO_DEPRECATE
to your code before the #include <dshow.h>
statement
adding STRSAFE_NO_DEPRECATE
to the Conditionals list in your Project Options.
This solution is described on MSDN:
When you include Strsafe.h in your file, the older functions replaced by the Strsafe.h functions will be deprecated. Attempts to use these older functions will result in a compiler error telling you to use the newer functions. If you want to override this behavior, include the following statement before including Strsafe.h.
#define STRSAFE_NO_DEPRECATE
To allow only character count functions, include the following statement before including Strsafe.h.
#define STRSAFE_NO_CB_FUNCTIONS
To allow only byte count functions, include the following statement before including Strsafe.h.
#define STRSAFE_NO_CCH_FUNCTIONS
Another supported solution is to define NO_DSHOW_STRSAFE
before #include <dshow.h>
so that it will not include strsafe.h
anymore, thanks to this code in dshow.h
:
#ifndef NO_DSHOW_STRSAFE
#define NO_SHLWAPI_STRFCNS
#include <strsafe.h>
#endif