Sunday, June 6, 2021

Learn Modern C++ by Building An Audio Plugin (in Linux!) - Prologue

I got a friendly freeCodeCamp newsletter in my email, and I was intrigued. "Learn Moden C++ by Building an Audio Plugin"? Sounds neat!


Currently my goal is to move into the amazing world of microcontrollers and/or Rust. Since I want to develop on linux and emacs, I thought working on this would be a good way to start off by setting up linux on my new Lenovo D330 10" laptop and emacs.

I will be starting a series of blog posts for anyone who's stuck in any one of the numerous issues I've had to deal with.

Part 1: Linux.

Part 2: emacs and it's configuration.

Part 3: JUCE, Projucer and SimpleEQ. 

Monday, February 16, 2015

WDKRemoteUser woes update

In my previous post on the topic, I mentioned that you can get the provisioning done using the IP address of the target machine. I believe this was wrong. The documentation clearly mentions that the target and the host must be able to ping each other by NAME.

I managed to get two VMs and one physical machine provisioned by connecting them to the network and then provisioning using the "computer name" in one case, and "computer name.domain" in the other.

One another note, if you are using a VMWare virtual machine as the target, consider provisioning using the serial port rather than the network. I have not managed to get debugging over the network working correctly using the network.

GetVersionInfoEx Windows 10

If you are writing drivers and have check the Windows version to enable the driver, then you typically use RtlGetVersion to check the OSVERSIONINFO for the dwMajorVersion and dwMinorVersion at a minimum.

The issue I had the other day was to test my driver on Windows 10. The version numbers for Windows 8.1 and below are all readily available online, but it's probably too early for Windows 10, so I had to find it on my own.

You can skip all the attempts and failures to get the version at the end.

-- ATTEMPT #1
Write an Win32 application to get the Windows version.

-- FAILURE #1
GetVersionEx on Windows 8.1 = C4496 "GetVersionEx" is deprecated error.

-- FAILURE #2
Read the source for VersionHelpers.h to find the _WIN32_WINNT_ preprocessor definition in the Windows Kit 8.1 include headers. Only defined till Windows 8.1. In hindsight that's kind of expected. I should install the Windows Kit for Windows 10 for that.

-- ATTEMPT #2
Write a Win32 app to get the Windows version using VerifyVersionInfo.

-- FAILURE #3
The app returns 6.2 (corresponding to Windows 8.0) as mentioned in the document. After trying to add the preprocessor definition as mentioned in the documentation, and also trying the manifest route (didn't work), I googled online for the proper way to embed the valid manifest.

-- SUCCESS
A post on correctly using VerifyVersionInfo mentioned that you can get the version using WMI.
Open powershell.

PS > $os = Get-WMIObject  win32_operatingsystem
PS> $os
SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 9926
RegisteredUser  : <ME>
SerialNumber    : <SR_NO>
Version         : 10.0.9926

YAY!



TL;DR; RtlGetVersion for Windows 10 = 10.0.

Tuesday, November 25, 2014

WDKRemoteUser woes

Scenario: You are writing a kernel driver project in Visual Studio 2013 and you want to provision a computer as a target. Unfortunately, you end up with a WDKRemoteUser user name or password not found. And the provisioning fails with a message similar to this:

Provisioning log
Installing necessary components...
Connecting to computer "192.168.68.28"
Connecting to driver test automation service
Getting computer system information
Copying driver test automation files
Copying driver test automation files succeeded
Configuring WDK Remote User Account
WDK Remote User Account successfully created
Installing .NET Framework (possible reboot)
Installing .NET Framework (possible reboot) failed. Please ensure connectivity with the remote machine, and then restart the provisioning process.
Installing VC Redist (x64)
Installing VC Redist (x64) failed. Please ensure connectivity with the remote machine, and then restart the provisioning process.
Installing test automation (x86)
Installing test automation (x86) failed. Please ensure connectivity with the remote machine, and then restart the provisioning process.
Installing test automation (x64)
Installing test automation (x64) failed. Please ensure connectivity with the remote machine, and then restart the provisioning process.
Installing debuggers (x86)
Installing debuggers (x86) failed. Please ensure connectivity with the remote machine, and then restart the provisioning process.
Installing debuggers (x64)
Installing debuggers (x64) failed. Please ensure connectivity with the remote machine, and then restart the provisioning process.
Installing driver test framework
Installing driver test framework failed. Please ensure connectivity with the remote machine, and then restart the provisioning process.
Registering logging components
Registering logging components failed. Please ensure connectivity with the remote machine, and then restart the provisioning process.
Configure debugger settings (x64) (possible reboot)
Configure debugger settings (x64) (possible reboot) failed. Please ensure connectivity with the remote machine, and then restart the provisioning process.
Configure computer settings (x64) (possible reboot)
Configure computer settings (x64) (possible reboot) failed. Please ensure connectivity with the remote machine, and then restart the provisioning process.
Creating system restore point
Creating system restore point failed. Please ensure connectivity with the remote machine, and then restart the provisioning process.


What do?

Solution: Log in to the target computer with an admin account. Go to 'Edit Local Users and Groups'.  You should see the 'WDKRemoteUser' in the list. Right-click the user and 'Set Password...'. Put in a password that will work  with your group password policy. Sign out and login as ".\WDKRemoteUser" with your current password. Retry the provisioning with the corresponding debug settings. It will run through the provisioning correctly this time.

Please leave a comment if you have any issues. I'll take a look and try to replicate it.

Thursday, January 26, 2012

Debug custom action dll

You have a pretty CustomAction element in your Wix configuration. For some reason you keep getting a 1723 error when the installer attempts to run your custom action. At this point, you think, "Well.. this sucks... I wish I could debug (step through) the DLL and see where the problem is."

So here's how this post will help you

PRE-CONDITIONS:
1. You are writing a C/ C++ Custom Action DLL which is stored as a Binary in the installer.
2. You have already created a .DEF file which exports the DLL entry point. (Sanity test)
3. Comment out any code that uses the MSIHANDLE parameter.

The best way is to create a stand alone .exe file which attempts to run the custom action dll. Here's what that code looks like:

#include "stdafx.h"
#include <msi.h>

typedef UINT (__stdcall * PCUSTOMACTION)(MSIHANDLE handle);


int _tmain(int argc, _TCHAR* argv[])
{
MSIHANDLE handle = 0;
HMODULE library = LoadLibrary(L"CustomAction.dll");
if (library != NULL)
{
PCUSTOMACTION pCustomAction = (PCUSTOMACTION)GetProcAddress(library, "DllEntryPoint");
if (pCustomAction != NULL)
{
pCustomAction(handle);
}
}
return 0;
}


Run it with your favorite debugger attached, and voila!

Wednesday, November 2, 2011

tracelog troubles fixed!

I had to do some tracelog stuff at work and it took me a couple of days to figure out how to enable tracelog for drivers at boot. Hopefully this will save time for the next guy who has to deal with this.



Scenario:
You're a conscientious developer who's sprinkled WPP_TRACING macros all over (and I mean, ALL OVER) your Windows driver. The driver goes out into the real world, and everything is fine, until one day you have a customer complaining that the driver is not working as it should. It's giving some cryptic (or worse, generic) error. You think, "Hmm... thank FSM that I used WPP. I'll just grab the tracing files from the offending computer & figure out what went wrong". That's where this blog post comes in handy.



Steps:

1. Substitute as needed. I've used these defaults to make the steps clear:

<arch> = x64.

<LoggerName> = MyDriverLogger. This is an arbitrary name that is used to register the autologger.

<ETL File> = MyDriverLog.etl. Arbitrary named WPP trace log file.

<Driver WPP GUID> = 144EBFEE-C4AD-4A88-B8AC-3B7F7E5AD442. This is from your WPP_DEFINE_CONTROL_GUID typically found in your trace.h file.

<Logger GUID> = C7A7ACEB-9A80-4898-98F0-E3F9A5F043FE. The logger name needs a unique GUID.



2. Send the customer three files. tracelog.exe (found in WinDDK\7600.16385.1\tools\tracing\<arch>\), a batch file called 'AddAutoLogger.bat'. This file contains a single line:



tracelog -addautologger MyDriverLogger -guid #144EBFEE-C4AD-4A88-B8AC-3B7F7E5AD442 -matchanykw 0x7FFFFFFF -sourceguid #144EBFEE-C4AD-4A88-B8AC-3B7F7E5AD442 -sessionguid #C7A7ACEB-9A80-4898-98F0-E3F9A5F043FE -flag 0xFFFF -level 0xFF -f %WINDIR%\Temp\MyDriverLog.etl -cir 2



Also send the customer a batch file called 'RemoveAutoLogger.bat' which contains:


tracelog -flush MyDriverLogger


tracelog -remove MyDriverLogger




2. Ask the customer to do the following:

a. Open an admin command prompt and cd to the location where the files are, and run 'AddAutoLogger.bat'.


Developer verification for step 2: If you are trying it on your system, you can open 'Regedit', Navigate to HKLM\System\CurrentControlSet\Control\WMI\AutoLogger. You should see a key called 'MyDriverLogger'. Verify:



  1. Start = 1
  2. GUID =C7A7ACEB-9A80-4898-98F0-E3F9A5F043FE
  3. File=%WINDIR%\Temp\MyDriverLog.etl

There will be a single sub-key in MyDriverLogger. This is the GUID '144EBFEE-C4AD-4A88-B8AC-3B7F7E5AD442'. Verify:

  1. Enabled=1
  2. EnableLevel=0xFF
  3. MatchAnyKeyword=0x7FFFFFFF


3. Ask the customer to perform the activity that causes the problem, or wait for a specified period of time.



4. Ask the customer to open command prompt again and cd to the directory and run RemoveAutoLogger.bat.



Developer verification for step 4: There should be a new file called %WINDIR%\Temp\MyDriverLog.etl.




5. Ask the customer to mail the file to you.



Developer verification for step 5: Check your email and see if the file is attached. :)




Now, the customer's job is done, and it's up to you to show off your debugging chops!




  1. Grab the source corresponding to the driver.
  2. Make a build (the configuration and architecture don't seem to matter).
  3. Run 7600.16385.1\tools\tracing\\tracepdb.exe -f . You get a brand new .TMF file.
  4. Run 7600.16385.1\tools\tracing\\traceview.exe. Select 'Open an Existing File'. Select the .etl file. In the next step, select the .TMF file and add the newly created .TMF file.
  5. Enjoy the WPP trace!!





References:
  1. ETW EtwRegister() provider list. [http://www.adras.com/ETW-EtwRegister-provider-list.t4828-192-1.html]. Thank you, thank you, Ivan
  2. Tracelog command syntax. [http://msdn.microsoft.com/en-us/windows/hardware/ff553012]
  3. Using TraceView. [http://msdn.microsoft.com/en-us/windows/hardware/ff556063%28v=VS.85%29]
  4. TracePDB [http://msdn.microsoft.com/en-us/windows/hardware/ff553043%28v=VS.85%29]

Tuesday, October 4, 2011

Longest subsequence (contd.)

Unfortunately, the solution I've suggested fails if the sequence is say, 10, 13, 12, 1, 2, 3, 4, 5. The solution is 5, but the above algorithm returns 2.

This means that a single stack is unlikely to solve the problem. We need a list of stacks, each starting off with the first number that cannot be added to an existing stack. Here's what the code looks like:

List<Stack<int>> stacks = new List<Stack<int>>();

foreach (int incoming in sequence)
{
bool makeNew = true;
foreach (Stack stack in stacks)
{
if (stack.top() < incoming)
{
stack.push(incoming);
makeNew = false;
}
}

if (makeNew == true)
{
Stack<int> s = new Stack<int>();
s.Push(incoming);
stacks.Add(s);
}
}



And at the end, the solution is the stack with the maximum Count.

This *almost* works. It fails to get the correct solution in a very interesting way. Consider the sequence, 10, 20, 30, 12, 13, 14, 15. The solution is 5 (10, 12, 13, 14, 15), but the algorithm will return the solution as 4 (12, 13, 14, 15).

Here's how it works. The algorithm creates a new stack for element 10. It then pushes 20, and 30 onto this stack. The next element 12 cannot be put into an existing stack, so it creates a new one and pushes to the new stack. The next elements 12, 13, 14, 15 are added to the top of the new stack giving a solution of 4.

So, we always need a stack handy with just the element 10 in it for the algorithm to succeed.


List<Stack<int>> stacks = new List<Stack<int>>();

foreach (int incoming in sequence)
{
bool makeNew = true;
foreach (Stack stack in stacks)
{
if (stack.top() < incoming)
{
Stack<int> dupStack = new Stack<int>();
//Copy the elements of stack to dupStack.
stacks.Add(dupStack);
stack.Push(incoming);
makeNew = false;
}
}

if (makeNew == true)
{
Stack<int> s = new Stack<int>();
s.Push(incoming);
stacks.Add(s);
}
}


This ensures that even when we add an element to an existing stack, the unchanged stack is also present in the list of stacks. This algorithm gives the correct solution under all circumstances.

Correctness accomplished!

Now, for optimization...

Here's an interesting observation: We are running the algorithm above, and we have a list of stacks. Consider 2 stacks in the list. They both have the same elements (Count is the same). However, the top element in stack B is larger than the top element in stack A. Given a new incoming element which is greater than both the top elements of A and B, either stack can work. In other words, since we need only one stack with that height, the stack with the smaller top value should be preferred. We can safely delete the stack with the larger top value. The end result will be unaffected.

So, after adding the element to the stacks, we can run though all stacks and compare them to each other. If the Count of 2 stacks is the same, delete the one with the larger top value.

List<Stack<int>> toDelete = new List<Stack<int>>();

foreach (Stack<int> a in stacks)
{
foreach (Stack<int> b in stacks)
{
if (a == b) //No need to compare the same objects.
continue;

if (a.Count == b.Count && a.Top() < b.Top())
toDelete.Add(b);
}
}

foreach (Stack s in toDelete)
{
stacks.Remove(s);
}



And finally, one more thing. The solution does not ask for the list in the largest sub-sequence. It asks simply for the count. Given that, we don't even need a family of stacks. We can simply do with a KeyValuePair where the first int remembers the height of the "stack" and the second contains the Top value of the stack.

The solution becomes space efficient. Instead of keeping a bunch of stacks in memory, it remembers a pair of numbers per stack. So, if the largest sub - is 100, the maximum number of elements that are stored in memory is 100 * 2 integers.

Since a pair of integers given that the key will always be unique can be stored in a single Dictionary, it prob. makes sense to make a single dictionary and work the algorithm around that.

I will write up that code on request.