Using QueryInterface on an IAudioSessionControl for ISimpleAudioVolume Fails to Compile - c++

I have been driving myself mad with this problem for a few hours now. Essentially, I have begun development on a service that allows users to control audio volume for individual applications by sending packets over TCP to the server application. This is a hobbyist project to further my knowledge of C++.
I am using the ISessionEnumerator interface to build a list of pointers to the sessions. I would have thought it possible to control the volume directly from the sessions. I quickly figured out I would need another interface for this, suck as the ISimpleAudioVolume interface. I have the following code so far for retrieving the interface:
int AudioManager::AdjustVolumeBy(IAudioSessionControl * pSession, int adjustBy) {
void * pVolumeControl;
pSession->QueryInterface(IID_ISimpleAudioVolume, &pVolumeControl);
return 0;
}
This produced the compilation error:
LNK2001 unresolved external symbol _IID_ISimpleAudioVolume
And I am including the following headers in my header file:
#pragma once
#include <Audioclient.h>
#include <audiopolicy.h>
#include <mmdeviceapi.h>
#include <AudioSessionTypes.h>
#include <endpointvolume.h>
#include <vector>
Additionally, when creating the project, I created it including ATL and MFC. Any input is greatly appreciated.

I ultimately managed to resolve this problem by adding "winmm.lib" to "Additional Dependencies" in Linker Input.
Additionally, for the reference of anyone else who may experience this issue, the original volume adjustment code was changed to:
int AudioManager::AdjustVolumeBy(IAudioSessionControl * pSession, float adjustBy) {
ISimpleAudioVolume * pVolumeControl;
float currentVolume;float newVolume;
pSession->QueryInterface(__uuidof(ISimpleAudioVolume),(void**)&pVolumeControl);
pVolumeControl->GetMasterVolume(&currentVolume);
newVolume = currentVolume + adjustBy;
if (newVolume < 0) {
newVolume = 0;
}
pVolumeControl->SetMasterVolume(newVolume,NULL);
return 0;
}
And I had to add:
allocationResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
To the class constructor to initialise the Component Object Model library for use.

Related

c++/cli dll wrapper for native c++ to be used in LabView

I'm trying to write a c++/cli wrapper for IO Industries Core2 DVR, which will then be used by LabView. The company provided a SDK with with all the headers (written in c++) and boost library. I've managed to build a wrapper that builds and LabView is able to see the function through the .net pallet.
// ManagedProject.h
#pragma once
#include "core_api_helper.h"
#include "core_api.h"
using namespace System;
using namespace CoreApi;
namespace ManagedProject {
//Setup class
public ref class Setup
{
private:
public:
unsigned int initializeTest();
};
}
// This is the DLL Wrapper.
#include "stdafx.h"
#include "ManagedProject.h"
#include "core_api_helper.h"
#include "core_api.h"
#include "resource.h"
using namespace CoreApi;
using namespace Common;
using namespace ManagedProject;
//Global handles
//A handle to the Core Api
InstanceHandle g_hApi;
//A handle to the Core Api Device Collection
DeviceCollectionHandle g_hCoreDeviceCollection;
unsigned int Setup::initializeTest()
{
try
{
//Initialize the Core API (must be called before any other Core API functions)
//Returns a handle to the Core Api
g_hApi = Instance::initialize();
// get a collection of Core devices
g_hCoreDeviceCollection = g_hApi->deviceCollection();
unsigned int deviceCount = g_hCoreDeviceCollection->deviceCount();
return deviceCount;
}
catch (GeneralException& e)
{
e.what();
return 3;
}
}
However when I run LabView through Visual studio 2015 in debug mode I run into the problem below, and what is returned to LabView is the 3 from the catch block.
First break in debug mode (NULL ptr)
NOTE: InstanceHandle is a shared_ptr
As can be seen the variable is a NULL pointer, the same thing happens for the g_hCoreDeviceCollectoin as well. I think I need to Instantiate it with the new command but am a little unsure as InstanceHandle is a shared_ptr.
Any help would be much appreciated
The C++/CLI has great feature called mixed mode. You can uses both managed and native data types in the same code (in the same C++/CLI class). Try to use object from that SDK written in C++ directly in your wrapper.

Get list of all types/classes from Mono Assembly.dll in C++

I am trying to get a list of all the types and/or classes of a Mono Assembly file, but I could not find out how to do that. My goal is to load an assembly.dll in C++ and read its types and classes so I can use mono_class_get_fields etc.
This is what I tried with a known class name in the .dll, but class1 was NULL. What is going wrong here?
ExampleClass is exending System.Object, which is in mscorlib.dll. Is there some kind of linking I need to do to get a proper MonoClass * inside my C++ code?
This is what I tried, but mono_class_from_name returns NULL:
#include <windows.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/mono-config.h>
#include <mono/metadata/appdomain.h>
#include <mono/jit/jit.h>
int main()
{
mono_set_dirs("C:\\Program Files\\Mono\\lib", "C:\\Program Files\\Mono\\etc");
MonoDomain *domain;
domain = mono_jit_init("test");
MonoAssembly *assembly = mono_domain_assembly_open(domain, "C:\\Users\\Ik\\Documents\\Visual Studio 2015\\Projects\\MonoExtractor\\x64\\Debug\\Assembly-CSharp.dll");
MonoImage *image = mono_assembly_get_image(assembly);
// testing to see if I can get a known class
MonoClass *class1 = mono_class_from_name(image, "", "ExampleClass");
if (!class1) {
printf("Could not find class!\n");
}
system("pause");
return 0;
}
I had to put the other related dlls in the same directory too in order for it too work.

Use C++/CLI Library in C++ native code and how to link

I want to use some code that executes a http-post, and because I'm not too familiar with c++ and what libraries you can use, and I am probably too dumb to get libcurl and curlpp to work, I found a link explaining how to use the .net version.
Alright so I created a class. Header File:
public ref class Element
{
public:
Element();
virtual ~Element();
void ExecuteCommand();
};
Class file:
#include "Element.h"
Element::Element()
{
}
Element::~Element()
{
Console::WriteLine("deletion");
}
void Element::ExecuteCommand(){
HttpWebRequest^ request = dynamic_cast<HttpWebRequest^>(WebRequest::Create("http://www.google.com"));
request->MaximumAutomaticRedirections = 4;
request->MaximumResponseHeadersLength = 4;
request->Credentials = gcnew NetworkCredential("username", "password", "domain");
HttpWebResponse^ response = dynamic_cast<HttpWebResponse^>(request->GetResponse());
Console::WriteLine("Content length is {0}", response->ContentLength);
Console::WriteLine("Content type is {0}", response->ContentType);
// Get the stream associated with the response.
Stream^ receiveStream = response->GetResponseStream();
// Pipes the stream to a higher level stream reader with the required encoding format.
StreamReader^ readStream = gcnew StreamReader(receiveStream, Encoding::UTF8);
Console::WriteLine("Response stream received.");
Console::WriteLine(readStream->ReadToEnd());
response->Close();
readStream->Close();
}
If I set the configuration type of this project to Application (exe), and create a new .cpp file where I create an Instance of this Element it works fine.
But my question is: Is it possible to create a .dll/.lib Library from this project and use it in a C++ project without CLI? (I don't want to use ^ for pointers :( )
Even if it's not possible, I have another problem.
When I link the library in a C++/CLI project. I get
unresolved token (06000001) Element::.ctor
unresolved token (06000002) Element::~Element
unresolved token (06000003) Element::ExecuteCommand
3 unresolved externals
the code for main.cpp in the second project is just the following:
#include <Element.h>
int main(){
return 0;
}
Thank you
As Hans Passant already stated: you must compile your C++/CLI code as Dynamic Library in order to be able to consume it from an unmanaged application. CLI/Managed code cannot run from/cannot reside in static libraries.
If you change the C++/CLI library target from Static library to Dynamic library you'll be able to compile successfully your unmanaged C++ application.
One thought from my side:
I think you'll be better if you use mixed mode C++/CLI DLLs to consume the managed functionality - you'll be able to free your consumer application completely from referencing the CLR.
The Header of such mixed mode Wrapper for your Element class would look like this:
#pragma once
#pragma unmanaged
#if defined(LIB_EXPORT)
#define DECLSPEC_CLASS __declspec(dllexport)
#else
#define DECLSPEC_CLASS __declspec(dllimport)
#endif
class ElementWrapperPrivate;
class __declspec(dllexport) ElementWrapper
{
private:
ElementWrapperPrivate* helper;
public:
ElementWrapper();
~ElementWrapper();
public:
void ExecuteCommand();
};
And the implementation would look like this:
#include "ElementWrapper.h"
#pragma managed
#include "Element.h"
#include <msclr\auto_gcroot.h>
using namespace System::Runtime::InteropServices;
class ElementWrapperPrivate
{
public:
msclr::auto_gcroot<Element^> elementInst; // For Managed-to-Unmanaged marshalling
};
ElementWrapper::ElementWrapper()
{
helper = new ElementWrapperPrivate();
helper->elementInst = gcnew Element();
}
ElementWrapper::~ElementWrapper()
{
delete helper;
}
void ElementWrapper::ExecuteCommand()
{
helper->elementInst->ExecuteCommand();
}
Then just compile your Element.cpp + ElementWrapper.cpp to a DLL and use the ElementWrapper.h in your unmanaged applications.

What can I do about loading this resource into a unit testing DLL?

I just ported all of my tests from Google Test to Visual Studio 2012's unit test framework. (Long story; Google's assertions are better but Microsoft's works with the Dev11 Unit Test Explorer out of the box, which makes getting code coverage really easy ...)
I got everything ported over fine, except for the following class, which reads a newline-delimited text file from a Win32 resource, and then allows for quick lookups against that resource:
#include "pch.hpp"
#include "resource.h"
#include <functional>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <windows.h>
#include "Win32Exception.hpp"
#include "Whitelist.hpp"
using Instalog::SystemFacades::Win32Exception;
namespace Instalog {
Whitelist::Whitelist( __int32 whitelistId , std::vector<std::pair<std::wstring, std::wstring>> const& replacements )
{
using namespace std::placeholders;
HRSRC resourceHandle = ::FindResource(0, MAKEINTRESOURCEW(whitelistId), L"WHITELIST");
if (resourceHandle == 0)
{
Win32Exception::ThrowFromLastError();
}
HGLOBAL resourceGlobal = ::LoadResource(0, resourceHandle);
if (resourceGlobal == 0)
{
Win32Exception::ThrowFromLastError();
}
void * resourceData = ::LockResource(resourceGlobal);
if (resourceData == 0)
{
Win32Exception::ThrowFromLastError();
}
wchar_t const* resourceDataCasted = static_cast<wchar_t const*>(resourceData);
DWORD resourceLen = ::SizeofResource(0, resourceHandle);
auto sourceRange = boost::make_iterator_range(resourceDataCasted, resourceDataCasted + (resourceLen / sizeof(wchar_t)));
boost::algorithm::split(innards, sourceRange, std::bind1st(std::equal_to<wchar_t>(), L'\n'));
std::for_each(innards.begin(), innards.end(), std::bind(boost::algorithm::to_lower<std::wstring>, _1, std::locale()));
std::for_each(innards.begin(), innards.end(), [&replacements] (std::wstring &a) {
std::for_each(replacements.begin(), replacements.end(), [&a] (std::pair<std::wstring, std::wstring> const&b) {
if (boost::algorithm::starts_with(a, b.first))
{
a.replace(a.begin(), a.begin() + b.first.size(), b.second);
}
});
});
std::sort(innards.begin(), innards.end());
}
bool Whitelist::IsOnWhitelist( std::wstring checked ) const
{
boost::algorithm::to_lower(checked);
return std::binary_search(innards.begin(), innards.end(), checked);
}
void Whitelist::PrintAll( std::wostream & str ) const
{
std::copy(innards.begin(), innards.end(), std::ostream_iterator<std::wstring, wchar_t>(str, L"\n"));
}
}
Unfortunately, Dev11's test projects generate DLLs, not EXEs, as under Google Test's system. So, while before I was able to embed the resource into the EXE and pass NULL to FindResource's first parameter, now Visual Studio is loading the DLL into its process during testing. Visual Studio certainly doesn't have my custom WHITELIST resource type inside, so this code blows up.
Is there some way I can either
Tell this code somehow to look in the right binary, either the testing DLL or the EXE, depending on context? (Currently this code is in a static library which gets linked with both the real EXE and the testing DLL) or
Embed relatively large (a few MB) text files into a C++ program cleanly without using the Win32 resource infrastructure at all?
It should be noted that putting the resource into a satellite DLL (which might have been a reasonable solution) is not an option; a requirement of this project is single EXE xcopy deployment.
Figured it out. I used the GetCurrentModule hack here -> How do I get the HMODULE for the currently executing code? . This gave the the HMODULE I needed to pass into the XxxResource APIs.

What Testframeworks are available for NI Lab Windows CVI?

I am forced to use NI Lab Windows CVI, and i would love work with TDD. However i could not find any testframeworks for that IDE. Are there any known solutions?
I just talked to someone at NI.
There are Unit Test Frameworks for NI Lab View wich is something completely different.
There is currently no solution from NI. In the past some people solved their issues using TestComplete - another route might be using CUnit.
EDIT:
Using CUNIT with CVI is really easy - though you still face some language Barriers:
#include "CUError.h"
#include "CUError.c"
#include "CUnit.h"
#include "MyMem.h"
#include "MyMem.c"
#include "TestDB.h"
#include "TestDB.c"
#include "TestRun.h"
#include "TestRun.c"
#include "Util.h"
#include "Util.c"
#include "Automated.h"
#include "Automated.c"
Using theese include statements should allow you to run this code:
static void testFail(void)
{
CU_ASSERT(0);
}
//Suite Definitions
static CU_TestInfo tests_GenList[] = {
{ "Should Fail", testFail },
CU_TEST_INFO_NULL,
};
static CU_SuiteInfo suites[] = {
{ "Generic List Suites", NULL, NULL, tests_GenList },
CU_SUITE_INFO_NULL,
};
void AddTests(void)
{
assert(NULL != CU_get_registry());
assert(!CU_is_test_running());
/* Register suites. */
if (CU_register_suites(suites) != CUE_SUCCESS) {
fprintf(stderr, "suite registration failed - %s\n",
CU_get_error_msg());
exit(EXIT_FAILURE);
}
}
int main (void)
{
CU_initialize_registry();
AddTests();
CU_set_output_filename("Result\\TestAutomated");
CU_list_tests_to_file();
CU_automated_run_tests();
CU_cleanup_registry();
return 0;
}
Also copy these files into your Result directory:
CUnit-List.dtd
CUnit-List.xsl
CUnit-Run.dtd
CUnit-Run.xsl
md2xml.pl
Memory-Dump.dtd
Memory-Dump.xsl
We also use CUnit with CMock (ThrowTheStick) over here. With cvi you can even automate the ruby scripts to execute the test_runner builder with somethine like
"%RUBY_HOME%\bin\ruby.exe" "%UNITY_HOME%\auto\generate_test_runner.rb"
Test_TestCycleFSM.c
in your pre-build steps. You maybe have to compile your project twice to generate the sourcefile and compile it then.
After all, CUnit seems to be the tests suite in C.