Can anyone Please tell me how to get Model name of Windows Machine.
I am new to Windows VC++.
For Example i have an IBM ThinkCenter M50 running on Windows. Here the Model name is "Think Center M50". I want to get this from the System using some API.
Thanks in Advance,
Shashi Kiran G M
Alternatively, you could use the registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SystemInformation
also: HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\BIOS (Win7 or later only)
The SystemManufacturer and SystemProductName entries should do it. Saves using WMI, which i try to avoid at all costs for performance reasons.
As Ben suggests, you'll need to use WMI for this.
The class you're looking for is Win32_ComputerSystem, which contains a read-only Model property of type string that returns the product name that a manufacturer gives to a computer.
I'll leave writing the C++ code to make this WMI call as an exercise for the reader.
Do note Ben's caveat as well: not all manufacturers publish this information in the BIOS. It's very likely that IBM does, so your test case should work out fine, but this is not a universal assumption that you are justified in making. Applications should not rely on this property containing a particular value.
With the help of the Microsoft example code, I was able to create this method.
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
std::pair<CString,CString> getComputerManufacturerAndModel() {
// Obtain the initial locator to Windows Management on a particular host computer.
IWbemLocator *locator = nullptr;
IWbemServices *services = nullptr;
auto hResult = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&locator);
auto hasFailed = [&hResult]() {
if (FAILED(hResult)) {
auto error = _com_error(hResult);
TRACE(error.ErrorMessage());
TRACE(error.Description().Detach());
return true;
}
return false;
};
auto getValue = [&hResult, &hasFailed](IWbemClassObject *classObject, LPCWSTR property) {
CString propertyValueText = "Not set";
VARIANT propertyValue;
hResult = classObject->Get(property, 0, &propertyValue, 0, 0);
if (!hasFailed()) {
if ((propertyValue.vt == VT_NULL) || (propertyValue.vt == VT_EMPTY)) {
} else if (propertyValue.vt & VT_ARRAY) {
propertyValueText = "Unknown"; //Array types not supported
} else {
propertyValueText = propertyValue.bstrVal;
}
}
VariantClear(&propertyValue);
return propertyValueText;
};
CString manufacturer = "Not set";
CString model = "Not set";
if (!hasFailed()) {
// Connect to the root\cimv2 namespace with the current user and obtain pointer pSvc to make IWbemServices calls.
hResult = locator->ConnectServer(L"ROOT\\CIMV2", nullptr, nullptr, 0, NULL, 0, 0, &services);
if (!hasFailed()) {
// Set the IWbemServices proxy so that impersonation of the user (client) occurs.
hResult = CoSetProxyBlanket(services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
if (!hasFailed()) {
IEnumWbemClassObject* classObjectEnumerator = nullptr;
hResult = services->ExecQuery(L"WQL", L"SELECT * FROM Win32_ComputerSystem", WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, &classObjectEnumerator);
if (!hasFailed()) {
IWbemClassObject *classObject;
ULONG uReturn = 0;
hResult = classObjectEnumerator->Next(WBEM_INFINITE, 1, &classObject, &uReturn);
if (uReturn != 0) {
manufacturer = getValue(classObject, (LPCWSTR)L"Manufacturer");
model = getValue(classObject, (LPCWSTR)L"Model");
}
classObject->Release();
}
classObjectEnumerator->Release();
}
}
}
if (locator) {
locator->Release();
}
if (services) {
services->Release();
}
CoUninitialize();
return { manufacturer, model };
}
Related
Consider the following code:
bool OpenStream(const std::wstring& fileName)
{
PIDLIST_ABSOLUTE pidl = nullptr;
if (FAILED(::SHILCreateFromPath(fileName.c_str(), &pidl, nullptr)))
return false;
wchar_t buffer[MAX_PATH + 1];
if (::SHGetPathFromIDListW(pidl, buffer))
{
::OutputDebugString(L"File IDL path: ");
::OutputDebugString(buffer);
::OutputDebugString(L"\r\n");
}
IShellFolder* pShellfolder = nullptr;
LPCITEMIDLIST pidlRelative = nullptr;
HRESULT hr = ::SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, &pidlRelative);
if (FAILED(hr))
{
::CoTaskMemFree(pidl);
return false;
}
if (::SHGetPathFromIDListW(pidl, buffer))
{
::OutputDebugString(L"Relative IDL path: ");
::OutputDebugString(buffer);
::OutputDebugString(L"\r\n");
}
IStream* pStream = nullptr;
//if (FAILED(pShellfolder->BindToObject(pidlRelative, NULL, IID_IStream, (void**)&pStream)))
if (FAILED(pShellfolder->BindToStorage(pidlRelative, NULL, IID_IStream, (void**)&pStream)))
{
pShellfolder->Release();
::CoTaskMemFree(pidl);
return false;
}
ULARGE_INTEGER size;
::IStream_Size(pStream, &size);
LARGE_INTEGER pos = {0};
pStream->Seek(pos, STREAM_SEEK_SET, nullptr);
unsigned char* pBuffer = new unsigned char[size.QuadPart];
ULONG actualRead;
hr = pStream->Read(pBuffer, size.QuadPart, &actualRead);
std::FILE* pFile;
fopen_s(&pFile, "__Copy.bin", "wb");
if (!pFile)
{
delete[] pBuffer;
pShellfolder->Release();
::CoTaskMemFree(pidl);
return false;
}
const std::size_t writeCount = std::fwrite(pBuffer, sizeof(unsigned char), size.QuadPart, pFile);
std::fclose(pFile);
delete[] pBuffer;
pStream->Seek(pos, STREAM_SEEK_SET, nullptr);
hr = pStream->Write("Test-test-test-test", 19, nullptr);
pShellfolder->Release();
::CoTaskMemFree(pidl);
return true;
}
This code opens the file passed in fileName in a stream and write its content in a new file, using the std to achieve that. Until here all works fine.
However, as a last operation, I want to modify the content of the opened file. However I cannot do that with the above code, indeed it compiles and runs, but it does nothing, and I receive an ACCESS_DENIED error as a result.
How should I modify the above code to allow the opened stream to read AND WRITE in my file?
Also, as a side question: Is the above code safe and well written (i.e will it generates memory leaks, or is something unsafe in it)? A detailed review would be welcome.
Your code doesn't work because you're implicitly opening the stream as read-only. To open it for write operations, you must pass a binding context, something like this:
IBindCtx* ctx;
CreateBindCtx(0, &ctx);
BIND_OPTS options = {};
options.cbStruct = sizeof(BIND_OPTS);
options.grfMode = STGM_WRITE;
ctx->SetBindOptions(&options);
...
pShellfolder->BindToStorage(pidlRelative, ctx, IID_IStream, (void**)&pStream);
...
Otherwise, when you work with COM objects, you should smart pointers to avoid COM reference leaks. For example, below, I use ATL that comes with Visual Studio, but there's also WRL and C++/WinRT (which can do some basic COM stuff too)
Also, for Shell objects it's much easier to work with IShellItem and friends and all ("Item") APIs that come with it, like this (error checks omitted here but you should test every single HRESULT for error) and avoid the old IShellFolder:
CComPtr<IShellItem> item;
SHCreateItemFromParsingName(fileName.c_str(), nullptr, IID_PPV_ARGS(&item));
CComPtr<IBindCtx> ctx;
CreateBindCtx(0, &ctx);
BIND_OPTS options = {};
options.cbStruct = sizeof(BIND_OPTS);
options.grfMode = STGM_WRITE;
ctx->SetBindOptions(&options);
CComPtr<IStream> stream;
item->BindToHandler(ctx, BHID_Stream, IID_PPV_ARGS(&stream));
stream->Write("Test-test-test-test", 19, nullptr);
I want to obtain a monitor handle (HMONITOR) that can be used with the Windows multi-monitor APIs for a specific monitor attached to the system by index. For example, say I have three monitors attached to my system and forming part of my desktop; I want to get a handle to monitor 3.
I already know how to get the device name for a specific monitor by index by calling the EnumDisplayDevices function. For example:
HMONITOR MonitorFromIndex(int index /* (zero-indexed) */)
{
DISPLAY_DEVICE dd;
dd.cb = sizeof(dd);
if (EnumDisplayDevices(NULL, index, &dd, 0) != FALSE)
{
// We found a match; make sure that it's part of the desktop.
if ((dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
{
// Yup. Now we've got the name of the device:
std::cout << dd.DeviceName << std::endl;
// But how do I obtain an HMONITOR for this device?
// ...
}
}
return NULL; // indicate failure
}
In the code above, we've found the name of the desired device (dd.DeviceName). I can use this name to create a DC for that monitor by calling CreateDC:
HDC hDC = CreateDC(dd.DeviceName, dd.DeviceName, NULL, NULL);
And I can obtain information about that monitor by calling EnumDisplaySettings:
DEVMODE dm;
dm.dmSize = sizeof(dm);
dm.dmDriverExtra = 0;
if (EnumDisplaySettings(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm) != FALSE)
{
std::cout << "The monitor supports " << dm.dmBitsPerPel << " bits per pixel." << std::endl;
}
Which is all great, but I want a handle to that monitor. How can I get it?
I tried to call EnumDisplayMonitors, passing a handle to the device context that I created using CreateDC, hoping to get a handle to the monitor passed to the callback function, but no such luck. The callback function was never called, and EnumDisplayMonitors returned FALSE (without setting an error code):
struct FoundMatch
{
BOOL found;
HMONITOR hMonitor;
};
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
{
FoundMatch* pfm = reinterpret_cast<FoundMatch*>(dwData);
pfm->found = TRUE;
pfm->hMonitor = hMonitor;
return FALSE; // stop enumerating
}
// elsewhere, after getting the device name and using it to create a DC
FoundMatch fm;
fm.found = FALSE;
fm.hMonitor = NULL;
BOOL result = EnumDisplayMonitors(hDC, NULL, MonitorEnumProc, reinterpret_cast<LPARAM>(&fm));
Sorry for such a late reply but maybe someone can find this useful.
The multi-monitor API is really minimalist to say the least. Once you've got your dd.DeviceName, it appears you have to go through EnumDisplayMonitors() enumeration until you find a match of dd.DeviceName against MONITORINFOEX.szDevice.
The MONITORINFOEX structure can be obtained by calling GetMonitorInfo().
Here is a non-compilable C++11 pseudo code:
struct DataBag
{
HMONITOR hmon;
TCHAR* devname;
} bag;
bag.hmon = NULL;
bag.devname = &dd.DeviceName;
BOOL bRes = EnumDisplayMonitors(
NULL, NULL,
[](HMONITOR hMonitor, HDC hDC, LPRECT rc, LPARAM data) -> BOOL {
auto& bag = *reinterpret_cast<DataBag*>(data);
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
if (/* match bag.devname against mi.szDevice */ && GetMonitorInfo(hMonitor, &mi))
{
bag.hmon = hMonitor;
return FALSE;
}
return TRUE;
},
reinterpret_cast<LPARAM>(&bag));
if (bRes && bag.hmon)
{
// Monitor found!
}
The MIDL compiler generates code for a proxy/stub with registration routines that write to HKEY_LOCAL_MACHINE. Is there any way (preferably without hacking the MIDL-generated code and without bypassing all that generated code in favor of custom code) to register a MIDL-generated p/s in HKEY_CURRENT_USER?
Also: Will this work if both the p/s and the COM server are registered per-user like this? I just found (after a very frustrating 48 hours) that a p/s registered machine-wide will not work correctly if the COM server is registered per-user. Specifically, the asynchronous call logic (ICallFactory::CreateCall) of the p/s will fail under these circumstances.
Using RegOverridePredefKey is the right answer.
Then register with:
regsvr32 /n /i:user C:\src\myCode.dll
With "/i:user", regsvr32 calls your the "DllInstall" function instead of DllRegisterServer.
Example implementation of DllInstall:
extern "C" STDAPI DllInstall(BOOL bInstall, _In_opt_ LPCWSTR pszCmdLine)
{
HRESULT hr = E_FAIL;
static const wchar_t szUserSwitch[] = L"user";
if (pszCmdLine != NULL)
{
if (_wcsnicmp(pszCmdLine, szUserSwitch, _countof(szUserSwitch)) == 0)
{
ATL::AtlSetPerUserRegistration(true); // is this really needed??
}
}
LSTATUS status = RegCreateKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\Classes", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hkcu_classes, &disposition);
if (status == ERROR_SUCCESS)
{
status = RegOverridePredefKey(HKEY_CLASSES_ROOT, hkcu_classes);
}
hr = HRESULT_FROM_NT(status);
if (SUCCEEDED(hr))
{
if (bInstall)
{
hr = DllRegisterServer();
if (FAILED(hr))
{
DllUnregisterServer();
}
}
else
{
hr = DllUnregisterServer();
}
}
}
I am using Outlook automation to find items in the Calendar. For that I use IsSearchSynchronous() method to see if I have to wait for the AdvancedSearchComplete event. BTW, is it ever synchronous???
Anyway, if I have Outlook running, I have no problems with this call. But if it doesn't run - the call fails with
HRESULT: 0x80020009 Exception occurred
The EXCEPINFO contains:
Source: "Microsoft Outlook"
Description: "The operation failed."
scode: 0x80004005
Any suggestions?
Here is my test case:
#include <atlstr.h>
int _tmain()
{
IDispatch* pApp;
HRESULT hr;
CoInitialize(NULL);
CLSID clsid;
hr = CLSIDFromProgID(L"Outlook.Application", &clsid);
if(SUCCEEDED(hr))
{
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pApp);
if(SUCCEEDED(hr))
{
// Get DISPID for "IsSearchSynchronous"
OLECHAR Name[] = {L"IsSearchSynchronous"};
LPOLESTR lp = Name;
DISPID dispID;
hr = pApp->GetIDsOfNames(IID_NULL, &lp, 1, LOCALE_USER_DEFAULT, &dispID);
if(SUCCEEDED(hr))
{
// The path name of the folders that the search will search through.
VARIANT path;
path.vt = VT_BSTR;
path.bstrVal = SysAllocString(L"'Calendar'");
// Build DISPPARAMS
DISPPARAMS dp = { NULL, NULL, 0, 0 };
dp.cArgs = 1;
dp.rgvarg = &path;
// get IsSearchSynchronous
VARIANT result;
VariantInit(&result);
EXCEPINFO ei = {0};
hr = pApp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dp, &result, &ei, NULL);
VARIANT_BOOL vbIsSearchSynchronous = result.boolVal;
}
}
}
CoUninitialize();
return 0;
}
In case anybody else is interested, this is resolved with a help from Microsoft.
The “scope” parameter to a IsSearchSynchronous() call should be a complete path to the calendar folder, like:
"\\Mailbox - <user_name>\\Calendar"
On Windows XP there is a known way to create a Remote Assistance Ticket.
http://msdn.microsoft.com/en-us/library/ms811079.aspx
But on Vista this does not appear to work. How does one do this on Vista or Windows 7?
There turns out to be two ways. The Microsoft API is called IRASrv and is documented here:
http://msdn.microsoft.com/en-us/library/cc240176(PROT.10).aspx
Another way is to simply call msra.exe. with password and novice params (e.g. msra.exe /saveasfile testfile thepassword). However that does prompt the user with the password dialog.
Here is example code for calling the IRASrv interface and generating a Remote Assistance Connection String.
COSERVERINFO si; ::ZeroMemory( &si, sizeof( si ) );
MULTI_QI qi; ::ZeroMemory( &qi, sizeof( qi ) );
HRESULT hr = S_OK;
BSTR bstrUserName = SysAllocString(L"jon");
BSTR bstrDomainName = SysAllocString(L"");
BSTR bstrPasswordStr = SysAllocString(L"testpass");
// Get the security information entered by the user
_bstr_t bstrUser(bstrUserName);
_bstr_t bstrDomain(bstrDomainName);
_bstr_t bstrPassword(bstrPasswordStr);
// Set AuthIdentity
SEC_WINNT_AUTH_IDENTITY_W AuthIdentity = {
(unsigned short*)bstrUserName,
bstrUser.length(),
(unsigned short*)bstrDomainName,
bstrDomain.length(),
(unsigned short*)bstrPasswordStr,
bstrPassword.length(),
SEC_WINNT_AUTH_IDENTITY_UNICODE
};
COAUTHINFO AuthInfo = {
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_DEFAULT,
NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // The authentication level used
RPC_C_IMP_LEVEL_IMPERSONATE,
(COAUTHIDENTITY*)&AuthIdentity,
EOAC_NONE
};
si.pAuthInfo = &AuthInfo;
si.pwszName = bstrMachineName;
qi.pIID = &(__uuidof(RAServerLib::IRASrv));
hr = ::CoCreateInstanceEx(
__uuidof(RAServerLib::RASrv), NULL, CLSCTX_REMOTE_SERVER,
&si, 1, &qi );
if (FAILED(hr))
{
return hr;
}
CComPtr<RAServerLib::IRASrv> prasrv;
hr = qi.pItf->QueryInterface(__uuidof(RAServerLib::IRASrv), (void**)&prasrv);
if (FAILED(hr))
{
return hr;
}
LPWSTR pstr=NULL;
hr = prasrv->raw_GetNoviceUserInfo(&pstr);
if (FAILED(hr))
{
return hr;
}
pstr contains the Remote Assistance Connection String (type 2)