Storing Information from a Callback Using a Struct in Windows C++

This article discusses how to use a call back function with EnumWindows to find the window handle for a window which contains the text “Acrobat”. You may try changing the string to search for something else.

This program also introduces basic pointers.


// This file is StoreWindowName.cpp
//   Compile with:
//   cd "C:\Users\Scott\CPP\"
//   "C:\MinGW64\bin\g++.exe" -o StoreWindowName.exe StoreWindowName.cpp

#include <tchar.h> // _tprintf, _tcsstr
#include <windows.h> // HWND, BOOL, TEXT, LPARAM, EnumWindows, GetProcessHeap, GetWindowText, HeapAlloc, HeapFree,
#include <iostream> // printf, cout, endl

using namespace std;

typedef struct MyData {
    TCHAR searchText[ 2048 ];
    HWND handle;
} MYDATA, *PMYDATA;

BOOL CALLBACK StoreWindowName(HWND hwnd, LPARAM lpParam)
{
    TCHAR windowTitle[1024];
    if (GetWindowText(hwnd, windowTitle, 1024))
    {
        _tprintf(TEXT("Checking Window:%s\n"), windowTitle);
  PMYDATA pData = (PMYDATA) lpParam;
        if (_tcsstr(windowTitle, LPCTSTR(pData->searchText)) != NULL)
        {
   _tprintf(TEXT("Found Window Matching Text:%s\n"), windowTitle);
   pData->handle = hwnd;
            return false;
        }
    }
    return true;
}

int main()
{
 cout << "MAIN START" << endl;

 PMYDATA pData = (PMYDATA) HeapAlloc(GetProcessHeap(), 0, sizeof(MYDATA));
 strcpy(pData->searchText, TEXT ("Acrobat"));
 EnumWindows(StoreWindowName, (LPARAM) pData);

 TCHAR title[250];
 GetWindowText(pData->handle,title,250);
 cout << "WindowTitle:" << title << endl;

 HeapFree(GetProcessHeap(), 0, pData);

 cout << "MAIN FINISH" << endl;
}

Here are some tips on working with pointers to introduce how to use “*”, “&”, “[0]”, “.” and “-&gt”:


 MyData mData = *pData; // MyData comes from dereferencing PMYDATA
 cout << "MyData mData.searchText:" << mData.searchText << endl; // Acrobat

 MYDATA myData = *pData; // MYDATA is same as MyData
 cout << "MYDATA myData.searchText:" << myData.searchText << endl; // Acrobat
 
 MyData* pMyData = pData; // PMYDATA is same as MyData*
 cout << "MyData* pMyData->searchText:" << pMyData->searchText << endl; // Acrobat
 
 MyData m_data = pData[0]; // Another way to dereference
 cout << "MyData m_data.searchText:" << m_data.searchText << endl; // Acrobat
 
 MyData* p_data = &*pData; // Taking the address is the inverse of dereferencing
 cout << "MyData* p_data->searchText:" << p_data->searchText << endl; // Acrobat
 
 MyData* p_mydata = &pData[0]; // Taking the address is the inverse of dereferencing
 cout << "MyData* p_mydata->searchText:" << p_mydata->searchText << endl; // Acrobat
 
 char* pSearchText = &mData.searchText[0]; // Taking the address of the first character
 cout << "char* pSearchText:" << pSearchText << endl; // Acrobat
 
 char pSearchText0 = *mData.searchText; // First character
 cout << "char pSearchText0:" << pSearchText0 << endl; // A
 
 char pSearchTextZ = mData.searchText[0]; // First character
 cout << "char pSearchTextZ:" << pSearchTextZ << endl; // A

Fixing the “Exception in thread main java.lang.NoClassDefFoundError:” in Eclipse

When working in Eclipse, you may be working with the main method of a Java class. Every once in a while you will see the error:


Exception in thread "main" java.lang.NoClassDefFoundError: ...
Caused by: java.lang.ClassNotFoundException:

Usually you can fix this by running one of the following outside of Eclipse, from the project directory in a Windows Command Prompt. Or in Eclipse, you can try Project->Clean, Project->Build.


mvn clean
mvn compile
mvn test
mvn eclipse:clean
mvn eclipse:eclipse

The compile and test commands recreate the class files for “src/main/java” and “src/test/java”. The eclipse:clean and eclipse:eclipse commands recreate the .classpath and .project files that Eclipse uses.

This is a ClassPath issue. If you look in your project directory, you will see a “.classpath” file. If you open this, you will see all the places Java will look to try to find the .class file to run.

For this example, you might see:


  <classpathentry kind="src" path="src/test/java" output="target/test-classes" including="**/*.java"/>
  <classpathentry kind="src" path="src/test/resources" output="target/test-classes" excluding="**/*.java"/>
  <classpathentry kind="src" path="src/main/java" including="**/*.java"/>
  <classpathentry kind="src" path="src/main/resources" excluding="**/*.java"/>
  <classpathentry kind="output" path="target/classes"/>

Based on the first line above, what this means is that if you right click->Run As->Java Application from a Java file in the src/test/java directory, Java will look for the class in the specified output directory (ie target/test-classes). Java will take the package (ie com.mycompany.myapp) and class name (ie HelloWorld) and search for the class file accordingly (ie target/test-classes/com/mycompany/myapp/HelloWorld.class).

Based on the third and fifth lines above, if you right click->Run As->Java Application from a Java file in the src/main/java directory, Java will look for the class in the default output directory (ie target/classes). Java will take the package (ie com.mycompany.myapp) and class name (ie HelloWorld) and search for the class file accordingly (ie target/classes/com/mycompany/myapp/HelloWorld.class).

Running Java From the Command Prompt to Avoid Classpath Issues

This is the long form of the Java commands that can be run from a Windows command prompt:


"C:\Program Files\Java\jdk1.6.0_18\bin\javac.exe" -classpath "C:\Users\Scott\workspace\myproject" com\mycompany\myapp\HelloWorld.java
"C:\Program Files\Java\jdk1.6.0_18\bin\java.exe" -classpath "C:\Users\Scott\workspace\myproject" com.mycompany.myapp.HelloWorld
  1. These commands can be run from any directory, meaning you don’t have to be in the directory where your HelloWorld.java file is.
  2. The first line compiles your HelloWorld.java file, creating a HelloWorld.class file.
  3. The second line runs the HelloWorld.class file.
  4. The -classpath tells java where to look for the specified file in each command.
  5. The Java compiler (javac.exe) expects the location of the java file, relative to the classpath (ie the file is located at C:\Users\Scott\workspace\myproject\com\mycompany\myapp\HelloWorld.java).
  6. Java (java.exe) expects the package (ie com.mycompany.myapp) and class (HelloWorld), relative to the classpath (ie the file is located at C:\Users\Scott\workspace\myproject\com\mycompany\myapp\HelloWorld.class).

Notice the classpath has no slash at the end. The javac.exe commands expects the file to end with “.java”. The java.exe command expects the full class name and does not end with “.class”.

There are a few ways to simplify these commands:

  1. You don’t have to specify the entire path to java.exe. Add Java to the Windows Path (Run->sysdm.cpl->Advanced Tab->Environment Variables->Select Path->Edit->Append “;C:\Program Files\Java\jdk1.6.0_18\bin\”). Or you can append JAVA_HOME and create that Environment Variable.
  2. You don’t have to enter the entire classpath (ie, you can just use -classpath “.”). Enter the directory you will be working in.
  3. You can use the default package (put the HelloWorld.java file directory in your working directory and don’t use the Java package directive).

If you make these changes you would run something like this (and you might be able to leave out -classpath “.”) to run the file located at “C:\Users\Scott\workspace\myproject\HelloWorld.java“:


cd "C:\Users\Scott\workspace\myproject\"
javac -classpath "." HelloWorld.java
java -classpath "." HelloWorld

Working with Postscript in Java

Before working with postscript files, you will need to download the Ghost Script Installer on your Windows machine and install it. This will give you access to gsdll32, which you will need to install to avoid an Java error like “Unable to load library ‘gsdll32’“.

At www.ghost4j.org, you can download the ghost4j-0.5.0.zip jar file.

Check out Adding an External Jar. Including an external dll file is similar, but you can copy the gsdll32.dll file to a Java Path folder such as “C:\Windows\System32” folder.

Example for DLL in Local Maven Repository


    <!-- "C:\apache-maven-3.1.0\bin\mvn" install:install-file -Dfile="C:\Program Files\gs\gs9.05\bin\gsdll32.dll" -Dpackaging=dll -DgroupId=org.ghost4j -DartifactId=gsdll32 -Dversion=9.05 -->
    <dependency>
      <groupId>org.ghost4j</groupId>
      <artifactId>gsdll32</artifactId>
      <version>9.0.5</version>
      <type>dll</type>
    </dependency>

Entry for pom.xml


    <!-- "C:\apache-maven-3.1.0\bin\mvn" install:install-file -Dfile="C:\Users\Scott\Desktop\ghost4j-0.5.0\ghost4j-0.5.0.jar" -Dpackaging=jar -DgroupId=org.ghost4j -DartifactId=ghost4j -Dversion=1.4 -->
    <dependency>
      <groupId>org.ghost4j</groupId>
      <artifactId>ghost4j</artifactId>
      <version>0.5.0</version>
    </dependency>
    <!-- "C:\apache-maven-3.1.0\bin\mvn" install:install-file -Dfile="C:\Users\Scott\Desktop\ghost4j-0.5.0\lib\xmlgraphics-commons-1.4.jar" -Dpackaging=jar -DgroupId=org.ghost4j -DartifactId=xmlgraphics-commons -Dversion=1.4 -->
    <dependency>
      <groupId>org.ghost4j</groupId>
      <artifactId>xmlgraphics-commons</artifactId>
      <version>1.4</version>
    </dependency>
    <!-- "C:\apache-maven-3.1.0\bin\mvn" install:install-file -Dfile="C:\Users\Scott\Desktop\ghost4j-0.5.0\lib\commons-io-1.3.1.jar" -Dpackaging=jar -DgroupId=org.ghost4j -DartifactId=commons-io -Dversion=1.3.1 -->
    <dependency>
      <groupId>org.ghost4j</groupId>
      <artifactId>commons-io</artifactId>
      <version>1.3.1</version>
    </dependency>
    <!-- "C:\apache-maven-3.1.0\bin\mvn" install:install-file -Dfile="C:\Users\Scott\Desktop\ghost4j-0.5.0\lib\log4j-1.2.15.jar" -Dpackaging=jar -DgroupId=org.ghost4j -DartifactId=log4j -Dversion=1.2.15 -->
    <dependency>
      <groupId>org.ghost4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.15</version>
    </dependency>
    <!-- "C:\apache-maven-3.1.0\bin\mvn" install:install-file -Dfile="C:\Users\Scott\Desktop\ghost4j-0.5.0\lib\commons-beanutils-1.8.3.jar" -Dpackaging=jar -DgroupId=org.ghost4j -DartifactId=commons-beanutils -Dversion=1.8.3 -->
    <dependency>
      <groupId>org.ghost4j</groupId>
      <artifactId>commons-beanutils</artifactId>
      <version>1.8.3</version>
    </dependency>
    <!-- "C:\apache-maven-3.1.0\bin\mvn" install:install-file -Dfile="C:\Users\Scott\Desktop\ghost4j-0.5.0\lib\commons-logging-1.1.1.jar" -Dpackaging=jar -DgroupId=org.ghost4j -DartifactId=commons-logging -Dversion=1.1.1 -->
    <dependency>
      <groupId>org.ghost4j</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1.1</version>
    </dependency>
    <!-- "C:\apache-maven-3.1.0\bin\mvn" install:install-file -Dfile="C:\Users\Scott\Desktop\ghost4j-0.5.0\lib\itext-2.1.7.jar" -Dpackaging=jar -DgroupId=org.ghost4j -DartifactId=itext -Dversion=2.1.7 -->
    <dependency>
      <groupId>org.ghost4j</groupId>
      <artifactId>itext</artifactId>
      <version>2.1.7</version>
    </dependency>
    <!-- "C:\apache-maven-3.1.0\bin\mvn" install:install-file -Dfile="C:\Users\Scott\Desktop\ghost4j-0.5.0\lib\jna-3.3.0.jar" -Dpackaging=jar -DgroupId=org.ghost4j -DartifactId=jna -Dversion=3.3.0 -->
    <dependency>
      <groupId>org.ghost4j</groupId>
      <artifactId>jna</artifactId>
      <version>3.3.0</version>
    </dependency>...

Read and Write Postscript Files


package com.mycompany.app.util;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;

import org.ghost4j.document.PSDocument;
import org.ghost4j.renderer.SimpleRenderer;

public class PostScriptFileHelper {
	static int SIDE_LENGTH = 200;
	public static void main(String[] args) {
		try {
			String inputFile = "/Users/Scott/workspace/myrepo/my-app/input/bell_206.ps";
			PostScriptFileHelper psfh = new PostScriptFileHelper();
			PSDocument document  = psfh.read(new File(inputFile));

			// Render an image from the PSDocument
			SimpleRenderer renderer = new SimpleRenderer();
			renderer.setResolution(300);
			List images = renderer.render(document);
			Image image = images.get(0);
			System.out.println("IMGLIST:"+images.size());
			
			// Resize the image to display
			BufferedImage resizedImage = new BufferedImage(SIDE_LENGTH, SIDE_LENGTH, BufferedImage.TYPE_INT_ARGB);
			Graphics2D g = resizedImage.createGraphics();
			g.drawImage(image, 0, 0, SIDE_LENGTH, SIDE_LENGTH, null);
			g.dispose();

			// Add the image to a JButton to display
			JButton button = new JButton();
			button.setSize(SIDE_LENGTH, SIDE_LENGTH);
			button.setIcon(new ImageIcon(resizedImage));
		
			// Create a JFrame to add the button with the image to display
			JFrame frame = new JFrame();
			frame.add(button);
			frame.setLocation(0,0);
			frame.setSize(SIDE_LENGTH, SIDE_LENGTH);
			frame.setVisible(true);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
	
	public PSDocument read(File inputFile) throws Exception{
		PSDocument document = new PSDocument();
		document.load(inputFile);
		return document;
	}
	
	public void write(File outputFile, PSDocument document) throws Exception{
		document.write(outputFile);
	}
}

PS to PDF Converter


PSDocument document = new PSDocument();
document.load(new File("input.ps"));
fos = new FileOutputStream(new File("rendition.pdf"));
PDFConverter converter = new PDFConverter();
converter.setPDFSettings(PDFConverter.OPTION_PDFSETTINGS_PREPRESS);
converter.convert(document, fos);

It would be great to convert List to PSDocument (see stackoverflow.com).

Getting Starting with HTML

This article gets you started with working with HTML code.

  1. Right click on your Desktop->New->Text Document.
  2. Right click the new file->Rename->HelloWorld.html.
  3. Right click the new file->Open With->Notepad.
  4. Add the following:
    
    <html>
    <body>
    Hello World!
    </body>
    </html>
    
  5. Save the file.
  6. Right click the new file->Open.

You can add more code like hyperlinks:


<a href="http://www.google.com">www.google.com</a>
<img src="myImage.png">

For the img tag above, you will need an img named “myImage.png” in the same directory as your file, in this case on your Desktop.

Decoupling Tiers through the Observer Design Pattern in Java

In Java, one type of architecture is an N-Tier design:

  1. DA: Access files, database, handle I/O, read/write.
  2. B: Business objects, business logic.
  3. GUI: Presentation, User Interaction.

The business objects should be global data objects and can be accessible by all layers in the system. The purpose of the DA layer is just to read data to memory and write from memory. The DA Layer handles loading data into business objects that can be used by the rest of the system. The purpose of the GUI is to interact with the user to change business objects as well as present those business objects.

Rather than keeping track of all the GUIs presenting the information in a given data object, each GUI can just register to find out if any of the underlying data changed using the Observer Design Pattern.

For instance, imagine you have a audio data sample object, responsible for storing samples, frequency and the appropriate information to play the audio. This would represent a business object.

In addition, suppose that you make sure that only one controller is responsible for changing the samples or manipulating the samples. This would represent the owner of the business object.

Now, you might have two or twenty GUIs displaying different features of the data: one for frequency analysis, another for raw data samples, another for sampling rate, etc. As long as each of these GUIs registers with the controller to be notified when the audio data sample object changes, none of the GUIs have to know about the Controller and the Controller does not have to know about any of the GUIs. Both the GUI and the Controller would know about the business object.

GUI Implements Observer


public class MathPlotJPanel extends Plot2DPanel implements Observer{

Adding code to change the GUI, when the business object changes, ie new samples are added


	@Override
	public void update(Observable arg0, Object arg1) {
		wavInputData = (WavData) arg1;
		createPlot();
	}

Business Owner Extends Observable


public class SoundRecorder extends Observable{

Adding code to notify observers, when the business object changes, ie new samples are added


		this.setChanged();
		notifyObservers(wavRecordData);

Main Method Can Setup Registration


    	MathPlotJPanel plotPanel = new MathPlotJPanel(SoundRecorder.getInstance().getRecordedData());
    	SoundRecorder.getInstance().addObserver(plotPanel);

Preferrably, something in the Gui Layer would know about both the controller and the GUI. You would not want to have the controller code change based on adding new GUIs, but you might want to have the GUIs fire off a change, like tell the controller to start recording.

Using Delegation to Pass a String from Java to C to C++

This article shows how to modify the JNI code from An Introduction to JNI using a Bottom Up Approach to pass in an argument from Java, which will pass in an argument to C which will pass the argument to C++.

Java Changes

JavaToCHelper.java Modifications

The Java Exit Class should have the available commmands. Add the String argument and the available commands.


    public native void cToCPPHelperEntryPoint(String command);
    public static final String OPEN_NOTEPAD = "OPEN_NOTEPAD";
    public static final String CLOSE_NOTEPAD = "CLOSE_NOTEPAD";

You can add helper functions so only JavaToCHelper has to deal with the string version of the commands:


    private void jToCHelperExitPoint(String command) {
        System.out.println("JAVA_TO_C_HELPER EXIT START");
        cToCPPHelperEntryPoint(command); // Comment to ignore CToCPPHelper
        System.out.println("JAVA_TO_C_HELPER EXIT FINISH");
    }

    public static void main(String[] args) {
        System.out.println("JAVA_TO_C_HELPER MAIN START");
        JavaToCHelper jToCHelper = new JavaToCHelper();
        jToCHelper.jToCHelperExitPoint(JavaToCHelper.OPEN_NOTEPAD);
        jToCHelper.jToCHelperExitPoint(JavaToCHelper.CLOSE_NOTEPAD);
        System.out.println("JAVA_TO_C_HELPER MAIN FINISH");
    }
	
    public void openNotepad(){
        System.out.println("JAVA_TO_C_HELPER ENTRY START");
        jToCHelperExitPoint(JavaToCHelper.OPEN_NOTEPAD);
        System.out.println("JAVA_TO_C_HELPER ENTRY FINISH");
    }
	
    public void closeNotepad(){
        System.out.println("JAVA_TO_C_HELPER ENTRY START");
        jToCHelperExitPoint(JavaToCHelper.CLOSE_NOTEPAD);
        System.out.println("JAVA_TO_C_HELPER ENTRY FINISH");
    }

C Changes

CToCPPHelper.h Modifications

The generated CHelper Header File will create a method which takes a jstring:


JNIEXPORT void JNICALL Java_com_ebay_webdriver_tools_accessibility_JavaToCHelper_cToCPPHelperEntryPoint(JNIEnv *, jobject, jstring);

CToCPPHelper.cpp Modifications

Add the string argument, convert to C equivalent and pass via delegation.


JNIEXPORT void JNICALL Java_com_ebay_webdriver_tools_accessibility_JavaToCHelper_cToCPPHelperEntryPoint(JNIEnv *env, jobject thisObj, jstring javaCommand)
{
    printf("C_TO_CPP_HELPER ENTRY START\n");
    char* cCommand = (char *) env->GetStringUTFChars(javaCommand,0);
    cToCPPHelperExitPoint(cCommand);
    env->ReleaseStringUTFChars(javaCommand,0);
    printf("C_TO_CPP_HELPER ENTRY FINISH\n");
} 

Change the other function declarations, calls:


void cToCPPHelperExitPoint(const char* cCommand) {
cppHelperEntryPoint(cCommand); // Comment to ignore CPPHelper
cToCPPHelperExitPoint("");

C++ Changes

CPPHelper.h

Change the function declaration to accept a pointer:


        void cppHelperEntryPoint(const char* cppCommand);

CPPHelper.cpp

Add the string argument, convert to C++ equivalent and pass via delegation.


void cppHelperEntryPoint(const char* cCommand)
{
    cout << "CPP_HELPER ENTRY START" << endl;
    cppHelperExitPoint(cCommand);
    cout << "CPP_HELPER ENTRY FINISH" << endl;
}

Change the other function declarations, calls:


void cppHelperExitPoint(cppCommand)
printOpenWindowsEntryPoint(cppCommand); // Comment to ignore PrintOpenWindows
cppHelperExitPoint("");

PrintOpenWindows.h


        void printOpenWindowsEntryPoint (const char* cppCommand);

PrintOpenWindows.cpp

Add the avaiable commands:


const char* OPEN_NOTEPAD = "OPEN_NOTEPAD";
const char* CLOSE_NOTEPAD = "CLOSE_NOTEPAD";

Add code to process the commands:


void printOpenWindowsEntryPoint(const char* cppCommand)
{
    cout << "PRINT_OPEN_WINDOWS ENTRY START" << endl;
    if(_tcsstr(OPEN_NOTEPAD, LPCTSTR((LPARAM) cppCommand))) != NULL)
    {
        openNotepad();
    } else if(_tcsstr(CLOSE_NOTEPAD, LPCTSTR((LPARAM) cppCommand))) != NULL)
    {
        closeNotepad();
    } else { // default operation, for empty string
        openNotepad();
    }
    cout << "PRINT_OPEN_WINDOWS ENTRY FINISH" << endl;
}

Getting Started with the Java Math Plot Library

In this article, I will talk about how to plot raw audio sound samples using the Java Math Plot library.

I googled “Download Java Math Plot“, went to code.google.com, clicked on jmathplot.jar.

I renamed the file with “jmathplot.zip” as “jmathplot.jar”. Here are instructions on how to open and modify Jar files.

  • A Jar file is just a zip file.
  • When editing files, you must remember some files will need to be recompiled.
  • You must remember to zip the directory in same folder as META-INF.
  • You may edit the files within the Jar File by doing the following:
    1. Change some resource files, ie xml files.
    2. Right click on org, Send to Compressed (zipped) Folder.
    3. Copy and paste the META-INF folder into the Zip File.
    4. Rename org.zip as org-mod.jar.

I installed into my local Maven Repository using org:jmathplot:1.0 (groupID:artifactID:version).
See Getting Started with Java’s Hello World: Part I) for instructions on how to setup a new project with Java, Maven and Eclipse. See Adding an External Jar to a Maven Project (JFugue) for instructions on how to add a jar to your local Maven repository.


"C:\apache-maven-3.1.0\bin\mvn" install:install-file -Dfile="C:\Users\Scott\Desktop\jmathplot.jar" -Dpackaging=jar -DgroupId=org -DartifactId=jmathplot -Dversion=1.0

You will need WavFileHelper.java, which you can get from An Introduction to Java Sound.

Add the dependency to the pom.xml file.

Run “mvn clean compile eclipse:eclipse“.

Refresh the project via F5.

Add the following “MathPlotHelper.java” file to your project:


package com.mycompany.app;

import java.awt.BorderLayout;
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.swing.JFrame;
import org.math.plot.*;
import com.mycompany.app.util.WavFileHelper;
import com.mycompany.app.util.WavFileHelper.WavData;
import com.mycompany.app.util.WavFileHelper.WaveSection;

public class MathPlotHelper {
	// The project directory is my-app
	// ie, my-app/src/main/java
	// ie, my-app/input/input.wav
	static String inputPath = "/Users/Scott/workspace/myrepo/my-app/input/input.wav";
	static int FRAME_WIDTH = 600;
	static int FRAME_HEIGHT = 600;
	public static void main(String[] args) {
		try {			
			JFrame frame = new JFrame("My First Sound Plot");
			frame.setLayout(new BorderLayout());
			
			WavFileHelper wavFileHelper = new WavFileHelper();
			WavData wavInputData = wavFileHelper.read(new File(inputPath));
			WavFileSampleConverter converter = new WavFileSampleConverter(wavInputData.get(WaveSection.DATA));
			
			int numOfSamples = converter.getNumOfSamples();
			double[] x = new double[numOfSamples];
			for(int i=0 ; i < numOfSamples ; i++) {
				x[i] = i;
			}
			
			Plot2DPanel plot = new Plot2DPanel();   // add a line plot to the PlotPanel
			plot.addLinePlot("Raw Data Samples Channel 0", x, converter.getChannel(0));   // put the PlotPanel in a JFrame, as a JPanel
			plot.addLinePlot("Raw Data Samples Channel 1", x, converter.getChannel(1));   // put the PlotPanel in a JFrame, as a JPanel
			frame.add(plot);
			
			frame.setVisible(true);
			frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
	
	/**
	 * This class interprets the raw data sample bytes
	 * 
	 * Bytes 1 to 0  :  Bits 15 to 0         :  Decimal
	 * HEX    HEX    :                       :
	 * 00     01     :  0000 0000 0000 0001  :  1
	 * 00     02     :  0000 0000 0000 0010  :  2
	 * 00     04     :  0000 0000 0000 0100  :  4
	 * 00     10     :  0000 0000 0001 0000  :  16
	 * 56     22     :  0101 0110 0010 0010  :  22050=16384+4096+1024+512+32+2
	 * 0E     DA     :  0000 1110 1101 1010  :  3802=2048+1024+512+128+64+16+8+2
	 * F1     9E     :  1111 0001 1001 1110  :  61854=32768+16384+8192+4096+256+128+16+8+4+2
	 * 
	 * These raw data samples should be interpreted using two's compliment signed integers
	 * Since 61854>32767 substracting 65536 gives -3682 so (F1 9E) represents -3682
	 * 
	 * @author Scott
	 *
	 */	public static class WavFileSampleConverter {
		double[] samplesChannel0 = null;
		double[] samplesChannel1 = null;
		
		public WavFileSampleConverter(byte[] bytes){
			int samplesLength = bytes.length/4;
			int samplesPerChannel = samplesLength/2;
			samplesChannel0 = new double[samplesPerChannel];
			samplesChannel1 = new double[samplesPerChannel];
			for(int i=0; i<samplesPerChannel;i++) {
				int firstByteMarker = 4*i;
				byte[] channel0Bytes = new byte[2];
				channel0Bytes[0] = bytes[firstByteMarker];
				channel0Bytes[1] = bytes[firstByteMarker+1];
				samplesChannel0[i] = (double) getTwosComplimentSignedIntegerFromBytes(channel0Bytes);		
				byte[] channel1Bytes = new byte[2];
				channel1Bytes[0] = bytes[firstByteMarker+2];
				channel1Bytes[1] = bytes[firstByteMarker+3];
				samplesChannel1[i] = (double) getTwosComplimentSignedIntegerFromBytes(channel1Bytes);
			}
		}

		/**
		 * 
		 * Samples represent Twos Compilement Signed Integers
		 * 
		 * @param bytes
		 * @return
		 */
		private int getTwosComplimentSignedIntegerFromBytes(byte[] bytes) {
			return (int) ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN)
					.getShort();
		}
			
		public double[] getChannel(int i) {
			if(i == 0) {
				return samplesChannel0;
			} else if (i == 1) {
				return samplesChannel1;
			}
			return null;
		}
		
		public int getNumOfSamples() {
			return samplesChannel0.length;
		}
	}
}

Notes: Here are some good keyboard shortcuts I used in Eclipse:
Highlight a Class reference, Ctrl+Shift+M, adds and import
Highlight a variable name, Shift+Alt+R, refactors the name

An Introduction to UI Automation in Windows using C++

For help compiling, see Spawning a Thread to Run a New Process in Windows C++.

This article discusses an first introduction to UI Automation in C++ for a Windows machine. One of the first major hurdles in UI Automation is stopping the running thread from blocking, such as when Dialog boxes open up. For this reason, UI Automation is inherintly multi threaded.

In Java, the Event Dispatch Thread handles all GUI events. Therefore, to interact with the GUI, you must run on a Thread separate from the Event Dispatch Thread. In C++ a similar concept exists.

This code has examples of using callback functions and iterating through child elements to find the appropriate elements. Then, either SendMessage or InputEvent is used to simulate GUI interaction.

Some lines of code may be commented out to see what a program like UISpy would tell you.


// Compile with
// "C:\MinGW64\bin\g++.exe" -o UIAutomationStart.exe UIAutomationStart.cpp -lpsapi
// This file is UIAutomationStart.cpp

#include "UIAutomationStart.h"
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <windows.h>
#include <iostream>
#include <conio.h>
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <Psapi.h>
#include <strsafe.h>
#include <cstring>

using namespace std;

BOOL CALLBACK PrintWindowName(HWND hwnd, LPARAM callerText)
{
 char windowText[2048];
 GetWindowText(hwnd, windowText, 2048);
 cout << "CALLERTEXT:" << callerText << ":WINDOWTEXT:" << windowText << endl;
}

BOOL CALLBACK ClickButton(HWND hwnd, LPARAM buttonText)
{
 char windowText[2048];
 GetWindowText(hwnd, windowText, 2048);
 //cout << "WINDOWTEXT:" << windowText << endl;
 if(_tcsstr(windowText, LPCTSTR(buttonText)) != NULL)
 {
  SendMessage(hwnd, BM_CLICK, 0, 0);
  cout << "BUTTONSELECTED:" << windowText << endl;
 }
 return true;
}

void selectMenuItem(HWND hwnd, HMENU menu, LPARAM menuText)
{
 int menuCount = GetMenuItemCount(menu);
 for(int i=0; i<menuCount; i++)
 {
  char menuString[2048];
  GetMenuString(menu, i, menuString, 2048, MF_BYPOSITION);
  //cout << "MENUSTRING:" << menuString << endl;
  if(_tcsstr(menuString, LPCTSTR(menuText)) != NULL)
  {
   int menuItemID = GetMenuItemID(menu, i);
   PostMessage(hwnd, WM_COMMAND, menuItemID, 0);
   cout << "MENUSELECTED:" << menuItemID << ":OF:" << menuCount << endl;
   return;
  }
 }
}

void typeKey(short virtualKey)
{
    // Set up a generic keyboard event.
 INPUT ip;
 ip.type = INPUT_KEYBOARD;
 ip.ki.wScan = 0; // hardware scan code for key
 ip.ki.time = 0;
 ip.ki.dwExtraInfo = 0;// Press the "A" key
 ip.ki.wVk = virtualKey; // virtual-key code for the character
 ip.ki.dwFlags = 0; // 0 for key press
 SendInput(1, &ip, sizeof(INPUT));// Release the character key
 ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
 SendInput(1, &ip, sizeof(INPUT));
}

void typeKeys(LPARAM stringToType)
{
 LPTSTR stringToTypeMod = (LPTSTR) stringToType;
 for(int i=0; i<_tcslen(stringToTypeMod); i++)
 {
  typeKey(VkKeyScan(stringToTypeMod[i]));
 }
}

HMENU findMenu(HMENU menu, LPARAM menuText) //TCHAR menuText[ 2048 ] = "File";
{
 int menuCount = GetMenuItemCount(menu);
 for(int i=0; i<menuCount; i++)
 {
  char menuString[2048];
  GetMenuString(menu, i, menuString, 2048, MF_BYPOSITION);
  if(_tcsstr(menuString, LPCTSTR(menuText)) != NULL)
  {
   cout << "FOUND:" << menuString << endl;
   return GetSubMenu(menu, i);
  }
 }
 return NULL;
}

HWND findWindow(LPARAM windowTitle) //TCHAR windowTitle[ 2048 ] = "Untitled - Notepad";
{ // Should iterate through all windows and find a window matching title without the need for an exact match.
 HWND notepadHandle = FindWindow(0, LPCTSTR(windowTitle));
 TCHAR title[250];
 GetWindowText(notepadHandle,title,250);
 cout << "TITLE:" << title << endl;
 return notepadHandle;
}

bool isProcessRunning(LPARAM processText) //TCHAR processText[ 2048 ] = "notepad.exe";
{
 bool retVal = false;
 DWORD aProcesses[2048], cbNeeded, cProcesses;
    EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded); // Stores all processes in array aProcesses
 for(int i = 0; i < 2048; i++) // iterate over all processes in the array
    {
  DWORD processID = aProcesses[i];
        TCHAR processName[50];
  HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
   PROCESS_VM_READ, FALSE, processID ); // Get process handle from process id
  GetModuleFileNameEx(hProcess, NULL, processName, 2048);  // Get process name from process handle
  if(_tcsstr(processName, LPCTSTR(processText)) != NULL) // Check if processName matches processText
  {
   cout << ":PROCESSNAME:" << processName << endl;
   retVal = true;
  }
 }

 return retVal;
}

void startProcess(LPARAM processPath) //TCHAR processPath[ 2048 ] = "C:\\Windows\\System32\\notepad.exe";
{
 STARTUPINFO info={sizeof(info)};
 PROCESS_INFORMATION processInfo;
 CreateProcess(NULL, (LPTSTR) processPath, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo); // Creates a new process, storing information in info,processInfo
 ::WaitForSingleObject(processInfo.hProcess, 1000); // Wait x seconds for process to start
 CloseHandle(processInfo.hProcess);
 CloseHandle(processInfo.hThread);
 cout << "FINISHED OPENDING PROCESS:" << processPath << endl;
}

DWORD WINAPI MyThreadFunction( LPVOID lpParam )
{
 cout << "THREAD START" << endl;

 // Click OK button to Save
 HWND saveAsDialog = findWindow((LPARAM) TEXT("Save As"));
 EnumChildWindows(saveAsDialog, ClickButton, (LPARAM) TEXT("Save")); // Call click button for every child of saveAsDialog

 cout << "THREAD FINISH" << endl;
}

int main()
{
 if(isProcessRunning((LPARAM) TEXT("notepad.exe")))
 {
  cout << "YOU SHOULD STOP NOTEPAD BEFORE RUNNING THIS CODE" << endl;
  exit(0);
 }

 cout << "MAIN START VERSION 11" << endl;

 // Start notepad
 startProcess((LPARAM) TEXT("C:\\Windows\\System32\\notepad.exe"));
 HWND notepadHandle = findWindow((LPARAM) TEXT("Untitled - Notepad"));
 
 // Type some keys in notepad
 typeKey(VK_TAB);
 Sleep(1000);

 // Type some more keys in notepad
 typeKeys((LPARAM) TEXT("The cat in the hat was running with a bat."));
 Sleep(1000);

 // Select File->Save As
 HMENU fileMenu = findMenu(GetMenu(notepadHandle), (LPARAM) TEXT("File"));
 selectMenuItem(notepadHandle, fileMenu, (LPARAM) TEXT("Save &As"));
 Sleep(1000);

 // Enter a filename
 typeKeys((LPARAM) TEXT("NewTextFile.txt"));
 Sleep(1000);

 // If file already exists, clicking Save will block the invoking thread, so we will click Save on a separate thread
 DWORD dwThreadId;
 CreateThread(NULL,0, MyThreadFunction, NULL, 0, &dwThreadId);
 Sleep(1000);
 
 // If file already exists, we will not confirm the Save As operation
 HWND alreadyExistsDialog = findWindow((LPARAM) TEXT("Confirm Save As"));
 if(alreadyExistsDialog != NULL)
 {
  cout << "Confirm Save As: ... already exists.  Do you want to replace it?...  Clicking No" << endl;
  EnumChildWindows(alreadyExistsDialog, ClickButton, (LPARAM) TEXT("No")); // Don't Save if the file already exists
  Sleep(1000);
 }
 
 // If file already exists, we will cancel the Save As operation
 alreadyExistsDialog = findWindow((LPARAM) TEXT("Save As"));
 if(alreadyExistsDialog != NULL)
 {
  cout << "Save As: ...  Clicking Cancel" << endl;
  EnumChildWindows(alreadyExistsDialog, ClickButton, (LPARAM) TEXT("Cancel")); // Don't Save if the file already exists
  Sleep(1000);
 }

 // Exit Notepad
 selectMenuItem(notepadHandle, fileMenu, (LPARAM) TEXT("E&xit"));
 Sleep(1000);
 
 // If file already exists, we will not save changes
 alreadyExistsDialog = findWindow((LPARAM) TEXT("Notepad"));
 if(alreadyExistsDialog != NULL)
 {
  cout << "Notepad: Do you want to save changes to Untitled...  Clicking Don't Save" << endl;
  EnumChildWindows(alreadyExistsDialog, ClickButton, (LPARAM) TEXT("Do&n't Save")); // Don't Save if the file already exists
  Sleep(1000);
 }

 cout << "MAIN FINISH" << endl;
}

Spawning a Thread to Run a New Process in Windows C++

This is an example of how to spawn a thread in C++. The idea is that within the thread code, you can run a process using CreateProcess.

To run:

  1. Download MinGW64 compiler (or MinGW for Windows 32-bit).
  2. Create an empty file “ThreadExample.h“.
  3. Create a file “ThreadExample.cpp” and copy code from below.
  4. Open a Windows command prompt and “cd” to the directory with the files.
  5. Compile using the ““C:\MinGW64\bin\g++.exe” -o ThreadExample.exe ThreadExample.cpp -lpsapi” command.

// Compile with
// "C:\MinGW64\bin\g++.exe" -o ThreadExample.exe ThreadExample.cpp -lpsapi
// This file is ThreadExample.cpp

#include "ThreadExample.h"
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <windows.h>
#include <iostream>
#include <conio.h>
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <Psapi.h>
#include <strsafe.h>
#include <cstring>

using namespace std;

typedef struct MyData {
    TCHAR processPath[ 32 ];
} MYDATA, *PMYDATA;

DWORD WINAPI MyThreadFunction( LPVOID lpParam )
{
 cout << "THREAD START" << endl;
 PMYDATA pData = (PMYDATA)lpParam;
 cout << "THREAD PATH:" << pData->processPath << endl;
 for(int i=0; i<10; i++)
 {
  Sleep(300);
  cout << "THREAD PRINT:" << i << endl;
 }
 cout << "THREAD FINISH" << endl;
}

int main()
{
 cout << "MAIN START VERSION 10" << endl;

 DWORD   dwThreadId;
 HANDLE  hThread;
 PMYDATA pData = (PMYDATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYDATA));
 strcpy(pData->processPath, TEXT ("C:\\Windows\\System32\\notepad.exe"));
 cout << "ORIG PATH:" << pData->processPath << endl;
 hThread = CreateThread(NULL,0,MyThreadFunction,pData,0,&dwThreadId);

 for(int i=0; i<10; i++)
 {
  Sleep(200);
  cout << "MAIN PRINT:" << i << endl;
 }

 cout << "MAIN FINISH" << endl;
}