Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Using GDI+ v1.1 in C++Builder XE6


This question is not answered. Helpful answers available: 1. Correct answers available: 1.


Permlink Replies: 15 - Last Post: Nov 23, 2015 12:45 AM Last Post By: Martin Nijhoff Threads: [ Previous | Next ]
Martin Nijhoff

Posts: 75
Registered: 8/26/10
Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 19, 2015 3:40 AM
Hi,

I want to use anti-aliasing when rendering metafiles. To do this, I first need to convert the metafile to EMF+, using Metafile::ConvertToEmfPlus(). This method requires GDI+ v1.1, but C++Builder XE6 uses GDI+ v1.0 by default.

From what I understand, GDI+ v1.1 is included in Windows Vista and later, and GDI+ v1.0 in Windows XP. Obviously, I can't use metafile anti-aliasing on Windows XP, but I would like to use it on Windows Vista and later.

The GDI+ header files check for (GDIPVER >= 0x0110), to determine which version of the GDI+ classes are used. My code needs to be compiled for GDI+ v1.1, so GDIPVER needs to be set to 0x0110 before gdiplus.h is included.

My questions:
1. What is the best place to set GDIPVER?
a. In the pre-compiled header?
b. In Project Options | C++ (Shared Options) | Conditional defines?
c. In Project Options | C++ Compiler | Directories and Conditionals | Conditional defines?

2. Do I also need to add something to the manifest?
If so, how do I do this?

3. How can I check in my code if GDI+ v1.1 is available, to avoid calling Metafile::ConvertToEmfPlus() on GDI+ v1.0?

--
Martin
Martin Nijhoff

Posts: 75
Registered: 8/26/10
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 17, 2015 4:13 AM   in response to: Martin Nijhoff in response to: Martin Nijhoff
It's been a while, but I had the chance to pick this up again today.

I've added GDIPVER=0x0110 in Project Options | C++ Compiler | Directories and Conditionals | Conditional defines.

When I compile my code, I get these errors:

[bcc32 Error] GdiplusEffects.h(185): E2238 Multiple declaration for 'Graphics'
[bcc32 Error] Vcl.Graphics.hpp(49): E2344 Earlier declaration of 'Graphics'

The code compiles without errors when I remove GDIPVER from the project options.

Any idea how I can fix these errors?

--
Martin
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Using GDI+ v1.1 in C++Builder XE6
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 17, 2015 9:16 AM   in response to: Martin Nijhoff in response to: Martin Nijhoff
Martin wrote:

When I compile my code, I get these errors:

[bcc32 Error] GdiplusEffects.h(185): E2238 Multiple declaration for
'Graphics' [bcc32 Error] Vcl.Graphics.hpp(49): E2344 Earlier
declaration of 'Graphics'

GdiplusEffects.h does not define a symbol named Graphics, but it does declare
a class named Graphics as being a friend of the Effects class.

Vcl.Graphics.hpp defines a Vcl::Graphics namespace, and there are 'using
namespace' statements at the bottom of the file that bring the content of
the Vcl and Graphics namespaces into the global namespace by default.

The code compiles without errors when I remove GDIPVER from the
project options.

Everything in GdiplusEffects.h is disabled when GDIPVER is not defined, or
is < 0x0110.

Any idea how I can fix these errors?

If you need to mix GdiPlusEffects.h and Vcl.Graphics.hpp together in the
same unit with GDIPVER >= 0x0110 defined, try adding the NO_USING_NAMESPACE_VCL_GRAPHICS
or DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE conditional in your project options.
Or else you will have to separate your GDI+ code from your VCL code so they
don't mix.

--
Remy Lebeau (TeamB)
Martin Nijhoff

Posts: 75
Registered: 8/26/10
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 18, 2015 1:02 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks for your reply, Remy.

Remy Lebeau (TeamB) wrote:
If you need to mix GdiPlusEffects.h and Vcl.Graphics.hpp together in the
same unit with GDIPVER >= 0x0110 defined, try adding the NO_USING_NAMESPACE_VCL_GRAPHICS
or DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE conditional in your project options.

Adding the NO_USING_NAMESPACE_VCL_GRAPHICS conditional alone didn't fix the compiler errors.
I also had to add the NO_USING_NAMESPACE_VCL conditional.

In my code, I added the following:

#pragma warn -hid
#include <GdiPlus.h>
#pragma warn .hid
 
#if (GDIPVER >= 0x0110) && defined(Vcl_GraphicsHPP)
    #if defined(NO_USING_NAMESPACE_VCL_GRAPHICS)
        using namespace Vcl::Graphics;
    #endif
    #if defined(NO_USING_NAMESPACE_VCL)
        using namespace Vcl;
    #endif
#endif

Or else you will have to separate your GDI+ code from your VCL code so they
don't mix.

My VCL forms application uses GDI+.
Do you mean putting the GDI+ code into a separate package, so I don't have to include GdiPlus.h in my application?

--
Martin
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 18, 2015 11:58 AM   in response to: Martin Nijhoff in response to: Martin Nijhoff
Martin wrote:

My VCL forms application uses GDI+.

But that does not require you to use VCL Graphics and GDI+ in the same source
file. You could move the GDI+ code to another unit that does not use VCL
Graphics, and then have the two units exchange data back and forth as needed.

Do you mean putting the GDI+ code into a separate package,
so I don't have to include GdiPlus.h in my application?

You could do it that way, though creating a separate package would be overkill.
Just create separate source files for your VCL Graphics and GDI+ codes.

--
Remy Lebeau (TeamB)
Martin Nijhoff

Posts: 75
Registered: 8/26/10
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 19, 2015 4:30 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
But that does not require you to use VCL Graphics and GDI+ in the same source
file. You could move the GDI+ code to another unit that does not use VCL
Graphics, and then have the two units exchange data back and forth as needed.
...
Just create separate source files for your VCL Graphics and GDI+ codes.

My GDI+ code is in a separate unit, which doesn't include Vcl.Graphics.hpp.
Another unit includes Vcl.Graphics.hpp, but not GdiPlus.h.
Since both units are used in the same project, both Vcl.Graphics.hpp and GdiPlus.h get included.

The order in which the units are built (and thus the headers are included) determines if the compiler generates the errors are not.
If GdiPlus.h is included before Vcl.Graphics.hpp, there's no compiler error.
So, setting the build order of the project would also fix the compiler errors.

--
Martin
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 19, 2015 12:13 PM   in response to: Martin Nijhoff in response to: Martin Nijhoff
Martin wrote:

My GDI+ code is in a separate unit, which doesn't include
Vcl.Graphics.hpp.
Another unit includes Vcl.Graphics.hpp, but not GdiPlus.h.

Apparently there is some cross-linkage where they are appearing in the same
souce file, otherwise you would not be getting 'multiple declaration' errors
to begin with.

Since both units are used in the same project, both Vcl.Graphics.hpp
and GdiPlus.h get included.

But they would not see each other, and thus would not conflict with each
other. You can't get the error unless some source file is including both
header files, either directly or indirectly.

--
Remy Lebeau (TeamB)
Martin Nijhoff

Posts: 75
Registered: 8/26/10
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 20, 2015 1:31 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
But they would not see each other, and thus would not conflict with each
other. You can't get the error unless some source file is including both
header files, either directly or indirectly.

In C/C++, once a header file is included in some unit, the namespaces and classes in it are defined globally and are not 'going away' when the compiler moves on to the next unit.

AFAIK, there is no way to 'undefine' namespaces and classes.

--
Martin
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 20, 2015 9:27 AM   in response to: Martin Nijhoff in response to: Martin Nijhoff
Martin wrote:

In C/C++, once a header file is included in some unit, the namespaces
and classes in it are defined globally and are not 'going away' when
the compiler moves on to the next unit.

That is not true, and no C++ spec or book I have ever seen claims that.
What they do say is that each translation unit (.c/.cpp file) is compiled
independantly of any other translation unit. Every translation unit defines
the types it uses, such as via header files (but that is not a requirement),
where multiple translation units are allowed to define the same type as long
as the definitions are equivilent (such as from a shared header file). This
is clearly outlined as part of C++'s "One Definition Rule".

AFAIK, there is no way to 'undefine' namespaces and classes.

Not within a single translation unit, no. But the next translation unit
will not have them defined unless they get defined again.

--
Remy Lebeau (TeamB)
Martin Nijhoff

Posts: 75
Registered: 8/26/10
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 18, 2015 4:09 AM   in response to: Martin Nijhoff in response to: Martin Nijhoff
Converting a Gdiplus::Metafile object to EMF+ now works.

Now, suppose I have a metafile (.WMF or .EMF) in a Gdiplus::Image instead of a Gdiplus::Metafile object.
Gdiplus::Image doesn't have a ConvertToEmfPlus() method.

Is this the correct way to convert the metafile to EMF+?

Gdiplus::Image Image(OpenPictureDialog1->FileName);
 
Gdiplus::Graphics g(PaintBox1->Canvas->Handle);
 
if (Image.GetType() == ImageTypeMetafile)
    ((Metafile*) &Image)->ConvertToEmfPlus(&g);


--
Martin
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 18, 2015 12:14 PM   in response to: Martin Nijhoff in response to: Martin Nijhoff
Martin wrote:

Now, suppose I have a metafile (.WMF or .EMF) in a Gdiplus::Image
instead of a Gdiplus::Metafile object.

If you know you are working with a WMF/EMF, why not load it into a Metafile
directly instead of a generic Image?

Is this the correct way to convert the metafile to EMF+?

No, it is not. You cannot type-cast a generic Image* pointer to a Metafile*
pointer if the Image object is not actually a Metafile object to begin with
(Metafile derives from Image). Your Image variable is not a Metafile object,
so the type-cast is wrong and will cause undefined behavior. To ensure the
proper Image class type, use the static Image::FromFile() method instead, eg:

Gdiplus::Image *pImage = Gdiplus::Image::FromFile(OpenPictureDialog1->FileName.c_str());
...
Gdiplus::Graphics g(PaintBox1->Canvas->Handle);
 
if (pImage->GetType() == ImageTypeMetafile)
    static_cast<Gdiplus::Metafile*>(pImage)->ConvertToEmfPlus(&g);
/*
or:
Gdiplus::Metafile *pMetafile = dynamic_cast<Gdiplus::Metafile*>(pImage);
if (pMetafile)
    pMetafile->ConvertToEmfPlus(&g);
*/
...
delete pImage;


BTW, you should not be using the TPaintBox1->Canvas propety from outside
of the TPaintBox::OnPaint event. The Canvas is only valid during painting.

--
Remy Lebeau (TeamB)
Martin Nijhoff

Posts: 75
Registered: 8/26/10
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 19, 2015 4:11 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks for your reply, Remy.

Remy Lebeau (TeamB) wrote:
If you know you are working with a WMF/EMF, why not load it into a Metafile
directly instead of a generic Image?

The image can also contain a BMP/JPG/PNG/..., so I don't know for sure it's a Metafile.
That's why I load it into an Image.

No, it is not. You cannot type-cast a generic Image* pointer to a Metafile*
pointer if the Image object is not actually a Metafile object to begin with
(Metafile derives from Image). Your Image variable is not a Metafile object,
so the type-cast is wrong and will cause undefined behavior.

The Metafile class doesn't introduce new data members in addition to those of Image or GdiplusBase.
Only the methods introduced in Metafile are used.

If Metafile introduced new data members, the type-cast would definitely cause undefined behavior, because Metafile would then access unallocated memory.

To ensure the proper Image class type, use the static Image::FromFile() method instead, eg:

Gdiplus::Image *pImage = Gdiplus::Image::FromFile(OpenPictureDialog1->FileName.c_str());
...
Gdiplus::Graphics g(PaintBox1->Canvas->Handle);
 
if (pImage->GetType() == ImageTypeMetafile)
    static_cast<Gdiplus::Metafile*>(pImage)->ConvertToEmfPlus(&g);

How is this any different from my type-cast?
pImage still points to an Image object, not a Metafile object.

/*
or:
Gdiplus::Metafile *pMetafile = dynamic_cast<Gdiplus::Metafile*>(pImage);
if (pMetafile)
    pMetafile->ConvertToEmfPlus(&g);
*/

This actually doesn't work (I tried it).
Since pImage doesn't point to a Metafile object, pMetafile is NULL, and ConvertToEmfPlus() is never called.

BTW, you should not be using the TPaintBox1->Canvas propety from outside
of the TPaintBox::OnPaint event. The Canvas is only valid during painting.

I know. My code snippet came from the OnPaint event handler.

--
Martin
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 19, 2015 12:11 PM   in response to: Martin Nijhoff in response to: Martin Nijhoff
Martin wrote:

The image can also contain a BMP/JPG/PNG/..., so I don't know for
sure it's a Metafile. That's why I load it into an Image.

OK.

The Metafile class doesn't introduce new data members in addition
to those of Image or GdiplusBase.

It doesn't have to. What is important is that the Image* pointer needs to
point at a real Metafile object in memory in order for a type-cast from Image*
to Metafile* to work correctly.

If Metafile introduced new data members, the type-cast would
definitely cause undefined behavior, because Metafile would then
access unallocated memory.

Metafile may not introduce new data members, but the new methods it does
introduce expect a Metafile* 'this' pointer to be passed in as input. When
you type-cast a base pointer to a derived pointer, you are adjusting the
pointer by an offset. When a new method needs to access a base member internally,
it has to adjust its 'this' pointer by a reverse offset so the base class
is accessed correctly.

So, for example, given your code:

Gdiplus::Image Image(...);
((Gdiplus::Metafile*)&Image)->ConvertToEmfPlus(&g);


ConvertToEmfPlus() receives a Metafile* pointer that is the Image* pointer
+ sizeof(Image), and then ConvertToEmfPlus() has to subtract sizeof(Image)
from its 'this' pointer when accessing base members. In this example, it
happens to work, but it is relying on a compiler implementation detail
and a specific vtable layout, neither of which are dictated by the C++ standard.

How is this any different from my type-cast?
pImage still points to an Image object, not a Metafile object.

In my example, I am expecting Image::FromFile() to create a real Metafile
object in memory and return a pointer to the Image portion of that object.
The subsequent type-cast to Metafile* when calling ConvertToEmfPlus() would
then adjust the pointer back to the Metafile portion of the same object.

In your example, there is no Metafile object, just an Image object by itself.
So technically, the generated Metafile* pointer passed to ConvertToEmfPlus()
is pointing at invalid memory outside of the Image object. So right there,
you have already violated the contract before ConvertToEmfPlus() has started
running. But since the 'this' pointer gets adjusted again when ConvertToEmfPlus()
accesses base Image members, it happens to work.

This actually doesn't work (I tried it).
Since pImage doesn't point to a Metafile object, pMetafile is NULL,
and ConvertToEmfPlus() is never called.

That would be true if:

1. FromFile() is not creating a Metafile object in memory, but just an Image
object.

2. FromFile() is creating a Metafile object, but the object's RTTI does not
match the RTTI that your application is expecting, so dynamic_cast fails
to find the Metafile class within the pointed Image object.

There are plenty of GDI+ examples floating around online that suggest a type-cast
from Image* to Metafile* works correctly when using FromFile() and GetType()
returns ImageTypeMetafile. It is possible (evne likely) that there is an
RTTI mismatch (since GDI+ is an external library afterall), so dynamic_cast
could fail where static_cast would succeed instead.

I know. My code snippet came from the OnPaint event handler.

OK.

--
Remy Lebeau (TeamB)
Martin Nijhoff

Posts: 75
Registered: 8/26/10
Re: Using GDI+ v1.1 in C++Builder XE6  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 20, 2015 1:17 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks for your reply, Remy.

Remy Lebeau (TeamB) wrote:
In my example, I am expecting Image::FromFile() to create a real Metafile
object in memory and return a pointer to the Image portion of that object.

According to the documentation:
https://msdn.microsoft.com/en-us/library/ms535370(v=vs.85).aspx

Image::FromFile() creates an Image object. It is nowhere stated that it creates a Metafile object.

If you assume that Image::FromFile() creates a Metafile object, you can equally assume that the constructor of Image creates a Metafile object, which is also not correct.

So technically, the generated Metafile* pointer passed to ConvertToEmfPlus()
is pointing at invalid memory outside of the Image object.

Which is not a problem, because Metafile has no data members of its own to access.
It is only a problem if Metafile accesses the invalid memory, which it doesn't.

But since the 'this' pointer gets adjusted again when ConvertToEmfPlus()
accesses base Image members, it happens to work.

It doesn't 'happen' to work. It works because that is how base members are accessed.

That would be true if:

1. FromFile() is not creating a Metafile object in memory, but just an Image
object.

Plausible, since that is what the documentation of Image::FromFile() says.

2. FromFile() is creating a Metafile object, but the object's RTTI does not
match the RTTI that your application is expecting, so dynamic_cast fails
to find the Metafile class within the pointed Image object.

Less plausible, in my opinion.

This code...

Gdiplus::Metafile* Image = (Gdiplus::Metafile*) Gdiplus::Image::FromFile(L"D:\\Test.wmf");
 
try
{
    if (dynamic_cast<Gdiplus::Image*>(Image) != NULL)
        Memo1->Lines->Add(L"Image");
 
    if (dynamic_cast<Gdiplus::Metafile*>(Image) != NULL)
        Memo1->Lines->Add(L"Metafile");
 
    if (dynamic_cast<Gdiplus::Bitmap*>(Image) != NULL)
        Memo1->Lines->Add(L"Bitmap");
}
__finally
{
    delete Image;
}


...produces both 'Image' and 'Metafile', indicating that the RTTI of the Metafile class is correct.

I don't see how the RTTI of a Metafile object created in Image::FromFile() could mismatch.

It is possible (evne likely) that there is an RTTI mismatch (since GDI+ is
an external library afterall), so dynamic_cast could fail where static_cast
would succeed instead.

The documentation on static_cast states:
https://msdn.microsoft.com/en-us/library/c36yw7x9.aspx

"The static_cast operator can be used for operations such as converting a pointer to a base class to a pointer to a derived class. Such conversions are not always safe."

It also states that you have to be certain of the data types involved in the conversion.

Assuming is not the same as being certain.

--
Martin
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Using GDI+ v1.1 in C++Builder XE6 [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 20, 2015 9:16 AM   in response to: Martin Nijhoff in response to: Martin Nijhoff
Martin wrote:

According to the documentation:
https://msdn.microsoft.com/en-us/library/ms535370(v=vs.85).aspx
Image::FromFile() creates an Image object. It is nowhere stated that
it creates a Metafile object.

Perhaps so, but on the other hand, the book "Graphics Programming with GDI+
and DirectX" by Alex Polyakov and Vitaly Brusentsev (ISBN 1-931769-39-7)
says otherwise:

3.2.1 Loading a Metafile

The Metafile class inherits from the Image class, in which two loading methods
are defined: from files (specifying the filename) and from IStream streams.
A short C++ example is shown in Listing 3.9.


Listing 3.9. Loading a metafile [C++]

// loading from a file
Metafile mf1(L"sample1.emf");
Metafile *mf2 = (Metafile*) Image::FromFile(L"sample2.emf");

// Loading from a stream
LPSTREAM pIS;
...
Metafile * mf3 = (Metafile*) Image::FromStream(pIS);
...
delete mf2;
delete mf3;


...

The Image::FromFile() and Image::FromStream() methods return a pointer to
the Image class. **If you are certain that a metafile and not a bitmap will
be loaded, the pointer can be forced to the necessary Metafile* type**. Otherwise,
the GetRawFormat() method (the RawFormat property in .NET) can be used to
ascertain the type of the loaded image by returning the globally unique identifier
of the codec used to load the image. For loaded EMF and EMF+ metafiles, the
method returns a constant of the ImageFormatEMF type (the property will take
on the ImageFormat.EMF value in .NET).

If you assume that Image::FromFile() creates a Metafile object

Which is a valid assumption to make, given the number of examples that show
it to be true.

you can equally assume that the constructor of Image creates a Metafile
object, which is also not correct.

No, you cannot make that assumption. You would be calling the base Image
constructor directly, not the derived Metafile constructor. The base Image
constructor may be able to load metafile data from a file, but you would
not be able to legally access any Metafile-specific functionality since it
is not a Metafile object in memory.

This code...

Gdiplus::Metafile* Image = (Gdiplus::Metafile*)
Gdiplus::Image::FromFile(L"D:\\Test.wmf");
try
{
if (dynamic_cast<Gdiplus::Image*>(Image) != NULL)
Memo1->Lines->Add(L"Image");
if (dynamic_cast<Gdiplus::Metafile*>(Image) != NULL)
Memo1->Lines->Add(L"Metafile");
if (dynamic_cast<Gdiplus::Bitmap*>(Image) != NULL)
Memo1->Lines->Add(L"Bitmap");
}
__finally
{
delete Image;
}


...produces both 'Image' and 'Metafile'

There you go then. Image::FromFile() has to be creating a Metafile object
in memory and returning an Image* pointer to it, otherwise dynamic_cast would
not be able to return a Metafile* pointer for it.

The documentation on static_cast states:
https://msdn.microsoft.com/en-us/library/c36yw7x9.aspx
"The static_cast operator can be used for operations such as
converting a pointer to a base class to a pointer to a derived class.
Such conversions are not always safe."

That is true if the base pointer is not pointing at an object of a derived
class. On the other hand, static_cast is evaluated at compile-time and does
use RTTI, so if you know the pointer is pointing at a derived object than
the cast is safe. dynamic_cast is evaluated at run-time and uses RTTI to
verify the cast.

--
Remy Lebeau (TeamB)
Martin Nijhoff

Posts: 75
Registered: 8/26/10
Re: Using GDI+ v1.1 in C++Builder XE6 [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 23, 2015 12:45 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks for your detailed explanation, Remy.
I appreciate it very much.

Do you know if it's possible to use both GDI+ 1.0 and 1.1 from the same code?
That is, use GDI+ 1.1 on Windows Vista and later, and fall back to GDI+ 1.0 on Windows XP?
Or does that require jumping through hoops?

--
Martin
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02