WMI Query for SQL Server FilestreamSettings - c++

Based on what I can find on the internet this doesn't seem to be something a lot of people do but I'm pretty stuck so I'm going to put it out here. I'm using WMI in C++ to try to manipulate SQL Server settings. I have the following code that doesn't return a result from my WMI query and I'm at a loss as to why:
hr = pLoc->ConnectServer(CComBSTR(L"root\\Microsoft\\SqlServer\\ComputerManagement10"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags
0, // Authority (e.g. Kerberos)
0, // Context object
&pSvc);
// ----- Check for success and set proxy blanket here -----
IEnumWbemClassObject* pClassEnum = 0;
hr = pSvc->ExecQuery(_bstr_t("WQL"), _bstr_t("SELECT * FROM FilestreamSettings"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pClassEnum);
if (SUCCEEDED(hr) && pClassEnum)
{
ULONG uReturn = 0;
while (pClassEnum && !myInstanceFound)
{
hr = pClassEnum->Next(WBEM_INFINITE, 1, &pObjInstance, &uReturn);
if (0 == uReturn || !pObjInstance)
{
break;
}
// Get the value of the InstanceName property - the SQL Server instance name
CComVariant vtProp;
hr = pObjInstance->Get(L"InstanceName", 0, &vtProp, 0, 0);
if (SUCCEEDED(hr) && (VT_BSTR) == vtProp.vt)
{
if (vtProp.bstrVal == _bstr_t('MyInstance'))
{
myInstanceFound = true;
}
}
}
.
.
.
}
The ExecQuery command succeeds. The pClassEnum enumerator object is not null, so the while loop executes. The call to 'Next', however, does not return an object (pObjectInstance is null) and &uReturn is 0 (which, as I understand it means that the call to 'Next' returned 0 results). However, if I run the same query in the wbemtest tool, I get two results (which is correct, as I have 2 SQL Server instances on this machine). I have limited C++ skills and this is my first time with WMI. Not only do I not see what's wrong here, I'm not even sure what else to try. The few code samples I've seen suggest this code should be correct. Any help would be greatly appreciated!
Thanks,
Dennis
Update: The call to Next() actually returns S_FALSE. Which, if I'm reading the docs correctly, mostly just confirms the issue of not getting a result. Next() returns S_FALSE if there are less than the number of requested results (in my case, less than 1 - or in other words, 0).
Update #2: This same code does work on my laptop (well, the Next() call does anyway). Differences are: Does work on my laptop - Win 10, Sql Server 2019 (have to change namespace to be ComputerManagement15 instead of 10), FileStream already enabled. Does not work - Win 7 VM, Sql Server 2008, FileStream not enabled. A query using Wbemtest tool gets the correct data in both cases. Just thought I'd post in case this helps.

FYI, in case anyone stumbles across this: I didn't technically solve this, in that I never got my C++ code to work. I wrote some C# code using SQL Server Management Objects (basically a wrapper over WMI) and made it into a COM server that I could call from C++. Even this didn't work directly because my C# COM server kept getting an "Access Denied" even if I ran the C++ COM client application as Administrator. What eventually worked was to extract the SSMO code out into its own C# console app which I then ran from my C# COM server as its own process using the "run as" verb so it would run as Administrator. This finally managed to enable Filestream on my SQL Server instance. It's possible there was a better/easier way to get this done but I found something that worked (although it was pretty kludgy). So if there's a chance this helps anyone else, I'm putting it out there.

Related

What does the BluetoothGATTSetCharacteristicValue ReliableWriteContext parameter actually do?

Using C++ VS2017 Win10...
I am testing some code to set a characteristic value on a BLE device. I had originally had the default function call
HRESULT hr = BluetoothGATTSetCharacteristicValue(
hBleService.get(),
_pGattCharacteristic,
gatt_value,
NULL,
BLUETOOTH_GATT_FLAG_NONE);
I had to switch the flag to BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE to get any sets to take place even though IsWritable and IsReadable were the only 2 properties set showing true but that is a different story and another topic.
Anyway, before I found the problem with the flag I had tried to use the ReliableWriteContext parameter so the code changed to
HANDLE hDevice = _bleDeviceContext.getBleDeviceHandle();
BTH_LE_GATT_RELIABLE_WRITE_CONTEXT ReliableWriteContext = NULL;
HRESULT hr1 = BluetoothGATTBeginReliableWrite(hDevice,
&ReliableWriteContext,
BLUETOOTH_GATT_FLAG_NONE);
HRESULT hr = BluetoothGATTSetCharacteristicValue(
hBleService.get(),
_pGattCharacteristic,
gatt_value,
ReliableWriteContext,
BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE);
if (NULL != ReliableWriteContext) {
BluetoothGATTEndReliableWrite(hDevice,
ReliableWriteContext,
BLUETOOTH_GATT_FLAG_NONE);
}
Once I fixed the flag issue my BluetoothGATTSetCharacteristicValue() function would work with either the NULL or the ReliableWriteContext parameters. No difference that I could see.
My question is,then, what does the ReliableWriteContext do exactly? MS docs only say:
The BluetoothGATTBeginReliableWrite function specifies that reliable
writes are about to begin.
Well that doesn't tell me anything. So should I keep the reliable writes because the word "Reliable" sure sounds like it is something that I want? Or do I not use it because it does not seem to be necessary>
Any insight would be appreciated.
Ed

DPAPI NG - NCryptProtectSecret returns NTE_ENCRYPTION_FAILURE

I am trying to encrypt data using DPAPI-NG but it fails on execution of NCryptProtectSecret, it returns:
0x80090034 (NTE_ENCRYPTION_FAILURE)
I have created NCryptCreateProtectionDescriptor with local user SID:
"SID=S-1-5-21-2942599413-360359348-3087651068-500"
Then I use this instance of descriptor as input for NCryptProtectSecret, but it does not work.
If I use a protection descriptor of:
"LOCAL=user"
everything seems okay, but it does not work with SID for user or group. I have tested this on Windows Server 2012R2 and Windows Server 2016.
Any idea?
Here is a code sample:
SECURITY_STATUS Status;
PBYTE ProtectedData = NULL;
ULONG ProtectedDataLength = 0;
NCRYPT_DESCRIPTOR_HANDLE DescriptorHandle = NULL;
LPCWSTR ProtectionDescString = L"SID=S-1-5-21-2942599413-360359348-3087651068-500";
Status = NCryptCreateProtectionDescriptor(
ProtectionDescString,
0,
&DescriptorHandle
);
// Status is ERROR_SUCCESS (zero)
LPCWSTR SecretString = L"Some message to protect";
PBYTE Secret = (PBYTE)SecretString;
DWORD SecretLength = (ULONG)( (wcslen(SecretString)+1)*sizeof(WCHAR) );
Status = NCryptProtectSecret(
DescriptorHandle,
0,
PlainText,
PlainTextLength,
NULL, // Use default allocations by LocalAlloc/LocalFree
NULL, // Use default parent windows handle.
&ProtectedData, // out LocalFree
&ProtectedDataLength
);
**// Status == NTE_ENCRYPTION_FAILURE**
I ran into this problem and found that the cause was our domain was running at a functional level that was less than 2012. After upgrading the domain to 2012 the problem was resolved.
A quick and easy way to determine the functional level is the following PowerShell cmdlet
[system.directoryservices.activedirectory.Forest]::GetCurrentForest().ForestMode
Replace PlainText and PlainTextLength with Secret and SecretLength.
I haven't figured out what was the problem, but everything worked fine in different domain. Microsoft also confirmed that working example that we have sent to them was correct, but they didn't explain what was be the problem.
Check that the user running the application really is user
S-1-5-21-2942599413-360359348-3087651068-500
You can test this from and command prompt:
>whoami /user
USER INFORMATION
----------------
User Name SID
============= ============================================
erbium\zeljko S-1-5-21-2942599413-360359348-3087651068-500
I got the NTE_ENCRYPTION_FAILURE when i was attempting to use a Group SID that i didn't actually have (the Domain Users group).
It might be you simply have the wrong sid compared to who is running the code.

How to use file bandwidth reservation (scheduled file IO) in Windows

I need to implement an application that streams data from disk. It is important that the data throughput is fairly constant and is not interupted by any other activity on the disk.
From Windows Vista onwards, the GetFileBandwidthReservation() and SetFileBandwidthReservation() functions have been introduced specifically for this purpose. However, I cannot get this to work. I've searched the internet but I cannot find much information on this (and no working code samples seem to exist online).
Code to reproduce:
HANDLE h = ::CreateFile(L"D:\\testfile", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
DWORD periodMilliseconds, bytesPerPeriod, transferSize, numOutstandingRequests;
BOOL discardable;
BOOL result = ::GetFileBandwidthReservation(h, &periodMilliseconds, &bytesPerPeriod,
&discardable, &transferSize, &numOutstandingRequests);
if (result == FALSE) // result is always false!
{
DWORD reason = ::GetLastError(); // reason is always 1!
std::cout << "Error: " << reason << std::endl;
}
result = ::CloseHandle(h);
The call to GetFileBandwidthReservation always returns FALSE which indicates a failure. GetLastError returns 1 which isn't very helpfull. If a try to invoke *Set*FileBandwithReservation I get the same result.
I am testing this on a PC with Windows Server 2008 SP2 (32-bit).
Does anybody have any idea of what I am doing wrong? Any help will be greatly appreciated.
This requires support from the disk device driver. The kind of driver that you'd find in an upscale server, not a consumer level machine. Ask more questions about this at serverfault.com

How do you programmatically determine whether a Windows computer is a member of a domain?

I need a way to determine whether the computer running my program is joined to any domain. It doesn't matter what specific domain it is part of, just whether it is connected to anything. I'm coding in vc++ against the Win32 API.
Straight from Microsoft:
How To Determine If a Windows NT/Windows 2000 Computer Is a Domain Member
This approach uses the Windows API. From the article summary:
This article describes how to
determine if a computer that is
running Windows NT 4.0 or Windows 2000
is a member of a domain, is a member
of a workgroup, or is a stand-alone
computer using the Local Security
Authority APIs.
The article also provides sample code for a small program that outputs whether the computer the program is running on is part of a domain, part of a workgroup, or a standalone computer.
I think the NetServerEnum function will help you in what you want; I would ask for the primary domain controllers with the SV_TYPE_DOMAIN_CTRL constant for servertype parameter. If you don't get any, then you're not in a domain.
The code in the MSDN sample is a little outdated. This is the function I came up with that works.
bool ComputerBelongsToDomain()
{
bool ret = false;
LSA_OBJECT_ATTRIBUTES objectAttributes;
LSA_HANDLE policyHandle;
NTSTATUS status;
PPOLICY_PRIMARY_DOMAIN_INFO info;
// Object attributes are reserved, so initialize to zeros.
ZeroMemory(&objectAttributes, sizeof(objectAttributes));
status = LsaOpenPolicy(NULL, &objectAttributes, GENERIC_READ | POLICY_VIEW_LOCAL_INFORMATION, &policyHandle);
if (!status)
{
status = LsaQueryInformationPolicy(policyHandle, PolicyPrimaryDomainInformation, (LPVOID*)&info);
if (!status)
{
if (info->Sid)
ret = true;
LsaFreeMemory(info);
}
LsaClose(policyHandle);
}
return ret;
}
Here is a dead simple approach I don't see mentioned.
TCHAR UserDnsDomain[128] = { 0 };
DWORD Result = 0;
Result = GetEnvironmentVariable("USERDNSDOMAIN", UserDnsDomain, sizeof(UserDnsDomain));
if (Result == 0 || Result >= sizeof(UserDnsDomain) || GetLastError() == ERROR_ENVVAR_NOT_FOUND)
{
return(FALSE); // Not logged in to a domain
}
This is predicated on the idea that if the user who is running this code is not currently logged in to a domain, then the USERDNSDOMAIN environment variable will be empty or unavailable. But there are some caveats you should think about.
Pros:
Very easy to implement.
99% reliable.
Cons:
May fail or return false results if the computer is domain joined, but the user executing this code is logged on to that computer with a local account.
May fail or return false results if the computer is domain joined, but network connectivity to a domain controller was unavailable at the time of logon/user logged on with cached credentials.
You can check the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon for the value of 'CachePrimaryDomain'.
Avoid LSA which is a wrong method.
You must use DS api (2 lines of code)
what about from the name of the computer?
edit: this was a crapy 'answer' from way back. What I meant was cheching for the form domain\name in the computer name. That of course implies that you do know the name of the domain, it does not solves the issue of just knowing if the computer is in any domain.

IDebugProgramProvider2.GetProviderProcessData on Vista

As part of a JavaScript Profiler for IE 6/7 I needed to load a custom debugger that I created into IE. I got this working fine on XP, but couldn't get it working on Vista (full story here: http://damianblog.com/2008/09/09/tracejs-v2-rip/).
The call to GetProviderProcessData is failing on Vista. Anyone have any suggestions?
Thanks,
Damian
// Create the MsProgramProvider
IDebugProgramProvider2* pIDebugProgramProvider2 = 0;
HRESULT st = CoCreateInstance(CLSID_MsProgramProvider, 0, CLSCTX_ALL, IID_IDebugProgramProvider2, (void**)&pIDebugProgramProvider2);
if(st != S_OK) {
return st;
}
// Get the IDebugProgramNode2 instances running in this process
AD_PROCESS_ID processID;
processID.ProcessId.dwProcessId = GetCurrentProcessId();
processID.ProcessIdType = AD_PROCESS_ID_SYSTEM;
CONST_GUID_ARRAY engineFilter;
engineFilter.dwCount = 0;
PROVIDER_PROCESS_DATA processData;
st = pIDebugProgramProvider2->GetProviderProcessData(PFLAG_GET_PROGRAM_NODES|PFLAG_DEBUGGEE, 0, processID, engineFilter, &processData);
if(st != S_OK) {
ShowError(L"GPPD Failed", st);
pIDebugProgramProvider2->Release();
return st;
}
It would help to know what the error result was.
Possible problems I can think of:
If your getting permission denied, your most likely missing some requried Privilege in your ACL. New ones are sometimes not doceumented well, check the latest Platform SDK headers to see if any new ones that still out. It may be that under vista the Privilege is not assigned my default to your ACL any longer.
If your getting some sort of Not Found type error, then it may be 32bit / 64bit problem. Your debbugging API may only be available under 64bit COM on vista 64. The 32bit/64bit interoperation can be very confusing.
I'm not familiar with these interfaces, but unexpected failures in Vista may require being past a UAC prompt. Have you tried starting the debugger with admin privileges?