Folks
I am in the process up upgrading a legacy application from VS2008 to VS2015. It is extremely dependent on ATL and COM. I have noticed a change in _ATL_COM_MODULE70 in atlbase.h that is causing issues.
In ...\9.0\VC\atlmfc\include\atlbase.h it is defined as:
struct _ATL_COM_MODULE70
{
UINT cbSize;
HINSTANCE m_hInstTypeLib;
_ATL_OBJMAP_ENTRY** m_ppAutoObjMapFirst;
_ATL_OBJMAP_ENTRY** m_ppAutoObjMapLast;
CComCriticalSection m_csObjMap;
};
However in ...\14.0\VC\atlmfc\include\atlcom.h it is defined as:
struct _ATL_COM_MODULE70
{
UINT cbSize;
HINSTANCE m_hInstTypeLib;
_ATL_OBJMAP_ENTRY_EX** m_ppAutoObjMapFirst;
_ATL_OBJMAP_ENTRY_EX** m_ppAutoObjMapLast;
CComCriticalSection m_csObjMap;
};
This causes the following code not to compile:
HRESULT FreeThreadSingletons (void)
{
for (_ATL_OBJMAP_ENTRY** ppEntry = _AtlComModule.m_ppAutoObjMapFirst; ppEntry < _AtlComModule.m_ppAutoObjMapLast; ppEntry++)
{
if (*ppEntry != NULL)
{
_ATL_OBJMAP_ENTRY* pEntry = *ppEntry;
CComClassFactoryThreadSingleton *pThread;
if ((pThread = dynamic_cast<CComClassFactoryThreadSingleton *>(pEntry->pCF)) != NULL)
{
pThread->ReleaseAllObjects ();
}
}
}
return S_OK;
}
The reason is because m_ppAutoObjMapFirst is of type _ATL_OBJMAP_ENTRY_EX in VC14 and _ATL_OBJMAP_ENTRY in VC9.
Microsoft's documentation for VS2015 says that the struct should be the same in VC14 as in VC9:
https://msdn.microsoft.com/en-us/library/ayw1b6h5.aspx
Now for the question. Has anyone seen this issue in the past and is there a workaround that does not involve rewriting everything to the _ATL_OBJMAP_ENTRY_EX interface?
Thanks for the response H. Guijit. Here is Microsoft's response:
As you have already noticed, the documentation in MSDN at _ATL_COM_MODULE70 Structure is inaccurate.
_ATL_OBJMAP_ENTRY and _ATL_OBJMAP_ENTRY_EX are typedefs for two different structures defined in atlbase.h
Unfortunately, you have fallen victim to code that relies on internal structures and implementations that are subject to change.
Not the most helpful thing I have ever read..
I have changed the code to retrieve IUnknown from pCache->pCF
HRESULT FreeThreadSingletons (void)
{
for (_ATL_OBJMAP_ENTRY_EX** ppEntry = _AtlComModule.m_ppAutoObjMapFirst; ppEntry < _AtlComModule.m_ppAutoObjMapLast; ppEntry++)
{
if (*ppEntry != NULL)
{
_ATL_OBJMAP_ENTRY_EX* pEntry = *ppEntry;
CComClassFactoryThreadSingleton *pThread;
if ((pThread = dynamic_cast<CComClassFactoryThreadSingleton *>((pEntry->pCache)->pCF)) != NULL)
{
pThread->ReleaseAllObjects ();
}
}
}
return S_OK;
}
I am not sure this will work yet as I'm still battling build problems but hope to run the application up in the next day or two.
You can rewrite it like this:
for(auto ppEntry = _AtlComModule.m_ppAutoObjMapFirst; ppEntry < _AtlComModule.m_ppAutoObjMapLast; ppEntry++)
This is always correct, no matter the type of the pointer.
I suppose you could avoid modifying your source with some preprocessor magic, but I would consider doing so to be a bit of a disaster for maintainability.
As for having seen this in the past... Microsoft quite often extends existing structs and functions, and when they do the extended struct or function gets 'ex' appended to its name. 'ex' structs are usually (always?) identical to the older version, just with extra fields. Same with 'ex' functions, which usually just get some extra parameters.
Related
My objective is to detect Windows 10 in my code which has to work cross-platform as well as across different versions of Windows (atleast 7 & up). Windows provides IsWindows10OrGreater() to tackle this problem, but there's another issue with it, this function isn't present in previous windows versions.
You'll find countless blogs and SO questions regarding this as well as the manifest madness where functions like this and getversion and others return some different version rather than the correct one.
For example on my machine - the method IsWindows10OrGreater() doesn't compile(I would've to install Win10 SDK), and IsWindowsVersionOrGreater() reports 6 as major version.
So is there a sane multi-version way I could solve this problem?
The most straight-forward way to retrieve the true OS version is to call RtlGetVersion. It is what GetVersionEx and VerifyVersionInfo call, but doesn't employ the compatibility shims.
You can either use the DDK (by #including <ntddk.h> and linking against NtosKrnl.lib from kernel mode, or ntdll.lib from user mode), or use runtime dynamic linking as in the following snippet:
typedef LONG NTSTATUS, *PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)
typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
RTL_OSVERSIONINFOW GetRealOSVersion() {
HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
if (hMod) {
RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
if (fxPtr != nullptr) {
RTL_OSVERSIONINFOW rovi = { 0 };
rovi.dwOSVersionInfoSize = sizeof(rovi);
if ( STATUS_SUCCESS == fxPtr(&rovi) ) {
return rovi;
}
}
}
RTL_OSVERSIONINFOW rovi = { 0 };
return rovi;
}
In case you need additional information you can pass an RTL_OSVERSIONINFOEXW structure in place of the RTL_OSVERSIONINFOW structure (properly assigning the dwOSVersionInfoSize member).
This returns the expected result on Windows 10, even when there is no manifest attached.
As an aside, it is commonly accepted as a better solution to provide different implementations based on available features rather than OS versions.
You can read real build number from the registry, and then infer Windows version from it. Your application does not need to have a manifest for this work: on my machine, it correctly detects OS build number as 10586. For example:
#include <Windows.h>
#include <sstream>
struct HKeyHolder
{
private:
HKEY m_Key;
public:
HKeyHolder() :
m_Key(nullptr)
{
}
HKeyHolder(const HKeyHolder&) = delete;
HKeyHolder& operator=(const HKeyHolder&) = delete;
~HKeyHolder()
{
if (m_Key != nullptr)
RegCloseKey(m_Key);
}
operator HKEY() const
{
return m_Key;
}
HKEY* operator&()
{
return &m_Key;
}
};
bool IsRunningOnWindows10()
{
HKeyHolder currentVersion;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", 0, KEY_QUERY_VALUE, ¤tVersion) != ERROR_SUCCESS)
return false;
DWORD valueType;
BYTE buffer[256];
DWORD bufferSize = 256;
if (RegQueryValueExW(currentVersion, L"CurrentBuild", nullptr, &valueType, buffer, &bufferSize) != ERROR_SUCCESS)
return false;
if (valueType != REG_SZ)
return false;
int version;
std::wistringstream versionStream(reinterpret_cast<wchar_t*>(buffer));
versionStream >> version;
return version > 9800;
}
IsWindows10OrGreater() from VersionHelpers.h
Check the notes at Version Helper functions on MSDN
File VersionHelpers.h is shipped with Windows 10 SDK, but it will work in previous versions, too. Just copy it to your development environment.
It's just a header-only defined small lib, which uses VerSetConditionMask and VerifyVersionInfoW functions, both available in WinAPI since Windows 2000.
Upd If you can not include manifest file with your source code, you can use simple hack: just get a version of any system dll, for example, kernel32.dll using GetFileVersionInfo function.
I'm using GetProcAddress to gain access to a standard Isapi Filter DLL method - the GetFilterVersion method which takes a pointer to a HTTP_FILTER_VERSION structure.
https://msdn.microsoft.com/en-us/library/ms525822(v=vs.90).aspx
https://msdn.microsoft.com/en-us/library/ms525465(v=vs.90).aspx
I've tested the code against a working Isapi filter that I've written and it works fine. I debug the code against an Isapi filter from a vendor (I don't have access to the source code or anything beyond the dll itself) and I get the exception, "access violation writing location". What could be the issue? (Both Isapi filters work in IIS.)
//Attempted to define function ptr several ways
typedef BOOL(__cdecl * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL( * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL(WINAPI * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
void arbitraryMethod()
{
HINSTANCE hDLL; // Handle to DLL
TRIRIGAISAPIV lpfnDllFunc2; // Function pointer
DWORD lastError;
BOOL uReturnVal2;
hDLL = LoadLibrary(L"iisWASPlugin_http.dll"); //vendor's dll
//hDLL = LoadLibrary(L"KLTWebIsapi.dll //my dll
if (hDLL != NULL)
{
lpfnDllFunc2 = (TRIRIGAISAPIV)GetProcAddress(hDLL, "GetFilterVersion");
if (!lpfnDllFunc2)
{
lastError = GetLastError();
// handle the error
FreeLibrary(hDLL);
//return 1;
}
else
{
HTTP_FILTER_VERSION pVer = { 6 };
//Call the function via pointer; Works with my dll, fails with vendor's
uReturnVal2 = lpfnDllFunc2(&pVer);
//................ HELP!!!!!!!!!!!!!
}
}
}
One issue that I see is that your function pointer declaration is incorrect.
According to the Microsoft documentation, GetFilterVersion is prototyped as:
BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer);
The WINAPI is a Windows macro that is actually defined as __stdcall, thus you are declaring the function pointer incorrectly when you used __cdecl.
What does WINAPI mean?
Thus, your declaration should be:
typedef BOOL(__stdcall * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
It could be that there are actually some additional structure fields filled by the custom filter.
You can try to increase the size of the structure to see if that will work, like for example:
struct HTTP_FILTER_VERSION_EXTRA {
HTTP_FILTER_VERSION v;
char[1024] extra;
};
HTTP_FILTER_VERSION_EXTRA ver;
ver.v.dwServerFilterVersion = 6;
uReturnVal2 = lpfnDllFunc2(&ver.v);
It is sometimes the case with the WinAPI structures that they allow versioning, so adding fields is possible. If the function doesn't then check (or doesn't know) the actual structure version, it might try to use an extended one which might be different than the one supplied - if the size of the supplied struct is then lesser than the structure version the func tries to use, bad things can happen.
Also check if the DLL is 64-bit or 32-bit. You cannot use 64-bit DLL by 32-bit app and vice versa (but I expect that would already fail during the LoadLibrary call).
We have a code base that is rather old that we are migrating to 64-bit to make use of some new 3rd party 64-bit tools. We are running on Windows 7, using VS2010, and code written in C++.
We spent the last two days trying to figure out an issue where a member variable was not getting set to the expected value. Using the VS2010 memory debugger, we were able to see that member variable memory locations were offset by four bytes. Some research lead us to struct alignment and 64 bit.
It turns out the first variable defined in the class was a 32-bit integer, the second variable was a pointer. If we moved the pointer to the top of the class, all member variables aligned up correctly. This fixed the issue.
We also noticed that either
adding a four byte buffer to the top of class "fixed" the issue
setting pack alignment to four bytes also "fixed" the issue.
The question is, how do you detect these errors? We set the warning level to EnabelAllWarnings and tried several static c++ analyzers, but no luck.
One example where this happens. I can't show the other due to an NDA.
class CTaskBar
{
public:
bool CreateTaskbarIcon( HWND hWnd,
HINSTANCE hInstance,
UINT unNotifyMessage,
UINT unIcon,
LPCTSTR szCaption)
{
if (hWnd == NULL)
return false;
if (hInstance == NULL)
return false;
m_hWnd = hWnd;
m_hInstance = hInstance;
m_NIDTaskbarIconData.cbSize = sizeof(NOTIFYICONDATA);
m_NIDTaskbarIconData.hWnd = m_hWnd;
m_NIDTaskbarIconData.uID = IDI_TASKBARICON;
if ( unIcon ) {
if ( m_hTaskbarIcon )
DestroyIcon( m_hTaskbarIcon );
m_hTaskbarIcon = ::LoadIcon( m_hInstance, MAKEINTRESOURCE( unIcon ) );
m_NIDTaskbarIconData.hIcon = m_hTaskbarIcon;
m_NIDTaskbarIconData.uFlags |= NIF_ICON;
}
if ( unNotifyMessage ) {
m_NIDTaskbarIconData.uCallbackMessage = unNotifyMessage;
m_NIDTaskbarIconData.uFlags |= NIF_MESSAGE;
}
if ( szCaption ) {
strcpy(m_NIDTaskbarIconData.szTip, szCaption);
m_NIDTaskbarIconData.uFlags |= NIF_TIP;
}
// The Shell_NotifyIcon call will add the icon to the task bar.
if ( Shell_NotifyIcon(NIM_ADD, &m_NIDTaskbarIconData) )
return true;
return false;
}
private:
NOTIFYICONDATA m_NIDTaskbarIconData;
HINSTANCE m_hInstance;
HWND m_hWnd;
HMENU m_hTaskbarMenu;
HICON m_hTaskbarIcon;
};
Basically, the application crashes at Shell_NotifyIcon deep inside the Windows API calls. If we look in the memory debugger, we can see memory getting set, but offset by four bytes. If I move m_NIDTaskbarIconData to just after m_hTaskbarIcon. Everything looks correct in the memory debugger and no crash.
So we did do a lot of searching on 64-bit C/C++ applications. As Andrey Cpp pointed out and linked to, there is a struct alignment issue based on the order and size of member variables. Unfortunately PVS-Studio did not point to this issue. I don't believe it to be an error on PVS-Studio. Further research, and trial and error, led us to trying to build the project in VS2013. The issue is gone in VS2013. The closest thing I can find to explain why it works in VS2013 is here.
On x64, the object layout of a class may change from previous releases. If it has a virtual function but it doesn’t have a base class that has a virtual function, the object model of the compiler inserts a pointer to a virtual function table after the data member layout. This means the layout may not be optimal in all cases. In previous releases, an optimization for x64 would try to improve the layout for you, but because it failed to work correctly in complex code situations, it was removed in Visual C++ in Visual Studio 2013.
It looks to be a bug in VS2010 that is fixed in VS2013. The memory debugger in VS2013 shows the alignment as I would expect.
I got a dll with the following prototype:
DLL_EXPORT std::list<std::wstring>* c_ExplodeWStringToList(std::wstring in_delimiter, std::wstring in_string, int in_limit);
The application uses this like that:
std::list<std::wstring>* exploded = mydllclass->c_ExplodeWStringToList(L" ", in_command.c_str(), 0);
This works great under XP 32, but when I try this at home with my Vista 64 my program just closes itself. No error and no warning?
Some days ago the DLL was returning the list directly - no pointer. But I switched to VC++ 2010 Express, and I could not compile my DLL without this modification.
Anything I am not seeing here?
Thank you :)
Update:
DLL_EXPORT std::list<std::wstring>* c_ExplodeWStringToList(std::wstring in_delimiter, std::wstring in_string, int in_limit)
{
std::list<std::wstring>* returnlist = new std::list<std::wstring>();
std::list<std::wstring>* stringlist = new std::list<std::wstring>();
UINT pos = 0;
while(true)
{
pos = in_string.find(in_delimiter, 0);
if(pos == std::string::npos)
{
stringlist->push_back(in_string.substr(0, pos));
break;
}
else
{
stringlist->push_back(in_string.substr(0, pos));
in_string = in_string.substr(pos + in_delimiter.length());
}
}
// ****
// Here is missing some code I've commented out while searching for the error.
// ****
returnlist = stringlist;
return returnlist;
}
T
I didn't dig into the code, but a conclusion I came to regarding working with DLLs is to not return anything but primitive types from DLL functions. This is because due to different compilers or different switches or project settings structs and classes are not aligned the same not have the same size in the DLL and in the code calling the DLL.
So returning a list from a DLL might be considered malformed in the caller application.
Same thing regards throwing exceptions from a DLL - the class thrown might be misinterpreted by the catching code.
So, best is export only C functions that return primitive types (to denote error codes).
I want to call a method in a COM component from C# using COM interop. This is the methods signature:
long GetPrecursorInfoFromScanNum(long nScanNumber,
LPVARIANT pvarPrecursorInfos,
LPLONG pnArraySize)
and this is sample code (which I checked is really working) to call it in C++:
struct PrecursorInfo
{
double dIsolationMass;
double dMonoIsoMass;
long nChargeState;
long nScanNumber;
};
void CTestOCXDlg::OnOpenParentScansOcx()
{
VARIANT vPrecursorInfos;
VariantInit(&vPrecursorInfos);
long nPrecursorInfos = 0;
m_Rawfile.GetPrecursorInfoFromScanNum(m_nScanNumber,
&vPrecursorInfos,
&nPrecursorInfos);
// Access the safearray buffer
BYTE* pData;
SafeArrayAccessData(vPrecursorInfos.parray, (void**)&pData);
for (int i=0; i < nPrecursorInfos; ++i)
{
// Copy the scan information from the safearray buffer
PrecursorInfo info;
memcpy(&info,
pData + i * sizeof(MS_PrecursorInfo),
sizeof(PrecursorInfo));
}
SafeArrayUnaccessData(vPrecursorInfos.parray);
}
And here's the corresponding C# signature after importing the typelib of the COM component:
void GetPrecursorInfoFromScanNum(int nScanNumber, ref object pvarPrecursorInfos, ref int pnArraySize);
If I'm not mistaken, I need to pass in null for pvarPrecursorInfos to make COM interop marshal it as the expected VT_EMPTY variant. When I'm doing it, I get a SafeArrayTypeMismatchException - not really surprising, looking at how the result is expected to be handled in the sample. So I was trying to use a custom marshaler. Since a cannot alter the component itself, I tried to introduce it this way:
[Guid("06F53853-E43C-4F30-9E5F-D1B3668F0C3C")]
[TypeLibType(4160)]
[ComImport]
public interface IInterfaceNew : IInterfaceOrig
{
[DispId(130)]
int GetPrecursorInfoFromScanNum(int nScanNumber, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshaler))] ref object pvarPrecursorInfos, ref int pnArraySize);
}
The TypeLibType and DispID attribute are the same as in the original version. This works as far as that the MyMarshaller.GetInstance() method is called, but I do not get a callback in MyMarshaller.NativeToManaged. Instead, an access violation is reported. So is this a reliable approach? If yes - how can I make it work? If no: are there any alternatives?
(Just a footnote: in theory I could try to use managed C++ to call the component natively. However, there are lots of other methods in it that work fine with COM interop, so I would very much like to stick with C# if there is any way.)
Since someone asked for it, here's my solution in Managed C++.
array<PrecursorInfo^>^ MSFileReaderExt::GetPrecursorInfo(int scanNumber)
{
VARIANT vPrecursorInfos;
VariantInit(&vPrecursorInfos);
long nPrecursorInfos = -1;
//call the COM component
long rc = pRawFile->GetPrecursorInfoFromScanNum(scanNumber, &vPrecursorInfos, &nPrecursorInfos);
//read the result
//vPrecursorInfos.parray points to a byte sequence
//that can be seen as array of MS_PrecursorInfo instances
//(MS_PrecursorInfo is a struct defined within the COM component)
MS_PrecursorInfo* pPrecursors;
SafeArrayAccessData(vPrecursorInfos.parray, (void**)&pPrecursors);
//now transform into a .NET object
array<PrecursorInfo^>^ infos = gcnew array<PrecursorInfo^>(nPrecursorInfos);
MS_PrecursorInfo currentPrecursor;
for (int i=0; i < nPrecursorInfos; ++i)
{
currentPrecursor = pPrecursors[i];
infos[i] = safe_cast<PrecursorInfo^>(Marshal::PtrToStructure(IntPtr(¤tPrecursor), PrecursorInfo::typeid));
}
SafeArrayUnaccessData(vPrecursorInfos.parray);
return infos;
}
I look at the github code mzLib, which I believe is related to this topic. The code looks good, where it calls
pin_ptr<const wchar_t> wch = PtrToStringChars(path);
I think it may cause some problem, better use
pin_ptr<const wchar_t> pathChar = static_cast<wchar_t*>(System::Runtime::InteropServices::Marshal::StringToHGlobalUni(path).ToPointer());
The code then seems to be worked just fine when compiles. However, it might run in problem when imported as dll. I worked on that by adding a constructor,such as
public ref class ThermoDLLClass
{
public:
ThermoDLLClass();
PrecursorInfo GetPrecursorInfo(int scanNum, String^ path);
};
Then, it seems to get precursorInfo and parameters appropriately.