I'm trying to create a buffer in GPU memory to upload data from CPU. GPU access will be readonly. Data will be used as an input buffer for a compute shader.
CreateBuffer() fails with error 0x80070057 (E_INVALIDARG). I read the docs and read it again without discovering which argument cause the failure.
InitDevice() return success.
Here is an extract from my code:
function TGpuImageControl.InitDevice: HRESULT;
var
hr : HRESULT;
createDeviceFlags : UINT;
driverTypes : array [0..0] of D3D_DRIVER_TYPE;
numDriverTypes : UINT;
driverTypeIndex : UINT;
sd : DXGI_SWAP_CHAIN_DESC;
FeatureLevels : D3D_FEATURE_LEVEL;
featureLevel : D3D_FEATURE_LEVEL;
const
D3D10_SHADER_DEBUG = 1;
begin
hr := S_OK;
createDeviceFlags := 0;
{$ifdef DEBUG}
createDeviceFlags := createDeviceFlags or D3D11_CREATE_DEVICE_DEBUG;
{$endif}
{$ifdef WARP}
driverTypes[0] := D3D_DRIVER_TYPE_REFERENCE;
{$else}
driverTypes[0] := D3D_DRIVER_TYPE_HARDWARE;
{$endif}
numDriverTypes := SizeOf(driverTypes) div SizeOf(driverTypes[0]);
ZeroMemory(#sd, SizeOf(sd));
sd.BufferCount := 1;
sd.BufferDesc.Width := width;
sd.BufferDesc.Height := height;
sd.BufferDesc.Format := DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator := 60;
sd.BufferDesc.RefreshRate.Denominator := 1;
sd.BufferUsage := DXGI_USAGE_RENDER_TARGET_OUTPUT or
DXGI_USAGE_UNORDERED_ACCESS;// or
//DXGI_USAGE_SHADER_INPUT;
sd.OutputWindow := Handle;
sd.SampleDesc.Count := 1;
sd.SampleDesc.Quality := 0;
sd.Windowed := TRUE;
//sd.Flags := DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
FeatureLevels := D3D_FEATURE_LEVEL_11_0;
for driverTypeIndex := 0 to numDriverTypes do begin
g_driverType := driverTypes[driverTypeIndex];
hr := D3D11CreateDeviceAndSwapChain(
nil, // Graphic Adapter, use default
g_driverType, // Driver type to use
0, // HModule for software driver
createDeviceFlags, // Create flags
#FeatureLevels, // Feature levels
1, // Feature level size
D3D11_SDK_VERSION, // SDK Version
#sd, // Swap Chain descriptor
g_pSwapChain, // Out: Created swap chain
g_pd3dDevice, // Out: Created device
featureLevel, // Out: Feature level
g_pImmediateContext); // Out: Context
if SUCCEEDED(hr) then
break;
end;
if FAILED(hr) then begin
Result := hr;
Exit;
end;
ImageResize();
Result := S_OK;
end;
procedure TGpuImageControl.ImageResize;
var
hr : HRESULT;
sd : DXGI_SWAP_CHAIN_DESC;
pTexture : ID3D11Texture2D;
vp : D3D11_VIEWPORT;
begin
if g_pd3dDevice = nil then
Exit;
// release first else resize problem
SAFE_RELEASE(IUnknown(g_pComputeOutput));
g_pSwapChain.GetDesc(sd);
hr := g_pSwapChain.ResizeBuffers(sd.BufferCount,
Width,
Height,
sd.BufferDesc.Format,
0); // Swap chain flags
if FAILED(hr) then begin
ShowError('SwapChain.ResizeBuffers failed with error %d', [hr]);
Exit;
end;
hr := g_pSwapChain.GetBuffer(0, TGUID(ID3D11Texture2D), pTexture);
if FAILED(hr) then begin
ShowError('SwapChain.GetBuffer failed with error %d', [hr]);
Exit;
end;
// create shader unordered access view on back buffer for compute shader to write into texture
hr := g_pd3dDevice.CreateUnorderedAccessView(pTexture,
nil,
g_pComputeOutput);
if FAILED(hr) then begin
ShowError('pd3dDevice.CreateUnorderedAccessView failed with error %d', [hr]);
Exit;
end;
pTexture := nil;
// Setup the viewport
vp.Width := Width;
vp.Height := Height;
vp.MinDepth := 0.0;
vp.MaxDepth := 1.0;
vp.TopLeftX := 0;
vp.TopLeftY := 0;
g_pImmediateContext.RSSetViewports(1, #vp);
end;
The code which fails is the following:
function TGpuImageControl.CreateStructuredBuffer(
uElementSize : UINT;
uCount : UINT;
pInitData : Pointer;
out ppBufOut : ID3D11Buffer): HRESULT;
var
desc : D3D11_BUFFER_DESC;
InitData : D3D11_SUBRESOURCE_DATA;
begin
ppBufOut := nil;
ZeroMemory(#desc, SizeOf(desc));
desc.BindFlags := D3D11_BIND_UNORDERED_ACCESS or
D3D11_BIND_SHADER_RESOURCE;
desc.Usage := D3D11_USAGE_DYNAMIC;
desc.CPUAccessFlags := D3D11_CPU_ACCESS_WRITE;
desc.ByteWidth := uElementSize * uCount;
desc.MiscFlags := UINT(D3D11_RESOURCE_MISC_BUFFER_STRUCTURED);
desc.StructureByteStride := uElementSize;
if pInitData <> nil then begin
InitData.pSysMem := pInitData;
Result := g_pd3dDevice.CreateBuffer(desc, #InitData, ppBufOut);
end
else
Result := g_pd3dDevice.CreateBuffer(desc, nil, ppBufOut);
end;
When calling the function, I pass uElementSize=2, uCount=100 and pInitData pointing to an allocated 200 bytes buffer in CPU memory.
I don't understand what I'm doing wrong.
Any help appreciated.
The answer has been given by Chuck Walbourn to the C++ question I asked there DirectCompute CreateBuffer fails with error 0x80070057 (E_INVALIDARG)
The most important part to debug this error is simply look at Delphi Event Viewer and just look the error message the API is triggering when debugging is enabled (I I already had enabled debugging but didn't figured that messages where output to the events windows).
Related
Language: delphi 6
I succeeded in opening the dialog using documentproperties.
However, I changed the settings and clicked OK, but it does not change.
I want to change the paper to A3.
Please tell me how to do it.
code:
var
FPrinterHandle:THandle;
aDevice: array[0..255] of char;
DevMode: PDeviceMode;
StubDevMode: TDeviceMode;
DeviceMode: THandle;
begin
strpcopy(aDevice, Combobox1.Text);
if OpenPrinter(aDevice,FPrinterHandle,nil) then begin
DeviceMode := GlobalAlloc(GHND, DocumentProperties(self.handle, FPrinterHandle, ADevice, StubDevMode, StubDevMode, 0));
if DeviceMode <> 0 then begin
DevMode := GlobalLock(DeviceMode);
DocumentProperties(0, FPrinterHandle, ADevice, DevMode^, DevMode^, DM_OUT_BUFFER);
DevMode^.dmFields := DM_PAPERSIZE;
DevMode^.dmPaperSize := DMPAPER_A3;
DocumentProperties(0, FPrinterHandle, ADevice, DevMode^, DevMode^, DM_OUT_BUFFER or DM_IN_BUFFER);
GlobalUnlock(DeviceMode);
GlobalFree(DeviceMode);
end;
end;
end;
It's not the code I wrote, it's from somewhere. Hope it helps.
Pass the printer name and desired paper size as parameters. (I used GetPrinter procedure)
If parameter(integer) is 0, it is set to A3, and if it is 1, it is set to A4.
And when I printed pdf file with shellexecute, I checked that it prints in the desired size.
※ Before print, the tray of the printer should be set to 'automatic selection'.
procedure SetPrinterInfo(APrinterName: PChar; Psize: Integer);
var
HPrinter : THandle;
InfoSize, BytesNeeded: Cardinal;
DevMode: PDeviceMode;
PI2: PPrinterInfo2;
PrinterDefaults: TPrinterDefaults;
begin
with PrinterDefaults do
begin
DesiredAccess := PRINTER_ACCESS_USE;
pDatatype := nil;
pDevMode := nil;
end;
if OpenPrinter(APrinterName, HPrinter, #PrinterDefaults) then
try
SetLastError(0);
//Determine the number of bytes to allocate for the PRINTER_INFO_2 construct...
if not GetPrinter(HPrinter, 2, nil, 0, #BytesNeeded) then
begin
//Allocate memory space for the PRINTER_INFO_2 pointer (PrinterInfo2)...
PI2 := AllocMem(BytesNeeded);
try
InfoSize := SizeOf(TPrinterInfo2);
if GetPrinter(HPrinter, 2, PI2, BytesNeeded, #BytesNeeded) then
begin
DevMode := PI2.pDevMode;
DevMode.dmFields := DevMode.dmFields or DM_PAPERSIZE;
if Psize = 0 then DevMode.dmPaperSize := DMPAPER_A3
else if Psize = 1 then DevMode.dmPaperSize := DMPAPER_A4;
PI2.pSecurityDescriptor := nil;
// Apply settings to the printer
if DocumentProperties(0, hPrinter, APrinterName, PI2.pDevMode^,
PI2.pDevMode^, DM_IN_BUFFER or DM_OUT_BUFFER) = IDOK then
begin
SetPrinter(HPrinter, 2, PI2, 0); // Ignore the result of this call...
end;
end;
finally
FreeMem(PI2, BytesNeeded);
end;
end;
finally
ClosePrinter(HPrinter);
end;
end;
I am using Delphi XE3. I am trying to learn more about how to properly use and then free the pointers. I have two pointers lpParams and pHolder. I would like to know if I must used a typed pointer for pHolder and what the proper way to free both pinters in this routine. Thank you.
function TmyLine.LineOpen(dwLineDevice: Integer;
var wMediaMode: Integer;
phLine,
plTAPIVersion: pLongInt): Integer;
var lOwnership,
lResult: LongInt;
lpParams: pTLineCallparams; // first pointer
pHolder: Pointer; // second pointer
dwAddressID, dwMediaMode: DWORD;
begin
if not bDeviceInitialized then
lResult := -1
else
begin
pHolder := nil; // default is nil unless we are using the singleaddress method
lOwnership := 0;
if FPrivilege.bSingleAddress then
begin
lOwnership := lOwnership + LINEOPENOPTION_SINGLEADDRESS;
try
lpParams := AllocMem(SizeOf(TLineCallParams) + 128); // make up a big enough value
except
lpParams := nil;
exit; // should I exit here? what about if there is an error? do I free pHolder?
end;
lpParams.dwTotalSize := sizeof(TLineCallParams) + 128;
lpParams.dwAddressMode := LINEADDRESSMODE_ADDRESSID;
pHolder := lpParams; // is this ok? Do i need a typed pointer?
end;
lResult := lineOpen(Fline.hLineApp, // fnd 64bit, see if longint needs to be dword_ptr
dwLineDevice,
phLine,
plTAPIVersion^,
FTAPI.lExtVersion,
DWORD_PTR(Self), //Pointer to the control, it is passed to the Callback routine as the lCallBackInstance parameter
lOwnership,
wMediaMode,
pHolder); // using the pointer here
if lResult <> 0 then
begin
pHolder := nil;
lpParams := nil; // is the the right way to do this?
end
else
begin
lineSetStatusMessages(temp);
end;
end;
pHolder := nil; // is this the correct way to free things up?
if lpParams <> nil then freemem(lpParams, sizeof(TLineCallParams) + 128);
LineOpen := lResult;
end;
First, you are filling in the dwAddressMode field, but you are not filling in the dwAddressID field. You need to fill in the dwAddressID or else there is no point in using LINEOPENOPTION_SINGLEADDRESS.
Second, lineOpen() does not take ownership of the LINECALLPARAMS pointer that you pass to it. You must deallocate the memory yourself after lineOpen() exits, regardless of its result, eg:
function TmyLine.LineOpen(dwLineDevice: Integer;
var wMediaMode: Integer;
phLine,
plTAPIVersion: pLongInt): Integer;
var
lOwnership: DWORD;
lpParams: pTLineCallparams;
begin
Result := -1;
if not bDeviceInitialized then Exit;
lOwnership := 0;
lpParams := nil;
try
if FPrivilege.bSingleAddress then
begin
try
lpParams := AllocMem(SizeOf(TLineCallParams));
except
Exit;
end;
lpParams.dwTotalSize := sizeof(TLineCallParams);
lpParams.dwAddressID := ...; // don't forget to fill this in!
lpParams.dwAddressMode := LINEADDRESSMODE_ADDRESSID;
lOwnership := LINEOPENOPTION_SINGLEADDRESS;
end;
Result := lineOpen(Fline.hLineApp,
dwLineDevice,
phLine,
plTAPIVersion^,
FTAPI.lExtVersion,
DWORD_PTR(Self),
lOwnership,
wMediaMode,
lpParams);
finally
if lpParams <> nil then
FreeMem(lpParams, sizeof(TLineCallParams));
end;
if Result = 0 then
lineSetStatusMessages(...);
end;
Third, since you are only filling in standard fields of LINECALLPARAMS and not any extensions, you don't really need to dynamically allocate the LINECALLPARAMS on the heap at all. You can alternatively allocate it statically on the stack instead (which is why lineOpen() cannot take ownership of it), eg:
function TmyLine.LineOpen(dwLineDevice: Integer;
var wMediaMode: Integer;
phLine,
plTAPIVersion: pLongInt): Integer;
var
lOwnership: DWORD;
Params: TLineCallparams;
lpParams: pTLineCallparams;
begin
Result := -1;
if not bDeviceInitialized then Exit;
lOwnership := 0;
lpParams := nil;
if FPrivilege.bSingleAddress then
begin
Params.dwTotalSize := sizeof(TLineCallParams);
Params.dwAddressID := ...; // don't forget to fill this in!
Params.dwAddressMode := LINEADDRESSMODE_ADDRESSID;
lpParams := #Params;
lOwnership := LINEOPENOPTION_SINGLEADDRESS;
end;
Result := lineOpen(Fline.hLineApp,
dwLineDevice,
phLine,
plTAPIVersion^,
FTAPI.lExtVersion,
DWORD_PTR(Self),
lOwnership,
wMediaMode,
lpParams);
if Result = 0 then
lineSetStatusMessages(...);
end;
i am trying to change the resolution of the frames to 320x240 because my webcam is providing frames in 640x480 and the encoder i am using is not working right with higher resolution, i do it this way
procedure OnDevieStart()
begin
FilterGraph.ClearGraph;
FilterGraph.Active := False;
Filter.BaseFilter.Moniker := SysDev.GetMoniker(TMenuItem(Sender).tag);
FilterGraph.Active := true;
SetVideoProperties(Filter as iBaseFilter);
with FilterGraph as ICaptureGraphBuilder2 do
try
RenderStream(#PIN_CATEGORY_PREVIEW, nil, Filter as IBaseFilter, SampleGrabber as IBaseFilter, VideoWindow as IbaseFilter);
FilterGraph.Play;
except
ShowMessage('Unable to use specified device!')
end;
end;
function SetVideoProperties(pVideoCapture: IBaseFilter):Boolean;
var
hr:HRESULT;
pStreamConfig: IAMStreamConfig;
pAM_Media: PAMMediaType;
pvih: PVIDEOINFOHEADER;
pICGP2: ICaptureGraphBuilder2;
begin
pICGP2 := FilterGraph as ICaptureGraphBuilder2;
hr := pICGP2.FindInterface(#PIN_CATEGORY_CAPTURE, nil, pVideoCapture,
IID_IAMStreamConfig, pStreamConfig);
if (SUCCEEDED(hr)) then begin
pStreamConfig.GetFormat(pAM_Media);
pvih := pAM_Media.pbFormat ;
pAM_Media.subtype := MEDIASUBTYPE_RGB24;
pvih.bmiHeader.biWidth := 320;
pvih.bmiHeader.biHeight := 240;
pvih.AvgTimePerFrame := 10000000 div 15;
pStreamConfig.SetFormat(pAM_Media^);
DeleteMediaType(pAM_Media);
pStreamConfig := nil;
end;
end;
But the resolution stays the same when grabbing the frames through the sample grabber
Is there anything wrong with this approach?
UPDATE
Ok i think i am now updating all the members
function SetVideoProperties(pVideoCapture: IBaseFilter):Boolean;
var
hr:HRESULT;
pStreamConfig: IAMStreamConfig;
pAM_Media: PAMMediaType;
pvih: PVIDEOINFOHEADER;
pICGP2: ICaptureGraphBuilder2;
begin
pICGP2 := FilterGraph as ICaptureGraphBuilder2;
hr := pICGP2.FindInterface(#PIN_CATEGORY_CAPTURE, nil, pVideoCapture,
IID_IAMStreamConfig, pStreamConfig);
if (SUCCEEDED(hr)) then begin
pStreamConfig.GetFormat(pAM_Media);
pAM_Media.subtype := MEDIASUBTYPE_RGB24;
pAM_Media.majortype := MEDIATYPE_Video;
pAM_Media.bFixedSizeSamples := True;
pAM_Media.bTemporalCompression := False;
pAM_Media.lSampleSize := 230400;
pAM_Media.formattype := FORMAT_VideoInfo;
pAM_Media.pUnk := nil;
pAM_Media.cbFormat := 88;
pvih := pAM_Media.pbFormat;
pvih.dwBitRate := 6912000;
pvih.AvgTimePerFrame := 10000000 div 15;
pvih.bmiHeader.biSize := 40;
pvih.bmiHeader.biWidth := 320;
pvih.bmiHeader.biHeight := 240;
pvih.bmiHeader.biPlanes := 1;
pvih.bmiHeader.biBitCount := 24;
pvih.bmiHeader.biCompression := 0;
pvih.bmiHeader.biSizeImage := 230400;
pvih.bmiHeader.biXPelsPerMeter := 0;
pvih.bmiHeader.biYPelsPerMeter := 0;
pvih.bmiHeader.biClrUsed := 0;
pvih.bmiHeader.biClrImportant := 0;
hr := pStreamConfig.SetFormat(pAM_Media^);
If Succeeded(hr) then ShowMessage('SUCCEED') else ShowMessage(IntToStr(hr));
DeleteMediaType(pAM_Media);
pStreamConfig := nil;
end;
end;
Your initialization of new media type with new resolution is incorrect/incomplete: update other members as well
You should be checking SetFormat result to detect failures in format settings
The code itself appears to be incomplete, there is no evidence you are at all changing the format and the source filter exists and added to the filter graph
See DSPack demo "...\dspack2.3.4\Demos\D6-D7\videocap"
You need to enumerate all availible formats of webcam, and then set one.
Thats the code from there:
(button Start OnClick handler)
VideoMediaTypes,
AudioMediaTypes: TEnumMediaType;
.......
// configure output Video media type
if VideoSourceFilter.FilterGraph <> nil then
begin
PinList := TPinList.Create(VideoSourceFilter as IBaseFilter);
if VideoFormats.ItemIndex <> -1 then
with (PinList.First as IAMStreamConfig) do
SetFormat(VideoMediaTypes.Items[VideoFormats.ItemIndex].AMMediaType^);
PinList.Free;
end;
Here
SetFormat(VideoMediaTypes.Items[VideoFormats.ItemIndex].AMMediaType^);
VideoMediaTypes is a list of available formats that populated when user selects the Source.
VideoFormat is GUI control (ListBox) for selecting the format
I got the following code from a newsgroup posting. Strangely, it isn't working for me in Delphi 2010; An exception is being thrown at the LsaOpenPolicy function call:
function AddLogonAsAService(ID: pchar): boolean;
const
Right: PChar = 'SeServiceLogonRight';
var
FResult: NTSTATUS;
//szSystemName: LPTSTR;
FObjectAttributes: TLSAObjectAttributes;
FPolicyHandle: LSA_HANDLE;
Server, Privilege: TLSAUnicodeString;
FSID: PSID;
cbSid: DWORD;
ReferencedDomain: LPTSTR;
cchReferencedDomain: DWORD;
peUse: SID_NAME_USE;
PrivilegeString: String;
begin
Result := false;
try
ZeroMemory(#FObjectAttributes, sizeof(FObjectAttributes));
Server.Buffer := nil;
Server.Length := 0;
Server.MaximumLength := 256;
PrivilegeString := Right; //or some other privilege
Privilege.Buffer := PChar(PrivilegeString);
Privilege.Length := 38;
Privilege.MaximumLength := 256;
FResult := LsaOpenPolicy(
#Server, //this machine, because the Buffer is NIL
#FObjectAttributes,
POLICY_ALL_ACCESS,
FPolicyHandle);
if FResult = STATUS_SUCCESS then begin
cbSid := 128;
cchReferencedDomain := 16;
GetMem(FSID, cbSid);
//FSID:=PSID(HeapAlloc(GetProcessHeap(), 0, cbSid));
GetMem(ReferencedDomain, cchReferencedDomain);
//ReferencedDomain := LPTSTR(HeapAlloc(GetProcessHeap(), 0, cchReferencedDomain * sizeof(ReferencedDomain^)));
if LookupAccountName(nil, ID, FSID, cbSid, ReferencedDomain,
cchReferencedDomain, peUse) then begin
FResult := LsaAddAccountRights(FPolicyHandle, FSID, #Privilege, 1);
Result := FResult = STATUS_SUCCESS;
end;
FreeMem(FSID, cbSid);
FreeMem(ReferencedDomain, cchReferencedDomain);
end;
except
Result := false;
end;
end;
Original posting may be found at Google Groups archive:
From: "andrew"
Newsgroups:
borland.public.delphi.winapi
Subject: NetUserAdd and assigning user
rights
Date: Tue, 25 Sep 2001 10:08:35 +1000
Thanks in advance for any answers.
According to the MSDN docs you should not use an LSA_UNICODE_STRING with the Buffer set to nil but pass nil instead: LsaOpenPolicy(nil, ...
/EDIT:
The code below works fine for me using Jedi Apilib so I think something might be wrong with your definition (maybe calling convention?), so please add this to your code.
Also you are specifying maximum buffer size of 256 in the LSA_UNICODE_STRING's which is incorrect, in the first case the maximum buffer is 0.
uses
JwaWinType, JwaNtSecApi;
procedure TForm1.Button1Click(Sender: TObject);
var
ObjectAttribs: LSA_OBJECT_ATTRIBUTES;
PolicyHandle: LSA_HANDLE;
nts: NTSTATUS;
begin
ZeroMemory(#ObjectAttribs, SizeOf(ObjectAttribs));
nts := LsaOpenPolicy(nil, ObjectAttribs, POLICY_ALL_ACCESS, PolicyHandle);
Memo1.Lines.Add(Format('nts=%.8x', [nts]));
end;
Fixed/changed function, tested on Win7 under D2009 (but should work on older/newer too). Of course app. must be running with admin rights.
uses
JwaWinNT, JwaWinType, JwaNtStatus, JwaNtSecApi, JwaLmCons;
function AddPrivilegeToAccount(AAccountName, APrivilege: String): DWORD;
var
lStatus: TNTStatus;
lObjectAttributes: TLsaObjectAttributes;
lPolicyHandle: TLsaHandle;
lPrivilege: TLsaUnicodeString;
lSid: PSID;
lSidLen: DWORD;
lTmpDomain: String;
lTmpDomainLen: DWORD;
lTmpSidNameUse: TSidNameUse;
{$IFDEF UNICODE}
lPrivilegeWStr: String;
{$ELSE}
lPrivilegeWStr: WideString;
{$ENDIF}
begin
ZeroMemory(#lObjectAttributes, SizeOf(lObjectAttributes));
lStatus := LsaOpenPolicy(nil, lObjectAttributes, POLICY_LOOKUP_NAMES, lPolicyHandle);
if lStatus <> STATUS_SUCCESS then
begin
Result := LsaNtStatusToWinError(lStatus);
Exit;
end;
try
lTmpDomainLen := JwaLmCons.DNLEN; // In 'clear code' this should be get by LookupAccountName
SetLength(lTmpDomain, lTmpDomainLen);
lSidLen := SECURITY_MAX_SID_SIZE;
GetMem(lSid, lSidLen);
try
if LookupAccountName(nil, PChar(AAccountName), lSid, lSidLen, PChar(lTmpDomain),
lTmpDomainLen, lTmpSidNameUse) then
begin
lPrivilegeWStr := APrivilege;
lPrivilege.Buffer := PWideChar(lPrivilegeWStr);
lPrivilege.Length := Length(lPrivilegeWStr) * SizeOf(Char);
lPrivilege.MaximumLength := lPrivilege.Length;
lStatus := LsaAddAccountRights(lPolicyHandle, lSid, #lPrivilege, 1);
Result := LsaNtStatusToWinError(lStatus);
end else
Result := GetLastError;
finally
FreeMem(lSid);
end;
finally
LsaClose(lPolicyHandle);
end;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
lStatus: DWORD;
begin
lStatus := AddPrivilegeToAccount('Administrators'{or any account/group name}, 'SeServiceLogonRight');
if lStatus = ERROR_SUCCESS then
Caption := 'OK'
else
Caption := SysErrorMessage(lStatus);
end;
Is there any simple function? I am searching something like that
Play(#data, 44000, 100 {time});
I have worked quite a lot with PCM audio manipulation. I always use this function when playing short sequences of custom waveform audio data:
var
PlaySoundStopper: PBoolean;
SoundPlayerActive: boolean = false;
procedure PlaySound(const Sound: TASSound);
var
hWave: HWAVEOUT;
hdr: TWaveHdr;
buf: PAnsiChar;
fmt: TWaveFormatEx;
i: Integer;
n: Integer;
begin
try
with fmt do
begin
wFormatTag := WAVE_FORMAT_PCM;
nChannels := length(Sound.Channels);
nSamplesPerSec := Sound.SampleRate;
wBitsPerSample := 32;
nAvgBytesPerSec := nChannels * nSamplesPerSec * wBitsPerSample div 8;
nBlockAlign := nChannels * wBitsPerSample div 8;
cbSize := 0;
end;
GetMem(buf, fmt.nChannels * length(Sound.Channels[0]) * sizeof(TASWaveformSample));
if length(Sound.Channels) = 1 then
CopyMemory(buf, #(Sound.Channels[0, 0]), length(Sound.Channels[0]) * sizeof(TASWaveformSample))
else
for i := 0 to high(Sound.Channels[0]) do
for n := 0 to high(Sound.Channels) do
CopyMemory(buf + sizeof(TASWaveformSample) * (i * fmt.nChannels + n), #(Sound.Channels[n, i]), sizeof(TASWaveformSample));
if waveOutOpen(#hWave, WAVE_MAPPER, #fmt, 0, 0, CALLBACK_NULL) <> MMSYSERR_NOERROR then
raise Exception.Create('SoundPlayerThread.Execute: waveOutOpen failed: ' + SysErrorMessage(GetLastError));
ZeroMemory(#hdr, sizeof(hdr));
with hdr do
begin
lpData := buf;
dwBufferLength := fmt.nChannels * length(Sound.Channels[0]) * sizeof(TASWaveformSample);
dwFlags := 0;
end;
try
SoundPlayerActive := true;
waveOutPrepareHeader(hWave, #hdr, sizeof(hdr));
waveOutWrite(hWave, #hdr, sizeof(hdr));
sleep(500);
while waveOutUnprepareHeader(hWave, #hdr, sizeof(hdr)) = WAVERR_STILLPLAYING do
if PlaySoundStopper^ then
begin
waveOutPause(hWave);
waveOutUnprepareHeader(hWave, #hdr, sizeof(hdr));
break;
end
else
sleep(100);
finally
SoundPlayerActive := false;
waveOutClose(hWave);
FreeMem(buf);
end;
except
on E: Exception do MessageBox(0, PChar(E.ClassName + ': ' + E.Message), 'Sound Playback Error', MB_ICONERROR);
end;
end;
where
type
TASWaveformSample = integer; // signed 32-bit; -2147483648..2147483647
TASWaveformSamples = packed array of TASWaveformSample; // one channel
PASSound = ^TASSound;
TASSound = record
Channels: packed array of TASWaveformSamples;
SampleRate: cardinal;
end;
A perhaps better way, is to use a thread for the playing. Then I do
var
OwnerForm: HWND; // = 0;
SndSource: PASSound; // = nil;
ThreadPlaying: boolean; // = false;
type
TSoundPlayerThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end;
implemented as
procedure TSoundPlayerThread.Execute;
var
hWave: HWAVEOUT;
hdr: TWaveHdr;
buf: PAnsiChar;
fmt: TWaveFormatEx;
i: Integer;
n: Integer;
begin
ThreadPlaying := true;
try
try
if not Assigned(SndSource) then
Exit;
with fmt do
begin
wFormatTag := WAVE_FORMAT_PCM;
nChannels := length(SndSource^.Channels);
nSamplesPerSec := SndSource^.SampleRate;
wBitsPerSample := 32;
nAvgBytesPerSec := nChannels * nSamplesPerSec * wBitsPerSample div 8;
nBlockAlign := nChannels * wBitsPerSample div 8;
cbSize := 0;
end;
GetMem(buf, fmt.nChannels * length(SndSource^.Channels[0]) * sizeof(TASWaveformSample));
if length(SndSource^.Channels) = 1 then
CopyMemory(buf, #(SndSource^.Channels[0, 0]), length(SndSource^.Channels[0]) * sizeof(TASWaveformSample))
else
for i := 0 to high(SndSource^.Channels[0]) do
for n := 0 to high(SndSource^.Channels) do
CopyMemory(buf + sizeof(TASWaveformSample) * (i * fmt.nChannels + n), #(SndSource^.Channels[n, i]), sizeof(TASWaveformSample));
if waveOutOpen(#hWave, WAVE_MAPPER, #fmt, 0, 0, CALLBACK_NULL) <> MMSYSERR_NOERROR then
raise Exception.Create('SoundPlayerThread.Execute: waveOutOpen failed: ' + SysErrorMessage(GetLastError));
ZeroMemory(#hdr, sizeof(hdr));
with hdr do
begin
lpData := buf;
dwBufferLength := fmt.nChannels * length(SndSource^.Channels[0]) * sizeof(TASWaveformSample);
dwFlags := 0;
end;
waveOutPrepareHeader(hWave, #hdr, sizeof(hdr));
waveOutWrite(hWave, #hdr, sizeof(hdr));
sleep(500);
while waveOutUnprepareHeader(hWave, #hdr, sizeof(hdr)) = WAVERR_STILLPLAYING do
begin
sleep(100);
if Terminated then
waveOutReset(hWave);
end;
waveOutClose(hWave);
FreeMem(buf);
except
on E: Exception do MessageBox(0, PChar(E.ClassName + ': ' + E.Message), 'TSoundPlayerThread', MB_ICONERROR);
end;
finally
ThreadPlaying := false;
end;
end;
Wave Audio Package has TLiveAudioPlayer component. It plays audio from buffer.
The Win32 API PlaySound function can play standard RIFF-encoded audio (such as WAV audio) from a memory block by using its SND_MEMORY flag. Alternatively, if the audio is in the app's resources, you can use the SND_RESOURCE flag instead.
Microsoft has a Knowledge Base article telling you how you can play sound from memory using MCI. You'll probably need to have the wave file header in your array, or otherwise copy in the right data during the first read, but other than that it should be fairly easy to port over.
I couldn't find a complete solution that isn't based on the outdated sndPlaySound, so here are two functions that play ".wav" files from both a TMemoryStream and from a file :
uses mmsystem;
procedure PlaySoundFromFile(FileName : String);
var
mStream : TMemoryStream;
begin
mStream := TMemoryStream.Create;
Try mStream.LoadFromFile(FileName); Except End;
If mStream.Size > 0 then PlaySoundFromStream(mStream);
mStream.Free;
end;
procedure PlaySoundFromStream(mStream : TMemoryStream);
begin
PlaySound(mStream.Memory,0,SND_MEMORY or SND_SYNC);
end;
The sound is played synchronously and from memory, you can find the other PlaySound flags in the link on Remy's answer. If you switch to async playback, make sure to not clear the sound memory before playback ends.