Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected empty InStream #7329

Closed
fartwhif opened this issue Feb 21, 2023 · 4 comments
Closed

Unexpected empty InStream #7329

fartwhif opened this issue Feb 21, 2023 · 4 comments

Comments

@fartwhif
Copy link

fartwhif commented Feb 21, 2023

1. Describe the bug
When a procedure returns or assigns an InStream object via a locally instantiated and referenced Codeunit "Temp Blob" object the InStream object becomes unexpectedly empty after the procedure returns and the call completes.

2. To Reproduce

  1. Go to '...'
procedure TextToInStream("text": Text; base64encoded: Boolean): InStream
var
    tb: Codeunit "Temp Blob";
    outs: OutStream;
    ins: InStream;
    b64: Codeunit "Base64 Convert";
begin
    outs := tb.CreateOutStream();
    if (base64encoded) then begin
        b64.FromBase64("text", outs);
    end else begin
        outs.WriteText("text");
    end;
    ins := tb.CreateInStream();
    exit(ins);
end;
procedure MyFaultyCaller()
var
    b64pages: List of [Text];
    filNam: Text;
    insPage: InStream;
begin
    ...
    insPage := cUtil.TextToInStream(b64pages.Get(1), true);
    filNam := 'page 1.pdf';
    if (DownloadFromStream(insPage, 'pdf file of first page', '', '', filNam)) then begin  // RESULTANT FILE IS 0 BYTES
        Message(StrSubstNo('your browser downloaded the first page of the input PDF as file %1', filNam));
    end;
    ...
end;

A workaround instantiates and holds the reference to the Codeunit "Temp Blob" object at higher level while it uses an InStream from it. Worth noting: 1) I tried using VAR decoration for the InStream and that did not help. 2) The workaround works whether the Codeunit "Temp Blob" object is passed via VAR or it is returned.

procedure TextToTempBlob(txt: Text; VAR tb: Codeunit "Temp Blob"; base64encoded: Boolean)
var
    outs: OutStream;
    b64: Codeunit "Base64 Convert";
begin
    tb.CreateOutStream(outs);
    if (base64encoded) then begin
        b64.FromBase64(txt, outs);
    end else begin
        outs.WriteText(txt);
    end;
end;
procedure MyWorkaroundCaller()
var
    b64pages: List of [Text];
    filNam: Text;
    tb: Codeunit "Temp Blob";
begin
    ...
    cUtil.TextToTempBlob(b64pages.Get(1), tb, true);
    filNam := 'page 1.pdf';
    if (DownloadFromStream(tb.CreateInStream(), 'pdf file of first page', '', '', filNam)) then begin  // RESULTANT FILE IS >0 BYTES
        Message(StrSubstNo('your browser downloaded the first page of the input PDF as file %1', filNam));
    end;
    ...
end;

3. Expected behavior
The file downloaded via the browser is non-zero length.

4. Actual behavior
The file downloaded via the browser is zero length.

5. Versions:

  • AL Language: v10.4.747197
  • Visual Studio Code: 1.75.1 (user setup)
  • Business Central: 21.4.52563.52602
  • List of Visual Studio Code extensions that you have installed: only AL Language
@JMA1972
Copy link

JMA1972 commented Feb 22, 2023

That's not unexpected behaviour as the Blob you have created an instream for is only local and ceases to exist after returning the instream. There's nothing anymore for the instream to read from. Maybe it should give an runtime error instead of just returning nothing.

@dzzzb
Copy link

dzzzb commented Feb 22, 2023

Seems a dupe of #7172

I agree, the stream requires its backing to be 'alive', and I think that's quite intuitive. But anything MS could do to help diagnose this (e.g. runtime error: stream was opened against a backing that has now gone away) would avoid the frequent 'gotcha'.

@fartwhif
Copy link
Author

fartwhif commented Feb 22, 2023

I appreciate the confirmation. It has such a broad footprint that I was unsure whether or not this is a known limitation. Is there any other way to achieve this without protecting the lifecycle of the wrapper object, or to guard it in a more elegant way? Can the OutStream to InStream conversion happen without the wrapper object? I imagine the wrapper is supposed to set the cursor to 0 and cast to a read-only type. It would be excellent to be able to maintain encapsulation of this backing-store pinning concept at a lower level, or better yet: this might be a pipedream (no pun intended), but please expose MemoryStream as a native type that is capable of interoperability with legacy streams in SaaS.

@thloke
Copy link
Contributor

thloke commented Mar 14, 2023

This really needs to go to the runtime team, it's not something we can address in the AL language itself. You should do one of the following:

  • Open a support request to CSS through the PartnerSource portal
  • Open a bug in Collaborate
  • Contact your Service Account Manager (SAM) in your local subsidiary to understand what is included in your contract as of support incident and PAH (Partner Advisory Hours). Your SAM might also direct you "step by step" how to open a support request or how to get credentials, if this is the first time for you or your company

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants