2 DLL versions

I KNOW this is a relatively dumb question but if there are no OS
dependencies in my DLL code, do I need to maintain (i.e. compile)
different versions for NT and 2000? Or will the each binary work on the
opposite OS?

Bill Casey

== SCSI Adapters & VirtualSCSI Target Mode Libs ==

No. In fact it is possible to write a single binary that runs on NT3.51,
4.0, Win9x and 2000. However, it does get a bit nasty coding all those
optional GetProcAddress calls for run-time loading. I believe it’s possible
to mark imports as optional somehow, but I’ve never really researched how to
implement that.

Regards,

Paul Bunn, UltraBac.com, 425-644-6000
Microsoft MVP - WindowsNT/2000
http://www.ultrabac.com

-----Original Message-----
From: Bill Casey [mailto:xxxxx@advstor.com]
Sent: Thursday, June 08, 2000 9:44 AM
To: NT Developers Interest List
Subject: [ntdev] 2 DLL versions

I KNOW this is a relatively dumb question but if there are no OS
dependencies in my DLL code, do I need to maintain (i.e. compile)
different versions for NT and 2000? Or will the each binary work on the
opposite OS?

Paul:
Can you elucidate why this “nasty coding” is necessary? Leave 3.51 and
Win9x out of the picture for now - just NT and 2000.
Bill
== SCSI Adapters & VirtualSCSI Target Mode Libs ==

-----Original Message-----

No. In fact it is possible to write a single binary that runs on
NT3.51,
4.0, Win9x and 2000. However, it does get a bit nasty coding all those
optional GetProcAddress calls for run-time loading. I believe it’s
possible
to mark imports as optional somehow, but I’ve never really researched
how to
implement that.

Regards,

Paul Bunn, UltraBac.com, 425-644-6000
Microsoft MVP - WindowsNT/2000
http://www.ultrabac.com

-----Original Message-----
From: Bill Casey [mailto:xxxxx@advstor.com]
Sent: Thursday, June 08, 2000 9:44 AM
To: NT Developers Interest List
Subject: [ntdev] 2 DLL versions

I KNOW this is a relatively dumb question but if there are no OS
dependencies in my DLL code, do I need to maintain (i.e. compile)
different versions for NT and 2000? Or will the each binary work on the
opposite OS?


You are currently subscribed to ntdev as: xxxxx@advstor.com
To unsubscribe send a blank email to $subst(‘Email.Unsub’)

Let’s take our backup software for example. We want to backup the AD on a
Win2000 machine, but the API isn’t available on NT4, but we do want the sw
to run on NT4/2000 for the same binary. The solution is this:

m_hLibHandle = LoadLibrary(L"NTDSBCLI.DLL");
m_bInitialized = (m_hLibHandle != NULL);
if(m_bInitialized)
{
m_DsIsNTDSOnlineW =
(DsIsNTDSOnlineWFunc)GetProcAddress(m_hLibHandle, “DsIsNTDSOnlineW”);
}

Any place that you wish to call this function becomes:
if(m_bInitialized && m_DsIsNTDSOnlineW)
bFlag = m_DsIsNTDSOnlineW(…);

A real PITA to code.

The reason “why” this is necessary is that if you simply let the linker do
it’s job by using .LIBs is that it will fail to load on OSes where the
library does not contain the imports that are expected. It’s possible to
use the /DELAYLOAD switch in the linker, but this doesn’t deal with
functions that might be missing from DLLs that are expected to be present.
Eg. MoveFileWithProgress will only be found in kernel32.dll on Windows2000.

If there’s an elegant solution to this problem, “I’m all ears”!

Regards,

Paul Bunn, UltraBac.com, 425-644-6000
Microsoft MVP - WindowsNT/2000
http://www.ultrabac.com

-----Original Message-----
From: Bill Casey [mailto:xxxxx@advstor.com]
Sent: Thursday, June 08, 2000 12:10 PM
To: NT Developers Interest List
Subject: [ntdev] RE: 2 DLL versions

Paul:
Can you elucidate why this “nasty coding” is necessary? Leave 3.51
and
Win9x out of the picture for now - just NT and 2000.
Bill
== SCSI Adapters & VirtualSCSI Target Mode Libs ==

-----Original Message-----

No. In fact it is possible to write a single binary that runs on
NT3.51,
4.0, Win9x and 2000. However, it does get a bit nasty coding all those
optional GetProcAddress calls for run-time loading. I believe it’s
possible
to mark imports as optional somehow, but I’ve never really researched
how to
implement that.

Regards,

Paul Bunn, UltraBac.com, 425-644-6000
Microsoft MVP - WindowsNT/2000
http://www.ultrabac.com

-----Original Message-----
From: Bill Casey [mailto:xxxxx@advstor.com]
Sent: Thursday, June 08, 2000 9:44 AM
To: NT Developers Interest List
Subject: [ntdev] 2 DLL versions

I KNOW this is a relatively dumb question but if there are no OS
dependencies in my DLL code, do I need to maintain (i.e. compile)
different versions for NT and 2000? Or will the each binary work on the
opposite OS?

Paul -

Walter Oney’s book, “Programming the Windows Driver Model”, has what I
thought was an elegant solution for this. Refer to Appendix A -

Ed

----- Original Message -----
From: “Paul Bunn”
To: “NT Developers Interest List”
Sent: Thursday, June 08, 2000 3:43 PM
Subject: [ntdev] RE: 2 DLL versions

> Let’s take our backup software for example. We want to backup the AD on a
> Win2000 machine, but the API isn’t available on NT4, but we do want the sw
> to run on NT4/2000 for the same binary. The solution is this:
>
> m_hLibHandle = LoadLibrary(L"NTDSBCLI.DLL");
> m_bInitialized = (m_hLibHandle != NULL);
> if(m_bInitialized)
> {
> m_DsIsNTDSOnlineW =
> (DsIsNTDSOnlineWFunc)GetProcAddress(m_hLibHandle, “DsIsNTDSOnlineW”);
> }
>
> Any place that you wish to call this function becomes:
> if(m_bInitialized && m_DsIsNTDSOnlineW)
> bFlag = m_DsIsNTDSOnlineW(…);
>
> A real PITA to code.
>
> The reason “why” this is necessary is that if you simply let the linker do
> it’s job by using .LIBs is that it will fail to load on OSes where the
> library does not contain the imports that are expected. It’s possible to
> use the /DELAYLOAD switch in the linker, but this doesn’t deal with
> functions that might be missing from DLLs that are expected to be present.
> Eg. MoveFileWithProgress will only be found in kernel32.dll on
Windows2000.
>
> If there’s an elegant solution to this problem, “I’m all ears”!
>
> Regards,
>
> Paul Bunn, UltraBac.com, 425-644-6000
> Microsoft MVP - WindowsNT/2000
> http://www.ultrabac.com
>
>
> -----Original Message-----
> From: Bill Casey [mailto:xxxxx@advstor.com]
> Sent: Thursday, June 08, 2000 12:10 PM
> To: NT Developers Interest List
> Subject: [ntdev] RE: 2 DLL versions
>
>
> Paul:
> Can you elucidate why this “nasty coding” is necessary? Leave 3.51
> and
> Win9x out of the picture for now - just NT and 2000.
> Bill
> == SCSI Adapters & VirtualSCSI Target Mode Libs ==
>
> -----Original Message-----
>
> No. In fact it is possible to write a single binary that runs on
> NT3.51,
> 4.0, Win9x and 2000. However, it does get a bit nasty coding all those
> optional GetProcAddress calls for run-time loading. I believe it’s
> possible
> to mark imports as optional somehow, but I’ve never really researched
> how to
> implement that.
>
> Regards,
>
> Paul Bunn, UltraBac.com, 425-644-6000
> Microsoft MVP - WindowsNT/2000
> http://www.ultrabac.com
>
>
> -----Original Message-----
> From: Bill Casey [mailto:xxxxx@advstor.com]
> Sent: Thursday, June 08, 2000 9:44 AM
> To: NT Developers Interest List
> Subject: [ntdev] 2 DLL versions
>
>
> I KNOW this is a relatively dumb question but if there are no OS
> dependencies in my DLL code, do I need to maintain (i.e. compile)
> different versions for NT and 2000? Or will the each binary work on the
> opposite OS?
>
> —
> You are currently subscribed to ntdev as: xxxxx@midcore.com
> To unsubscribe send a blank email to $subst(‘Email.Unsub’)
>

From: “Ed Lau”
Sent: Thursday, June 08, 2000 3:59 PM

> Paul -
>
> Walter Oney’s book, “Programming the Windows Driver Model”, has what I
> thought was an elegant solution for this. Refer to Appendix A -

Well, it’s a neat trick, but realize that it only works for WDM drivers
loaded on Windows 9x.

The approach in Appendix A of Walter’s book is meant to provide a way to
load WDM drivers on Windows 9x that reference functions only implemented on
Windows 2000. Without the “tricks” implemented by Walter’s WDMSTUBS.VXD,
Windows 9x wouldn’t even load such drivers.

Note that WDMSTUBS.VXD relies on WDM loader features found only on Windows
9x (directly messing with the system’s import/export tables uses to resolve
references in WDM modules). It also obviously requires the loading of a VxD
module (or at least calling of VxD-only APIs), and so is not a
general-purpose approach to this problem under Windows 2000, for example.

- Matt

From: “Paul Bunn”
Sent: Thursday, June 08, 2000 3:43 PM

[snip]

> m_hLibHandle = LoadLibrary(L"NTDSBCLI.DLL");
> m_bInitialized = (m_hLibHandle != NULL);
> if(m_bInitialized)
> {
> m_DsIsNTDSOnlineW =
> (DsIsNTDSOnlineWFunc)GetProcAddress(m_hLibHandle, “DsIsNTDSOnlineW”);
> }
>
> Any place that you wish to call this function becomes:
> if(m_bInitialized && m_DsIsNTDSOnlineW)
> bFlag = m_DsIsNTDSOnlineW(…);

[snip]

> If there’s an elegant solution to this problem, “I’m all ears”!

Well, you don’t have to pollute your code with all the explicit checking
everywhere (as shown above).

For example, you could instead initialize a global table of function
pointers (one for each of the potentially missing API entry-points your
program calls) once at startup. For each entry-point that’s not available
on the platform your running on, you simply set its pointer to point to some
“dummy” implementation of the function.

These dummy implementations can either, 1) fail by returning an appropriate
error code (such as STATUS_NOT_IMPLEMENTED), or 2) emulate the required
functionality (if possible). Then, you can simply call through the function
table throughout your program without doing any checking (for whether or not
it’s “safe” to call the function itself). You handle failures
(STATUS_NOT_IMPLEMENTED, etc.) as you would normally be prepared to anyway.

If you’re programming in C++, you might even use an abstract base class (all
pure virtual functions, representing each potentially missing API
entry-point) instead of a “raw” table of function pointers. Depending on
which OS you launch under, you construct a concrete implementation of the
abstruct base class that emulates or fails each call as appropriate. Again,
you need only do the initialization once and then simply make the calls (via
some global pointer to the abstract base type) throughout the rest of your
program.

- Matt