IMFTransform::ProcessOutput returns E_INVALIDARG - c

The problem
I am trying to get call ProcessOutput to get decoded data from my decoder and get the following error:
E_INVALIDARG One or more arguments are invalid.
What I have tried
As ProcessOutput has many arguments I have tried to pinpoint what the error might be. Documentation for ProcessOutput does not mention E_INVALIDARG. However, the documentation for MFT_OUTPUT_DATA_BUFFER, the datatype for one of the arguments, mentions in its Remarks section that:
Any other combinations are invalid and cause ProcessOutput to return E_INVALIDARG
What it talks about there is how the MFT_OUTPUT_DATA_BUFFER struct is setup. So an incorrectly setup MFT_OUTPUT_DATA_BUFFER might cause that error. I have however tried to set it up correctly.
By calling GetOutputStreamInfo I find that I need to allocate the sample sent to ProcessOutput which is what I do. I'm using pretty much the same method that worked for ProcessInput so I don't know what I am doing wrong here.
I have also tried to make sure that the other arguments, who logically should also be able to cause an E_INVALIDARG. They look good to me and I have not been able to find any other leads to which of my arguments to ProcessOutput might be invalid.
The code
I have tried to post only the relevant parts of the code below. I have removed or shortened many of the error checks for brevity. Note that I am using plain C.
"Prelude"
...
hr = pDecoder->lpVtbl->SetOutputType(pDecoder, dwOutputStreamID, pMediaOut, dwFlags);
...
// Send input to decoder
hr = pDecoder->lpVtbl->ProcessInput(pDecoder, dwInputStreamID, pSample, dwFlags);
if (FAILED(hr)) { /* did not fail */ }
So before the interesting code below I have successfully setup things (I hope) and sent them to ProcessInput which did not fail. I have 1 input stream and 1 output stream, AAC in, PCM out.
Code directly leading to the error
// Input has now been sent to the decoder
// To extract a sample from the decoder we need to create a strucure to hold the output
// First we ask the OutputStream for what type of output sample it will produce and who should allocate it
// Then we create both the sample in question (if we should allocate it that is) and the MFT_OUTPUT_DATA_BUFFER
// which holds the sample and some other information that the decoder will fill in.
#define SAMPLES_PER_BUFFER 1 // hardcoded here, should depend on GetStreamIDs results, which right now is 1
MFT_OUTPUT_DATA_BUFFER pOutputSamples[SAMPLES_PER_BUFFER];
DWORD *pdwStatus = NULL;
// There are different allocation models, find out which one is required here.
MFT_OUTPUT_STREAM_INFO streamInfo = { 0,0,0 };
MFT_OUTPUT_STREAM_INFO *pStreamInfo = &streamInfo;
hr = pDecoder->lpVtbl->GetOutputStreamInfo(pDecoder, dwOutputStreamID, pStreamInfo);
if (FAILED(hr)) { ... }
if (pStreamInfo->dwFlags == MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) { ... }
else if (pStreamInfo->dwFlags == MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES) { ... }
else {
// default, the client must allocate the output samples for the stream
IMFSample *pOutSample = NULL;
DWORD minimumSizeOfBuffer = pStreamInfo->cbSize;
IMFMediaBuffer *pBuffer = NULL;
// CreateMediaSample is explained further down.
hr = CreateMediaSample(minimumSizeOfBuffer, sampleDuration, &pBuffer, &pOutSample);
if (FAILED(hr)) {
BGLOG_ERROR("error");
}
pOutputSamples[0].pSample = pOutSample;
}
// since GetStreamIDs return E_NOTIMPL then dwStreamID does not matter
// but its recomended that it is set to the array index, 0 in this case.
// dwOutputStreamID will be 0 when E_NOTIMPL is returned by GetStremIDs
pOutputSamples[0].dwStreamID = dwOutputStreamID; // = 0
pOutputSamples[0].dwStatus = 0;
pOutputSamples[0].pEvents = NULL; // have tried init this myself, but MFT_OUTPUT_DATA_BUFFER documentation says not to.
hr = pDecoder->lpVtbl->ProcessOutput(pDecoder, dwFlags, outputStreamCount, pOutputSamples, pdwStatus);
if (FAILED(hr)) {
// here E_INVALIDARG is found.
}
CreateMediaSample that is used in the code is derived from an example from the official documentation but modified to call SetSampleDuration and SetSampleTime. I get the same error by not setting those two though so it should be something else causing the problem.
Some of the actual data that was sent to ProcessOutput
In case I might have missed something which is easy to see from the actual data:
hr = pDecoder->lpVtbl->ProcessOutput(
pDecoder, // my decoder
dwFlags, // 0
outputStreamCount, // 1 (from GetStreamCount)
pOutputSamples, // se comment below
pdwStatus // NULL
);
// pOutputSamples[0] holds this struct:
// dwStreamID = 0,
// pSample = SampleDefinedBelow
// dwStatus = 0,
// pEvents = NULL
// SampleDefinedBelow:
// time = 0
// duration = 0.9523..
// buffer = with max length set correctly
// attributes[] = NULL
Question
So anyone have any ideas on what I am doing wrong or how I could debug this further?

ProcessOutput needs a valid pointer as the last argument, so this does not work:
DWORD *pdwStatus = NULL;
pDecoder->lpVtbl->ProcessOutput(..., pdwStatus);
This is okay:
DWORD dwStatus;
pDecoder->lpVtbl->ProcessOutput(..., &dwStatus);
Regarding further E_FAIL - your findings above, in general, looks good. It is not that I see something obvious, and also the error code does not suggest that the problem is with MFT data flow. Perhaps it could be bad data or data not matching media types set.

Related

No-op remuxing of .avi file

I want to demux and then mux .avi file without changing anything.
My program is this (redacted for brevity):
AVFormatContext *input_format_context = NULL;
avformat_open_input(
&input_format_context,
input_url,
NULL, // fmt
NULL // options
);
avformat_find_stream_info(input_format_context, NULL);
AVFormatContext *output_format_context = NULL;
avformat_alloc_output_context2(
&output_format_context,
NULL, // oformat
NULL, // format_name
output_url
);
avio_open2(
&output_format_context->pb,
output_url,
AVIO_FLAG_WRITE,
NULL, // int_cb,
NULL // options
);
for (int i = 0; i < input_format_context->nb_streams; i++) {
avformat_new_stream(output_format_context, NULL);
AVStream *input_stream = input_format_context->streams[i];
AVStream *output_stream = output_format_context->streams[i];
AVCodecParameters *params = avcodec_parameters_alloc();
avcodec_parameters_copy(params, input_stream->codecpar);
output_stream->codecpar = params;
}
avformat_write_header(output_format_context, NULL))
AVPacket *input_packet = NULL;
input_packet = av_packet_alloc();
while (!av_read_frame(
input_format_context,
input_packet
)) {
av_write_frame(output_format_context, input_packet);
av_packet_unref(input_packet);
}
av_write_trailer(output_format_context);
Problem:
Output file is created but instead of close to 10 minute video it is a 24-second slide show consisting of around 3 frames.
It seems that the problem is (perhaps not the only one) lack of PTS on the packet.
When I explicitly print it (input_packet->pts) for each packet it is -9223372036854775808. And also the following warning is printed:
[avi # 0x562868c6c000] Timestamps are unset in a packet for stream 0. This is deprecated and will stop working in the future. Fix your code to set the timestamps properly
How do I then "fix my code to set the timestamps properly"?
I just found a solution.
I added this:
output_stream->time_base = input_stream->time_base;
which then, I understand, allows the video player to calculate PTS on the fly.
This does not remove the warning itself, though. I understand that .avi simply does not have PTS, so it's not a bug as such. To get rid of the warning one can manually set PTS on the packets:
input_packet->pts = calculated_ts;
I would think I should be able to also just do:
output_format_context->oformat->flags |= AVFMT_NOTIMESTAMPS;
However, I cannot do that:
error: assignment of member ‘flags’ in read-only object
So, it looks like ffmpeg is requiring PTS even for .avi or there's a bug or I'm still doing something wrong.

How do I read OpenVINO IR models from memory with the OpenVINO C API

I am having trouble reading OpenVINO IR networks (XML and bin) from memory using ie_core_read_network_from_memory() in the OpenVINO 2021.4 C API ie_c_api.h.
I suspect that I am creating the network weight blob wrong, but I cannot find any information on how to create weight blobs correctly for networks.
I have read the OpenVINO C API docs but cannot deduce from docs what I am doing wrong. The OpenVINO code repo contains some C code samples, but none of the samples seem to use ie_core_read_network_from_memory().
Below is a cut out of the code I am having trouble with.
// void* dmem->data - network memory buffer (float32)
// size_t dmem->size - size of network memory buffer (bytes)
ie_core_t* ov_core = NULL;
IEStatusCode status = ie_core_create("", &ov_core);
if (status != OK)
{
// error handling
}
const dimensions_t weights_tensor_dims =
{ 4, { 1, 1, 1, dmem->size/sizeof(float) } };
tensor_desc_t weights_tensor_desc = { OIHW, weights_tensor_dims, FP32 };
ie_blob_t* ov_model_weight_blob = NULL;
status = ie_blob_make_memory_from_preallocated(
&weights_tensor_desc, dmem->data, dmem->size, &ov_model_weight_blob);
if (status != OK)
{
// error handling
}
// char* model_xml_desc - the model's XML string
uint8_t* ov_model_xml_content = (uint8_t*)model_xml_desc;
ie_network_t* ov_network = NULL;
size_t xml_sz = strlen(ov_model_xml_content);
status = ie_core_read_network_from_memory(
ov_core, ov_model_xml_content, xml_sz, ov_model_weight_blob, &ov_network);
if (status != OK)
{
// Always get "GENERAL_ERROR (-1)"
}
The code works fine down to the ie_core_read_network_from_memory() call which results in "GENERAL_ERROR".
I have tried two models that were converted from Tensorflow. One is a simple [X] -> [Y] regression model (single input value, single output value). The other is also a regression model [X_1, X_2, ..., X_9] -> [Y] (nine input values, single output value). They work fine when reading them from file with ie_core_read_network(), but for my use case I must provide the network as a binary memory buffer and XML string.
I would appreciate any help, either by pointing out what I am getting wrong or directing me to some code samples that use ie_core_read_network_from_memory().
System information:
Windows 10
OpenVINO v2021.4.689
Microsoft Visual Studio 2019
UPDATE: An Intel employee reached out to me in another forum and pointed out that there is a unit test for ie_core_read_network_from_memory(). The unit test successfully reads a network from memory and made clear that I was in fact using a faulty tensor description to produce the weight blob, just as I suspected. Apparently the weight blob descriptor should be one dimensional, have memory layout ANY and datatype U8 even though the model weights are fp32.
From the unit test:
std::string bin_std = TestDataHelpers::generate_model_path("test_model", "test_model_fp32.bin");
const char* bin = bin_std.c_str();
//...
std::vector<uint8_t> weights_content(content_from_file(bin, true));
tensor_desc_t weights_desc { ANY, { 1, { weights_content.size() } }, U8 };
However, simply changing the tensor descriptor was not enough to get my code to work so it remains for me to properly translate the C++ code from the unit test to my C environment before the issue to can be considered solved.
Thanks
Refer to tensor_desc struct and standard layout format.
Apart from that, it is recommended to use the Benchmark_app tool to test the inference performance.

AudioFileCreateWithURL failed('wht?')

I'm trying to record sound using Audio Queue, but every time I want to write to file I get the message AudioFileCreateWithURL failed('wht?'). I haven't been able to find a corresponding solution to this error, for I haven't found a similar (wht?) error elsewhere. I acquired the code from Apple's official guide for Audio Queue programming and it looks like this:
char* filePath = "Users/linus/voicies/output.wav";
CFURLRef myFileURL = CFURLCreateFromFileSystemRepresentation( // 1
NULL, // 2
(const UInt8 *) filePath, // 3
strlen (filePath), // 4
false // 5
);
OSStatus err = AudioFileCreateWithURL(
myFileURL,
kAudioFileWAVEType,
&recordFormat,
kAudioFileFlags_EraseFile,
&recorder.recordFile
);
CheckError(err);
in which CheckError finds the corresponding error, which is (wht?). I have no idea what that means and what I must do to make it happen, since the code I have used is almost identical to the sample codes. I appreciate any kind of clue.

How to chain BCryptEncrypt and BCryptDecrypt calls using AES in GCM mode?

Using the Windows CNG API, I am able to encrypt and decrypt individual blocks of data with authentication, using AES in GCM mode. I now want to encrypt and decrypt multiple buffers in a row.
According to documentation for CNG, the following scenario is supported:
If the input to encryption or decryption is scattered across multiple
buffers, then you must chain calls to the BCryptEncrypt and
BCryptDecrypt functions. Chaining is indicated by setting the
BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG flag in the dwFlags member.
If I understand it correctly, this means that I can invoke BCryptEncrypt sequentially on multiple buffers an obtain the authentication tag for the combined buffers at the end. Similarly, I can invoke BCryptDecrypt sequentially on multiple buffers while deferring the actual authentication check until the end. I can not get that to work though, it looks like the value for dwFlags is ignored. Whenever I use BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG, I get a return value of 0xc000a002 , which is equal to STATUS_AUTH_TAG_MISMATCH as defined in ntstatus.h.
Even though the parameter pbIV is marked as in/out, the elements pointed to by the parameter pbIV do not get modified by BCryptEncrypt(). Is that expected? I also looked at the field pbNonce in the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure, pointed to by the pPaddingInfo pointer, but that one does not get modified either. I also tried "manually" advancing the IV, modifying the contents myself according to the counter scheme, but that did not help either.
What is the right procedure to chain the BCryptEncrypt and/or BCryptDecrypt functions successfully?
I managed to get it to work. It seems that the problem is in MSDN, it should mention setting BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG instead of BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG.
#include <windows.h>
#include <assert.h>
#include <vector>
#include <Bcrypt.h>
#pragma comment(lib, "bcrypt.lib")
std::vector<BYTE> MakePatternBytes(size_t a_Length)
{
std::vector<BYTE> result(a_Length);
for (size_t i = 0; i < result.size(); i++)
{
result[i] = (BYTE)i;
}
return result;
}
std::vector<BYTE> MakeRandomBytes(size_t a_Length)
{
std::vector<BYTE> result(a_Length);
for (size_t i = 0; i < result.size(); i++)
{
result[i] = (BYTE)rand();
}
return result;
}
int _tmain(int argc, _TCHAR* argv[])
{
NTSTATUS bcryptResult = 0;
DWORD bytesDone = 0;
BCRYPT_ALG_HANDLE algHandle = 0;
bcryptResult = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_AES_ALGORITHM, 0, 0);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptOpenAlgorithmProvider");
bcryptResult = BCryptSetProperty(algHandle, BCRYPT_CHAINING_MODE, (BYTE*)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptSetProperty(BCRYPT_CHAINING_MODE)");
BCRYPT_AUTH_TAG_LENGTHS_STRUCT authTagLengths;
bcryptResult = BCryptGetProperty(algHandle, BCRYPT_AUTH_TAG_LENGTH, (BYTE*)&authTagLengths, sizeof(authTagLengths), &bytesDone, 0);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGetProperty(BCRYPT_AUTH_TAG_LENGTH)");
DWORD blockLength = 0;
bcryptResult = BCryptGetProperty(algHandle, BCRYPT_BLOCK_LENGTH, (BYTE*)&blockLength, sizeof(blockLength), &bytesDone, 0);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGetProperty(BCRYPT_BLOCK_LENGTH)");
BCRYPT_KEY_HANDLE keyHandle = 0;
{
const std::vector<BYTE> key = MakeRandomBytes(blockLength);
bcryptResult = BCryptGenerateSymmetricKey(algHandle, &keyHandle, 0, 0, (PUCHAR)&key[0], key.size(), 0);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGenerateSymmetricKey");
}
const size_t GCM_NONCE_SIZE = 12;
const std::vector<BYTE> origNonce = MakeRandomBytes(GCM_NONCE_SIZE);
const std::vector<BYTE> origData = MakePatternBytes(256);
// Encrypt data as a whole
std::vector<BYTE> encrypted = origData;
std::vector<BYTE> authTag(authTagLengths.dwMinLength);
{
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
authInfo.pbNonce = (PUCHAR)&origNonce[0];
authInfo.cbNonce = origNonce.size();
authInfo.pbTag = &authTag[0];
authInfo.cbTag = authTag.size();
bcryptResult = BCryptEncrypt
(
keyHandle,
&encrypted[0], encrypted.size(),
&authInfo,
0, 0,
&encrypted[0], encrypted.size(),
&bytesDone, 0
);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptEncrypt");
assert(bytesDone == encrypted.size());
}
// Decrypt data in two parts
std::vector<BYTE> decrypted = encrypted;
{
DWORD partSize = decrypted.size() / 2;
std::vector<BYTE> macContext(authTagLengths.dwMaxLength);
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
authInfo.pbNonce = (PUCHAR)&origNonce[0];
authInfo.cbNonce = origNonce.size();
authInfo.pbTag = &authTag[0];
authInfo.cbTag = authTag.size();
authInfo.pbMacContext = &macContext[0];
authInfo.cbMacContext = macContext.size();
// IV value is ignored on first call to BCryptDecrypt.
// This buffer will be used to keep internal IV used for chaining.
std::vector<BYTE> contextIV(blockLength);
// First part
authInfo.dwFlags = BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG;
bcryptResult = BCryptDecrypt
(
keyHandle,
&decrypted[0*partSize], partSize,
&authInfo,
&contextIV[0], contextIV.size(),
&decrypted[0*partSize], partSize,
&bytesDone, 0
);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptDecrypt");
assert(bytesDone == partSize);
// Second part
authInfo.dwFlags &= ~BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG;
bcryptResult = BCryptDecrypt
(
keyHandle,
&decrypted[1*partSize], partSize,
&authInfo,
&contextIV[0], contextIV.size(),
&decrypted[1*partSize], partSize,
&bytesDone, 0
);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptDecrypt");
assert(bytesDone == partSize);
}
// Check decryption
assert(decrypted == origData);
// Cleanup
BCryptDestroyKey(keyHandle);
BCryptCloseAlgorithmProvider(algHandle, 0);
return 0;
}
#Codeguard's answer got me through the project I was working on which lead me to find this question/answer in the first place; however, there were still a number of gotchas I struggled with. Below is the process I followed with the tricky parts called out. You can view the actual code at the link above:
Use BCryptOpenAlgorithmProvider to open the algorithm provider using BCRYPT_AES_ALGORITHM.
Use BCryptSetProperty to set the BCRYPT_CHAINING_MODE to BCRYPT_CHAIN_MODE_GCM.
Use BCryptGetProperty to get the BCRYPT_OBJECT_LENGTH to allocate for use by the BCrypt library for the encrypt/decrypt operation. Depending on your implementation, you may also want to:
Use BCryptGetProperty to determine BCRYPT_BLOCK_SIZE and allocate scratch space for the IV. The Windows API updates the IV with each call, and the caller is responsible for providing the memory for that usage.
Use BCryptGetProperty to determine BCRYPT_AUTH_TAG_LENGTH and allocate scratch space for the largest possible tag. Like the IV, the caller is responsible for providing this space, which the API updates each time.
Initialize the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO struct:
Initialize the structure with BCRYPT_INIT_AUTH_MODE_INFO()
Initialize the pbNonce and cbNonce field. Note that for the first call to BCryptEncrypt/BCryptDecrypt, the IV is ignored as an input and this field is used as the "IV". However, the IV parameter will be updated by that first call and used by subsequent calls, so space for it must still be provided. In addition, the pbNonce and cbNonce fields must remain set (even though they are unused after the first call) for all calls to BCryptEncrypt/BCryptDecrypt or those calls will complain.
Initialize pbAuthData and cbAuthData. In my project, I set these fields just before the first call to BCryptEncrypt/BCryptDecrypt and immediately reset them to NULL/0 immediately afterward. You can pass NULL/0 as the input and output parameters during these calls.
Initialize pbTag and cbTag. pbTag can be NULL until the final call to BCryptEncrypt/BCryptDecrypt when the tag is retrieved or checked, but cbTag must be set or else BCryptEncrypt/BCryptDecrypt will complain.
Initialize pbMacContext and cbMacContext. These point to a scratch space for the BCryptEncrypt/BCryptDecrypt to use to keep track of the current state of the tag/mac.
Initialize cbAAD and cbData to 0. The APIs use these fields, so you can read them at any time, but you should not update them after initially setting them to 0.
Initialize dwFlags to BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG. After initialization, changes to this field should be made by using |= or &=. Windows also sets flags within this field that the caller needs to take care not to alter.
Use BCryptGenerateSymmetricKey to import the key to use for encryption/decryption. Note that you will need to supply the memory associated with BCRYPT_OBJECT_LENGTH to this call for use by BCryptEncrypt/BCryptDecrypt during operation.
Call BCryptEncrypt/BCryptDecrypt with your AAD, if any; no input nor space for output need be supplied for this call. (If the call succeeds, you can see the size of your AAD reflected in the cbAAD field of the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure.)
Set pbAuthData and cbAuthData to reflect the AAD.
Call BCryptEncrypt or BCryptDecrypt.
Set pbAuthData and cbAuthData back to NULL and 0.
Call BCryptEncrypt/BCryptDecrypt "N - 1" times
The amount of data passed to each call must be a multiple of the algorithm's block size.
Do not set the dwFlags parameter of the call to anything other than 0.
The output space must be equal to or greater than the size of the input
Call BCryptEncrypt/BCryptDecrypt one final time (with or without plain/cipher text input/output). The size of the input need not be a multiple of the algorithm's block size for this call. dwFlags is still set to 0.
Set the pbTag field of the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure either to the location at which to store the generated tag or to the location of the tag to verify against, depending on whether the operation is an encryption or decryption.
Remove the BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG from the dwFlags field of the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure using the &= syntax.
Call BCryptDestroyKey
Call BCryptCloseAlgorithmProvider
It would be wise, at this point, to wipe out the space associated with BCRYPT_OBJECT_LENGTH.

SetProp problem

Can anybody tell me why the following code doesn't work? I don't get any compiler errors.
short value = 10;
SetProp(hCtl, "value", (short*) value);
The third parameter is typed as a HANDLE, so IMO to meet the explicit contract of the function you should save the property as a HANDLE by allocating a HGLOBAL memory block. However, as noted in the comments below, MSDN states that any value can be specified, and indeed when I try it on Windows 7 using...
SetProp(hWnd, _T("TestProp"), (HANDLE)(10)); // or (HANDLE)(short*)(10)
...
(short)GetProp(hWnd, _T("TestProp"));
... I get back 10 from GetProp. I suspect somewhere between your SetProp and GetProp one of two things happens: (1) the value of hWnd is different -- you're checking a different window or (2) a timing issue -- the property hasn't been set yet or had been removed.
If you wanted to use an HGLOBAL instead to follow the specific types of the function signature, you can follow this example in MSDN.
Even though a HANDLE is just a pointer, it's a specific data type that is allocated by calls into the Windows API. Lots of things have handles: icons, cursors, files, ... Unless the documentation explicitly states otherwise, to use a blob of data such as a short when the function calls for a HANDLE, you need a memory handle (an HGLOBAL).
The sample code linked above copies data as a string, but you can instead set it as another data type:
// TODO: Add error handling
hMem = GlobalAlloc(GPTR, sizeof(short));
lpMem = GlobalLock(hMem);
if (lpMem != NULL)
{
*((short*)lpMem) = 10;
GlobalUnlock(hMem);
}
To read it back, when you GetProp to get the HANDLE you must lock it to read the memory:
// TODO: Add error handling
short val;
hMem = (HGLOBAL)GetProp(hwnd, ...);
if (hMem)
{
lpMem = GlobalLock(hMem);
if (lpMem)
{
val = *((short*)lpMem);
}
}
I would create the short on the heap, so that it continues to exist, or perhaps make it global, which is perhaps what you did. Also the cast for the short address needs to be void *, or HANDLE.

Resources